Merge remote-tracking branch 'origin/main' into cg32/fix-9153

This commit is contained in:
Ludovic Marie committed 2026-11-04 16:39:58 +01:00
commit 5eef8152d8
619 files changed
+118456 -4158

No files matched your search

+2 -2
View File
@@ -868,7 +868,7 @@ Banners in End cities show as plain magenta instead of with their pattern.
<details><summary>Technical</summary>
Compare the banner block entities the End city templates place with vanilla's (`EndCityPieces`, the templates' block entity NBT, `BannerBlockEntity`'s patterns), in `crates/horizon-world/src/worldgen/structure/`. Issue [#1438](https://github.com/horizon-rust/horizon/issues/1438); possibly related: [#526](https://github.com/horizon-rust/horizon/issues/526).
Compare the banner block entities the End city templates place with vanilla's (`EndCityPieces`, the templates' block entity NBT, `BannerBlockEntity`'s patterns), in `crates/horizon-world/src/worldgen/structure/`. Issue [#1438](https://github.com/horizon-rust/horizon/issues/1438); possibly related: [#526](https://github.com/horizon-rust/horizon/issues/526). The templates' block entities now keep their patterns ([#1928](https://github.com/horizon-rust/horizon/issues/1928)).
</details>
@@ -878,7 +878,7 @@ Armour and tools in End city chests should come enchanted, as in vanilla.
<details><summary>Technical</summary>
`loot.rs` implements `enchant_with_levels`: find why the End city chest table's rolls come out bare, and compare them with vanilla's for a few seeds. Issues [#1437](https://github.com/horizon-rust/horizon/issues/1437), [#1660](https://github.com/horizon-rust/horizon/issues/1660).
`loot.rs` implements `enchant_with_levels`: find why the End city chest table's rolls come out bare, and compare them with vanilla's for a few seeds. Issues [#1437](https://github.com/horizon-rust/horizon/issues/1437), [#1660](https://github.com/horizon-rust/horizon/issues/1660). The loot's enchantment functions now reach every table that rolls ([#1911](https://github.com/horizon-rust/horizon/issues/1911), [#2037](https://github.com/horizon-rust/horizon/issues/2037)).
</details>
+2
View File
@@ -16,6 +16,8 @@ are in that folder (`LICENSE.MIT`, `LICENSE.APACHE`).
The GLSL frontend also resets the common type for each comma-separated declarator so one
variable's array specifier does not change later variables.
It also reads a struct member outside a constant expression through a pointer to the member,
not out of a loaded copy of the whole struct, so a member an atomic function uses can be read.
## libmimalloc-sys and mimalloc
+156
View File
@@ -160,6 +160,162 @@ impl ResourceSource for Bundled {
"assets/horizon/lang/el_gr.json" => {
include_str!("../../horizon-client/assets/horizon/lang/el_gr.json")
}
"assets/horizon/lang/ca_es.json" => {
include_str!("../../horizon-client/assets/horizon/lang/ca_es.json")
}
"assets/horizon/lang/et_ee.json" => {
include_str!("../../horizon-client/assets/horizon/lang/et_ee.json")
}
"assets/horizon/lang/lv_lv.json" => {
include_str!("../../horizon-client/assets/horizon/lang/lv_lv.json")
}
"assets/horizon/lang/sl_si.json" => {
include_str!("../../horizon-client/assets/horizon/lang/sl_si.json")
}
"assets/horizon/lang/sr_cs.json" => {
include_str!("../../horizon-client/assets/horizon/lang/sr_cs.json")
}
"assets/horizon/lang/is_is.json" => {
include_str!("../../horizon-client/assets/horizon/lang/is_is.json")
}
"assets/horizon/lang/af_za.json" => {
include_str!("../../horizon-client/assets/horizon/lang/af_za.json")
}
"assets/horizon/lang/ms_my.json" => {
include_str!("../../horizon-client/assets/horizon/lang/ms_my.json")
}
"assets/horizon/lang/be_by.json" => {
include_str!("../../horizon-client/assets/horizon/lang/be_by.json")
}
"assets/horizon/lang/sr_sp.json" => {
include_str!("../../horizon-client/assets/horizon/lang/sr_sp.json")
}
"assets/horizon/lang/az_az.json" => {
include_str!("../../horizon-client/assets/horizon/lang/az_az.json")
}
"assets/horizon/lang/uz_uz.json" => {
include_str!("../../horizon-client/assets/horizon/lang/uz_uz.json")
}
"assets/horizon/lang/kk_kz.json" => {
include_str!("../../horizon-client/assets/horizon/lang/kk_kz.json")
}
"assets/horizon/lang/ka_ge.json" => {
include_str!("../../horizon-client/assets/horizon/lang/ka_ge.json")
}
"assets/horizon/lang/hy_am.json" => {
include_str!("../../horizon-client/assets/horizon/lang/hy_am.json")
}
"assets/horizon/lang/bs_ba.json" => {
include_str!("../../horizon-client/assets/horizon/lang/bs_ba.json")
}
"assets/horizon/lang/mk_mk.json" => {
include_str!("../../horizon-client/assets/horizon/lang/mk_mk.json")
}
"assets/horizon/lang/sq_al.json" => {
include_str!("../../horizon-client/assets/horizon/lang/sq_al.json")
}
"assets/horizon/lang/cy_gb.json" => {
include_str!("../../horizon-client/assets/horizon/lang/cy_gb.json")
}
"assets/horizon/lang/ga_ie.json" => {
include_str!("../../horizon-client/assets/horizon/lang/ga_ie.json")
}
"assets/horizon/lang/eu_es.json" => {
include_str!("../../horizon-client/assets/horizon/lang/eu_es.json")
}
"assets/horizon/lang/gl_es.json" => {
include_str!("../../horizon-client/assets/horizon/lang/gl_es.json")
}
"assets/horizon/lang/fil_ph.json" => {
include_str!("../../horizon-client/assets/horizon/lang/fil_ph.json")
}
"assets/horizon/lang/ta_in.json" => {
include_str!("../../horizon-client/assets/horizon/lang/ta_in.json")
}
"assets/horizon/lang/mn_mn.json" => {
include_str!("../../horizon-client/assets/horizon/lang/mn_mn.json")
}
"assets/horizon/lang/lo_la.json" => {
include_str!("../../horizon-client/assets/horizon/lang/lo_la.json")
}
"assets/horizon/lang/so_so.json" => {
include_str!("../../horizon-client/assets/horizon/lang/so_so.json")
}
"assets/horizon/lang/fy_nl.json" => {
include_str!("../../horizon-client/assets/horizon/lang/fy_nl.json")
}
"assets/horizon/lang/gd_gb.json" => {
include_str!("../../horizon-client/assets/horizon/lang/gd_gb.json")
}
"assets/horizon/lang/lb_lu.json" => {
include_str!("../../horizon-client/assets/horizon/lang/lb_lu.json")
}
"assets/horizon/lang/ky_kg.json" => {
include_str!("../../horizon-client/assets/horizon/lang/ky_kg.json")
}
"assets/horizon/lang/kn_in.json" => {
include_str!("../../horizon-client/assets/horizon/lang/kn_in.json")
}
"assets/horizon/lang/mt_mt.json" => {
include_str!("../../horizon-client/assets/horizon/lang/mt_mt.json")
}
"assets/horizon/lang/eo_uy.json" => {
include_str!("../../horizon-client/assets/horizon/lang/eo_uy.json")
}
"assets/horizon/lang/haw_us.json" => {
include_str!("../../horizon-client/assets/horizon/lang/haw_us.json")
}
"assets/horizon/lang/ig_ng.json" => {
include_str!("../../horizon-client/assets/horizon/lang/ig_ng.json")
}
"assets/horizon/lang/la_la.json" => {
include_str!("../../horizon-client/assets/horizon/lang/la_la.json")
}
"assets/horizon/lang/tt_ru.json" => {
include_str!("../../horizon-client/assets/horizon/lang/tt_ru.json")
}
"assets/horizon/lang/yi_de.json" => {
include_str!("../../horizon-client/assets/horizon/lang/yi_de.json")
}
"assets/horizon/lang/yo_ng.json" => {
include_str!("../../horizon-client/assets/horizon/lang/yo_ng.json")
}
"assets/horizon/lang/ba_ru.json" => {
include_str!("../../horizon-client/assets/horizon/lang/ba_ru.json")
}
"assets/horizon/lang/br_fr.json" => {
include_str!("../../horizon-client/assets/horizon/lang/br_fr.json")
}
"assets/horizon/lang/cv_cu.json" => {
include_str!("../../horizon-client/assets/horizon/lang/cv_cu.json")
}
"assets/horizon/lang/fo_fo.json" => {
include_str!("../../horizon-client/assets/horizon/lang/fo_fo.json")
}
"assets/horizon/lang/fur_it.json" => {
include_str!("../../horizon-client/assets/horizon/lang/fur_it.json")
}
"assets/horizon/lang/li_li.json" => {
include_str!("../../horizon-client/assets/horizon/lang/li_li.json")
}
"assets/horizon/lang/lmo.json" => {
include_str!("../../horizon-client/assets/horizon/lang/lmo.json")
}
"assets/horizon/lang/oc_fr.json" => {
include_str!("../../horizon-client/assets/horizon/lang/oc_fr.json")
}
"assets/horizon/lang/sah_sah.json" => {
include_str!("../../horizon-client/assets/horizon/lang/sah_sah.json")
}
"assets/horizon/lang/se_no.json" => {
include_str!("../../horizon-client/assets/horizon/lang/se_no.json")
}
"assets/horizon/lang/szl.json" => {
include_str!("../../horizon-client/assets/horizon/lang/szl.json")
}
"assets/horizon/lang/vec_it.json" => {
include_str!("../../horizon-client/assets/horizon/lang/vec_it.json")
}
"assets/horizon/lang/de_de.json" | "assets/horizon/lang/de_at.json" => {
include_str!("../../horizon-client/assets/horizon/lang/de_de.json")
}
+182 -34
View File
@@ -79,28 +79,65 @@ fn elements(tag: &Tag) -> Option<Vec<Tag>> {
_ => return None,
})
}
fn number(t: &Tag) -> Option<i64> {
match t {
Tag::Byte(v) => Some(i64::from(*v)),
Tag::Short(v) => Some(i64::from(*v)),
Tag::Int(v) => Some(i64::from(*v)),
Tag::Long(v) => Some(*v),
_ => None,
/// `DoubleTag.intValue` / `FloatTag.intValue` floor before the i32 conversion;
/// integral tags use their intValue narrowing. Byte arrays then keep the low byte.
fn int_value(t: &Tag) -> Option<i32> {
Some(match t {
Tag::Byte(v) => i32::from(*v),
Tag::Short(v) => i32::from(*v),
Tag::Int(v) => *v,
Tag::Long(v) => *v as i32,
Tag::Float(v) => f32::from_bits(*v).floor() as i32,
Tag::Double(v) => f64::from_bits(*v).floor() as i32,
_ => return None,
})
}
/// `FloatTag.longValue` truncates; `DoubleTag.longValue` floors before narrowing.
fn long_value(t: &Tag) -> Option<i64> {
Some(match t {
Tag::Byte(v) => i64::from(*v),
Tag::Short(v) => i64::from(*v),
Tag::Int(v) => i64::from(*v),
Tag::Long(v) => *v,
// FloatTag.longValue truncates; DoubleTag.longValue floors first.
Tag::Float(v) => f32::from_bits(*v) as i64,
Tag::Double(v) => f64::from_bits(*v).floor() as i64,
_ => return None,
})
}
#[derive(Clone, Copy)]
enum ElementKind {
List,
NumericArray,
}
impl ElementKind {
// Admission only: arrays require NumericTag; lists accept every tag type.
fn accepts(self, value: &Tag) -> bool {
matches!(self, Self::List)
|| matches!(
value,
Tag::Byte(_)
| Tag::Short(_)
| Tag::Int(_)
| Tag::Long(_)
| Tag::Float(_)
| Tag::Double(_)
)
}
}
/// Run `f` over the elements of a list/array, writing array results back.
/// Rust view adapter: run `f` over the elements, then write arrays using their numeric accessor.
/// None when `tag` is not list-like.
fn with_elements<R>(tag: &mut Tag, f: impl FnOnce(&mut Vec<Tag>) -> R) -> Option<R> {
fn with_elements<R>(tag: &mut Tag, f: impl FnOnce(&mut Vec<Tag>, ElementKind) -> R) -> Option<R> {
match tag {
Tag::List(l) => Some(f(l)),
Tag::List(l) => Some(f(l, ElementKind::List)),
Tag::ByteArray(_) | Tag::IntArray(_) | Tag::LongArray(_) => {
let mut v = elements(tag)?;
let r = f(&mut v);
let nums: Vec<i64> = v.iter().filter_map(number).collect();
let r = f(&mut v, ElementKind::NumericArray);
match tag {
Tag::ByteArray(a) => *a = nums.iter().map(|n| *n as u8).collect(),
Tag::IntArray(a) => *a = nums.iter().map(|n| *n as i32).collect(),
Tag::LongArray(a) => *a = nums,
// Float/double byteValue uses intValue's floor, then narrows.
Tag::ByteArray(a) => *a = v.iter().filter_map(int_value).map(|n| n as u8).collect(),
Tag::IntArray(a) => *a = v.iter().filter_map(int_value).collect(),
Tag::LongArray(a) => *a = v.iter().filter_map(long_value).collect(),
_ => {}
}
Some(r)
@@ -349,7 +386,7 @@ impl NbtPath {
});
removed
}
/// `insert` (also append and prepend): into every list the path reaches.
/// `NbtPathArgument.NbtPath.insert`: insert accepted tags into every reached collection.
pub fn insert(
&self,
index: i32,
@@ -372,19 +409,23 @@ impl NbtPath {
return;
}
let shown = crate::snbt::to_snbt(target);
let result = with_elements(target, |list| {
let result = with_elements(target, |list, kind| {
let start = if index < 0 {
list.len() as i64 + i64::from(index) + 1
} else {
i64::from(index)
};
let mut any = false;
for (n, v) in values.iter().enumerate() {
let at = start + n as i64;
let mut at = start;
for v in values {
if !kind.accepts(v) {
continue;
}
if at < 0 || at as usize > list.len() {
return Err(CollectionError::InvalidIndex(at as i32));
}
list.insert(at as usize, v.clone());
at += 1;
any = true;
}
Ok(any)
@@ -428,7 +469,8 @@ fn preferred_parent(node: &Node) -> Tag {
_ => Tag::Compound(Compound::new()),
}
}
/// `getOrCreateTag` (or `getTag` without `create`): the children of `parent`.
/// Children of `parent`; the array creation arm follows
/// `NbtPathArgument.AllElementsNode.getOrCreateTag` admission.
fn step_mut(
node: &Node,
parent: &mut Tag,
@@ -468,11 +510,14 @@ fn step_mut(
}
Node::AllElements => {
let make = create;
let _ = with_elements(parent, |list| {
let _ = with_elements(parent, |list, kind| {
if list.is_empty() {
if let Some(make) = make {
list.push(make());
out(&mut list[0]);
let value = make();
if kind.accepts(&value) {
list.push(value);
out(&mut list[0]);
}
}
} else {
for t in list.iter_mut() {
@@ -482,7 +527,7 @@ fn step_mut(
});
}
Node::Index(i) => {
let _ = with_elements(parent, |list| {
let _ = with_elements(parent, |list, _kind| {
let n = list.len() as i64;
let idx = if *i < 0 {
n + i64::from(*i)
@@ -518,7 +563,8 @@ fn step_mut(
}
}
}
/// `Node.setTag`: replace the child(ren) of `parent` with `value`.
/// Element setters follow `NbtPathArgument.AllElementsNode.setTag` and
/// `NbtPathArgument.IndexedElementNode.setTag`; object nodes replace their children.
fn set_tag(node: &Node, parent: &mut Tag, value: &Tag) -> usize {
match node {
Node::Child(name) => {
@@ -538,29 +584,36 @@ fn set_tag(node: &Node, parent: &mut Tag, value: &Tag) -> usize {
}
0
}
Node::AllElements => with_elements(parent, |list| {
if list.is_empty() {
list.push(value.clone());
Node::AllElements => with_elements(parent, |list, kind| {
let len = list.len();
if len == 0 {
if kind.accepts(value) {
list.push(value.clone());
}
// AllElementsNode returns 1 here even when addTag rejects.
return 1;
}
// Compare the original tags, before numeric conversion.
let changed = list.iter().filter(|t| *t != value).count();
if changed == 0 {
return 0;
}
for t in list.iter_mut() {
*t = value.clone();
list.clear();
if !kind.accepts(value) {
return 0;
}
list.resize(len, value.clone());
changed
})
.unwrap_or(0),
Node::Index(i) => with_elements(parent, |list| {
Node::Index(i) => with_elements(parent, |list, kind| {
let n = list.len() as i64;
let idx = if *i < 0 {
n + i64::from(*i)
} else {
i64::from(*i)
};
if (0..n).contains(&idx) && list[idx as usize] != *value {
if (0..n).contains(&idx) && list[idx as usize] != *value && kind.accepts(value) {
list[idx as usize] = value.clone();
1
} else {
@@ -586,6 +639,7 @@ fn set_tag(node: &Node, parent: &mut Tag, value: &Tag) -> usize {
Node::MatchRoot(_) => 0,
}
}
/// Removal arms, including `NbtPathArgument.AllElementsNode.removeTag`.
fn remove_tag(node: &Node, parent: &mut Tag) -> usize {
match node {
Node::Child(name) => match parent {
@@ -603,13 +657,13 @@ fn remove_tag(node: &Node, parent: &mut Tag) -> usize {
}
_ => 0,
},
Node::AllElements => with_elements(parent, |l| {
Node::AllElements => with_elements(parent, |l, _kind| {
let n = l.len();
l.clear();
n
})
.unwrap_or(0),
Node::Index(i) => with_elements(parent, |l| {
Node::Index(i) => with_elements(parent, |l, _kind| {
let n = l.len() as i64;
let idx = if *i < 0 {
n + i64::from(*i)
@@ -887,6 +941,100 @@ mod tests {
));
}
#[test]
fn numeric_array_edits_use_the_destination_numeric_accessor() {
for (initial, value, inserted, indexed, all) in [
(
"{v:[B;1b,2b]}",
"-3.5f",
"{v:[B;1b,-4b,2b]}",
"{v:[B;-4b,2b]}",
"{v:[B;-4b,-4b]}",
),
(
"{v:[B;1b,2b]}",
"-3.5d",
"{v:[B;1b,-4b,2b]}",
"{v:[B;-4b,2b]}",
"{v:[B;-4b,-4b]}",
),
(
"{v:[I;1,2]}",
"-3.5f",
"{v:[I;1,-4,2]}",
"{v:[I;-4,2]}",
"{v:[I;-4,-4]}",
),
(
"{v:[I;1,2]}",
"-3.5d",
"{v:[I;1,-4,2]}",
"{v:[I;-4,2]}",
"{v:[I;-4,-4]}",
),
(
"{v:[L;1L,2L]}",
"-3.5f",
"{v:[L;1L,-3L,2L]}",
"{v:[L;-3L,2L]}",
"{v:[L;-3L,-3L]}",
),
(
"{v:[L;1L,2L]}",
"-3.5d",
"{v:[L;1L,-4L,2L]}",
"{v:[L;-4L,2L]}",
"{v:[L;-4L,-4L]}",
),
] {
let value = tag(value);
let mut root = tag(initial);
assert_eq!(
path("v")
.insert(1, &mut root, std::slice::from_ref(&value))
.unwrap(),
1
);
assert_eq!(root, tag(inserted));
let mut root = tag(initial);
assert_eq!(path("v[0]").set(&mut root, &value).unwrap(), 1);
assert_eq!(root, tag(indexed));
let mut root = tag(initial);
assert_eq!(path("v[]").set(&mut root, &value).unwrap(), 2);
assert_eq!(root, tag(all));
}
}
#[test]
fn numeric_arrays_reject_nonnumeric_insert_and_indexed_set() {
for initial in ["{v:[B;1b,2b]}", "{v:[I;1,2]}", "{v:[L;1L,2L]}"] {
let original = tag(initial);
let value = tag("\"bad\"");
for index in [0, 99, -99] {
let mut root = original.clone();
assert_eq!(
path("v")
.insert(index, &mut root, std::slice::from_ref(&value))
.unwrap(),
0
);
assert_eq!(root, original);
}
let mut root = original.clone();
assert_eq!(path("v[0]").set(&mut root, &value).unwrap(), 0);
assert_eq!(root, original);
}
}
#[test]
fn empty_array_wildcard_set_preserves_vanillas_count_on_rejection() {
for initial in ["{v:[B;]}", "{v:[I;]}", "{v:[L;]}"] {
let mut root = tag(initial);
assert_eq!(path("v[]").set(&mut root, &tag("\"bad\"")).unwrap(), 1);
assert_eq!(root, tag(initial));
}
}
#[test]
fn partial_comparison() {
let actual = tag("{a:1,b:{c:[1,2,3],d:\"x\"},e:2}");
@@ -173,6 +173,16 @@ impl Registries {
fill(&mut self.templates, "templates");
fill(&mut self.persistent_components, "persistent_components");
fill(&mut self.learnable_recipes, "learnable_recipes");
if self.summonable.is_empty() {
self.summonable = strings(&r["summonable"]);
self.summonable.sort();
self.summonable.dedup();
}
if self.entity_types.is_empty() {
self.entity_types = strings(&r["entity_types"]);
self.entity_types.sort();
self.entity_types.dedup();
}
const RELOADED: [&str; 5] = [
"minecraft:advancement",
"minecraft:recipe",
@@ -236,6 +246,16 @@ impl Registries {
fill(&mut self.templates, &e.templates);
fill(&mut self.persistent_components, &e.persistent_components);
fill(&mut self.learnable_recipes, &e.learnable_recipes);
if self.summonable.is_empty() {
self.summonable.clone_from(&e.summonable);
self.summonable.sort();
self.summonable.dedup();
}
if self.entity_types.is_empty() {
self.entity_types.clone_from(&e.entity_types);
self.entity_types.sort();
self.entity_types.dedup();
}
for (name, ids) in &e.by_registry {
if !self.by_registry.contains_key(name) {
self.by_registry.insert(name.clone(), ids.clone());
+237 -39
View File
@@ -179,12 +179,13 @@ impl PadState {
#[derive(Clone, Debug, Default)]
pub struct Edges {
held: u32,
suppressed: u32,
}
impl Edges {
pub fn update(&mut self, state: &PadState, threshold: f32) -> Vec<(Button, bool)> {
let mut now = state.buttons;
for (trigger, button) in [(0, Button::LeftTrigger), (1, Button::RightTrigger)] {
let was = self.held & button.bit() != 0;
let was = (self.held | self.suppressed) & button.bit() != 0;
let value = state.triggers[trigger];
let on = if was {
value >= (threshold - 0.1).max(0.05)
@@ -197,6 +198,8 @@ impl Edges {
now &= !button.bit();
}
}
self.suppressed &= now;
now &= !self.suppressed;
let changed = now ^ self.held;
self.held = now;
Button::ALL
@@ -217,6 +220,50 @@ impl Edges {
pub fn is_held(&self, button: Button) -> bool {
self.held & button.bit() != 0
}
/// Cancel a context without turning its still-held buttons into new presses.
pub fn cancel(&mut self) -> Vec<(Button, bool)> {
self.suppressed |= self.held;
self.release_all()
}
}
/// Intentional activity may take input ownership; an unchanged hold may not.
#[derive(Default)]
pub struct Activity {
buttons: u32,
sticks: [[f32; 2]; 2],
}
impl Activity {
pub fn observe(&mut self, state: &PadState, settings: &PadSettings) -> bool {
let mut buttons = state.buttons;
for (i, button) in [(0, Button::LeftTrigger), (1, Button::RightTrigger)] {
let threshold = if self.buttons & button.bit() != 0 {
(settings.trigger_threshold - 0.1).max(0.05)
} else {
settings.trigger_threshold
};
if state.triggers[i] >= threshold {
buttons |= button.bit();
}
}
let mut activity = buttons & !self.buttons != 0;
self.buttons = buttons;
for (i, (stick, deadzone)) in [
(state.left, settings.move_deadzone),
(state.right, settings.look_deadzone),
]
.into_iter()
.enumerate()
{
if length(stick) <= deadzone {
self.sticks[i] = [0.0; 2];
} else if length([stick[0] - self.sticks[i][0], stick[1] - self.sticks[i][1]]) > 0.08 {
activity = true;
self.sticks[i] = stick;
}
}
activity
}
}
/// Horizon's pad-only actions (no vanilla key mapping).
@@ -248,6 +295,39 @@ pub const ACTIONS: &[(&str, Option<Button>)] = &[
("key.smoothCamera", None),
];
/// How a bindable action reaches the game: one of its own handlers, shared
/// with the keyboard where the keyboard has one, or else the vanilla default
/// key or button of its mapping, as a rebound keyboard key would.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Route {
HotbarStep {
next: bool,
},
/// Held, or flipped when the pad's toggle sneak is on.
Sneak,
Screenshot,
SmoothCamera,
Key(winit::keyboard::KeyCode),
Mouse(winit::event::MouseButton),
}
/// The route of a bindable action; `None` for one the pad can't reach.
pub fn route(action: &str) -> Option<Route> {
match action {
HOTBAR_PREVIOUS => Some(Route::HotbarStep { next: false }),
HOTBAR_NEXT => Some(Route::HotbarStep { next: true }),
"key.sneak" => Some(Route::Sneak),
"key.screenshot" => Some(Route::Screenshot),
"key.smoothCamera" => Some(Route::SmoothCamera),
_ => {
let default = crate::keybinds::KeyBinds::default_of(action);
crate::keybinds::mouse_button(default)
.map(Route::Mouse)
.or_else(|| crate::keybinds::key_code(default).map(Route::Key))
}
}
}
/// An action's name in the binds list: vanilla's translation key, or
/// Horizon's own English for the pad-only actions.
pub fn action_caption(action: &str) -> &str {
@@ -313,31 +393,6 @@ impl GlyphStyle {
}
}
/// How a controller drives menus (docs/controllers.md, "Menus and
/// inventories").
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum MenuNav {
/// A console-style highlight moved between widgets and slots.
#[default]
Focus,
/// A virtual mouse pointer.
Pointer,
}
impl MenuNav {
pub fn name(self) -> &'static str {
match self {
MenuNav::Focus => "focus",
MenuNav::Pointer => "pointer",
}
}
pub fn parse(name: &str) -> Self {
match name {
"pointer" => MenuNav::Pointer,
_ => MenuNav::Focus,
}
}
}
/// The on-screen keyboard A opens on a text field in focus mode.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum TextKeyboard {
@@ -399,9 +454,12 @@ pub struct PadSettings {
/// holding it, unless vanilla's toggle crouch is on already.
pub toggle_sneak: bool,
pub glyphs: GlyphStyle,
/// How the pad drives menus: a focus highlight or a virtual pointer.
pub menu_nav: MenuNav,
pub keyboard: TextKeyboard,
/// Menu pointer multiplier, scroll lines/second, and repeat times in seconds.
pub pointer_speed: f32,
pub scroll_speed: f32,
pub repeat_delay: f32,
pub repeat_interval: f32,
/// Action name -> button, `None` for unbound; missing means the default.
bindings: BTreeMap<&'static str, Option<Button>>,
}
@@ -420,8 +478,11 @@ impl Default for PadSettings {
trigger_threshold: 0.4,
toggle_sneak: true,
glyphs: GlyphStyle::Auto,
menu_nav: MenuNav::Focus,
keyboard: TextKeyboard::Auto,
pointer_speed: 1.0,
scroll_speed: 12.0,
repeat_delay: 0.35,
repeat_interval: 0.09,
bindings: BTreeMap::new(),
}
}
@@ -462,6 +523,13 @@ impl PadSettings {
.filter(|v| v.is_finite())
.map_or(d, |v| v.clamp(0.0, 1.0))
};
let bounded = |default: f32, min: f32, max: f32| {
value
.parse::<f32>()
.ok()
.filter(|v| v.is_finite())
.map_or(default, |v| v.clamp(min, max))
};
match key.trim() {
"enabled" => s.enabled = flag(s.enabled),
"vibration" => s.vibration = flag(s.vibration),
@@ -477,8 +545,11 @@ impl PadSettings {
}
"toggleSneak" => s.toggle_sneak = flag(s.toggle_sneak),
"glyphs" => s.glyphs = GlyphStyle::parse(value),
"menuNavigation" => s.menu_nav = MenuNav::parse(value),
"keyboard" => s.keyboard = TextKeyboard::parse(value),
"pointerSpeed" => s.pointer_speed = bounded(s.pointer_speed, 0.25, 3.0),
"scrollSpeed" => s.scroll_speed = bounded(s.scroll_speed, 1.0, 36.0),
"repeatDelay" => s.repeat_delay = bounded(s.repeat_delay, 0.1, 1.0),
"repeatInterval" => s.repeat_interval = bounded(s.repeat_interval, 0.03, 0.3),
other => {
if let Some(action) = other.strip_prefix("bind_") {
let button = if value == "none" {
@@ -497,7 +568,7 @@ impl PadSettings {
}
pub fn serialize(&self) -> String {
let mut text = format!(
"enabled:{}\nvibration:{}\nadaptiveTriggers:{}\nlightbar:{}\nlookSensitivity:{}\ninvertX:{}\ninvertY:{}\nmoveDeadzone:{}\nlookDeadzone:{}\ntriggerThreshold:{}\ntoggleSneak:{}\nglyphs:{}\nmenuNavigation:{}\nkeyboard:{}\n",
"enabled:{}\nvibration:{}\nadaptiveTriggers:{}\nlightbar:{}\nlookSensitivity:{}\ninvertX:{}\ninvertY:{}\nmoveDeadzone:{}\nlookDeadzone:{}\ntriggerThreshold:{}\ntoggleSneak:{}\nglyphs:{}\nkeyboard:{}\n",
bool_text(self.enabled),
bool_text(self.vibration),
bool_text(self.adaptive_triggers),
@@ -510,9 +581,12 @@ impl PadSettings {
self.trigger_threshold,
bool_text(self.toggle_sneak),
self.glyphs.name(),
self.menu_nav.name(),
self.keyboard.name(),
);
text.push_str(&format!(
"pointerSpeed:{}\nscrollSpeed:{}\nrepeatDelay:{}\nrepeatInterval:{}\n",
self.pointer_speed, self.scroll_speed, self.repeat_delay, self.repeat_interval,
));
for (action, button) in &self.bindings {
text.push_str(&format!(
"bind_{action}:{}\n",
@@ -1163,6 +1237,131 @@ pub fn bow_power(ticks: u32) -> f32 {
mod tests {
use super::*;
#[test]
fn every_bindable_action_reaches_the_game() {
for (action, _) in ACTIONS {
assert!(route(action).is_some(), "{action} has nowhere to go");
}
}
#[test]
fn navigation_preferences_migrate_and_stay_with_each_player() {
let dir = std::env::temp_dir().join(format!(
"horizon-pad-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
let p2 = dir.join("P2");
fs::create_dir_all(&p2).unwrap();
let fresh = PadSettings::load(&dir);
assert_eq!(fresh, PadSettings::default());
// An older file, with the removed menuNavigation key: bindings kept.
let legacy =
PadSettings::parse("menuNavigation:pointer\nbind_key.jump:east\nlookSensitivity:0.7\n");
assert_eq!(legacy.binding("key.jump"), Some(Button::East));
assert_eq!(legacy.look_sensitivity, 0.7);
fresh.save(&dir);
assert!(!dir.join(FILE).exists(), "defaults write no file");
let mut other = legacy;
other.pointer_speed = 2.5;
other.scroll_speed = 24.0;
other.repeat_delay = 0.6;
other.repeat_interval = 0.12;
other.save(&p2);
assert_eq!(PadSettings::load(&p2), other);
assert_eq!(PadSettings::load(&dir), fresh, "P2 did not write P1");
let invalid = PadSettings::parse(
"pointerSpeed:NaN\nscrollSpeed:inf\nrepeatDelay:-10\nrepeatInterval:20\n",
);
assert_eq!((invalid.pointer_speed, invalid.scroll_speed), (1.0, 12.0));
assert_eq!((invalid.repeat_delay, invalid.repeat_interval), (0.1, 0.3));
let bounded = PadSettings::parse(
"pointerSpeed:10\nscrollSpeed:-1\nrepeatDelay:NaN\nrepeatInterval:inf\n",
);
assert_eq!((bounded.pointer_speed, bounded.scroll_speed), (3.0, 1.0));
assert_eq!(
(bounded.repeat_delay, bounded.repeat_interval),
(0.35, 0.09)
);
fs::remove_dir_all(dir).unwrap();
}
#[test]
fn ownership_requires_new_activity_and_ignores_drift() {
let settings = PadSettings::default();
let mut p1 = Activity::default();
let mut p2 = Activity::default();
let mut state = PadState {
left: [0.04, 0.03],
..PadState::default()
};
assert!(!p1.observe(&state, &settings));
state.set(Button::South, true);
assert!(p1.observe(&state, &settings));
assert!(
!p1.observe(&state, &settings),
"held pad cannot reclaim mouse ownership"
);
assert!(
p2.observe(&state, &settings),
"another player's tracker is independent"
);
state.set(Button::South, false);
assert!(!p1.observe(&state, &settings));
state.left = [0.5, 0.0];
assert!(p1.observe(&state, &settings));
state.left = [0.52, 0.0];
assert!(!p1.observe(&state, &settings));
for x in [0.54, 0.56] {
state.left = [x, 0.0];
assert!(!p1.observe(&state, &settings));
}
state.left = [0.6, 0.0];
assert!(
p1.observe(&state, &settings),
"slow intentional movement accumulates"
);
state.left = [0.0; 2];
state.triggers[0] = 0.5;
assert!(p1.observe(&state, &settings));
state.triggers[0] = 0.35;
assert!(!p1.observe(&state, &settings));
state.triggers[0] = 0.45;
assert!(
!p1.observe(&state, &settings),
"trigger threshold noise is not new activity"
);
}
#[test]
fn cancelled_holds_stay_released_until_neutral_then_press_again() {
let mut edges = Edges::default();
let mut state = PadState::default();
state.set(Button::South, true);
state.triggers[1] = 0.8;
assert_eq!(
edges.update(&state, 0.4),
vec![(Button::South, true), (Button::RightTrigger, true)]
);
assert_eq!(
edges.cancel(),
vec![(Button::South, false), (Button::RightTrigger, false)]
);
assert!(edges.update(&state, 0.4).is_empty());
state.triggers[1] = 0.35;
assert!(edges.update(&state, 0.4).is_empty());
assert!(edges.update(&PadState::default(), 0.4).is_empty());
assert_eq!(edges.update(&state, 0.4), vec![(Button::South, true)]);
state.triggers[1] = 0.7;
assert_eq!(
edges.update(&state, 0.4),
vec![(Button::RightTrigger, true)]
);
}
#[test]
fn defaults_follow_bedrock() {
let s = PadSettings::default();
@@ -1414,14 +1613,13 @@ mod tests {
assert_eq!(stick_direction([0.9, 0.2], 0.5), Some(Button::DPadRight));
assert_eq!(stick_direction([-0.2, -0.8], 0.5), Some(Button::DPadDown));
assert_eq!(stick_direction([0.3, 0.3], 0.5), None);
let mut s = PadSettings::default();
assert_eq!(s.menu_nav, MenuNav::Focus);
s.menu_nav = MenuNav::Pointer;
s.keyboard = TextKeyboard::Horizon;
let back = PadSettings::parse(&s.serialize());
let s = PadSettings {
keyboard: TextKeyboard::Horizon,
..PadSettings::default()
};
assert_eq!(
(back.menu_nav, back.keyboard),
(MenuNav::Pointer, TextKeyboard::Horizon)
PadSettings::parse(&s.serialize()).keyboard,
TextKeyboard::Horizon
);
assert!(steam_keyboard(TextKeyboard::Steam) && !steam_keyboard(TextKeyboard::Horizon));
}
+155
View File
@@ -0,0 +1,155 @@
//! The keys a player's mappings hold, as their vanilla default keys, and which
//! devices hold each one. A key stays held while any device holds it, so one
//! device letting go (splitscreen moving the keyboard to another player, say)
//! can't release what another still presses.
use std::collections::HashMap;
use winit::keyboard::KeyCode;
/// Where a press came from.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Source {
/// The keyboard and mouse, and the scripting socket acting as them.
Keyboard,
Pad,
}
impl Source {
fn bit(self) -> u8 {
match self {
Source::Keyboard => 1,
Source::Pad => 2,
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct HeldKeys {
held: HashMap<KeyCode, u8>,
}
impl HeldKeys {
pub fn contains(&self, key: &KeyCode) -> bool {
self.held.contains_key(key)
}
/// `source` holds `key`. True when nothing held it before.
pub fn press(&mut self, key: KeyCode, source: Source) -> bool {
let sources = self.held.entry(key).or_insert(0);
let new = *sources == 0;
*sources |= source.bit();
new
}
/// `source` lets go of `key`. True when nothing holds it any more.
pub fn release(&mut self, key: &KeyCode, source: Source) -> bool {
let Some(sources) = self.held.get_mut(key) else {
return true;
};
*sources &= !source.bit();
if *sources == 0 {
self.held.remove(key);
true
} else {
false
}
}
pub fn held_by(&self, key: &KeyCode, source: Source) -> bool {
self.held
.get(key)
.is_some_and(|sources| sources & source.bit() != 0)
}
/// `source` lets go of everything: the keys nothing holds any more.
pub fn release_source(&mut self, source: Source) -> Vec<KeyCode> {
let keys: Vec<KeyCode> = self
.held
.iter()
.filter(|(_, sources)| *sources & source.bit() != 0)
.map(|(key, _)| *key)
.collect();
keys.into_iter()
.filter(|key| self.release(key, source))
.collect()
}
/// Held, by the keyboard unless a device already holds it: for callers
/// that don't say where the press came from.
pub fn insert(&mut self, key: KeyCode) -> bool {
if self.contains(&key) {
return false;
}
self.press(key, Source::Keyboard)
}
/// Let go of `key` whoever holds it.
pub fn remove(&mut self, key: &KeyCode) -> bool {
self.held.remove(key).is_some()
}
pub fn clear(&mut self) {
self.held.clear();
}
pub fn is_empty(&self) -> bool {
self.held.is_empty()
}
pub fn len(&self) -> usize {
self.held.len()
}
pub fn iter(&self) -> impl Iterator<Item = &KeyCode> {
self.held.keys()
}
}
impl IntoIterator for HeldKeys {
type Item = KeyCode;
type IntoIter = std::collections::hash_map::IntoKeys<KeyCode, u8>;
fn into_iter(self) -> Self::IntoIter {
self.held.into_keys()
}
}
impl<'a> IntoIterator for &'a HeldKeys {
type Item = &'a KeyCode;
type IntoIter = std::collections::hash_map::Keys<'a, KeyCode, u8>;
fn into_iter(self) -> Self::IntoIter {
self.held.keys()
}
}
impl FromIterator<KeyCode> for HeldKeys {
fn from_iter<I: IntoIterator<Item = KeyCode>>(keys: I) -> Self {
let mut held = HeldKeys::default();
for key in keys {
held.insert(key);
}
held
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_key_stays_held_while_any_device_holds_it() {
let mut keys = HeldKeys::default();
assert!(keys.press(KeyCode::Space, Source::Pad));
assert!(!keys.press(KeyCode::Space, Source::Keyboard));
assert!(keys.press(KeyCode::KeyW, Source::Keyboard));
assert_eq!(keys.release_source(Source::Keyboard), vec![KeyCode::KeyW]);
assert!(keys.contains(&KeyCode::Space) && !keys.contains(&KeyCode::KeyW));
assert!(keys.held_by(&KeyCode::Space, Source::Pad));
assert!(!keys.held_by(&KeyCode::Space, Source::Keyboard));
assert!(keys.release(&KeyCode::Space, Source::Pad));
assert!(keys.is_empty());
}
#[test]
fn untagged_inserts_and_removes_act_as_before() {
let mut keys = HeldKeys::default();
keys.press(KeyCode::ShiftLeft, Source::Pad);
assert!(!keys.insert(KeyCode::ShiftLeft), "already held");
assert!(!keys.held_by(&KeyCode::ShiftLeft, Source::Keyboard));
assert!(
keys.remove(&KeyCode::ShiftLeft),
"remove lets go for everyone"
);
assert!(!keys.contains(&KeyCode::ShiftLeft));
assert!(keys.release(&KeyCode::KeyA, Source::Keyboard), "never held");
}
}
+2
View File
@@ -5,6 +5,7 @@
#![forbid(unsafe_code)]
pub mod clipboard;
pub mod gamepad;
pub mod held;
pub mod input;
pub mod interaction_input;
pub mod key_names;
@@ -12,4 +13,5 @@ pub mod keybinds;
pub mod keymapping;
pub mod modifiers;
pub mod mouse;
pub mod snap;
pub mod window;
+152
View File
@@ -0,0 +1,152 @@
//! Where the controller's menu cursor settles once the stick is let go, and
//! how it gets there. Every rule's number lives in [`Tuning`], so the feel can
//! be adjusted in one place.
/// The snapping engine's knobs, in GUI pixels and seconds.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Tuning {
/// A let-go cursor settles on a target this close to it.
pub radius: f32,
/// How long the glide onto a target takes.
pub glide_seconds: f32,
/// Movement shorter than this is a wobble: the cursor stays where it was.
pub min_travel: f32,
/// A flick that ends still nearest its starting target commits to the
/// neighbour it was heading for once it covered this fraction of the way.
pub commit: f32,
/// How far off the flick's direction a neighbour may lie (cosine of the
/// angle: 0.7 is about 45 degrees).
pub cone: f32,
/// The pointer's speed over a slot or control, as a fraction of its
/// speed elsewhere (1 leaves it unchanged).
pub over_target_speed: f32,
}
pub const TUNING: Tuning = Tuning {
radius: 16.0,
glide_seconds: 0.025,
min_travel: 1.5,
commit: 0.15,
cone: 0.7,
over_target_speed: 1.0,
};
type Point = (f32, f32);
fn distance(a: Point, b: Point) -> f32 {
((a.0 - b.0).powi(2) + (a.1 - b.1).powi(2)).sqrt()
}
/// The target nearest `at`, if one lies within `radius`.
pub fn nearest(at: Point, targets: &[Point], radius: f32) -> Option<usize> {
targets
.iter()
.enumerate()
.map(|(i, t)| (i, distance(at, *t)))
.filter(|(_, d)| *d <= radius)
.min_by(|a, b| a.1.total_cmp(&b.1))
.map(|(i, _)| i)
}
/// Where a stick movement from `start` to `end` settles among `targets`
/// (their centres), or `None` to leave the cursor where it is.
pub fn settle(start: Point, end: Point, targets: &[Point], tuning: &Tuning) -> Option<usize> {
let near = nearest(end, targets, tuning.radius);
let travel = (end.0 - start.0, end.1 - start.1);
let length = distance(start, end);
if length < tuning.min_travel {
return near;
}
let origin = nearest(start, targets, tuning.radius);
if near != origin {
return near;
}
// Still nearest where it started: a quick flick toward a neighbour.
let Some(o) = origin else {
return near;
};
let from = targets[o];
let heading = (travel.0 / length, travel.1 / length);
let next = targets
.iter()
.enumerate()
.filter(|(i, _)| *i != o)
.filter_map(|(i, t)| {
let to = (t.0 - from.0, t.1 - from.1);
let gap = distance(from, *t);
let aligned = (to.0 * heading.0 + to.1 * heading.1) / gap;
(gap > 0.0 && aligned >= tuning.cone).then_some((i, gap, to))
})
.min_by(|a, b| a.1.total_cmp(&b.1));
match next {
Some((i, gap, to)) => {
let progress = ((end.0 - from.0) * to.0 + (end.1 - from.1) * to.1) / (gap * gap);
if progress >= tuning.commit {
Some(i)
} else {
near
}
}
None => near,
}
}
/// The cursor `seconds` into a glide from `from` to `to`: eased out, so it
/// slows as it lands, and exactly `to` once the glide is over.
pub fn glide(from: Point, to: Point, seconds: f32, tuning: &Tuning) -> Point {
let t = (seconds / tuning.glide_seconds).clamp(0.0, 1.0);
let eased = 1.0 - (1.0 - t).powi(3);
(
from.0 + (to.0 - from.0) * eased,
from.1 + (to.1 - from.1) * eased,
)
}
#[cfg(test)]
mod tests {
use super::*;
/// A row of inventory slots, 18 pixels apart, and the slot below the first.
const SLOTS: [Point; 4] = [(8.0, 8.0), (26.0, 8.0), (44.0, 8.0), (8.0, 26.0)];
#[test]
fn a_short_flick_moves_to_the_neighbour_it_heads_for() {
// 4 pixels right of slot 0: still nearest slot 0, but heading for slot 1.
assert_eq!(settle((8.0, 8.0), (12.0, 8.0), &SLOTS, &TUNING), Some(1));
assert_eq!(settle((8.0, 8.0), (8.0, 12.0), &SLOTS, &TUNING), Some(3));
assert_eq!(settle((26.0, 8.0), (22.0, 8.0), &SLOTS, &TUNING), Some(0));
}
#[test]
fn a_wobble_stays_put() {
assert_eq!(settle((8.0, 8.0), (8.5, 8.4), &SLOTS, &TUNING), Some(0));
// Barely started toward the neighbour: not committed.
assert_eq!(settle((8.0, 8.0), (9.6, 8.0), &SLOTS, &TUNING), Some(0));
}
#[test]
fn a_long_move_settles_on_the_nearest_target() {
assert_eq!(settle((8.0, 8.0), (42.0, 9.0), &SLOTS, &TUNING), Some(2));
// Far from everything: left where it is.
assert_eq!(settle((8.0, 8.0), (200.0, 200.0), &SLOTS, &TUNING), None);
}
#[test]
fn a_flick_off_any_row_does_not_jump_sideways() {
// Up from slot 0: nothing lies that way, so it stays on slot 0.
assert_eq!(settle((8.0, 8.0), (8.0, 4.0), &SLOTS, &TUNING), Some(0));
}
#[test]
fn the_glide_eases_onto_its_target_and_stops_there() {
let (from, to) = ((0.0, 0.0), (20.0, -10.0));
assert_eq!(glide(from, to, 0.0, &TUNING), from);
let half = glide(from, to, TUNING.glide_seconds / 2.0, &TUNING);
assert!(
half.0 > 10.0 && half.0 < 20.0,
"eased out: past halfway at half time"
);
assert_eq!(glide(from, to, TUNING.glide_seconds, &TUNING), to);
assert_eq!(glide(from, to, 1.0, &TUNING), to);
}
}
@@ -68,7 +68,7 @@ fn fixture(window: Arc<Window>) -> Result<(), Box<dyn std::error::Error>> {
true,
))?;
assert!(!renderer.pack_entity_shadows(), "no selected pack");
renderer.set_shader_pack(0, None, &[]);
renderer.set_shader_pack(0, None, &[]).unwrap();
assert!(
!renderer.pack_entity_shadows(),
"selected pack without a compiled post"
@@ -165,6 +165,7 @@ fn fixture(window: Arc<Window>) -> Result<(), Box<dyn std::error::Error>> {
}
let (_, notes) = renderer
.set_shader_pack(dimension, Some(post), &[])
.unwrap()
.unwrap();
if break_pipeline {
assert!(
@@ -190,8 +190,9 @@ fn replay(window: Arc<Window>) -> Result<(), Box<dyn std::error::Error>> {
}
let one = [255u8; 4];
let zero = [0u8; 4];
let pixels = render.pixels.get();
let prepared = PreparedTextures::new(
&render.pixels,
&pixels,
render.sprite_size,
render.texture_layers,
&render.sprite_mips,
@@ -206,7 +207,7 @@ fn replay(window: Arc<Window>) -> Result<(), Box<dyn std::error::Error>> {
let mut renderer = pollster::block_on(Renderer::new(
window,
true,
&render.pixels,
&pixels,
render.sprite_size,
render.sprite_minimum_size,
render.texture_layers,
@@ -224,6 +225,8 @@ fn replay(window: Arc<Window>) -> Result<(), Box<dyn std::error::Error>> {
false,
false,
))?;
drop(pixels);
render.pixels.pack();
let state = &scene.renderer;
let [width, height] = o.size.unwrap_or(state.size);
renderer.resize(width, height);
@@ -79,7 +79,10 @@ fn pack(renderer: &mut Renderer, folder: &Path, valid: bool) {
let (properties, _) = horizon_shaders::read_properties(&pack, &mut macros);
let post = horizon_shaders::load_post(&pack, "", &properties, &macros, &renderer.pack_inputs());
assert_eq!(post.notes.is_empty(), valid, "{:?}", post.notes);
let (_, notes) = renderer.set_shader_pack(0, Some(post), &[]).unwrap();
let (_, notes) = renderer
.set_shader_pack(0, Some(post), &[])
.unwrap()
.unwrap();
assert!(
!valid || notes.iter().all(|n| !n.contains("skipped")),
"{notes:?}"
@@ -378,6 +381,7 @@ fn fixture_frame(hud: &HudBatch) -> Frame<'_> {
celestial: &[],
stars: None,
hand: &[],
hand_items: &[],
hand_fov_degrees: 70.0,
screen: &[],
pack: Default::default(),
+99 -2
View File
@@ -84,7 +84,9 @@ pub struct Assets {
pub struct RenderAssets {
pub sprite_size: u32,
pub sprite_minimum_size: u32,
pub pixels: Vec<u8>,
/// The block sprites' RGBA, layer by layer: as built until a renderer has
/// them, then kept compressed ([`SpritePixels`]).
pub pixels: SpritePixels,
pub texture_layers: u32,
pub model_catalog: crate::models::StateModels,
/// Shared parsed resources; item-only validation errors surface at item preparation.
@@ -111,6 +113,76 @@ pub struct RenderAssets {
/// sprites are when it changes (`native_block_sprites_version`).
pub generation: u64,
}
/// The block sprites' pixels for a renderer to upload. Once a renderer has
/// them ([`Self::pack`]) they are kept lz4-compressed: the GPU holds the
/// texture array, and the copy only serves a renderer made again (another
/// window, a lost device, another graphics API), which waits for
/// [`Self::get`] to uncompress them. Shared by the window and the workers'
/// `RenderAssets`, so the compressed form replaces the raw one in place.
pub struct SpritePixels(Arc<PixelStore>);
struct PixelStore {
len: usize,
/// Set by the first `pack`: later calls cost one atomic read.
packing: std::sync::atomic::AtomicBool,
/// As built; dropped once `packed` holds them.
raw: std::sync::Mutex<Option<Arc<Vec<u8>>>>,
packed: std::sync::OnceLock<Vec<u8>>,
}
impl SpritePixels {
pub fn new(pixels: Vec<u8>) -> Self {
Self(Arc::new(PixelStore {
len: pixels.len(),
packing: std::sync::atomic::AtomicBool::new(false),
raw: std::sync::Mutex::new(Some(Arc::new(pixels))),
packed: std::sync::OnceLock::new(),
}))
}
/// Their size in bytes, uncompressed.
pub fn len(&self) -> usize {
self.0.len
}
pub fn is_empty(&self) -> bool {
self.0.len == 0
}
/// The pixels as built, uncompressed again once packed.
pub fn get(&self) -> Arc<Vec<u8>> {
let raw = self.0.raw.lock().unwrap_or_else(|e| e.into_inner()).clone();
raw.unwrap_or_else(|| {
let packed = self.0.packed.get().map(Vec::as_slice).unwrap_or_default();
Arc::new(
lz4_flex::decompress_size_prepended(packed)
.ok()
.filter(|p| p.len() == self.0.len)
.unwrap_or_else(|| vec![0; self.0.len]),
)
})
}
/// Keep them compressed from now on, once a renderer has uploaded them:
/// compressed on a thread of their own, the raw copy dropped after.
pub fn pack(&self) {
use std::sync::atomic::Ordering;
if self.0.len == 0 || self.0.packing.swap(true, Ordering::Relaxed) {
return;
}
let store = self.0.clone();
let job = move || {
let raw = store.raw.lock().unwrap_or_else(|e| e.into_inner()).clone();
if let Some(raw) = raw {
let _ = store.packed.set(lz4_flex::compress_prepend_size(&raw));
// `get` reads the compressed copy from here on.
*store.raw.lock().unwrap_or_else(|e| e.into_inner()) = None;
}
};
let fallback = job.clone();
if std::thread::Builder::new()
.name("Sprite packing".into())
.spawn(job)
.is_err()
{
fallback();
}
}
}
/// The last `RenderAssets::generation` handed out.
static GENERATION: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
fn invalid(message: &str) -> io::Error {
@@ -333,7 +405,7 @@ impl RenderAssets {
sprite_size: sprites.size,
sprite_minimum_size: sprites.minimum_size,
texture_layers: u32::try_from(sprites.count())?,
pixels: sprites.pixels,
pixels: SpritePixels::new(sprites.pixels),
model_catalog: models.catalog,
model_library,
model_placements: models.placements,
@@ -431,4 +503,29 @@ mod tests {
[ConsumeEffect::ApplyEffects { probability, .. }] if probability == 0.3
));
}
#[test]
fn sprite_pixels_come_back_the_same_once_packed() {
// Two layers of 16x16 RGBA with some structure, as sprites have.
let pixels: Vec<u8> = (0..2 * 16 * 16 * 4).map(|i| (i * 7 % 251) as u8).collect();
let stored = super::SpritePixels::new(pixels.clone());
assert_eq!(*stored.get(), pixels);
stored.pack();
stored.pack();
// The packing thread drops the raw copy once the compressed one is set.
let started = std::time::Instant::now();
while stored.0.raw.lock().unwrap().is_some() {
assert!(started.elapsed().as_secs() < 10, "packing never finished");
std::thread::yield_now();
}
assert!(
stored
.0
.packed
.get()
.is_some_and(|p| p.len() < pixels.len())
);
assert_eq!(*stored.get(), pixels);
assert_eq!(stored.len(), pixels.len());
}
}
@@ -11,6 +11,7 @@ use crate::{
block_entity_anim::{self, EnchantingBook},
breaking::entity_geometry,
fast_hash::FxHashMap,
font::Glyph,
gfx::mark_oit,
hud::Rect,
mesher::{CULL_BACK, Vertex},
@@ -660,7 +661,7 @@ fn sign_text_calls(
} else {
(dark, 0, vertex_light(packed))
};
let advance = |c: char| (scene.glyph)(c).1;
let advance = |c: char| (scene.glyph)(c).advance;
let width = |s: &str| s.chars().map(advance).sum::<f32>().ceil();
text.lines
.iter()
@@ -699,10 +700,19 @@ fn draw_text(scene: &Scene, call: &TextCall, out: &mut Vec<Vertex>) {
let mut x = call.x + dx;
let y0 = call.y + dy;
for ch in call.text.chars() {
let (cell, advance) = (scene.glyph)(ch);
if let Some(r) = cell {
let corners = [(x, y0 + 8.0), (x + 8.0, y0 + 8.0), (x + 8.0, y0), (x, y0)];
let uvs = [[r.u0, r.v1], [r.u1, r.v1], [r.u1, r.v0], [r.u0, r.v0]];
let g = (scene.glyph)(ch);
if let Some(cell) = g.rect {
// BakedSheetGlyph: the glyph's own box from the pen, not a
// fixed 8x8 cell (a Unifont glyph is 0.5 units per texel).
let (x0, x1) = (x + g.left, x + g.right);
let (top, bottom) = (y0 + g.top, y0 + g.bottom);
let corners = [(x0, bottom), (x1, bottom), (x1, top), (x0, top)];
let uvs = [
[cell.u0, cell.v1],
[cell.u1, cell.v1],
[cell.u1, cell.v0],
[cell.u0, cell.v0],
];
let c =
0xFF00_0000 | (argb & 255) << 16 | (argb >> 8 & 255) << 8 | (argb >> 16 & 255);
for ((cx, cy), uv) in corners.into_iter().zip(uvs) {
@@ -721,7 +731,7 @@ fn draw_text(scene: &Scene, call: &TextCall, out: &mut Vec<Vertex>) {
});
}
}
x += advance;
x += g.advance;
}
};
if call.outline != 0 {
@@ -818,8 +828,8 @@ pub struct Scene<'a> {
/// profile → skin): a downloaded `skins/<sha1>` slot or the profile's
/// `DefaultPlayerSkin` fallback; None keeps `SKIN_BY_TYPE` (`DefaultPlayerSkin.getDefaultTexture`, slim steve).
pub skull: &'a dyn Fn([i32; 3]) -> Option<String>,
/// Font glyph cell (none for blank glyphs such as the space) and advance.
pub glyph: &'a dyn Fn(char) -> (Option<Rect>, f32),
/// Font glyph: atlas cell (none for blank glyphs such as the space), box and advance.
pub glyph: &'a dyn Fn(char) -> Glyph,
/// Changes whenever `glyph` may answer differently (`Font::epoch`): sign
/// text laid out under another value is laid out again.
pub glyphs: u64,
@@ -3342,7 +3352,7 @@ mod tests {
sign: &|_| None,
banner: &|_| Vec::new(),
skull: &|_| None,
glyph: &|_| (None, 0.0),
glyph: &|_| Glyph::default(),
glyphs: 0,
};
// Compare the cached world path with the original model renderer,
@@ -3610,7 +3620,13 @@ mod tests {
w: 8,
h: 8,
};
(Some(rect), 6.0)
Glyph {
rect: Some(rect),
right: 8.0,
bottom: 8.0,
advance: 6.0,
..Glyph::default()
}
};
let scene = Scene {
camera: DVec3::ZERO,
@@ -3660,6 +3676,78 @@ mod tests {
assert!(out.iter().all(|v| v.pos[2] == 0.0));
}
/// BakedSheetGlyph draws a glyph over its own box from the pen
/// (x + left .. x + right, y + up .. y + down), not a fixed 8x8 cell. With
/// Force Unicode Font a Latin letter is an 8x16 Unifont glyph at half
/// scale: 4 units wide and 8 tall, advancing 5 (#8699).
#[test]
fn sign_text_draws_each_glyph_over_its_own_box() {
let world = World::new(BlockStateId(0), 1);
let sprites = HashMap::new();
let glyph = |_: char| Glyph {
rect: Some(Rect {
u0: 0.0,
v0: 0.0,
u1: 0.0625,
v1: 0.125,
w: 8,
h: 16,
}),
left: 0.0,
top: 0.0,
right: 4.0,
bottom: 8.0,
advance: 5.0,
offset: 0.5,
};
let scene = Scene {
camera: DVec3::ZERO,
orientation: Quat::IDENTITY,
anim: &|_| BlockAnim::default(),
texts: None,
partial: 0.0,
game_time: 0,
max_y: 320,
world: &world,
sprites: &sprites,
emissive: &Default::default(),
translucent_sprites: &HashSet::new(),
atlas_revision: 1,
nether: false,
lid: &|_| 0.0,
sign: &|_| None,
banner: &|_| Vec::new(),
skull: &|_| None,
glyph: &glyph,
glyphs: 0,
};
let call = TextCall {
matrix: Mat4::IDENTITY,
x: 10.0,
y: 2.0,
text: "ab".into(),
color: 0xFF00_0000,
outline: 0,
light: 240 | 240 << 8,
};
let mut out = Vec::new();
draw_text(&scene, &call, &mut out);
let boxes: Vec<_> = out
.chunks(4)
.map(|q| {
let xs = q.iter().map(|v| v.pos[0]);
let ys = q.iter().map(|v| v.pos[1]);
[
xs.clone().fold(f32::MAX, f32::min),
xs.fold(f32::MIN, f32::max),
ys.clone().fold(f32::MAX, f32::min),
ys.fold(f32::MIN, f32::max),
]
})
.collect();
assert_eq!(boxes, [[10.0, 14.0, 2.0, 10.0], [15.0, 19.0, 2.0, 10.0]]);
}
#[test]
fn cached_sign_text_matches_and_follows_changes() {
let renderer = BlockEntityRenderer {
@@ -3714,7 +3802,13 @@ mod tests {
w: 8,
h: 8,
};
((c != ' ').then_some(rect), wide.get())
Glyph {
rect: (c != ' ').then_some(rect),
right: 8.0,
bottom: 8.0,
advance: wide.get(),
..Glyph::default()
}
};
let (first_sign, changed_sign, glowing_sign) = (
|_| Some(&first_text),
@@ -3879,7 +3973,7 @@ mod tests {
sign: &|_| None,
banner: &|_| Vec::new(),
skull: &|_| None,
glyph: &|_| (None, 0.0),
glyph: &|_| Glyph::default(),
glyphs: 0,
};
let mut verts = Vec::new();
@@ -3964,7 +4058,7 @@ mod tests {
sign: &|_| None,
banner: &|_| Vec::new(),
skull: &|_| None,
glyph: &|_| (None, 0.0),
glyph: &|_| Glyph::default(),
glyphs: 0,
};
// A closed single chest of `block` drawn by `renderer`: positions and UVs.
@@ -4,6 +4,7 @@ use crate::{
ModelPart, SIGN_TEXT_KEEP, Scene, StaticSectionRevision, classify,
},
chunk::Chunk,
font::Glyph,
hud::Rect,
mesher::Vertex,
signs::{SignData, SignText},
@@ -355,7 +356,7 @@ fn batched_callbacks_preserve_entity_ranges_and_allow_immediate_culling() {
sign: &|_| None,
banner: &|_| Vec::new(),
skull: &|_| None,
glyph: &|_| (None, 0.0),
glyph: &|_| Glyph::default(),
glyphs: 0,
};
// Reverse local-position order to check that cache indexing does not
@@ -443,7 +444,7 @@ fn static_cache_qualifies_local_positions_by_section_and_drops_old_entries() {
sign: &|_| None,
banner: &|_| Vec::new(),
skull: &|_| None,
glyph: &|_| (None, 0.0),
glyph: &|_| Glyph::default(),
glyphs: 0,
};
let options = CacheOptions {
@@ -497,7 +498,7 @@ fn gpu_static_cache_uses_section_local_chests_and_keeps_visibility_separate() {
sign: &|_| None,
banner: &|_| Vec::new(),
skull: &|_| None,
glyph: &|_| (None, 0.0),
glyph: &|_| Glyph::default(),
glyphs: 0,
};
let blocks = [([100, 70, -32], 0)];
@@ -651,18 +652,19 @@ fn sign_text_cache_can_be_disabled_and_reenabled_without_changing_output() {
};
let sign_data = &data;
let sign = move |_: [i32; 3]| Some(sign_data);
let glyph = |_: char| {
(
Some(Rect {
u0: 0.0,
v0: 0.0,
u1: 0.0625,
v1: 0.0625,
w: 8,
h: 8,
}),
6.0,
)
let glyph = |_: char| Glyph {
rect: Some(Rect {
u0: 0.0,
v0: 0.0,
u1: 0.0625,
v1: 0.0625,
w: 8,
h: 8,
}),
right: 8.0,
bottom: 8.0,
advance: 6.0,
..Glyph::default()
};
let scene = Scene {
camera: DVec3::new(2.25, 64.5, -3.75),
@@ -735,7 +737,7 @@ fn shulker_cache_tracks_settled_poses_and_does_not_freeze_opening_animation() {
sign: &|_| None,
banner: &|_| Vec::new(),
skull: &|_| None,
glyph: &|_| (None, 0.0),
glyph: &|_| Glyph::default(),
glyphs: 0,
};
let blocks = [([0, 64, 0], 0)];
@@ -836,7 +838,7 @@ fn shulker_cache_covers_all_colors_and_facing_directions() {
sign: &|_| None,
banner: &|_| Vec::new(),
skull: &|_| None,
glyph: &|_| (None, 0.0),
glyph: &|_| Glyph::default(),
glyphs: 0,
};
let options = CacheOptions {
@@ -890,7 +892,7 @@ fn cached_bells_keep_shaking_until_the_animation_ends() {
sign: &|_| None,
banner: &|_| Vec::new(),
skull: &|_| None,
glyph: &|_| (None, 0.0),
glyph: &|_| Glyph::default(),
glyphs: 0,
};
let blocks = [([0, 64, 0], 0)];
@@ -951,7 +953,7 @@ fn cached_closed_chests_match_the_direct_model_and_rebuild_when_opening() {
sign: &|_| None,
banner: &|_| Vec::new(),
skull: &|_| None,
glyph: &|_| (None, 0.0),
glyph: &|_| Glyph::default(),
glyphs: 0,
};
let blocks = [([0, 64, 0], 0)];
@@ -1046,7 +1048,7 @@ fn closed_chest_cache_covers_all_materials_and_atlas_reloads() {
sign: &|_| None,
banner: &|_| Vec::new(),
skull: &|_| None,
glyph: &|_| (None, 0.0),
glyph: &|_| Glyph::default(),
glyphs: 0,
};
let pos = [0, 64, 0];
@@ -1134,7 +1136,7 @@ fn double_chest_cache_bypasses_both_halves_while_either_lid_is_open() {
sign: &|_| None,
banner: &|_| Vec::new(),
skull: &|_| None,
glyph: &|_| (None, 0.0),
glyph: &|_| Glyph::default(),
glyphs: 0,
};
let pair = [([0, 64, 0], 0), ([1, 64, 0], 1)];
@@ -1209,7 +1211,7 @@ fn decorated_pot_cache_tracks_sherds_and_bypasses_wobble_animation() {
sign: &|_| None,
banner: &|_| Vec::new(),
skull: &|_| None,
glyph: &|_| (None, 0.0),
glyph: &|_| Glyph::default(),
glyphs: 0,
};
let blocks = [([0, 64, 0], 0)];
@@ -1287,7 +1289,7 @@ fn banner_cache_keeps_the_pole_static_while_the_flag_and_patterns_update() {
sign: &|_| None,
banner: &banner_patterns,
skull: &|_| None,
glyph: &|_| (None, 0.0),
glyph: &|_| Glyph::default(),
glyphs: 0,
};
let blocks = [([0, 64, 0], 0)];
@@ -1353,7 +1355,7 @@ fn cached_wall_banner_poles_use_the_wall_model() {
sign: &|_| None,
banner: &|_| Vec::new(),
skull: &|_| None,
glyph: &|_| (None, 0.0),
glyph: &|_| Glyph::default(),
glyphs: 0,
};
let pos = [0, 64, 0];
@@ -1400,7 +1402,7 @@ fn copper_statue_cache_rebuilds_when_its_pose_state_changes() {
sign: &|_| None,
banner: &|_| Vec::new(),
skull: &|_| None,
glyph: &|_| (None, 0.0),
glyph: &|_| Glyph::default(),
glyphs: 0,
};
let options = CacheOptions {
@@ -1505,7 +1507,7 @@ fn copper_statue_cache_covers_weathering_waxing_and_all_poses() {
sign: &|_| None,
banner: &|_| Vec::new(),
skull: &|_| None,
glyph: &|_| (None, 0.0),
glyph: &|_| Glyph::default(),
glyphs: 0,
};
let options = CacheOptions {
@@ -58,6 +58,24 @@ impl DataPack {
}
out
}
/// The files of `data/<namespace>/<folder>/` in every namespace, as
/// `list` names them; a folder pack walks only those folders.
pub fn list_folder(&self, folder: &str) -> Vec<String> {
let files = self.list_kinds(&[folder]);
let Source::Zip(_) = &self.source else {
return files;
};
files
.into_iter()
.filter(|f| {
f.strip_prefix("data/")
.and_then(|r| r.split_once('/'))
.is_some_and(|(_, r)| {
r.strip_prefix(folder).is_some_and(|r| r.starts_with('/'))
})
})
.collect()
}
pub fn read(&self, path: &str) -> Option<Vec<u8>> {
match &self.source {
Source::Zip(z) => z.read(path).ok(),
+39 -1
View File
@@ -8,6 +8,8 @@ use std::{collections::VecDeque, fs, path::Path};
pub const FILE: &str = "horizon-extras.txt";
/// The name the options screen uses while the sort key waits for a key.
pub const SORT_KEY: &str = "horizon.sort";
/// The 3D skin layers line, here and in the `options.txt` that held it first.
const SKIN_LAYERS_KEY: &str = "threeDSkinLayers";
/// The minimap's sizes in GUI pixels, and its zooms in blocks per pixel.
pub const MINIMAP_SIZES: [(u8, &str); 3] = [(64, "Small"), (96, "Medium"), (128, "Large")];
@@ -76,6 +78,13 @@ pub struct Extras {
pub zoom_smooth: bool,
/// Divide mouse turning by the zoom.
pub zoom_sensitivity: bool,
/// Far terrain a zoom magnifies is planned finer, so a spyglass does not
/// show the coarse cells it was given for an unzoomed screen
/// (`lod::Aim`). Costs far tiles while the zoom is held and gives them
/// back after, and does nothing at all unless far terrain is on
/// (`lodDistance`) and the view is actually zoomed. Off by default until
/// a measurement shows the extra tiles land while the zoom is still held.
pub lod_zoom_detail: bool,
/// The FPS counter and keystrokes extras are official mods now: their
/// old settings stay here, kept as they were.
pub fps: bool,
@@ -157,6 +166,10 @@ pub struct Extras {
pub cosmetics_mine: bool,
pub cosmetics_others: horizon_cosmetics::prefs::Others,
pub cosmetics_auras: bool,
/// 3D skin layers (`threeDSkinLayers`, Options → Skin Customization): each
/// painted texel of a player's outer layer drawn as a slab
/// (`horizon_client::mobs::skin_layers`).
pub three_d_skin_layers: bool,
}
impl Default for Extras {
fn default() -> Self {
@@ -169,6 +182,7 @@ impl Default for Extras {
zoom_level: 4,
zoom_smooth: true,
zoom_sensitivity: true,
lod_zoom_detail: false,
fps: false,
fps_corner: Corner::TopLeft,
fps_lows: false,
@@ -206,6 +220,7 @@ impl Default for Extras {
cosmetics_mine: true,
cosmetics_others: horizon_cosmetics::prefs::Others::Shown,
cosmetics_auras: false,
three_d_skin_layers: false,
}
}
}
@@ -243,6 +258,7 @@ impl Extras {
}
"zoomSmooth" => e.zoom_smooth = flag(e.zoom_smooth),
"zoomSensitivity" => e.zoom_sensitivity = flag(e.zoom_sensitivity),
"lodZoomDetail" => e.lod_zoom_detail = flag(e.lod_zoom_detail),
"fps" => e.fps = flag(e.fps),
"fpsCorner" => e.fps_corner = Corner::parse(value, e.fps_corner),
"fpsLows" => e.fps_lows = flag(e.fps_lows),
@@ -307,6 +323,7 @@ impl Extras {
horizon_cosmetics::prefs::Others::parse(value).unwrap_or(e.cosmetics_others)
}
"cosmeticsAuras" => e.cosmetics_auras = flag(e.cosmetics_auras),
SKIN_LAYERS_KEY => e.three_d_skin_layers = flag(e.three_d_skin_layers),
_ => {}
}
}
@@ -314,7 +331,7 @@ impl Extras {
}
pub fn serialize(&self) -> String {
format!(
"packBrowser:{}\nfullbright:{}\ndynamicLights:{}\nzoom:{}\nzoomKey:{}\nzoomLevel:{}\nzoomSmooth:{}\nzoomSensitivity:{}\nfps:{}\nfpsCorner:{}\nfpsLows:{}\nkeystrokes:{}\nkeystrokesCorner:{}\nkeystrokesCps:{}\nmovedToMods:{}\nminimap:{}\nminimapCorner:{}\nminimapSize:{}\nminimapShape:{}\nminimapZoom:{}\nminimapRotate:{}\nautoReconnect:{}\nuiSoundReserve:{}\nownNameTag:{}\nhideBlockParticles:{}\nmouseTweaks:{}\nmouseTweaksWheel:{}\nsort:{}\nsortKey:{}\nchatAnimation:{}\nchatIndicator:{}\n{}:{}\nagentControl:{}\nreproKeys:{}\ndiscordPresence:{}\nsplitscreen:{}\nsplitscreenTwo:{}\nsplitscreenThree:{}\nsplitscreenNames:{}\nwheelCyclesSettings:{}\nmusicOff:{}\nmusicPage:{}\ncosmeticsMine:{}\ncosmeticsOthers:{}\ncosmeticsAuras:{}\n",
"packBrowser:{}\nfullbright:{}\ndynamicLights:{}\nzoom:{}\nzoomKey:{}\nzoomLevel:{}\nzoomSmooth:{}\nzoomSensitivity:{}\nlodZoomDetail:{}\nfps:{}\nfpsCorner:{}\nfpsLows:{}\nkeystrokes:{}\nkeystrokesCorner:{}\nkeystrokesCps:{}\nmovedToMods:{}\nminimap:{}\nminimapCorner:{}\nminimapSize:{}\nminimapShape:{}\nminimapZoom:{}\nminimapRotate:{}\nautoReconnect:{}\nuiSoundReserve:{}\nownNameTag:{}\nhideBlockParticles:{}\nmouseTweaks:{}\nmouseTweaksWheel:{}\nsort:{}\nsortKey:{}\nchatAnimation:{}\nchatIndicator:{}\n{}:{}\nagentControl:{}\nreproKeys:{}\ndiscordPresence:{}\nsplitscreen:{}\nsplitscreenTwo:{}\nsplitscreenThree:{}\nsplitscreenNames:{}\nwheelCyclesSettings:{}\nmusicOff:{}\nmusicPage:{}\ncosmeticsMine:{}\ncosmeticsOthers:{}\ncosmeticsAuras:{}\n{}:{}\n",
bool_text(self.pack_browser),
bool_text(self.fullbright),
bool_text(self.dynamic_lights),
@@ -323,6 +340,7 @@ impl Extras {
self.zoom_level,
bool_text(self.zoom_smooth),
bool_text(self.zoom_sensitivity),
bool_text(self.lod_zoom_detail),
bool_text(self.fps),
self.fps_corner.name(),
bool_text(self.fps_lows),
@@ -365,8 +383,28 @@ impl Extras {
bool_text(self.cosmetics_mine),
self.cosmetics_others.name(),
bool_text(self.cosmetics_auras),
SKIN_LAYERS_KEY,
bool_text(self.three_d_skin_layers),
)
}
/// Adopts the settings `options.txt` held before they moved here, while
/// this file (`own`) has no line of its own: 3D skin layers was an
/// `options.txt` line. Vanilla's `Options.save` drops lines it doesn't
/// know, so it is read there once, the first start after the update.
pub fn read_old_options(&mut self, own: &str, options_txt: &str) {
fn line(text: &str) -> Option<&str> {
text.lines().find_map(|l| {
l.split_once(':')
.filter(|(key, _)| *key == SKIN_LAYERS_KEY)
.map(|(_, value)| value)
})
}
if line(own).is_none()
&& let Some(value) = line(options_txt)
{
self.three_d_skin_layers = value == "true";
}
}
/// Binds the extra key `name` (`SORT_KEY`); false for
/// another name (a vanilla mapping).
pub fn set_key(&mut self, name: &str, value: String) -> bool {
+108 -2
View File
@@ -24,7 +24,7 @@ pub const DEFAULT: &str = "minecraft:default";
/// A glyph ready to draw: atlas UVs and its box relative to the pen
/// position (GlyphBitmap left/top/right/bottom), in GUI pixels.
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, Default)]
pub struct Glyph {
pub rect: Option<Rect>,
pub left: f32,
@@ -134,6 +134,7 @@ pub struct Font {
/// Atlas size and the reserved dynamic region (y, height).
atlas: (u32, u32),
region: (u32, u32),
metrics_only: bool,
cache: RefCell<Cache>,
/// Set anew for each font and each `reset_cache` (see `epoch`).
generation: std::cell::Cell<u64>,
@@ -284,6 +285,7 @@ fn load_bitmap(
sheets: &HashMap<String, Rect>,
pixels: &[u8],
atlas: (u32, u32),
metrics_only: bool,
) -> HashMap<char, BitmapGlyph> {
let mut out = HashMap::new();
let file = p["file"].as_str().unwrap_or("");
@@ -349,6 +351,10 @@ fn load_bitmap(
advance: (0.5 + used as f32 * scale).floor() + 1.0,
offset: 1.0,
};
if metrics_only {
out.insert(*ch, BitmapGlyph::Atlas(glyph));
continue;
}
let glyph = match &sheet {
None => BitmapGlyph::Atlas(Glyph {
rect: Some(glyph_rect(
@@ -596,6 +602,30 @@ impl Font {
atlas: (u32, u32),
region: (u32, u32),
white: Rect,
) -> Self {
Self::load_with(root, sprites, pixels, atlas, region, white, false)
}
/// A server measures text but never draws it. Decode bitmap sheets only
/// to obtain advances, then release their pixels; never rasterize glyphs.
pub fn metrics(root: &Path) -> Self {
let white = Rect {
u0: 0.0,
v0: 0.0,
u1: 1.0,
v1: 1.0,
w: 1,
h: 1,
};
Self::load_with(root, &HashMap::new(), &[], (1, 1), (0, 0), white, true)
}
fn load_with(
root: &Path,
sprites: &HashMap<String, Rect>,
pixels: &[u8],
atlas: (u32, u32),
region: (u32, u32),
white: Rect,
metrics_only: bool,
) -> Self {
let sheets = sprites
.iter()
@@ -617,6 +647,7 @@ impl Font {
root: root.to_owned(),
atlas,
region,
metrics_only,
cache: RefCell::new(Cache {
sets: Vec::new(),
region: Region {
@@ -665,7 +696,14 @@ impl Font {
)),
"bitmap" => Some(Provider::Bitmap(
Filter::of(p),
load_bitmap(&self.root, p, &self.sheets, pixels, self.atlas),
load_bitmap(
&self.root,
p,
&self.sheets,
pixels,
self.atlas,
self.metrics_only,
),
)),
"unihex" => Some(Provider::Unihex(Filter::of(p), p.clone(), None)),
"ttf" => Some(Provider::TrueType(
@@ -841,6 +879,14 @@ impl Font {
if let Some(id) = ttf.ids.get(&c)
&& !(non_fishy && fishy_advance(ttf.advance(*id)))
{
if self.metrics_only {
return Some(Glyph {
rect: None,
advance: ttf.advance(*id),
offset: 1.0,
..self.missing
});
}
let (glyph, bitmap) = ttf.glyph(*id);
return match bitmap {
Some((w, h, rgba)) => {
@@ -861,6 +907,17 @@ impl Font {
}
fn rasterize_hex(&self, region: &mut Region, h: HexGlyph) -> Option<Glyph> {
let width = h.right.saturating_sub(h.left) + 1;
if self.metrics_only {
return Some(Glyph {
rect: None,
left: 0.0,
top: 0.0,
right: width as f32 / 2.0,
bottom: 8.0,
advance: hex_advance(&h),
offset: 0.5,
});
}
let mut rgba = Vec::with_capacity((width * 16 * 4) as usize);
for row in h.rows {
for bit in h.left..=h.right {
@@ -1361,6 +1418,55 @@ mod tests {
out
}
#[test]
fn server_metrics_preserve_bitmap_true_type_and_unihex_advances() {
let sheet = png(16, 8, |x, _| x < 2 || (8..14).contains(&x));
let def = br#"{"providers":[{"type":"space","advances":{" ":4}},{"type":"bitmap","file":"minecraft:font/a.png","ascent":7,"chars":["ab"]}]}"#;
let ttf = br#"{"providers":[{"type":"ttf","file":"minecraft:mono.ttf","size":11,"oversample":2}]}"#;
let root = root(
"metrics",
&[
("assets/minecraft/font/default.json", def),
("assets/minecraft/font/alt.json", ttf),
(
"assets/minecraft/font/mono.ttf",
horizon_theme::fonts::GEIST_MONO,
),
("assets/minecraft/textures/font/a.png", &sheet),
],
);
let rendered = load(&root);
let metrics = Font::metrics(&root);
assert_eq!(metrics.width("a b"), 14.0);
assert_eq!(metrics.width("§lab"), 12.0);
let text = Text::literal("ab ab");
assert_eq!(
metrics.split_formatted(&text, 10.0),
rendered.split_formatted(&text, 10.0)
);
let alt = Style {
font: Some("minecraft:alt".into()),
bold: Some(true),
..Style::default()
};
for c in ['A', 'i', ' ', '\u{fffd}'] {
assert_eq!(metrics.advance(c, &alt), rendered.advance(c, &alt));
assert!(metrics.glyph_styled(c, &alt).rect.is_none());
}
let mut hex = HashMap::new();
parse_hex(b"0041:0000000018242442427E424242420000\n", &mut hex);
let h = hex[&0x41];
let glyph = metrics
.rasterize_hex(&mut metrics.cache.borrow_mut().region, h)
.unwrap();
assert_eq!(glyph.advance, 4.0);
assert_eq!(glyph.offset, 0.5);
assert!(glyph.rect.is_none());
assert!(metrics.take_patches().is_empty());
assert!(!rendered.take_patches().is_empty());
let _ = std::fs::remove_dir_all(root);
}
#[test]
fn true_type_glyphs_are_rasterized_at_the_provider_size() {
let def = br#"{"providers":[{"type":"ttf","file":"minecraft:mono.ttf","size":11,"oversample":2,"shift":[0.5,1],"skip":["B"]}]}"#;
File diff suppressed because it is too large. Load diff
@@ -885,6 +885,7 @@ mod tests {
sorted_indices: None,
flat_id: None,
gpu_id: None,
gpu_fading: false,
fading: None,
leaving: None,
animated_layers: Box::new([]),
@@ -1081,6 +1082,7 @@ mod tests {
sorted_indices: None,
flat_id: Some(id),
gpu_id: None,
gpu_fading: false,
fading: None,
leaving: None,
animated_layers: Box::new([]),
@@ -1307,6 +1309,7 @@ mod tests {
sorted_indices: (translucent > 0).then_some(if indices { at } else { (7, 7) }),
flat_id: None,
gpu_id: None,
gpu_fading: false,
fading: None,
leaving: None,
animated_layers: Box::new([]),
@@ -485,6 +485,7 @@ impl Scene {
let f = &self.frame;
Frame {
weather: &[],
hand_items: &[],
weather_time: crate::weather::Time::new(0, 0.0),
camera: DVec3::from_array(f.camera),
yaw: f.yaw,
@@ -14,9 +14,11 @@
//! each in one draw (the pipelines cull the faces turned away), with the origin
//! the walk would give it: `resident::origin`'s f32 bits, copied, never
//! computed on the GPU. Within the render distance the CPU's walk then lists
//! only the sections whose translucent quads it draws, or that have no record
//! (`Resident::walk`); past it, it lists every section as before.
//! Unlike the walk, the pass draws sections fading in or out whole.
//! only the sections whose translucent quads it draws, that have no record, or
//! that fade (`Resident::walk`); past it, it lists every section as before. A
//! section whose instance carries a fade, fading in or out, has its record
//! flagged (`FADING`, gfx.rs `mark_fades`) until it is whole: the pass leaves
//! it out, and the CPU draws it with its fade.
//!
//! The graph is the one the walk reads that frame (`occlusion::Reach`). Its
//! search keeps the cells it reached as bits too, and `Occlusion` a generation
@@ -59,6 +61,7 @@ use super::{
upload::Uploads,
};
use glam::{DVec3, DVec4, Vec3};
use std::{cmp::Reverse, collections::BinaryHeap, time::Instant};
/// One section (`gpu_culling.wgsl`'s `Section`).
#[repr(C)]
@@ -68,10 +71,14 @@ pub(super) struct Record {
key: [i32; 4],
/// The first vertex of its solid quads (a draw's base vertex, an i32: a
/// page holds fewer vertices), its solid quads, its box (`rounded_box`),
/// then a word kept at 0.
/// then its flags (`FADING`).
a: [u32; 4],
}
/// In `Record::a[3]`: the CPU draws the section's solid quads while it fades
/// (gfx.rs `SectionGpu::gpu_fading`), and the pass leaves it out.
const FADING: u32 = 1;
impl Record {
const FREE: Self = Self {
key: [0, 0, 0, -1],
@@ -86,9 +93,19 @@ impl Record {
a: [start as u32, quads, boxed, 0],
}
}
/// The record, `FADING` when `on`.
pub fn fading(mut self, on: bool) -> Self {
if on {
self.a[3] |= FADING;
}
self
}
fn page(&self) -> Option<u32> {
u32::try_from(self.key[3]).ok()
}
fn is_fading(&self) -> bool {
(self.a[3] & FADING) != 0
}
}
/// `rounded_box` for bounds past what a box's codes hold, or not finite: no
@@ -359,6 +376,10 @@ pub(super) fn supported(
// The pass binds 8, the last cave culling's graph.
&& limits.max_storage_buffers_per_shader_stage >= 8
&& !super::flat::is_broken_intel_vulkan(info)
// DX12's count draws leave each draw's base vertex out of `vertex_index`, by which the
// pulled solid pipelines (those of a device with flat page groups) read the page.
// shortcut: not offered there, until GPU culling's draws carry their base vertex otherwise.
&& !(info.backend == wgpu::Backend::Dx12 && flat_min_sections.is_some())
}
/// A buffer that is recreated larger when it must hold more.
@@ -419,6 +440,8 @@ pub(super) struct Records {
all_dirty: bool,
/// Per arena page, the records on it.
page_sections: Vec<u32>,
/// The records flagged `FADING`.
fading: u32,
}
impl Records {
@@ -435,6 +458,7 @@ impl Records {
};
self.records[id as usize] = record;
self.add_to(record.key[3] as u32);
self.fading += u32::from(record.is_fading());
self.mark(id);
id
}
@@ -455,6 +479,7 @@ impl Records {
continue;
};
self.page_sections[page as usize] -= 1;
self.fading -= u32::from(self.records[id as usize].is_fading());
self.records[id as usize] = Record::FREE;
// shortcut: a teleport frees more than `DIRTY_LIMIT`, so `prepare` writes the
// whole table (0.6-2.6 ms at render distance 64); upgrade to a list of freed ids
@@ -502,6 +527,22 @@ impl Records {
self.add_to(page);
self.mark(id);
}
/// The section with `id` began to draw faded, or stopped (gfx.rs
/// `SectionGpu::gpu_fading`): its record is `FADING` when `on`.
pub fn set_fading(&mut self, id: u32, on: bool) {
let page = self.records.get(id as usize).and_then(Record::page);
debug_assert!(page.is_some(), "no record {id} to flag");
if page.is_none() || self.records[id as usize].is_fading() == on {
return;
}
self.records[id as usize].a[3] ^= FADING;
if on {
self.fading += 1;
} else {
self.fading -= 1;
}
self.mark(id);
}
/// Every section went.
pub fn clear(&mut self) {
self.records.clear();
@@ -510,12 +551,17 @@ impl Records {
self.dirty = Vec::new();
self.all_dirty = false;
self.page_sections.clear();
self.fading = 0;
}
/// The sections with a record.
pub fn live(&self) -> usize {
let free: u32 = self.free.iter().map(|word| word.count_ones()).sum();
self.records.len() - free as usize
}
/// The sections whose record is `FADING`, which the CPU draws.
pub fn fading(&self) -> u32 {
self.fading
}
/// The record of the section with `id`; `None` for a free id.
#[cfg(any(test, debug_assertions))]
pub fn at(&self, id: u32) -> Option<&Record> {
@@ -549,6 +595,42 @@ impl Records {
}
}
/// The flagged sections by when their fade is over at the latest (gfx.rs
/// `SectionGpu::fade_end`), for gfx.rs `mark_fades` to look at them again
/// then, and the chunk fade-in time their flags were decided with: `None`
/// until a frame has decided them, just after GPU culling was switched on.
/// The ends of sections that went or were handed back are skipped as they come.
#[derive(Default)]
pub(super) struct Fades {
due: BinaryHeap<Reverse<(Instant, SectionKey)>>,
pub fade_ms: Option<u64>,
}
impl Fades {
/// The section at `key` is looked at again at `end`.
pub fn push(&mut self, end: Instant, key: SectionKey) {
self.due.push(Reverse((end, key)));
}
/// The section of the earliest end at or before `now`, taken out.
pub fn pop_due(&mut self, now: Instant) -> Option<SectionKey> {
let &Reverse((end, key)) = self.due.peek()?;
if end > now {
return None;
}
self.due.pop();
Some(key)
}
/// Every end goes; `fade_ms` stays.
pub fn clear(&mut self) {
self.due.clear();
}
/// The sections an end is waited for.
#[cfg(debug_assertions)]
pub fn pending(&self) -> impl Iterator<Item = SectionKey> {
self.due.iter().map(|r| r.0.1)
}
}
/// The pass, its buffers and the records it draws from: made when GPU culling
/// is switched on, dropped when it is switched off.
pub(super) struct GpuCulling {
@@ -574,6 +656,9 @@ pub(super) struct GpuCulling {
/// that the arena's compaction moves records while it borrows the
/// renderer's other fields.
pub records: Records,
/// When the flagged records' fades end, a field of its own as `records`
/// is: gfx.rs `mark_fades` takes both beside the renderer's sections.
pub fades: Fades,
/// This frame's pages with records: (page, first draw of its region, the
/// region's size).
pages: Vec<(u32, u32, u32)>,
@@ -700,6 +785,7 @@ impl GpuCulling {
reach_generation: None,
group: None,
records: Records::default(),
fades: Fades::default(),
pages: Vec::new(),
ids: 0,
n_updates: 0,
@@ -975,8 +1061,8 @@ mod tests {
}
/// Where a section's solid quads are: key, arena page, first vertex, quads,
/// and its box.
type Placed = (SectionKey, u32, u64, u32, u32);
/// its box, and whether it fades (`FADING`).
type Placed = (SectionKey, u32, u64, u32, u32, bool);
/// `gpu_culling.wgsl`'s `near`, on the CPU.
fn near(params: &Params, x: i32, z: i32) -> bool {
@@ -1025,7 +1111,7 @@ mod tests {
/// (and the pages `prepare` gives them).
fn keeps(params: &Params, words: &[u32], record: &Record) -> bool {
let (key, a) = (record.key, record.a);
if key[3] < 0 || a[1] == 0 || !near(params, key[0], key[2]) {
if key[3] < 0 || a[1] == 0 || (a[3] & FADING) != 0 || !near(params, key[0], key[2]) {
return false;
}
let c: [u32; 3] = std::array::from_fn(|i| key[i].wrapping_sub(params.first[i]) as u32);
@@ -1099,10 +1185,13 @@ mod tests {
Some([min, max])
}
/// Ids, page counts and changes against a plain list of what each id holds:
/// the lowest free id comes back, free ids at the end leave, counts follow
/// moves (to pages past the last too), a move keeps the box, a free is a
/// change, and more changes than updates take mark the whole table.
/// Ids, page counts, flags and changes against a plain list of what each id
/// holds: the lowest free id comes back, flagged only if inserted so, free
/// ids at the end leave, counts follow moves (to pages past the last too), a
/// move keeps the box and the flag, a free is a change, a flag set changes
/// its record only when it differs, the flagged ones are counted through
/// inserts, flags, removals and clears, and more changes than updates take
/// mark the whole table.
#[test]
fn records_keep_ids_page_counts_and_moves() {
let mut records = Records::default();
@@ -1140,7 +1229,7 @@ mod tests {
};
let mut rng = Rng(0x9E37_79B9_7F4A_7C15);
// What each id holds; `None`: a free id.
let mut model: Vec<Option<Placed>> = vec![Some(((2, 0, 0), 1, 0, 3, 11))];
let mut model: Vec<Option<Placed>> = vec![Some(((2, 0, 0), 1, 0, 3, 11, false))];
for step in 0..4_000 {
let live: Vec<u32> = (0..model.len() as u32)
.filter(|&id| model[id as usize].is_some())
@@ -1149,20 +1238,22 @@ mod tests {
rng.below(4 + step / 500),
u64::from(rng.below(1 << 20)) * 256,
);
match rng.below(5) {
let dirty = records.dirty.len();
match rng.below(6) {
op if op < 2 || live.is_empty() => {
let key = (rng.range(-9, 9), rng.range(0, 23), rng.range(-9, 9));
let (quads, boxed) = (1 + rng.below(500), rng.below(1 << 30));
let fading = rng.below(4) == 0;
let lowest = model
.iter()
.position(Option::is_none)
.unwrap_or(model.len());
let record = Record::new(key, page, start, quads, boxed);
let record = Record::new(key, page, start, quads, boxed).fading(fading);
assert_eq!(records.insert(record) as usize, lowest);
if lowest == model.len() {
model.push(None);
}
model[lowest] = Some((key, page, start, quads, boxed));
model[lowest] = Some((key, page, start, quads, boxed, fading));
}
op => {
let id = live[rng.below(live.len() as u32) as usize];
@@ -1170,6 +1261,12 @@ mod tests {
if op < 4 {
records.relocate(id, page, start);
(held.1, held.2) = (page, start);
} else if op == 4 {
let on = rng.below(2) == 0;
records.set_fading(id, on);
let changed = usize::from(held.5 != on);
assert_eq!(records.dirty.len(), dirty + changed, "step {step}");
held.5 = on;
} else {
records.remove(id);
// Marked: the GPU must have it free before the pass culls.
@@ -1183,11 +1280,13 @@ mod tests {
}
assert_eq!(records.ids() as usize, model.len(), "step {step}");
assert_eq!(records.live(), model.iter().flatten().count());
let flagged = model.iter().flatten().filter(|held| held.5).count();
assert_eq!(records.fading() as usize, flagged, "step {step}");
let mut pages = vec![0; records.page_sections().len()];
for (id, held) in model.iter().enumerate() {
let record = held.map(|(key, page, start, quads, boxed)| {
let record = held.map(|(key, page, start, quads, boxed, fading)| {
pages[page as usize] += 1;
Record::new(key, page, start, quads, boxed)
Record::new(key, page, start, quads, boxed).fading(fading)
});
assert_eq!(
records.at(id as u32),
@@ -1204,16 +1303,17 @@ mod tests {
let (mut each, mut all) = (Records::default(), Records::default());
for n in 0..1_000 {
let (key, page, start) = ((n % 31, n % 24, n / 31), n as u32 % 3, n as u64 * 256);
let record = Record::new(key, page, start, 1, 0);
let record = Record::new(key, page, start, 1, 0).fading(n % 7 == 0);
assert_eq!(each.insert(record), all.insert(record));
}
let gone: Vec<u32> = (0..1_000).filter(|id| id % 3 != 1 || *id >= 900).collect();
let counts = |r: &Records| (r.ids(), r.live(), r.fading());
for ids in [&[1u32, 4, 898][..], &gone[..]] {
for &id in ids {
each.remove(id);
}
all.remove_all(ids.iter().copied());
assert_eq!((all.ids(), all.live()), (each.ids(), each.live()));
assert_eq!(counts(&all), counts(&each));
assert_eq!(all.page_sections(), each.page_sections());
assert_eq!(free_ids(&all), free_ids(&each));
assert_eq!(all.dirty, each.dirty);
@@ -1228,8 +1328,9 @@ mod tests {
assert_eq!((all.ids(), all.live(), all.free.len()), (0, 0, 0));
assert!(all.page_sections().iter().all(|&n| n == 0));
assert!(records.fading() > 0);
records.clear();
assert_eq!((records.ids(), records.live()), (0, 0));
assert_eq!((records.ids(), records.live(), records.fading()), (0, 0, 0));
assert!(records.page_sections().is_empty() && records.dirty.is_empty());
for x in 0..DIRTY_LIMIT as i32 {
records.insert(Record::new((x, 0, 0), 0, 0, 1, 0));
@@ -1360,6 +1461,7 @@ mod tests {
("UNBOUNDED", u64::from(UNBOUNDED)),
("REACH", u64::from(REACH)),
("SKY", u64::from(SKY)),
("FADING", u64::from(FADING)),
] {
assert_eq!(constant(name), value, "{name}");
}
@@ -1784,7 +1886,9 @@ mod tests {
/// reached or not: the pass reads the copy it holds when a frame brings the
/// same graph (once through a bind group made anew), sends a new one after
/// a frame the CPU draws, and makes its copy anew for one too large for its
/// room.
/// room. A fifth of the sections fade, flagged anew before each case
/// (`FADING`, sent as updates or with the whole table): the pass leaves
/// them to the CPU.
#[test]
fn the_gpu_draws_what_the_integer_test_keeps() {
on_each_device(draws_match);
@@ -1792,11 +1896,14 @@ mod tests {
fn draws_match(device: &wgpu::Device, queue: &Queue) {
let mut rng = Rng(0x2545_F491_4F6C_DD1D);
// The flags have their own sequence: the cases are otherwise as before.
let mut flags = Rng(0x9E37_79B9_7F4A_7C15);
let (min_y, height) = (-64, 24);
let mut gpu = GpuCulling::new(device).expect("the pass");
let mut uploads = Uploads::new(device);
let mut held: BTreeMap<u32, Placed> = BTreeMap::new();
let (mut camera, mut compared, mut culled) = (DVec3::ZERO, 0, 0);
// Fading sections the frustum and the graph keep, left to the CPU.
let (mut camera, mut compared, mut culled, mut faded) = (DVec3::ZERO, 0, 0, 0);
let mut words = Vec::new();
// Cave culling's graph while a case has one (its generation, from x and
// z, radius, height and whether its search reached the sky) and its bits.
@@ -1897,7 +2004,8 @@ mod tests {
.records
.insert(Record::new(key, page, start, quads, boxed));
assert!(
held.insert(id, (key, page, start, quads, boxed)).is_none(),
held.insert(id, (key, page, start, quads, boxed, false))
.is_none(),
"id {id} twice"
);
};
@@ -1912,6 +2020,13 @@ mod tests {
place(&mut gpu, &mut held, &mut rng);
}
}
// A fifth of the sections fade: the flags that change go up as
// updates, or with the whole table (cases 4 and 18), and through
// case 7's frame the CPU draws.
for (&id, placed) in held.iter_mut() {
placed.5 = flags.below(5) == 0;
gpu.records.set_fading(id, placed.5);
}
let whole = gpu.records.all_dirty;
let mut encoder = device.create_command_encoder(&Default::default());
let input = FrameInputs {
@@ -1947,10 +2062,12 @@ mod tests {
let mut params = tables(&input, &mut words).expect("the planes fit");
params.pages = gpu.pass_pages;
let mut expected = Vec::new();
for &(key, page, start, quads, boxed) in held.values() {
for &(key, page, start, quads, boxed, fading) in held.values() {
let record = Record::new(key, page, start, quads, boxed);
let kept = keeps(&params, &words, &record);
let kept = keeps(&params, &words, &record.fading(fading));
let reached = reach.is_none_or(|r| r.contains(key));
// In view, but fading: the CPU's.
faded += usize::from(fading && reached && keeps(&params, &words, &record));
if kept && let Some(r) = reach {
graphed += 1;
unreached += usize::from(!reached);
@@ -1965,7 +2082,7 @@ mod tests {
let origin = resident::origin(key, min_y, camera).as_vec3();
let instance = super::super::section_instance(origin.to_array(), 0, 1.0);
expected.push((page, start as u32, quads * 6, instance.map(f32::to_bits)));
} else if in_view_distance((cx, cz), radius, key.0, key.2) {
} else if !fading && in_view_distance((cx, cz), radius, key.0, key.2) {
culled += 1;
}
}
@@ -2012,8 +2129,10 @@ mod tests {
// Not a comparison of empty lists, nor of a test that keeps everything.
// The graphs leave out a real share of what the frustum keeps, and their
// sky band keeps sections above a box in some cases and not in others.
// The flags leave out about a fifth of what they would keep.
assert!(compared > 2_000, "{compared} draws");
assert!(culled > 2_000, "{culled} within the distance left out");
assert!(faded * 10 > compared, "{faded} fading of {compared} drawn");
assert!(
unreached * 10 > graphed && unreached < graphed,
"{unreached} of {graphed} in view unreached"
@@ -9,7 +9,7 @@
// them as the GPU schedules, and where faces of two sections meet at the same
// depth the later draw keeps the pixel). Nothing here computes with floats: the
// origins are the CPU's bits, copied, and the frustum test adds integers the
// CPU computed.
// CPU computed. `cull` leaves sections fading in or out to the CPU (`FADING`).
struct Params {
// Ids to test, records `apply` writes, the arena pages the counts and
@@ -45,7 +45,7 @@ struct Section {
// free id).
key: vec4<i32>,
// The first vertex of its solid quads, its solid quads, its box
// (gpu_culling.rs `rounded_box`), then a word unused here.
// (gpu_culling.rs `rounded_box`), then its flags (`FADING`).
a: vec4<u32>,
}
@@ -91,6 +91,8 @@ const UNBOUNDED: u32 = 0x80000000u;
// reached the box's top layer (the sky band).
const REACH: u32 = 1u;
const SKY: u32 = 2u;
// In a section's `a.w`: it fades in or out, and the CPU draws it with its fade.
const FADING: u32 = 1u;
// The count of `block`'s draws in `page` (`cull`), then where they start (`scan`).
fn block_count(block: u32, page: u32) -> u32 {
@@ -186,7 +188,8 @@ fn cull(@builtin(global_invocation_id) gid: vec3<u32>) {
}
runs[id] = 0u;
let s = sections[id];
if s.key.w < 0 || s.a.y == 0u || !near(s.key.x, s.key.z) || !reached(s.key.xyz) {
if s.key.w < 0 || s.a.y == 0u || (s.a.w & FADING) != 0u || !near(s.key.x, s.key.z)
|| !reached(s.key.xyz) {
return;
}
let page = u32(s.key.w);
+24 -2
View File
@@ -42,18 +42,32 @@ impl Output {
}
}
/// `HORIZON_HDR_OUTPUT`'s names, in order of preference.
const NAMES: [(Output, &str); 2] = [(Output::Pq, "pq"), (Output::ScRgb, "scrgb")];
/// The HDR output `caps` can present: HDR10 where offered, else scRGB.
/// `HORIZON_HDR_OUTPUT=pq` or `scrgb` allows only that one.
pub fn output(caps: &wgpu::SurfaceCapabilities) -> Option<Output> {
pick(caps, std::env::var("HORIZON_HDR_OUTPUT").ok().as_deref())
}
fn pick(caps: &wgpu::SurfaceCapabilities, only: Option<&str>) -> Option<Output> {
[(Output::Pq, "pq"), (Output::ScRgb, "scrgb")]
NAMES
.into_iter()
.filter(|(_, name)| only.is_none_or(|o| o == *name))
.map(|(output, _)| output)
.find(|o| caps.color_spaces(o.format()).contains(o.space()))
}
/// The HDR output an offscreen renderer (`--offscreen`) offers: it has no display to ask, so
/// only the one `HORIZON_HDR_OUTPUT` names, for HDR screenshots without an HDR display.
pub fn offscreen_output() -> Option<Output> {
named(std::env::var("HORIZON_HDR_OUTPUT").ok().as_deref())
}
fn named(name: Option<&str>) -> Option<Output> {
NAMES
.into_iter()
.find(|(_, n)| name == Some(*n))
.map(|(output, _)| output)
}
struct Targets {
size: [u32; 2],
@@ -637,7 +651,7 @@ fn scene_gain(peak: f32, paper_white: f32) -> f32 {
#[cfg(test)]
mod tests {
use super::{Output, pick};
use super::{Output, named, pick};
use wgpu::{SurfaceColorSpaces as S, TextureFormat as F};
#[test]
@@ -687,6 +701,14 @@ mod tests {
assert_eq!(pick(&scrgb, Some("pq")), None);
assert_eq!(pick(&caps(&[(F::Bgra8UnormSrgb, S::SRGB)]), None), None);
}
#[test]
fn offscreen_offers_only_the_named_output() {
assert_eq!(named(Some("pq")), Some(Output::Pq));
assert_eq!(named(Some("scrgb")), Some(Output::ScRgb));
assert_eq!(named(Some("hdr10")), None);
assert_eq!(named(None), None);
}
}
#[cfg(test)]
@@ -59,6 +59,25 @@ fn chosen_cells(
chosen.unwrap_or(d.cells)
}
/// `[shadow]` with the option values `constants`: none while its `option` is off, else the
/// resolution and distance its `resolution_option` and `distance_option` pick.
fn chosen_shadow(
s: &horizon_shader_api::manifest::Shadow,
options: &[horizon_shader_api::manifest::PackOption],
constants: &[(String, f64)],
) -> Option<horizon_shader_api::manifest::Shadow> {
let number = |id: Option<&String>| {
let option = options.iter().find(|o| Some(&o.id) == id)?;
let (_, value) = constants.iter().find(|(k, _)| *k == option.id)?;
option.kind.number(*value)
};
option_on(constants, s.option.as_ref()).then(|| horizon_shader_api::manifest::Shadow {
resolution: number(s.resolution_option.as_ref()).map_or(s.resolution, |r| r as u32),
distance: number(s.distance_option.as_ref()).map_or(s.distance, |d| d as f32),
..s.clone()
})
}
impl NativePack {
/// The pack of the checked stages `report` with the option values `constants`, drawing
/// onto the world's targets as `terrain` describes; the sky reads the terrain's group 0.
@@ -74,8 +93,12 @@ impl NativePack {
) -> Result<(Self, Vec<String>), String> {
let (globals, format) = (terrain.globals, terrain.format);
let textures = super::native_post::pack_textures(device, queue, &report);
let settings = report
// `[shadow]`, unless its option is off, at the size its options pick.
let chosen = report
.shadow
.as_ref()
.and_then(|s| chosen_shadow(s, &report.manifest.options, constants));
let settings = chosen
.as_ref()
.map(|settings| super::native_shadow::fitted(device, settings));
let mut frame = NativeFrame::new(device, settings.as_ref(), textures, lights, terrain.pbr);
@@ -103,8 +126,7 @@ impl NativePack {
report.shadow.is_some(),
)
});
let shadow = report
.shadow
let shadow = chosen
.as_ref()
.map(|s| {
let vertex = report.stages.iter().find(|s| s.kind == StageKind::Vertex);
@@ -240,3 +262,61 @@ impl NativePack {
|| self.post.is_some()
}
}
#[cfg(test)]
mod tests {
use super::chosen_shadow;
use horizon_shader_api::manifest::{OptionKind, PackOption, Shadow};
fn option(id: &str, kind: OptionKind) -> PackOption {
PackOption {
id: id.into(),
name: id.into(),
description: String::new(),
kind,
}
}
#[test]
fn the_shadow_map_follows_its_options() {
let options = [
option("shadows", OptionKind::Bool { default: true }),
option(
"quality",
OptionKind::Choice {
values: vec!["1024".into(), "4096".into()],
default: 0,
},
),
option(
"reach",
OptionKind::Int {
default: 96,
min: 32,
max: 512,
},
),
];
let shadow = Shadow {
option: Some("shadows".into()),
resolution_option: Some("quality".into()),
distance_option: Some("reach".into()),
..Shadow::default()
};
let values = |on: f64, quality: f64, reach: f64| {
[("shadows", on), ("quality", quality), ("reach", reach)].map(|(k, v)| (k.into(), v))
};
let size = |values: &[(String, f64)]| {
chosen_shadow(&shadow, &options, values).map(|s| (s.resolution, s.distance))
};
assert_eq!(size(&values(1.0, 1.0, 200.0)), Some((4096, 200.0)));
assert_eq!(size(&values(1.0, 0.0, 64.0)), Some((1024, 64.0)));
assert_eq!(size(&values(0.0, 1.0, 200.0)), None);
// Without the options' keys: the manifest's own numbers, always drawn.
assert_eq!(
chosen_shadow(&Shadow::default(), &options, &values(0.0, 1.0, 200.0))
.map(|s| (s.resolution, s.distance)),
Some((2048, 128.0))
);
}
}
@@ -76,14 +76,14 @@ pub struct WorldDrawn<'a> {
/// the run's first vertex and the entity, `(1 << 32) | id` for a mob or a player and
/// `(2 << 32) | id` for a dropped item (their entity ids), 0 for anything else. A run lasts
/// until the next. Unlike `spans`, whose ids name a type, these follow one entity from
/// frame to frame (API @API:world_drawn_motion@).
/// frame to frame (API 371).
pub motion: &'a [(u32, u64)],
/// The shared atlas entity UVs point into.
pub atlas: Option<&'a wgpu::Texture>,
/// Quad indices (0 1 2 0 2 3 for each 4 vertices), for as many quads as `entities` holds.
pub quad_indices: &'a wgpu::Buffer,
/// The block textures the terrain samples, for drawing blocks with the player's own
/// textures (`native_block_sprites` says where a block's are). API @API:native_block_sprites@.
/// textures (`native_block_sprites` says where a block's are). API 380.
pub block_sprites: BlockSpriteArrays<'a>,
}
@@ -154,7 +154,7 @@ pub fn native_world_drawn(world: &mut WorldDrawn<'_>) {
}
/// The first-person hand is drawn (see [`HandDrawn`]), on every frame [`native_world_drawn`]
/// runs, with no hand too. API @API:hand_drawn@.
/// runs, with no hand too. API 364.
#[inline(never)]
pub fn native_hand_drawn(hand: &mut HandDrawn<'_>) {
generated::dispatch_hand_drawn(RENDERERS.get().map_or(&[], |renderers| renderers), hand);
@@ -806,12 +806,7 @@ mod tests {
#[test]
fn what_the_camera_stands_on_is_inside_the_shadow_cameras_depth() {
let settings = Settings {
resolution: 2048,
distance: 128.0,
solid_materials: Vec::new(),
};
let shadow = camera(&settings);
let shadow = camera(&Settings::default());
let (view, projection) = super::super::pack_uniforms::shadow_camera(
glam::Vec3::new(0.3, 0.9, 0.0).normalize(),
glam::DVec3::new(-2064.5, 246.0, -4304.5),
@@ -827,11 +822,7 @@ mod tests {
#[test]
fn the_centre_stays_near_the_player_on_both_sides_of_zero() {
let shadow = camera(&Settings {
resolution: 2048,
distance: 128.0,
solid_materials: Vec::new(),
});
let shadow = camera(&Settings::default());
let light = glam::Vec3::Y;
for x in [5.5, -5.5, -2064.5, 2064.5] {
let camera = glam::DVec3::new(x, 64.0, x);
@@ -26,6 +26,37 @@ impl PackAtlas {
split: u32,
layers: u32,
sprite: u32,
) -> Option<Self> {
Self::with_levels(
device,
encoder,
arrays,
split,
layers,
sprite,
arrays[0].mip_level_count(),
)
}
/// Encoded material channels must select an existing texel, including at distance.
pub fn material(
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
arrays: &[wgpu::Texture],
split: u32,
layers: u32,
) -> Option<Self> {
Self::with_levels(device, encoder, arrays, split, layers, arrays[0].width(), 1)
}
fn with_levels(
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
arrays: &[wgpu::Texture],
split: u32,
layers: u32,
sprite: u32,
levels: u32,
) -> Option<Self> {
let layers = layers.max(1);
let columns = (f64::from(layers).sqrt().ceil() as u32).max(1);
@@ -35,7 +66,7 @@ impl PackAtlas {
return None;
}
// Levels past a sprite's single pixel would mix neighbours: stop there.
let levels = arrays[0].mip_level_count().min(sprite.ilog2() + 1);
let levels = levels.min(sprite.ilog2() + 1);
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("Shader pack block atlas"),
size: wgpu::Extent3d {
@@ -65,6 +96,17 @@ impl PackAtlas {
Some(atlas)
}
/// The table flat terrain reads (`hz_sprite_rects`): per layer, its sprite's top left corner
/// (x | y << 16) and side, in atlas pixels.
pub fn rects_table(&self) -> Vec<u32> {
(0..self.columns * self.texture.height() / self.sprite)
.flat_map(|layer| {
let (x, y) = (layer % self.columns, layer / self.columns);
[(x * self.sprite) | ((y * self.sprite) << 16), self.sprite]
})
.collect()
}
/// The atlas's size in pixels (`atlasSize`).
pub fn size(&self) -> [u32; 2] {
[self.texture.width(), self.texture.height()]
@@ -173,7 +215,8 @@ impl PackAtlas {
// vertices (`TerrainVertex`, six words each).
declarations.push_str(
"layout(set = 3, binding = 0) readonly buffer HzBlockIds { int hz_block_ids[]; };\n\
layout(set = 3, binding = 1) readonly buffer HzVertices { uint hz_vertices[]; };\n",
layout(set = 3, binding = 1) readonly buffer HzVertices { uint hz_vertices[]; };\n\
layout(set = 3, binding = 2) readonly buffer HzSpriteRects { uvec2 hz_sprite_rects[]; };\n",
);
"hz_slot_origins[hz_block_slots[uint(gl_VertexIndex) >> 8u]]"
} else {
@@ -181,11 +224,29 @@ impl PackAtlas {
"hz_v_origin"
};
let [width, height] = self.size();
let scale = [
self.sprite as f32 / width as f32,
self.sprite as f32 / height as f32,
];
// A sprite's cell in its own sides, and the share of the atlas a side is: from the table
// in flat draws (`rects_table`), from the grid in others.
let columns = self.columns;
let (cell, side, scale) = if flat {
(
"uvec2 hz_rect = hz_sprite_rects[hz_layer];\n \
hz_cell = vec2(float(hz_rect.x & 65535u), float(hz_rect.x >> 16u)) / float(hz_rect.y);\n "
.to_owned(),
"float(hz_rect.y)",
[1.0 / width as f32, 1.0 / height as f32],
)
} else {
(
format!(
"hz_cell = vec2(float(hz_layer % {columns}u), float(hz_layer / {columns}u));\n "
),
"1.0",
[
self.sprite as f32 / width as f32,
self.sprite as f32 / height as f32,
],
)
};
// UVs past a sprite (models up to 2, distant tiles) cannot repeat in an atlas: clamped.
// The colour's alpha holds HDR's emission level (`mesher::EMISSION_BITS`): packs get 1 unless separate_ao.
let color_a = if self.separate_ao {
@@ -196,8 +257,8 @@ impl PackAtlas {
let body = format!(
" vec4 hz_origin = {origin};\n \
uint hz_layer = hz_v_layer & 32767u;\n \
hz_cell = vec2(float(hz_layer % {columns}u), float(hz_layer / {columns}u));\n \
hz_scale = vec2({:?}, {:?});\n \
{cell}\
hz_scale = vec2({:?}, {:?}) * {side};\n \
hz_Vertex = vec4(hz_v_pos + hz_origin.xyz, 1.0);\n \
hz_uv_scale = hz_origin.w > 0.0 ? uintBitsToFloat(floatBitsToUint(hz_origin.w) & 0xff800000u) : fract(abs(hz_origin.w));\n \
hz_mid = vec2(0.5);\n \
@@ -252,6 +313,7 @@ impl PackAtlas {
.collect(),
after: String::new(),
alpha_test: None,
item_id_declared: false,
}
}
}
@@ -183,7 +183,7 @@ fn sample(
}],
vec![storage(0), storage(1)],
vec![],
vec![storage(0), storage(1)],
vec![storage(0), storage(1), storage(2)],
];
let layouts: Vec<_> = entries
.iter()
@@ -268,11 +268,15 @@ fn sample(
bytemuck::cast_slice(vertices),
wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::VERTEX,
);
let rects = buffer(
bytemuck::cast_slice(&atlas.rects_table()),
wgpu::BufferUsages::STORAGE,
);
let bindings = [
vec![(0, &uniform)],
vec![(0, &slots), (1, &origin)],
vec![],
vec![(0, &ids), (1, &data)],
vec![(0, &ids), (1, &data), (2, &rects)],
];
let groups: Vec<_> = bindings
.iter()
@@ -58,7 +58,8 @@ impl EntitySpan {
}
}
/// `HzDraw`'s size: `renderStage`, `entityId`, `blockEntityId`, a pad, `entityColor`.
/// `HzDraw`'s size: `renderStage`, `entityId`, `blockEntityId`, `currentRenderedItemId`,
/// `entityColor`.
const SIZE: u32 = 32;
pub(super) struct DrawUniforms {
@@ -73,8 +74,10 @@ impl DrawUniforms {
let mut contents = vec![0u8; stride as usize * RENDER_STAGES.len()];
for (i, slot) in contents.chunks_mut(stride as usize).enumerate() {
slot[..4].copy_from_slice(&(i as i32).to_le_bytes());
// `blockEntityId` outside block entities, as Iris has it.
// `blockEntityId` outside block entities, as Iris has it; `currentRenderedItemId`
// outside items, as Iris 1.11.7 gives it to terrain, water and the sky (observed).
slot[8..12].copy_from_slice(&(-1i32).to_le_bytes());
slot[12..16].copy_from_slice(&(-1i32).to_le_bytes());
}
let buffer = wgpu::util::DeviceExt::create_buffer_init(
device,
@@ -101,6 +101,7 @@ pub fn inputs() -> horizon_shaders::GbuffersInputs {
op: ">",
reference: 0.1,
}),
item_id_declared: false,
}
}
@@ -162,6 +163,7 @@ pub fn cloud_inputs() -> horizon_shaders::GbuffersInputs {
.collect(),
after: String::new(),
alpha_test: None,
item_id_declared: false,
}
}
@@ -426,7 +428,9 @@ impl PackEntities {
}),
};
let pack = program.is_some_and(|p| drawn[p]) && !span.hidden;
let casts = part == Part::Plain && program.is_some_and(|p| shadow.cast(&span, p));
// Portal faces cast too (a pack's shadow pass sees block entities whole, as Iris
// draws them); beams do not.
let casts = part != Part::Beam && program.is_some_and(|p| shadow.cast(&span, p));
if !pack && !span.hidden {
extend_without_portals(rest, run);
}
@@ -1032,6 +1036,57 @@ mod gpu_span_tests {
assert_eq!(skin.spans.len(), 1);
}
#[test]
fn end_portal_faces_reach_the_shadow_pass_with_their_block_entity() {
let instance = crate::gfx::test_gpu::instance();
let Ok(adapter) = pollster::block_on(instance.request_adapter(&Default::default())) else {
return;
};
let Ok((device, queue)) = pollster::block_on(adapter.request_device(&Default::default()))
else {
return;
};
let layout = super::super::pack_draw::DrawUniforms::new(&device);
let mut geometry = PackEntities::new(&device, &layout.layout);
// One block entity: a plain face, then an End portal face (overlay layer 9).
let cpu: Vec<Vertex> = [0, 0, 0, 0, 9, 9, 9, 9]
.into_iter()
.map(|layer| Vertex {
layer,
..bytemuck::Zeroable::zeroed()
})
.collect();
let spans = [EntitySpan {
start: 0,
id: 99,
stage: render_stage("BLOCK_ENTITIES"),
..Default::default()
}];
let mut rest = Vec::new();
geometry.prepare(
&device,
&queue,
&layout.layout,
&cpu,
&spans,
[true; PROGRAMS],
([0.; 4], [1; 2]),
Casters {
blocks: true,
..Default::default()
},
&mut rest,
None,
);
assert_eq!(
(geometry.draws.iter())
.map(|d| (d.portal, d.shadow))
.collect::<Vec<_>>(),
[(false, true), (true, true)],
"the portal face casts as its block entity does"
);
}
#[test]
fn posed_and_cpu_draws_keep_offsets_uniforms_and_special_fallbacks() {
let instance = crate::gfx::test_gpu::instance();
@@ -13,9 +13,14 @@ const HAND_DEPTH: f32 = 0.125;
/// `HzHand`: the hand's projection and its inverse.
const UNIFORM: u64 = 2 * 64;
/// `HzDraw`'s size (pack_draw.rs).
const SLOT: u64 = 32;
/// What `gbuffers_hand` reads: the entity inputs, and the hand's projection from set 1.
pub fn inputs() -> horizon_shaders::GbuffersInputs {
let mut inputs = super::pack_entities::inputs();
// Iris 1.11.7 declares `currentRenderedItemId` in the hand's programs (observed).
inputs.item_id_declared = true;
inputs.declarations.push_str(
"layout(set = 1, binding = 1) uniform HzHand { mat4 hz_hand_projection; mat4 hz_hand_projection_inverse; };\n",
);
@@ -45,10 +50,17 @@ pub struct PackHand {
capacity: u32,
/// Quads uploaded this frame.
pub quads: u32,
/// `HzDraw` (set 2) of each run: the arm or one held item, and its `currentRenderedItemId`.
slots: wgpu::Buffer,
pub slot_group: wgpu::BindGroup,
stride: u64,
/// This frame's runs: first quad, quads, and the dynamic offset of their slot.
pub runs: Vec<(u32, u32, u32)>,
}
impl PackHand {
pub fn new(device: &wgpu::Device) -> Self {
/// `draws`: the layout of `HzDraw` (pack_draw.rs).
pub fn new(device: &wgpu::Device, draws: &wgpu::BindGroupLayout) -> Self {
let entry = |binding, ty| wgpu::BindGroupLayoutEntry {
binding,
visibility: wgpu::ShaderStages::VERTEX,
@@ -74,6 +86,28 @@ impl PackHand {
});
let capacity = 256;
let (vertices, group) = Self::vertex_buffer(device, &layout, &uniform, capacity);
let stride = SLOT.max(u64::from(
device.limits().min_uniform_buffer_offset_alignment,
));
// The arm and the two hands' items: a few runs a frame.
let slots = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shader pack hand draws"),
size: SLOTS * stride,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let slot_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Shader pack hand draws"),
layout: draws,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: &slots,
offset: 0,
size: wgpu::BufferSize::new(SLOT),
}),
}],
});
Self {
indices: super::pack_entities::quad_indices(device, capacity),
layout,
@@ -82,6 +116,10 @@ impl PackHand {
group,
capacity,
quads: 0,
slots,
slot_group,
stride,
runs: Vec::new(),
}
}
@@ -116,18 +154,31 @@ impl PackHand {
(buffer, group)
}
/// Uploads this frame's hand and its projection.
/// Uploads this frame's hand, its projection, and the `HzDraw` of each run `items` starts
/// (first vertex, `currentRenderedItemId`; the slots past [`SLOTS`] share the last).
pub fn prepare(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
hand: &[Vertex],
items: &[(u32, i32)],
projection: Mat4,
) {
self.quads = (hand.len() / 4) as u32;
self.runs.clear();
if self.quads == 0 {
return;
}
let mut contents = Vec::new();
for (start, quads, id) in runs(self.quads, items) {
let slot = (self.runs.len() as u64).min(SLOTS - 1);
if slot as usize == self.runs.len() {
contents.extend_from_slice(&draw_slot(id));
contents.resize(((slot + 1) * self.stride) as usize, 0);
}
self.runs.push((start, quads, (slot * self.stride) as u32));
}
queue.write_buffer(&self.slots, 0, &contents);
if self.quads > self.capacity {
self.capacity = self.quads.next_power_of_two();
(self.vertices, self.group) =
@@ -144,10 +195,54 @@ impl PackHand {
}
}
/// The hand's runs a frame keeps slots for.
const SLOTS: u64 = 8;
/// `HzDraw` of a hand run: `renderStage` the solid hand's, `blockEntityId` -1 as outside block
/// entities, and `currentRenderedItemId`.
fn draw_slot(id: i32) -> [u8; SLOT as usize] {
let mut bytes = [0u8; SLOT as usize];
let stage = horizon_shaders::render_stage("HAND_SOLID") as i32;
bytes[0..4].copy_from_slice(&stage.to_le_bytes());
bytes[8..12].copy_from_slice(&(-1i32).to_le_bytes());
bytes[12..16].copy_from_slice(&id.to_le_bytes());
bytes
}
/// The runs of `quads` quads that `items` starts (first vertex, id), as first quad, quads and
/// id; geometry before the first start, or with no starts at all, reads 0, as the arm does.
fn runs(quads: u32, items: &[(u32, i32)]) -> Vec<(u32, u32, i32)> {
let mut starts: Vec<(u32, i32)> = vec![(0, 0)];
for &(start, id) in items {
let q = (start / 4).min(quads);
match starts.last_mut() {
Some(last) if last.0 == q => last.1 = id,
_ => starts.push((q, id)),
}
}
(0..starts.len())
.map(|i| {
let end = starts.get(i + 1).map_or(quads, |s| s.0);
(starts[i].0, end - starts[i].0, starts[i].1)
})
.filter(|r| r.1 > 0)
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
/// Each item's quads draw with its id; geometry before the first item is the arm's (0).
#[test]
fn runs_split_the_hand_by_item() {
assert_eq!(runs(3, &[]), [(0, 3, 0)]);
assert_eq!(runs(5, &[(0, 11), (8, 65535)]), [(0, 2, 11), (2, 3, 65535)]);
assert_eq!(runs(4, &[(4, 21)]), [(0, 1, 0), (1, 3, 21)]);
// An item that drew nothing gives way to the next.
assert_eq!(runs(2, &[(0, 11), (0, 22)]), [(0, 2, 22)]);
}
/// The hand's window depth lies within `MC_HAND_DEPTH` of the middle (packs tell the hand
/// apart by it), and x, y match the plain projection.
#[test]
@@ -50,6 +50,7 @@ pub fn inputs() -> horizon_shaders::GbuffersInputs {
.collect(),
after,
alpha_test: None,
item_id_declared: false,
}
}
@@ -0,0 +1,259 @@
//! Original synthetic material transport oracle; no external shader algorithms or assets.
use super::*;
use wgpu::util::DeviceExt;
#[test]
#[ignore = "requires a host GPU; missing adapter is a failure"]
fn terrain_material_samplers_preserve_surface_and_encoded_bytes() {
let instance = crate::gfx::test_gpu::instance();
let adapter = pollster::block_on(instance.request_adapter(&Default::default())).unwrap();
eprintln!("Material transport GPU: {:?}", adapter.get_info());
assert_ne!(adapter.get_info().device_type, wgpu::DeviceType::Cpu);
let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
required_limits: adapter.limits(),
..Default::default()
}))
.unwrap();
let root = std::env::temp_dir().join(format!("horizon-material-pack-{}", std::process::id()));
std::fs::create_dir_all(root.join("shaders")).unwrap();
std::fs::write(
root.join("shaders/gbuffers_terrain.vsh"),
"#version 120\nvoid main(){gl_Position=gl_Vertex;}",
)
.unwrap();
std::fs::write(
root.join("shaders/gbuffers_terrain.fsh"),
r#"#version 120
uniform sampler2D gtexture;
uniform sampler2D normals;
uniform sampler2D specular;
void main() {
int x = int(floor(gl_FragCoord.x));
int layer = x - (x / 5) * 5;
// Source coordinate (1.25,1.25): inside a texel, away from both its centre and boundary.
vec2 uv = (vec2(float(layer - (layer / 3) * 3), float(layer / 3)) + 0.3125) / vec2(3.0, 2.0);
if (x < 5) gl_FragData[0] = texture2DLod(specular, uv, 12.0);
else if (x < 10) gl_FragData[0] = texture2DLod(normals, uv, 12.0);
else gl_FragData[0] = texture2DLod(gtexture, uv, 0.0);
}
"#,
)
.unwrap();
let (split, layers) = (2u32, 5u32);
let mut encoder = device.create_command_encoder(&Default::default());
let atlases: Vec<_> = (0..3)
.map(|kind| {
let side = if kind == 0 { 4 } else { 2 };
let levels = if kind == 0 { 3 } else { 1 };
let arrays: Vec<_> = (0..layers.div_ceil(split))
.map(|array| {
let count = (layers - array * split).min(split);
let pixels: Vec<u8> = (0..count)
.flat_map(|l| {
let layer = (array * split + l) as u8;
(0..levels).flat_map(move |level| {
let edge = side >> level;
(0..edge * edge).flat_map(move |pixel| {
if level != 0 {
// Sampling unused source mip levels must fail the oracle.
[211, 212, 213, 214]
} else {
match kind {
0 if layer == 3 => crate::sprites::NO_SPECULAR,
0 => [
17 + layer,
if pixel % 2 == 0 { 0 } else { 240 },
65,
if pixel % 2 == 0 { 0 } else { 255 },
],
1 if layer == 2 => crate::sprites::FLAT_NORMAL,
1 => [128 + layer, 126, 255, 19 + layer],
_ => [40 + layer, 50, 60, 255],
}
}
})
})
})
.collect();
device.create_texture_with_data(
&queue,
&wgpu::TextureDescriptor {
label: Some("Original fixture companion array"),
size: wgpu::Extent3d {
width: side,
height: side,
depth_or_array_layers: count,
},
mip_level_count: levels,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::COPY_SRC,
view_formats: &[],
},
wgpu::util::TextureDataOrder::LayerMajor,
&pixels,
)
})
.collect();
super::super::pack_atlas::PackAtlas::material(
&device,
&mut encoder,
&arrays,
split,
layers,
)
.unwrap()
})
.collect();
queue.submit([encoder.finish()]);
let pack = horizon_shaders::Pack::open(&root).unwrap();
let macros = horizon_shaders::Macros::standard();
// Encoded zero alpha is data here and must remain observable in readback.
let properties = horizon_shaders::Properties::parse("alphaTest.gbuffers_terrain=off", &macros);
let inputs = horizon_shaders::GameInputs {
terrain: Some(horizon_shaders::GbuffersInputs {
body: "vec2 p = vec2(float((gl_VertexIndex << 1) & 2), float(gl_VertexIndex & 2)); hz_Vertex=vec4(p*2.0-1.0,0.0,1.0);".into(),
..Default::default()
}),
..Default::default()
};
let post = horizon_shaders::load_post(&pack, "", &properties, &macros, &inputs);
assert!(post.terrain.is_some(), "{:?}", post.notes);
let world = World {
sprite_rects: &[],
atlas: Some((atlases[2].view.clone(), atlases[2].size())),
material_maps: Some([atlases[1].view.clone(), atlases[0].view.clone()]),
lightmap: atlases[2].view.clone(),
terrain: Some(Geometry {
buffers: &[],
table: None,
depth_write: true,
atlas: None,
material_maps: true,
ids: None,
cull: false,
}),
block_ids: &[],
celestial: None,
gui_size: [1, 1],
clouds: None,
weather: false,
hand: false,
};
let mut post = PackPost::new(
&device,
&queue,
&adapter,
post,
wgpu::TextureFormat::Rgba8Unorm,
world,
)
.expect("the pack runs");
let mut pass = post
.terrain
.take()
.unwrap_or_else(|| panic!("terrain pipeline must compile: {:?}", post.notes));
// Request the existing copy usage for the fixture's colour readback.
post.copied |= 1;
// First draw preserves production Geometry -> Pass scope, so a routing regression fails.
for case in 0..3 {
if case == 1 {
pass.material_maps = false;
}
if case == 2 {
pass.material_maps = true;
post.material_maps = None;
}
let maps_expected = case == 0;
pass.groups.clear();
post.resize(&device, [15, 1]);
let read = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 256,
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
mapped_at_creation: false,
});
let mut encoder = device.create_command_encoder(&Default::default());
{
let _clear = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Initialize fixture depth"),
color_attachments: &[],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &post.targets.as_ref().unwrap().depth,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
..Default::default()
});
}
let group = post.bind(&device, &queue, &mut encoder, &mut pass, [15, 1]);
{
let mut render = post.begin(&mut encoder, &pass);
render.set_pipeline(pass.render());
render.set_bind_group(0, &group, &[]);
if pass.draw {
post.draws.bind(&mut render, "TERRAIN_SOLID");
}
render.draw(0..3, 0..1);
}
let output = post.targets.as_ref().unwrap().color[0].as_ref().unwrap()[0].texture();
encoder.copy_texture_to_buffer(
output.as_image_copy(),
wgpu::TexelCopyBufferInfo {
buffer: &read,
layout: wgpu::TexelCopyBufferLayout {
bytes_per_row: Some(256),
..Default::default()
},
},
wgpu::Extent3d {
width: 15,
height: 1,
depth_or_array_layers: 1,
},
);
queue.submit([encoder.finish()]);
read.slice(..).map_async(wgpu::MapMode::Read, |_| {});
device.poll(wgpu::PollType::wait_indefinitely()).unwrap();
let data = read.slice(..).get_mapped_range().unwrap();
for i in 0..15usize {
let layer = (i % 5) as u8;
let expected = if i >= 10 {
[40 + layer, 50, 60, 255]
} else if i >= 5 && (!maps_expected || layer == 2) {
crate::sprites::FLAT_NORMAL
} else if i >= 5 {
[128 + layer, 126, 255, 19 + layer]
} else if !maps_expected || layer == 3 {
crate::sprites::NO_SPECULAR
} else {
[17 + layer, 240, 65, 255]
};
assert_eq!(&data[i * 4..i * 4 + 4], &expected, "case={case}, pixel={i}");
}
eprintln!("Material oracle case={case}: 15 exact RGBA pixels pass");
}
// Fault injection: the same production binding must refuse a filtering sampler.
// If a regression changed both the binding and sampler, the fractional positive
// probes above would instead mix 0.75*240=180 and 0.75*255=191.25, not 240/255.
pass.material_maps = true;
post.material_maps = Some([atlases[1].view.clone(), atlases[0].view.clone()]);
post.nearest_repeat = sampler(&device, wgpu::FilterMode::Linear, wgpu::AddressMode::Repeat);
pass.groups.clear();
let validation = device.push_error_scope(wgpu::ErrorFilter::Validation);
let mut encoder = device.create_command_encoder(&Default::default());
let _invalid_group = post.bind(&device, &queue, &mut encoder, &mut pass, [15, 1]);
let error = pollster::block_on(validation.pop())
.expect("production material binding must reject a filtering sampler");
assert!(
error.to_string().to_ascii_lowercase().contains("filtering"),
"{error}"
);
eprintln!("Material oracle negative filtering control: rejected by production binding");
std::fs::remove_dir_all(root).unwrap();
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,111 @@
//! A buffer that isn't cleared, in passes that leave pixels alone or blend: what is under them.
//! The expected values are Iris 1.11.7's, measured with probe packs of our own (#9009).
use super::*;
/// colortex6 is written three times a frame, the first write leaving the right half alone:
/// there it reads the previous frame's last write, 0.125 (Iris 1.11.7), not the 0.25 written into
/// that texture before. colortex8 is written twice a frame the same way: its right half was never
/// written and reads 0 (Iris 1.11.7). colortex10 is only blended onto (`ONE ONE`), 0.125 a frame:
/// it grows by 0.125 every frame (Iris 1.11.7 adds once per frame).
#[test]
fn a_buffer_that_is_not_cleared_starts_the_frame_with_its_last_write() {
let Some(gpu) = Fixture::new() else { return };
let mut zip = zip::ZipWriter::new(Cursor::new(Vec::new()));
let mut add = |name: &str, source: &str| {
zip.start_file(
format!("shaders/{name}"),
zip::write::SimpleFileOptions::default(),
)
.unwrap();
zip.write_all(source.as_bytes()).unwrap();
};
let fill = |v: f32| format!("vec4({v},{v},{v},1)");
let programs = [
(
"composite",
format!(
"#version 120\nvarying vec2 uv;\n/* RENDERTARGETS:6 */\n\
/*\nconst int colortex6Format=RGBA16F;\nconst int colortex7Format=RGBA16F;\n\
const int colortex8Format=RGBA16F;\nconst int colortex9Format=RGBA16F;\n\
const int colortex10Format=RGBA16F;\nconst bool colortex6Clear=false;\n\
const bool colortex8Clear=false;\nconst bool colortex10Clear=false;\n*/\n\
void main() {{ if(uv.x>0.5) discard; gl_FragData[0]={}; }}\n",
fill(0.5)
),
),
(
"composite1",
format!(
"#version 120\nuniform sampler2D colortex6;\nvarying vec2 uv;\n/* RENDERTARGETS:7,6 */\n\
void main() {{ gl_FragData[0]=texture2D(colortex6,uv); gl_FragData[1]={}; }}\n",
fill(0.25)
),
),
(
"composite2",
format!(
"#version 120\n/* RENDERTARGETS:6 */\nvoid main() {{ gl_FragData[0]={}; }}\n",
fill(0.125)
),
),
(
"composite3",
format!(
"#version 120\nvarying vec2 uv;\n/* RENDERTARGETS:8 */\n\
void main() {{ if(uv.x>0.5) discard; gl_FragData[0]={}; }}\n",
fill(0.5)
),
),
(
"composite4",
format!(
"#version 120\nuniform sampler2D colortex8;\nvarying vec2 uv;\n/* RENDERTARGETS:9,8 */\n\
void main() {{ gl_FragData[0]=texture2D(colortex8,uv); gl_FragData[1]={}; }}\n",
fill(0.125)
),
),
(
"composite5",
"#version 120\n/* RENDERTARGETS:10 */\nvoid main() { gl_FragData[0]=vec4(0.125,0.125,0.125,0); }\n"
.to_owned(),
),
(
"final",
"#version 120\nuniform sampler2D colortex7;\nuniform sampler2D colortex9;\n\
uniform sampler2D colortex10;\nvarying vec2 uv;\n\
void main() { gl_FragColor=vec4(texture2D(colortex7,uv).r,texture2D(colortex9,uv).r,texture2D(colortex10,uv).r,1); }\n"
.to_owned(),
),
];
for (program, fragment) in &programs {
add(&format!("{program}.vsh"), VERTEX);
add(&format!("{program}.fsh"), fragment);
}
add("shaders.properties", "blend.composite5=ONE ONE ONE ONE\n");
let pack = horizon_shaders::Pack::from_zip_bytes(
"clear-false-history",
zip.finish().unwrap().into_inner(),
)
.unwrap();
let mut macros = horizon_shaders::Macros::standard();
let (properties, _) = horizon_shaders::read_properties(&pack, &mut macros);
let post = horizon_shaders::load_post(&pack, "", &properties, &macros, &Default::default());
let mut post = gpu.build(post);
for frame in 0..4u8 {
let actual = gpu.render(&mut post, [16, 16]);
let sum = 32 * (frame + 1);
let wrong: Vec<_> = (actual.iter().enumerate())
.filter(|(n, p)| {
let expected = match (n % 16 < 8, frame) {
(true, _) => [128, 128, sum, 255],
(false, 0) => [0, 0, sum, 255],
(false, _) => [32, 0, sum, 255],
};
p.iter().zip(expected).any(|(a, b)| a.abs_diff(b) > 1)
})
.map(|(n, p)| (n, *p))
.take(4)
.collect();
assert!(wrong.is_empty(), "frame {frame}: {wrong:?}");
}
}
@@ -0,0 +1,70 @@
//! A texel offset given by a `const` local reads the texel that far away, as a literal offset
//! does (GLSL 4.60, 4.3.3 and 8.9.2, as #8462 quotes them). #8462's probe.
use super::destination_tests::Fixture;
use super::*;
use std::io::{Cursor, Write};
const VERTEX: &str = "#version 120\nvarying vec2 uv;\nvoid main() { uv=gl_MultiTexCoord0.xy; gl_Position=ftransform(); }\n";
fn pack() -> Post {
let mut zip = zip::ZipWriter::new(Cursor::new(Vec::new()));
let mut add = |name: &str, source: &str| {
zip.start_file(
format!("shaders/{name}"),
zip::write::SimpleFileOptions::default(),
)
.unwrap();
zip.write_all(source.as_bytes()).unwrap();
};
add("composite.vsh", VERTEX);
// Each texel of colortex1 holds its own column and row, in eighths.
add(
"composite.fsh",
"#version 120\n/* RENDERTARGETS: 1 */\n\
void main() { gl_FragData[0] = vec4(floor(gl_FragCoord.xy) / 8.0, 0.0, 1.0); }\n",
);
add("final.vsh", VERTEX);
// Red and green: what the texel two columns right and one row down holds; blue: this row.
add(
"final.fsh",
"#version 330 compatibility\nuniform sampler2D colortex1;\nin vec2 uv;\n\
vec4 f(vec2 p) { const ivec2 offset = ivec2(2, -1); return textureLodOffset(colortex1, p, 0.0, offset); }\n\
void main() { gl_FragColor = vec4(f(uv).rg, floor(gl_FragCoord.y) / 8.0, 1.0); }\n",
);
let pack = horizon_shaders::Pack::from_zip_bytes(
"original-const-offset",
zip.finish().unwrap().into_inner(),
)
.unwrap();
let mut macros = horizon_shaders::Macros::standard();
let (properties, _) = horizon_shaders::read_properties(&pack, &mut macros);
let post = horizon_shaders::load_post(&pack, "", &properties, &macros, &Default::default());
assert_eq!(post.programs.len(), 2, "{:?}", post.notes);
post
}
#[test]
fn a_const_local_offset_reads_the_offset_texel() {
let Some(gpu) = Fixture::new() else { return };
let mut post = gpu.build(pack());
let pixels = gpu.render(&mut post, [8, 8]);
let eighth = |n: u8| (f32::from(n) * 255.0 / 8.0).round() as u8;
let mut checked = 0;
for (n, p) in pixels.iter().enumerate() {
let x = (n % 8) as u8;
// The bottom row (GL's row 0) and the two right columns read past the edge.
if x >= 6 || p[2] == 0 {
continue;
}
let row = (0..8)
.find(|&r| eighth(r).abs_diff(p[2]) <= 1)
.expect("row in blue");
let want = [eighth(x + 2), eighth(row - 1)];
assert!(
p[0].abs_diff(want[0]) <= 1 && p[1].abs_diff(want[1]) <= 1,
"pixel {n} (column {x}, GL row {row}): {p:?}, expected red and green {want:?}"
);
checked += 1;
}
assert_eq!(checked, 6 * 7);
}
@@ -83,7 +83,8 @@ impl Gpu {
post,
T::Rgba8Unorm,
world,
);
)
.expect("the pack runs");
assert!(
built.passes.iter().any(|p| p.group == Group::Final),
"{:?}",
@@ -315,23 +316,26 @@ fn check(sampler: &str, stage: Stage, scene: Scene, expected: [u8; 4]) {
let hand = matches!(scene, Scene::Terrain { hand: true, .. });
let water = matches!(scene, Scene::Terrain { water: true, .. } | Scene::WaterOnly);
let inputs=horizon_shaders::GameInputs {
terrain:terrain.then(|| horizon_shaders::GbuffersInputs { declarations:"layout(location=0)in vec3 fixture_position;\n".into(),body:"hz_Vertex=vec4(fixture_position,1.0);hz_Color=vec4(1.0);hz_MultiTexCoord0=vec4(0.0);hz_MultiTexCoord1=vec4(240.0);hz_MultiTexCoord2=vec4(240.0);hz_Normal=vec3(0.0,0.0,1.0);\n".into(),attributes:vec![],after:String::new(),alpha_test:None }),
terrain:terrain.then(|| horizon_shaders::GbuffersInputs { declarations:"layout(location=0)in vec3 fixture_position;\n".into(),body:"hz_Vertex=vec4(fixture_position,1.0);hz_Color=vec4(1.0);hz_MultiTexCoord0=vec4(0.0);hz_MultiTexCoord1=vec4(240.0);hz_MultiTexCoord2=vec4(240.0);hz_Normal=vec3(0.0,0.0,1.0);\n".into(),attributes:vec![],after:String::new(),alpha_test:None,item_id_declared:false }),
hand:hand.then(crate::gfx::pack_hand::inputs),..Default::default()
};
let mut post = gpu.load(
shaders(sampler, stage, scene),
World {
atlas: None,
material_maps: None,
lightmap: color.clone(),
terrain: terrain.then_some(Geometry {
buffers: &layouts,
table: None,
depth_write: true,
atlas: None,
material_maps: false,
ids: None,
cull: false,
}),
block_ids: &[],
sprite_rects: &[],
celestial: None,
gui_size: SIZE,
clouds: None,
@@ -617,6 +621,7 @@ fn fixture_frame(hud: &HudBatch) -> Frame<'_> {
celestial: &[],
stars: None,
hand: &[],
hand_items: &[],
hand_fov_degrees: 70.0,
screen: &[],
pack: Default::default(),
@@ -635,3 +640,144 @@ fn fixture_frame(hud: &HudBatch) -> Frame<'_> {
post_effects: crate::post_chain::Request::none(),
}
}
/// shadowtex0 and shadowtex1 read through a plain sampler a quarter texel past a texel's centre,
/// over a shadow map whose depth rises a sixteenth a texel: red and green are how far each
/// moved toward the next texel, times four (0 nearest, 255 linear). `nearest` is a line for
/// both stage files.
fn shadow_depth_filtering(nearest: &str) -> Option<Vec<[u8; 4]>> {
let gpu = Gpu::new()?;
if !gpu
.device
.features()
.contains(wgpu::Features::FLOAT32_FILTERABLE)
{
eprintln!("DEPTH_FIXTURE_NO_FLOAT32_FILTERABLE");
return None;
}
let color = gpu.texture(T::Rgba8Unorm);
let depth = gpu.texture(T::Depth32Float);
gpu.clear(&color, &depth, 0.0);
const ATTRS: [wgpu::VertexAttribute; 1] = wgpu::vertex_attr_array![0=>Float32x3];
let layouts = [Some(wgpu::VertexBufferLayout {
array_stride: 12,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &ATTRS,
})];
let inputs = horizon_shaders::GameInputs {
terrain: Some(horizon_shaders::GbuffersInputs {
declarations: "layout(location=0)in vec3 fixture_position;\n".into(),
body: "hz_Vertex=vec4(fixture_position,1.0);hz_Color=vec4(1.0);\
hz_MultiTexCoord0=vec4(0.0);hz_MultiTexCoord1=vec4(240.0);\
hz_MultiTexCoord2=vec4(240.0);hz_Normal=vec3(0.0,0.0,1.0);\n"
.into(),
attributes: vec![],
after: String::new(),
alpha_test: None,
item_id_declared: false,
}),
..Default::default()
};
let settings = format!("const int shadowMapResolution = 16;\n{nearest}\n");
let files = vec![
("final.vsh".to_string(), VERTEX.to_string()),
(
"final.fsh".into(),
format!(
"#version 330 compatibility\n{settings}\
uniform sampler2D shadowtex0;\nuniform sampler2D shadowtex1;\n\
float moved(sampler2D s, ivec2 t) {{\n\
float a = texelFetch(s, t, 0).r;\n\
return (texture(s, (vec2(t) + vec2(0.75, 0.5)) / 16.0).r - a) * 16.0;\n\
}}\n\
void main() {{\n\
ivec2 t = ivec2(int(gl_FragCoord.x) % 15, 8);\n\
gl_FragColor = vec4(moved(shadowtex0, t) * 4.0, moved(shadowtex1, t) * 4.0, 0.0, 1.0);\n\
}}\n"
),
),
// The shadow map's depth runs from 0 at its left edge to 1 at its right.
(
"shadow.vsh".into(),
"#version 120\nvoid main(){gl_Position=vec4(gl_Vertex.xy,gl_Vertex.x,1.0);}".into(),
),
(
"shadow.fsh".into(),
format!("#version 120\n{settings}void main(){{gl_FragData[0]=vec4(1.0);}}"),
),
];
let mut post = gpu.load(
files,
World {
sprite_rects: &[],
atlas: None,
material_maps: None,
lightmap: color.clone(),
terrain: Some(Geometry {
buffers: &layouts,
table: None,
depth_write: true,
atlas: None,
ids: None,
cull: false,
material_maps: false,
}),
block_ids: &[],
celestial: None,
gui_size: SIZE,
clouds: None,
weather: false,
hand: false,
},
inputs,
);
assert!(post.has_shadow(), "{:?}", post.notes);
let positions: [[f32; 3]; 6] = [
[-1., -1., 0.],
[1., -1., 0.],
[1., 1., 0.],
[-1., -1., 0.],
[1., 1., 0.],
[-1., 1., 0.],
];
let vertices = gpu
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Original shadow depth ramp"),
contents: bytemuck::cast_slice(&positions),
usage: wgpu::BufferUsages::VERTEX,
});
let hud = HudBatch::new(SIZE);
let frame = fixture_frame(&hud);
Some(
gpu.render(&mut post, &color, &depth, &frame, &mut |rp, list, _, _| {
if list == TerrainList::ShadowSolid {
rp.set_vertex_buffer(0, vertices.slice(..));
rp.draw(0..6, 0..1);
}
}),
)
}
#[test]
fn shadow_depth_is_read_linearly_through_a_plain_sampler() {
// Iris 1.11.7 filters both (probe packs of our own; the PR has the readings).
if let Some(pixels) = shadow_depth_filtering("") {
all_pixels(&pixels, [255, 255, 0, 255], "shadowtex linear");
}
}
#[test]
fn shadowtex_nearest_settings_read_the_nearest_texel() {
for (line, expected) in [
("const bool shadowtex0Nearest = true;", [0, 255, 0, 255]),
("const bool shadowtexNearest = true;", [0, 255, 0, 255]),
("const bool shadow0MinMagNearest = true;", [0, 255, 0, 255]),
("const bool shadowtex1Nearest = true;", [255, 0, 0, 255]),
("const bool shadow1MinMagNearest = true;", [255, 0, 0, 255]),
] {
if let Some(pixels) = shadow_depth_filtering(line) {
all_pixels(&pixels, expected, line);
}
}
}
@@ -199,8 +199,19 @@ impl Fixture {
}
pub(super) fn build(&self, post: Post) -> PackPost {
let post = self.try_build(post).expect("the pack runs");
assert!(
post.notes.iter().all(|n| n.starts_with("shadow map ")),
"{:?}",
post.notes
);
post
}
/// [`Self::build`] for a pack that may be refused.
pub(super) fn try_build(&self, post: Post) -> Result<PackPost, Refused> {
let lightmap = self.texture([1, 1], T::Rgba8Unorm);
let post = PackPost::new(
PackPost::new(
&self.device,
&self.queue,
&self.adapter,
@@ -208,22 +219,18 @@ impl Fixture {
T::Rgba8Unorm,
World {
atlas: None,
material_maps: None,
lightmap,
terrain: None,
block_ids: &[],
sprite_rects: &[],
celestial: None,
gui_size: [16, 16],
clouds: None,
weather: false,
hand: false,
},
);
assert!(
post.notes.iter().all(|n| n.starts_with("shadow map ")),
"{:?}",
post.notes
);
post
)
}
pub(super) fn render(&self, post: &mut PackPost, size: [u32; 2]) -> Vec<[u8; 4]> {
@@ -346,8 +353,8 @@ fn frame_clear_and_retained_history_apply_before_partial_post_writes() {
let Some(gpu) = Fixture::new() else { return };
for clear in [true, false] {
let mut post = gpu.build(pack(Coverage::Full, false, false, Some(clear), false));
// Every frame changes which physical image is main. The other image is fully
// seeded red before the partial write, independent of its initial clearing.
// The other image is fully seeded red before the partial write, independent of its
// initial clearing.
for frame in 0..3 {
let actual = gpu.render(&mut post, [16, 16]);
assert!(
@@ -433,6 +440,7 @@ fn fixture_frame(hud: &HudBatch) -> Frame<'_> {
celestial: &[],
stars: None,
hand: &[],
hand_items: &[],
hand_fov_degrees: 70.0,
screen: &[],
pack: Default::default(),
@@ -521,3 +529,6 @@ fn a_cleared_buffer_starts_every_frame_cleared_on_both_textures() {
assert!(wrong.is_empty(), "frame {frame}: {wrong:?}");
}
}
#[path = "clear_false_tests.rs"]
mod clear_false_tests;
@@ -0,0 +1,73 @@
//! `flip.<program>.<buffer> = true` on a buffer the program writes (#9010). The expected values
//! are Iris 1.11.7's, measured with probe packs of our own.
use super::destination_tests::Fixture;
use std::io::{Cursor, Write};
const VERTEX: &str = "#version 120\nvarying vec2 uv;\nvoid main() { uv=gl_MultiTexCoord0.xy; gl_Position=ftransform(); }\n";
/// `composite` writes 0.25 to colortex2 and colortex4 and forces both to flip: the forced flip
/// cancels the write's own, so `composite1` samples colortex2's cleared side (Iris 1.11.7: 0),
/// and `composite2`, blended `ONE ONE`, adds 0.5 onto colortex4's 0.25 (Iris 1.11.7: 0.749).
#[test]
fn a_forced_flip_of_a_written_buffer_leaves_it_unflipped() {
let Some(gpu) = Fixture::new() else { return };
let mut zip = zip::ZipWriter::new(Cursor::new(Vec::new()));
let mut add = |name: &str, source: &str| {
zip.start_file(
format!("shaders/{name}"),
zip::write::SimpleFileOptions::default(),
)
.unwrap();
zip.write_all(source.as_bytes()).unwrap();
};
for (program, fragment) in [
(
"composite",
"#version 120\n/* RENDERTARGETS:2,4 */\n\
void main() { gl_FragData[0]=vec4(0.25,0.25,0.25,1); gl_FragData[1]=vec4(0.25,0.25,0.25,1); }\n",
),
(
"composite1",
"#version 120\nuniform sampler2D colortex2;\nvarying vec2 uv;\n/* RENDERTARGETS:3 */\n\
void main() { gl_FragData[0]=texture2D(colortex2,uv); }\n",
),
(
"composite2",
"#version 120\n/* RENDERTARGETS:4 */\nvoid main() { gl_FragData[0]=vec4(0.5,0.5,0.5,0); }\n",
),
(
"final",
"#version 120\nuniform sampler2D colortex3;\nuniform sampler2D colortex4;\nvarying vec2 uv;\n\
void main() { gl_FragColor=vec4(texture2D(colortex4,uv).r,texture2D(colortex3,uv).r,0,1); }\n",
),
] {
add(&format!("{program}.vsh"), VERTEX);
add(&format!("{program}.fsh"), fragment);
}
add(
"shaders.properties",
"flip.composite.colortex2=true\nflip.composite.colortex4=true\nblend.composite2=ONE ONE ONE ONE\n",
);
let pack = horizon_shaders::Pack::from_zip_bytes(
"forced-flip-of-a-written-buffer",
zip.finish().unwrap().into_inner(),
)
.unwrap();
let mut macros = horizon_shaders::Macros::standard();
let (properties, _) = horizon_shaders::read_properties(&pack, &mut macros);
let post = horizon_shaders::load_post(&pack, "", &properties, &macros, &Default::default());
let mut post = gpu.build(post);
for frame in 0..2 {
let actual = gpu.render(&mut post, [16, 16]);
let wrong: Vec<_> = (actual.iter().enumerate())
.filter(|(_, p)| {
p.iter()
.zip([191, 0, 0, 255])
.any(|(a, b)| a.abs_diff(b) > 1)
})
.map(|(n, p)| (n, *p))
.take(4)
.collect();
assert!(wrong.is_empty(), "frame {frame}: {wrong:?}");
}
}
@@ -0,0 +1,89 @@
//! A pack may give colortex0 an integer format (OptiFine's `shaders.txt`, "Texture Formats",
//! lists `RGBA32UI`, as #8459 quotes it): the pack still loads, and reads back what it wrote
//! there. #8459's probe; #8459 reports Iris 1.11.7 drawing (0, 200, 0) at every point
//! sampled from it. Without a `final` that draws it, the pack is refused (#9165).
use super::destination_tests::Fixture;
use super::*;
use std::io::{Cursor, Write};
const VERTEX: &str = "#version 120\nvarying vec2 uv;\nvoid main() { uv=gl_MultiTexCoord0.xy; gl_Position=ftransform(); }\n";
/// A `final` that shows the integer colortex0.
const FINAL: &str = "#version 330 compatibility\nuniform usampler2D colortex0;\n\
void main() { gl_FragColor = vec4(texelFetch(colortex0, ivec2(gl_FragCoord.xy), 0)) / 255.0; }\n";
/// A `final` that doesn't compile (it calls a function nobody declares), so it is skipped.
const BROKEN_FINAL: &str =
"#version 330 compatibility\nvoid main() { gl_FragColor = undeclared_function(); }\n";
/// `depth`: the composite reads depthtex0, so the blit copies the world's depth; `last`: the
/// pack's `final`, if it has one.
fn pack(depth: bool, last: Option<&str>) -> Post {
let mut zip = zip::ZipWriter::new(Cursor::new(Vec::new()));
let mut add = |name: &str, source: &str| {
zip.start_file(
format!("shaders/{name}"),
zip::write::SimpleFileOptions::default(),
)
.unwrap();
zip.write_all(source.as_bytes()).unwrap();
};
add("composite.vsh", VERTEX);
// With depthtex0: green where it holds the empty world's far plane (1), red elsewhere.
let value = if depth {
"texelFetch(depthtex0, ivec2(gl_FragCoord.xy), 0).r == 1.0 ? uvec4(0u, 200u, 0u, 255u) : uvec4(200u, 0u, 0u, 255u)"
} else {
"uvec4(0u, 200u, 0u, 255u)"
};
add(
"composite.fsh",
&format!(
"#version 330 compatibility\n/* RENDERTARGETS: 0 */\n/*\nconst int colortex0Format = RGBA32UI;\n*/\n\
uniform sampler2D depthtex0;\nlayout(location = 0) out uvec4 o;\nvoid main() {{ o = {value}; }}\n"
),
);
if let Some(source) = last {
add("final.vsh", VERTEX);
add("final.fsh", source);
}
let pack = horizon_shaders::Pack::from_zip_bytes(
"original-integer-colortex0",
zip.finish().unwrap().into_inner(),
)
.unwrap();
let mut macros = horizon_shaders::Macros::standard();
let (properties, _) = horizon_shaders::read_properties(&pack, &mut macros);
horizon_shaders::load_post(&pack, "", &properties, &macros, &Default::default())
}
#[test]
fn an_integer_colortex0_keeps_what_the_pack_wrote() {
let Some(gpu) = Fixture::new() else { return };
for depth in [false, true] {
let mut post = gpu.build(pack(depth, Some(FINAL)));
// Twice: the second frame's world blit runs over a colortex0 the first frame wrote.
for frame in 0..2 {
let pixels = gpu.render(&mut post, [8, 8]);
let wrong: Vec<_> = pixels.iter().filter(|p| **p != [0, 200, 0, 255]).collect();
assert!(wrong.is_empty(), "depth {depth}, frame {frame}: {wrong:?}");
}
}
}
/// Without a `final` left to draw it (none, or one that is skipped), an integer colortex0 has
/// no colours the screen can show: the pack is refused, so the game turns it off and draws
/// without it instead of a black screen (#9165). The refusal keeps why `final` was skipped.
#[test]
fn an_integer_colortex0_without_a_working_final_is_refused() {
let Some(gpu) = Fixture::new() else { return };
for last in [None, Some(BROKEN_FINAL)] {
let Err(refused) = gpu.try_build(pack(false, last)) else {
panic!("{last:?}: a pack that can't show colortex0 runs");
};
assert_eq!(
refused.notes.iter().any(|n| n.starts_with("final")),
last.is_some(),
"{refused:?}"
);
}
}
@@ -0,0 +1,205 @@
//! The small packed colortex formats `RGB5_A1`, `RGB565` and `R3_G3_B2`, read back as in Iris
//! 1.11.7. wgpu has no such formats, so these buffers are 8-bit (`texture_format`); what a pass
//! writes into one and its clear colour go through [`quantise`] first.
//!
//! Probe pack p8106c of our own wrote 600 values to each format and read them back with
//! `texelFetch` in Iris 1.11.7 (NVIDIA 615.78.08). Our probe's Iris values fit this, with no
//! exception: each channel becomes the byte an `RGBA8` buffer stores for it, keeps that byte's
//! top bits (5, 5, 5 and 1 for `RGB5_A1`; 5, 6 and 5 for `RGB565` and also for `R3_G3_B2`), and
//! reads back those bits repeated to fill 8 (`0.3337` → byte 85 → 10 of 31 → 82 / 255). This is
//! a fit to one driver's values, not a rule Iris states. `RGBA4` and `RGBA2` read back the `RGBA8`
//! bytes in the same probe, as Horizon's 8-bit buffers already do.
use horizon_shaders::{Format, Kind};
use naga::{BinaryOperator, Expression, Literal, MathFunction, Statement, VectorSize};
/// The bits each channel keeps in a buffer of format `f`, for the formats that keep fewer than
/// 8; a three-channel format's alpha keeps 8 (it reads 1 anyway).
pub(super) fn bits(f: Format) -> Option<[u8; 4]> {
match (f.kind, f.channels, f.bits) {
(Kind::Unorm, 4, 5) => Some([5, 5, 5, 1]),
// `RGB565`, and `R3_G3_B2` (3 × 3 bits in `config.rs`), which our probe's Iris values
// put in 5, 6 and 5 bits too.
(Kind::Unorm, 3, 6 | 3) => Some([5, 6, 5, 8]),
_ => None,
}
}
/// The value a buffer keeping `bits` per channel reads back after `v` is written or cleared.
pub(super) fn quantise(v: [f32; 4], bits: [u8; 4]) -> [f32; 4] {
std::array::from_fn(|i| {
let byte = (v[i].clamp(0.0, 1.0) * 255.0 + 0.5).floor() as u32;
let n = u32::from(bits[i]);
let kept = byte >> (8 - n);
// The kept bits, repeated until there are 8.
let mut wide = 0;
let mut have = 0;
while have < 8 {
wide = wide << n | kept;
have += n;
}
(wide >> (have - 8)) as f32 / 255.0
})
}
/// The factors of the shader's [`quantise`] for a channel keeping `n` bits: the kept value is
/// `floor(byte / step)`, and it reads back `kept × step + floor(kept × repeat)`.
fn factors(n: u8) -> (f32, f32) {
let step = f32::from(1u16 << (8 - n));
// `kept × repeat` is the kept bits shifted down to fill what `kept × step` leaves: one bit
// fills the other 7 (× 127); 5 or 6 bits fill 3 or 2 with their top bits.
let repeat = match n {
1 => 127.0,
_ => 2f32.powi(8 - 2 * i32::from(n)),
};
(step, repeat)
}
/// Makes the fragment entry point of `module` write each output location of `outputs` as
/// [`quantise`] gives it for its bits. The translator's entry point ends in one `return` of its
/// outputs; a module without one is left as it is.
pub(super) fn quantise_outputs(module: &mut naga::Module, outputs: &[(u32, [u8; 4])]) {
let Some(ep) =
(module.entry_points.iter_mut()).find(|e| e.stage == naga::ShaderStage::Fragment)
else {
return;
};
let Some(result) = &ep.function.result else {
return;
};
let naga::TypeInner::Struct { members, .. } = &module.types[result.ty].inner else {
return;
};
let vec4 = |ty: naga::Handle<naga::Type>| {
matches!(
module.types[ty].inner,
naga::TypeInner::Vector {
size: VectorSize::Quad,
scalar: naga::Scalar::F32
}
)
};
// (member, bits, the member's type) of each output to change.
let changed: Vec<(usize, [u8; 4], naga::Handle<naga::Type>)> = (members.iter().enumerate())
.filter_map(|(i, m)| {
let Some(naga::Binding::Location { location, .. }) = m.binding else {
return None;
};
let bits = outputs.iter().find(|o| o.0 == location)?.1;
vec4(m.ty).then_some((i, bits, m.ty))
})
.collect();
if changed.is_empty() {
return;
}
let struct_ty = result.ty;
let f = &mut ep.function;
let body = std::mem::take(&mut f.body);
let mut out = naga::Block::with_capacity(body.len() + 1);
for (statement, span) in body.span_into_iter() {
let Statement::Return { value: Some(value) } = statement else {
out.push(statement, span);
continue;
};
let Expression::Compose { components, .. } = &f.expressions[value] else {
out.push(statement, span);
continue;
};
let mut components = components.clone();
let e = &mut f.expressions;
let lit = |e: &mut naga::Arena<Expression>, v: f32| {
e.append(Expression::Literal(Literal::F32(v)), span)
};
// Literals need no emitting: they come before the range that is emitted.
let scalars: Vec<_> = (changed.iter())
.map(|&(_, bits, _)| {
let per = bits.map(factors);
(
per.map(|(step, _)| lit(e, step)),
per.map(|(step, _)| lit(e, 1.0 / step)),
per.map(|(_, repeat)| lit(e, repeat)),
)
})
.collect();
let (zero, one, max, half) = (lit(e, 0.0), lit(e, 1.0), lit(e, 255.0), lit(e, 0.5));
let mut add = |x: Expression| e.append(x, span);
let splat = |add: &mut dyn FnMut(Expression) -> _, value| {
add(Expression::Splat {
size: VectorSize::Quad,
value,
})
};
let (zero, one, max, half) = (
splat(&mut add, zero),
splat(&mut add, one),
splat(&mut add, max),
splat(&mut add, half),
);
let binary = |add: &mut dyn FnMut(Expression) -> _, op, left, right| {
add(Expression::Binary { op, left, right })
};
let math = |add: &mut dyn FnMut(Expression) -> _, fun, arg, arg1, arg2| {
add(Expression::Math {
fun,
arg,
arg1,
arg2,
arg3: None,
})
};
for (&(member, _, ty), (steps, inverse, repeats)) in changed.iter().zip(scalars) {
let mut vector = |components: [_; 4]| {
add(Expression::Compose {
ty,
components: components.to_vec(),
})
};
let (step, inverse, repeat) = (vector(steps), vector(inverse), vector(repeats));
let x = components[member];
let clamped = math(&mut add, MathFunction::Clamp, x, Some(zero), Some(one));
let scaled = binary(&mut add, BinaryOperator::Multiply, clamped, max);
let rounded = binary(&mut add, BinaryOperator::Add, scaled, half);
let byte = math(&mut add, MathFunction::Floor, rounded, None, None);
let shifted = binary(&mut add, BinaryOperator::Multiply, byte, inverse);
let kept = math(&mut add, MathFunction::Floor, shifted, None, None);
let high = binary(&mut add, BinaryOperator::Multiply, kept, step);
let low = binary(&mut add, BinaryOperator::Multiply, kept, repeat);
let low = math(&mut add, MathFunction::Floor, low, None, None);
let wide = binary(&mut add, BinaryOperator::Add, high, low);
components[member] = binary(&mut add, BinaryOperator::Divide, wide, max);
}
let value = add(Expression::Compose {
ty: struct_ty,
components,
});
// Everything from the first splat on is new and not a literal.
let range = naga::Range::new_from_bounds(zero, value);
out.push(Statement::Emit(range), span);
out.push(Statement::Return { value: Some(value) }, span);
}
f.body = out;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn clear_colours_keep_the_same_bits() {
// The CPU side, for clear colours. Iris 1.11.7 read p8106d's clear colour back as below in
// RGB5_A1 and RGB565, and p8106c's 0.3337 as 82/255 in the 5-bit channels and 85/255 in
// the 6-bit one.
let to_255 = |v: [f32; 4]| v.map(|c| (c * 255.0 * 1000.0).round() / 1000.0);
let rgb5_a1 = bits(Format::parse("RGB5_A1").unwrap()).unwrap();
let rgb565 = bits(Format::parse("RGB565").unwrap()).unwrap();
let r3_g3_b2 = bits(Format::parse("R3_G3_B2").unwrap()).unwrap();
let clear = [0.3337, 0.1, 0.6, 0.3];
assert_eq!(to_255(quantise(clear, rgb5_a1)), [82.0, 24.0, 156.0, 0.0]);
assert_eq!(to_255(quantise(clear, rgb565))[..3], [82.0, 24.0, 156.0]);
assert_eq!(
to_255(quantise([0.3337; 4], r3_g3_b2))[..3],
[82.0, 85.0, 82.0]
);
assert!(bits(Format::parse("RGBA4").unwrap()).is_none());
}
}
@@ -0,0 +1,95 @@
//! `RGB5_A1`, `RGB565` and `R3_G3_B2` buffers read back as in Iris 1.11.7. Expected values are
//! those probe packs p8106c (written values) and p8106d (clear colours) read back in Iris (the PR
//! has them); the programs here are the probes', cut down. Written values are ones whose byte
//! (× 255) is not near a half: GPUs may round those either way.
use super::destination_tests::Fixture;
use super::*;
use std::io::{Cursor, Write};
const VERTEX: &str = "#version 120\nvarying vec2 uv;\nvoid main() { uv=gl_MultiTexCoord0.xy; gl_Position=ftransform(); }\n";
fn pack() -> Post {
let mut zip = zip::ZipWriter::new(Cursor::new(Vec::new()));
let mut add = |name: &str, source: &str| {
zip.start_file(
format!("shaders/{name}"),
zip::write::SimpleFileOptions::default(),
)
.unwrap();
zip.write_all(source.as_bytes()).unwrap();
};
add("composite.vsh", VERTEX);
add(
"composite.fsh",
"#version 120\n/* RENDERTARGETS: 1,2,3 */\n\
/*\nconst int colortex1Format = RGB5_A1;\nconst int colortex2Format = RGB565;\n\
const int colortex3Format = R3_G3_B2;\nconst int colortex4Format = RGB5_A1;\n\
const int colortex5Format = RGB565;\n*/\n\
const vec4 colortex4ClearColor = vec4(0.3337, 0.1, 0.6, 0.3);\n\
const vec4 colortex5ClearColor = vec4(0.3337, 0.1, 0.6, 0.3);\n\
void main() {\n\
gl_FragData[0] = vec4(175.0, 176.0, 766.5, 256.0) / 511.0;\n\
gl_FragData[1] = vec4(400.0, 175.0, 44.0, 0.0) / 511.0;\n\
gl_FragData[2] = vec4(-255.5, 176.0, 300.0, 0.0) / 511.0;\n}\n",
);
add("final.vsh", VERTEX);
// Pixel column x shows component x % 4 of buffer 1 + x / 4, as the bytes of its bits.
add(
"final.fsh",
"#version 330 compatibility\nuniform sampler2D colortex1;\nuniform sampler2D colortex2;\n\
uniform sampler2D colortex3;\nuniform sampler2D colortex4;\nuniform sampler2D colortex5;\n\
void main() {\n ivec2 p = ivec2(gl_FragCoord.xy);\n vec4 c = vec4(0.0);\n\
switch (p.x / 4) {\n\
case 0: c = texelFetch(colortex1, p, 0); break;\n\
case 1: c = texelFetch(colortex2, p, 0); break;\n\
case 2: c = texelFetch(colortex3, p, 0); break;\n\
case 3: c = texelFetch(colortex4, p, 0); break;\n\
case 4: c = texelFetch(colortex5, p, 0); break;\n\
}\n uint u = floatBitsToUint(c[p.x % 4]);\n\
gl_FragColor = vec4(uvec4(u >> 24u, u >> 16u, u >> 8u, u) & 255u) / 255.0;\n}\n",
);
let pack = horizon_shaders::Pack::from_zip_bytes(
"original-packed",
zip.finish().unwrap().into_inner(),
)
.unwrap();
let mut macros = horizon_shaders::Macros::standard();
let (properties, _) = horizon_shaders::read_properties(&pack, &mut macros);
let post = horizon_shaders::load_post(&pack, "", &properties, &macros, &Default::default());
assert_eq!(post.programs.len(), 2, "{:?}", post.notes);
post
}
#[test]
fn small_packed_buffers_keep_their_bits() {
let Some(gpu) = Fixture::new() else { return };
let mut post = gpu.build(pack());
// Twice: the second frame writes the buffers' other sides.
for frame in 0..2 {
let pixels = gpu.render(&mut post, [20, 1]);
let read: Vec<f32> = pixels
.iter()
.map(|p| f32::from_bits(u32::from_be_bytes(*p)))
.collect();
// Iris 1.11.7, in 255ths: RGB5_A1 written (175, 176, 766.5, 256) / 511; RGB565 written
// (400, 175, 44, 0) / 511; R3_G3_B2 written (-255.5, 176, 300, 0) / 511; RGB5_A1 and
// RGB565 cleared to (0.3337, 0.1, 0.6, 0.3). Three-channel formats read alpha 1.
let expected: [f32; 20] = [
82.0, 90.0, 255.0, 255.0, //
206.0, 85.0, 16.0, 255.0, //
0.0, 89.0, 148.0, 255.0, //
82.0, 24.0, 156.0, 0.0, //
82.0, 24.0, 156.0, 255.0,
];
for (n, (got, want)) in read.iter().zip(expected).enumerate() {
assert!(
(got - want / 255.0).abs() <= 1e-6,
"frame {frame}, buffer {} component {}: {}/255, expected {want}/255; all {:?}",
1 + n / 4,
n % 4,
got * 255.0,
read.iter().map(|v| v * 255.0).collect::<Vec<_>>()
);
}
}
}
@@ -0,0 +1,78 @@
//! `min3`, `max3` and `mid3` of GL_AMD_shader_trinary_minmax: the per-component minimum, maximum
//! and median of three values, for float, int and uint vectors (the extension's additions to
//! GLSL 4.30 Section 8.3). An original probe; the expected values are worked out by hand.
use super::destination_tests::Fixture;
use super::*;
use std::io::{Cursor, Write};
const VERTEX: &str = "#version 120\nvarying vec2 uv;\nvoid main() { uv=gl_MultiTexCoord0.xy; gl_Position=ftransform(); }\n";
fn pack() -> Post {
let mut zip = zip::ZipWriter::new(Cursor::new(Vec::new()));
let mut add = |name: &str, source: &str| {
zip.start_file(
format!("shaders/{name}"),
zip::write::SimpleFileOptions::default(),
)
.unwrap();
zip.write_all(source.as_bytes()).unwrap();
};
add("final.vsh", VERTEX);
// Pixel column x shows case x % 8 in red, green and blue.
add(
"final.fsh",
"#version 330 compatibility\n#extension GL_AMD_shader_trinary_minmax : require\n\
void main() {\n int x = int(gl_FragCoord.x) % 8;\n\
vec3 a = vec3(0.1, 0.9, 0.4), b = vec3(0.6, 0.2, 0.8), c = vec3(0.3, 0.5, 0.0);\n\
ivec3 i = ivec3(-3, 7, 2), j = ivec3(5, -1, 9), k = ivec3(0, 4, -6);\n\
uvec3 u = uvec3(1u, 8u, 3u), v = uvec3(6u, 2u, 7u), w = uvec3(4u, 5u, 0u);\n\
vec3 o = vec3(0.0);\n\
switch (x) {\n\
case 0: o = min3(a, b, c); break;\n\
case 1: o = max3(a, b, c); break;\n\
case 2: o = mid3(a, b, c); break;\n\
case 3: o = vec3(mid3(i, j, k)) / 10.0; break;\n\
case 4: o = vec3(max3(u, v, w)) / 10.0; break;\n\
case 5: o = vec3(min3(i, j, k) + 6) / 10.0; break;\n\
case 6: o = vec3(mid3(u.x, v.x, w.x), mid3(u.y, v.y, w.y), mid3(u.z, v.z, w.z)) / 10.0; break;\n\
case 7: o = vec3(max3(a.x, b.x, c.x), min3(a.yz, b.yz, c.yz)); break;\n\
}\n gl_FragColor = vec4(o, 1.0);\n}\n",
);
let pack = horizon_shaders::Pack::from_zip_bytes(
"original-trinary",
zip.finish().unwrap().into_inner(),
)
.unwrap();
let mut macros = horizon_shaders::Macros::standard();
let (properties, _) = horizon_shaders::read_properties(&pack, &mut macros);
let post = horizon_shaders::load_post(&pack, "", &properties, &macros, &Default::default());
assert_eq!(post.programs.len(), 1, "{:?}", post.notes);
post
}
#[test]
fn trinary_min_max_and_median_per_component() {
let Some(gpu) = Fixture::new() else { return };
let mut post = gpu.build(pack());
let pixels = gpu.render(&mut post, [8, 8]);
// Tenths, by hand: a = (1, 9, 4), b = (6, 2, 8), c = (3, 5, 0); i = (-3, 7, 2),
// j = (5, -1, 9), k = (0, 4, -6); u = (1, 8, 3), v = (6, 2, 7), w = (4, 5, 0).
let tenths: [[u8; 3]; 8] = [
[1, 2, 0],
[6, 9, 8],
[3, 5, 4],
[0, 4, 2],
[6, 8, 7],
[3, 5, 0],
[4, 5, 3],
[6, 2, 0],
];
let row: Vec<[u8; 3]> = pixels[..8].iter().map(|p| [p[0], p[1], p[2]]).collect();
for (case, (got, want)) in row.iter().zip(tenths).enumerate() {
let want = want.map(|t| (f32::from(t) * 25.5).round() as u8);
assert!(
got.iter().zip(want).all(|(g, w)| g.abs_diff(w) <= 1),
"case {case}: {got:?}, expected {want:?}; row {row:?}"
);
}
}
@@ -215,16 +215,19 @@ fn pack(
T::Rgba8Unorm,
World {
atlas: None,
material_maps: None,
lightmap: lightmap.clone(),
terrain: None,
block_ids: &[],
sprite_rects: &[],
celestial: None,
gui_size: [1, 1],
clouds: None,
weather: false,
hand: false,
},
);
)
.expect("the pack runs");
assert!(post.notes.is_empty(), "{:?}", post.notes);
post
}
@@ -351,6 +354,7 @@ fn frame(hud: &HudBatch) -> Frame<'_> {
celestial: &[],
stars: None,
hand: &[],
hand_items: &[],
hand_fov_degrees: 70.0,
screen: &[],
pack: Default::default(),
@@ -35,6 +35,13 @@ pub(super) struct ShadowTargets {
pub depth: (wgpu::Texture, wgpu::TextureView),
/// `shadowtex1`: a copy of `shadowtex0` taken before translucent terrain, when read.
pub depth1: Option<(wgpu::Texture, wgpu::TextureView)>,
/// `shadowtex0`/`1` as a plain sampler filters them: Iris 1.11.7 reads them linearly unless
/// a pack asks for the nearest texel (measured). wgpu filters a depth format only for a
/// comparison, so these are `R32Float` copies of the final depths, when read that way.
pub linear: [Option<wgpu::TextureView>; 2],
/// The rows a depth copy goes through to `linear` (a depth format copies only to a buffer),
/// and their stride in bytes.
staging: Option<(wgpu::Buffer, u32)>,
/// Both sides of each shadowcolor buffer used.
pub color: [Option<[wgpu::TextureView; 2]>; SHADOW_COLORS],
/// The clear colour of each buffer cleared every frame.
@@ -77,13 +84,15 @@ fn target(
impl ShadowTargets {
/// The buffers of `shadow` the pack uses: `colors` (bit n: shadowcolor n), `depth1` when
/// some program reads `shadowtex1`. The resolution is kept within the device's limit.
/// some program reads `shadowtex1`, `linear` (bit n: shadowtex n read through a filtering
/// sampler). The resolution is kept within the device's limit.
pub fn new(
device: &wgpu::Device,
shadow: &horizon_shaders::Shadow,
formats: [wgpu::TextureFormat; SHADOW_COLORS],
colors: u16,
depth1: bool,
linear: u8,
) -> Self {
let size = shadow
.resolution
@@ -105,10 +114,43 @@ impl ShadowTargets {
})
})
});
let linear = [0, 1].map(|n| {
(linear & 1 << n != 0).then(|| {
let label = format!("Shader pack shadowtex{n} (filtered)");
device
.create_texture(&wgpu::TextureDescriptor {
label: Some(&label),
size: wgpu::Extent3d {
width: size,
height: size,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::R32Float,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[],
})
.create_view(&Default::default())
})
});
let staging = linear.iter().any(Option::is_some).then(|| {
let stride = (size * 4).next_multiple_of(wgpu::COPY_BYTES_PER_ROW_ALIGNMENT);
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shader pack shadow depth rows"),
size: u64::from(stride) * u64::from(size),
usage: wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
(buffer, stride)
});
Self {
size,
depth: target(device, "Shader pack shadowtex0", size, depth, copy),
depth1: depth1.then(|| target(device, "Shader pack shadowtex1", size, depth, copy)),
linear,
staging,
color,
clears: std::array::from_fn(|n| {
let b = &shadow.colors[n];
@@ -214,4 +256,38 @@ impl ShadowTargets {
);
}
}
/// `linear`: the filtered copies of the depths the shadow pass left (`shadowtex1` from its
/// copy before translucent geometry).
pub fn copy_linear(&self, encoder: &mut wgpu::CommandEncoder) {
let Some((buffer, stride)) = &self.staging else {
return;
};
for (n, linear) in self.linear.iter().enumerate() {
let Some(linear) = linear else {
continue;
};
let depth = match (n, &self.depth1) {
(1, Some(d)) => &d.0,
_ => &self.depth.0,
};
let rows = wgpu::TexelCopyBufferInfo {
buffer,
layout: wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(*stride),
rows_per_image: None,
},
};
encoder.copy_texture_to_buffer(
wgpu::TexelCopyTextureInfo {
aspect: wgpu::TextureAspect::DepthOnly,
..depth.as_image_copy()
},
rows,
depth.size(),
);
encoder.copy_buffer_to_texture(rows, linear.texture().as_image_copy(), depth.size());
}
}
}
@@ -97,6 +97,7 @@ pub fn inputs() -> horizon_shaders::GbuffersInputs {
// At the far plane: the sky shows only where nothing else drew, and it writes no depth.
after: " gl_Position.z = gl_Position.w;\n".into(),
alpha_test: None,
item_id_declared: false,
}
}
@@ -122,6 +123,7 @@ pub fn celestial_inputs() -> horizon_shaders::GbuffersInputs {
],
after: " gl_Position.z = gl_Position.w;\n".into(),
alpha_test: None,
item_id_declared: false,
}
}
@@ -17,6 +17,8 @@ pub(super) struct PackTexture {
pub(super) sample_type: wgpu::TextureSampleType,
pub(super) blur: bool,
pub(super) clamp: bool,
/// Decoded from a PNG, the pack's or the game's (not a raw dump, not a generated texture).
pub(super) png: bool,
pub(super) image: Image,
}
@@ -30,7 +32,7 @@ pub(super) fn upload(
let features = device.features();
let mut out = Vec::new();
for t in textures {
let (image, dimension, sample_type, blur, clamp) = match t.source {
let (image, dimension, sample_type, blur, clamp, png) = match t.source {
TextureSource::Image(texture) => {
let (view, sample_type) = match create(device, queue, &texture, features) {
Ok(created) => created,
@@ -50,6 +52,7 @@ pub(super) fn upload(
sample_type,
texture.blur,
texture.clamp,
!texture.raw,
)
}
TextureSource::Resource(location) => {
@@ -66,7 +69,14 @@ pub(super) fn upload(
continue;
};
let float = wgpu::TextureSampleType::Float { filterable: true };
(image, wgpu::TextureViewDimension::D2, float, true, true)
(
image,
wgpu::TextureViewDimension::D2,
float,
true,
true,
false,
)
}
};
out.push(PackTexture {
@@ -76,6 +86,7 @@ pub(super) fn upload(
sample_type,
blur,
clamp,
png,
image,
});
}
@@ -285,6 +296,7 @@ mod tests {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
blur: false,
clamp: false,
png: false,
image: Image::Lightmap,
}
}
@@ -297,6 +309,7 @@ mod tests {
texels,
blur: false,
clamp: false,
raw: true,
}
}
@@ -1029,11 +1029,6 @@ mod initial_history_tests {
#[test]
#[ignore = "requires a host GPU; run with --ignored --nocapture"]
fn core_matrix_inputs_render_provider_values() {
use horizon_shaders::{
GbuffersInputs, Macros, Pack, Target, preprocess, translate_gbuffers,
};
use std::io::Write;
use wgpu::util::DeviceExt;
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle());
let adapter = pollster::block_on(instance.request_adapter(&Default::default())).unwrap();
eprintln!("Core matrix GPU: {:?}", adapter.get_info());
@@ -1093,137 +1088,7 @@ mod initial_history_tests {
vec2 p = vec2((gl_VertexID << 1) & 2, gl_VertexID & 2);\n\
gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);\n}}"
);
let mut zip = zip::ZipWriter::new(std::io::Cursor::new(Vec::new()));
for (path, source) in [
("shaders/gbuffers_line.vsh", vertex.as_str()),
(
"shaders/gbuffers_line.fsh",
"#version 330\nin vec3 result; out vec4 color; void main() { color = vec4(result, 1.0); }",
),
] {
zip.start_file(path, zip::write::SimpleFileOptions::default())
.unwrap();
zip.write_all(source.as_bytes()).unwrap();
}
let pack = Pack::from_zip_bytes("matrix-probe.zip", zip.finish().unwrap().into_inner())
.unwrap();
let macros = Macros::standard();
let program = translate_gbuffers(
&preprocess(&pack, "gbuffers_line.vsh", &macros),
&preprocess(&pack, "gbuffers_line.fsh", &macros),
Target::Buffers,
&GbuffersInputs::default(),
)
.unwrap();
let mut bytes = vec![0; program.interface.uniform_size as usize];
for uniform in &program.interface.uniforms {
if let Some(i) = index(&uniform.name) {
values.values[i].write(&uniform.ty, &mut bytes, uniform.offset as usize);
}
}
let uniform = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None,
contents: &bytes,
usage: wgpu::BufferUsages::UNIFORM,
});
let module = |module| {
device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Naga(std::borrow::Cow::Owned(module)),
})
};
let vs = module(program.vertex);
let fs = module(program.fragment);
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
layout: None,
vertex: wgpu::VertexState {
module: &vs,
entry_point: Some("main"),
compilation_options: Default::default(),
buffers: &[],
},
primitive: Default::default(),
depth_stencil: None,
multisample: Default::default(),
fragment: Some(wgpu::FragmentState {
module: &fs,
entry_point: Some("main"),
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba8Unorm,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview_mask: None,
cache: None,
});
let group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &pipeline.get_bind_group_layout(0),
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: uniform.as_entire_binding(),
}],
});
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
view_formats: &[],
});
let view = texture.create_view(&Default::default());
let read = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 256,
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
mapped_at_creation: false,
});
let mut encoder = device.create_command_encoder(&Default::default());
{
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
depth_slice: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: wgpu::StoreOp::Store,
},
})],
..Default::default()
});
pass.set_pipeline(&pipeline);
pass.set_bind_group(0, &group, &[]);
pass.draw(0..3, 0..1);
}
encoder.copy_texture_to_buffer(
texture.as_image_copy(),
wgpu::TexelCopyBufferInfo {
buffer: &read,
layout: wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(256),
rows_per_image: None,
},
},
texture.size(),
);
queue.submit([encoder.finish()]);
read.slice(..)
.map_async(wgpu::MapMode::Read, |result| result.unwrap());
device.poll(wgpu::PollType::wait_indefinitely()).unwrap();
let mapped = read.slice(..).get_mapped_range().unwrap();
let mapped = draw_line_program(&device, &queue, &values, &vertex);
// Independent result: translated point (1, .5, -2), perspective (aspect 2,
// vertical FOV 90): clip (.5, .5, z, 2), hence (.25, .25, .5, 1).
for (actual, expected) in mapped[..4].iter().zip(expected) {
@@ -1236,6 +1101,203 @@ mod initial_history_tests {
}
}
/// `normalMatrix` in a core-profile gbuffers program is the view's rotation; in the
/// compatibility profile it stays zero (the issue's Iris 1.11.7 probes, #9136).
#[test]
#[ignore = "requires a host GPU; run with --ignored --nocapture"]
fn core_normal_matrix_renders_the_view_rotation() {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle());
let adapter = pollster::block_on(instance.request_adapter(&Default::default())).unwrap();
eprintln!("Normal matrix GPU: {:?}", adapter.get_info());
let (device, queue) =
pollster::block_on(adapter.request_device(&Default::default())).unwrap();
let hud = HudBatch::new([800, 400]);
let mut f = frame(&hud);
// Facing west (-X), level: view space looks down -Z with +X to the right (north).
f.yaw = 90.0;
f.pitch = 0.0;
let mut values = FrameUniforms::default();
values.update(&f, &PackInputs::default(), [800, 400]);
for (profile, world, expected) in [
// East (behind the camera) is view +Z.
("330 core", "vec3(1.0, 0.0, 0.0)", [128u8, 128, 255, 255]),
// South (to the left) is view -X; up stays up.
("330 core", "vec3(0.0, 0.0, 1.0)", [0, 128, 128, 255]),
("330 core", "vec3(0.0, 1.0, 0.0)", [128, 255, 128, 255]),
(
"330 compatibility",
"vec3(1.0, 0.0, 0.0)",
[128, 128, 128, 255],
),
] {
let vertex = format!(
"#version {profile}\nuniform mat3 normalMatrix;\nout vec3 result;\n\
void main() {{\n\
result = normalMatrix * {world} * 0.5 + 0.5;\n\
vec2 p = vec2((gl_VertexID << 1) & 2, gl_VertexID & 2);\n\
gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);\n}}"
);
let pixel = draw_line_program(&device, &queue, &values, &vertex);
eprintln!("{profile} {world}: {pixel:?}");
for (actual, expected) in pixel.iter().zip(expected) {
assert!(
actual.abs_diff(expected) <= 1,
"{profile} {world}: {pixel:?}"
);
}
}
}
/// Draws one fragment through a translated `gbuffers_line` program with `vertex` and the
/// provider's `values`, and reads it back.
fn draw_line_program(
device: &wgpu::Device,
queue: &wgpu::Queue,
values: &FrameUniforms,
vertex: &str,
) -> [u8; 4] {
use horizon_shaders::{
GbuffersInputs, Macros, Pack, Target, preprocess, translate_gbuffers,
};
use std::io::Write;
use wgpu::util::DeviceExt;
let mut zip = zip::ZipWriter::new(std::io::Cursor::new(Vec::new()));
for (path, source) in [
("shaders/gbuffers_line.vsh", vertex),
(
"shaders/gbuffers_line.fsh",
"#version 330\nin vec3 result; out vec4 color; void main() { color = vec4(result, 1.0); }",
),
] {
zip.start_file(path, zip::write::SimpleFileOptions::default())
.unwrap();
zip.write_all(source.as_bytes()).unwrap();
}
let pack =
Pack::from_zip_bytes("matrix-probe.zip", zip.finish().unwrap().into_inner()).unwrap();
let macros = Macros::standard();
let program = translate_gbuffers(
&preprocess(&pack, "gbuffers_line.vsh", &macros),
&preprocess(&pack, "gbuffers_line.fsh", &macros),
Target::Buffers,
&GbuffersInputs::default(),
)
.unwrap();
let mut bytes = vec![0; program.interface.uniform_size as usize];
for uniform in &program.interface.uniforms {
if let Some(i) = index(&uniform.name) {
values.values[i].write(&uniform.ty, &mut bytes, uniform.offset as usize);
}
}
let uniform = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None,
contents: &bytes,
usage: wgpu::BufferUsages::UNIFORM,
});
let module = |module| {
device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Naga(std::borrow::Cow::Owned(module)),
})
};
let vs = module(program.vertex);
let fs = module(program.fragment);
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
layout: None,
vertex: wgpu::VertexState {
module: &vs,
entry_point: Some("main"),
compilation_options: Default::default(),
buffers: &[],
},
primitive: Default::default(),
depth_stencil: None,
multisample: Default::default(),
fragment: Some(wgpu::FragmentState {
module: &fs,
entry_point: Some("main"),
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba8Unorm,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview_mask: None,
cache: None,
});
let group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &pipeline.get_bind_group_layout(0),
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: uniform.as_entire_binding(),
}],
});
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
view_formats: &[],
});
let view = texture.create_view(&Default::default());
let read = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 256,
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
mapped_at_creation: false,
});
let mut encoder = device.create_command_encoder(&Default::default());
{
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
depth_slice: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: wgpu::StoreOp::Store,
},
})],
..Default::default()
});
pass.set_pipeline(&pipeline);
pass.set_bind_group(0, &group, &[]);
pass.draw(0..3, 0..1);
}
encoder.copy_texture_to_buffer(
texture.as_image_copy(),
wgpu::TexelCopyBufferInfo {
buffer: &read,
layout: wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(256),
rows_per_image: None,
},
},
texture.size(),
);
queue.submit([encoder.finish()]);
read.slice(..)
.map_async(wgpu::MapMode::Read, |result| result.unwrap());
device.poll(wgpu::PollType::wait_indefinitely()).unwrap();
let mapped = read.slice(..).get_mapped_range().unwrap();
let pixel = mapped[..4].try_into().unwrap();
drop(mapped);
read.unmap();
pixel
}
fn int(u: &FrameUniforms, at: usize) -> i32 {
match u.values[at] {
Value::I(v) => v[0],
@@ -1411,6 +1473,7 @@ mod initial_history_tests {
celestial: &[],
stars: None,
hand: &[],
hand_items: &[],
hand_fov_degrees: 70.0,
screen: &[],
pack: Default::default(),
@@ -44,6 +44,7 @@ pub fn inputs() -> horizon_shaders::GbuffersInputs {
op: ">",
reference: 0.1,
}),
item_id_declared: false,
}
}
@@ -67,6 +67,17 @@ impl Readback {
let Some(texture) = self.texture.clone() else {
return;
};
self.draw_onto(device, encoder, &texture, surface);
}
/// Draws `frame` onto the swapchain image (or offscreen target) `surface`.
pub fn draw_onto(
&mut self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
frame: &wgpu::Texture,
surface: &wgpu::Texture,
) {
// The frame's passes write the swapchain through its non-sRGB view
// (`view_formats`), so the copy goes there unconverted too.
let format = surface.format().remove_srgb_suffix();
@@ -74,7 +85,7 @@ impl Readback {
format: Some(format),
..Default::default()
});
self.blit(device, encoder, &texture, &target, format);
self.blit(device, encoder, frame, &target, format);
}
/// `frame` (a 16-bit frame) in an 8-bit texture of `format`, clamped to 0..1
@@ -92,7 +92,7 @@ impl<T> Resident<T> {
self.items.iter_mut().map(|(_, v)| v)
}
/// Every section's key, value and bounds.
#[cfg(debug_assertions)]
#[cfg(any(test, debug_assertions))]
pub fn iter(&self) -> impl Iterator<Item = (SectionKey, &T, Option<[Vec3; 2]>)> {
let items = self.items.iter().zip(&self.bounds);
items.map(|((key, value), bounds)| (*key, value, *bounds))
@@ -194,6 +194,12 @@ impl<T> Resident<T> {
column.low = column.low.min(key.1);
column.high = column.high.max(key.1);
}
/// Marks or unmarks the section at `key`, if it is resident.
pub fn set_marked(&mut self, key: SectionKey, marked: bool) {
if let Some(&slot) = self.slots.get(&key) {
self.mark_slot(slot, marked);
}
}
/// Marks or unmarks the section in `slot`, in its column's list too.
fn mark_slot(&mut self, slot: u32, marked: bool) {
let was = std::mem::replace(&mut self.marked[slot as usize], marked);
@@ -657,7 +663,8 @@ mod tests {
(!value.is_multiple_of(3)).then(|| [Vec3::splat((value % 5) as f32), Vec3::splat(16.0)])
};
// A deterministic mix of inserts, replacements and removals, marked at
// random: a replacement can mark or unmark its section.
// random: a replacement can mark or unmark its section, and so can a
// mark set alone (`set_marked`), which leaves a key not resident alone.
let mut seed = 0x2545_f491_u32;
for step in 0..4000u32 {
seed ^= seed << 13;
@@ -694,6 +701,12 @@ mod tests {
assert_eq!(removed, expected);
} else if (seed >> 12).is_multiple_of(3) {
assert_eq!(r.remove(key), model.remove(&key).map(|(value, _)| value));
} else if (seed >> 12).is_multiple_of(5) {
let marked = (seed >> 20).is_multiple_of(2);
r.set_marked(key, marked);
if let Some(held) = model.get_mut(&key) {
held.1 = marked;
}
} else {
let marked = (seed >> 20).is_multiple_of(2);
r.insert_marked(key, step, bounds(step), marked);
@@ -1050,7 +1050,9 @@ impl Item {
Item::Horizon(H::Hdr) => g.hdr,
Item::Horizon(H::BatteryMode) => g.battery,
Item::Horizon(H::ShadowCache) => g.shadow_cache,
Item::Horizon(H::TranslucentBatching) => g.translucent_batching,
Item::Horizon(H::TranslucentBatching) => {
g.translucent_batching && crate::graphics::translucent_batching_offered()
}
Item::Horizon(H::HdrSmoothGradients) => {
g.hdr_smooth_gradients && crate::graphics::smooth_gradients_offered()
}
@@ -3164,13 +3166,13 @@ mod tests {
assert_eq!(page, Some(Page::Advanced));
let mut s = Settings::default();
s.horizon.preset = Preset::Balanced;
assert_eq!(item.switch(&s), Some(true));
let mut ui = GraphicsUi::default();
// Where the device can't, the saved On reads Off, and neither a click nor Enter
// after it does anything: no sound, no change, no save.
crate::graphics::offer_translucent_batching(false);
assert!(item.blocked(&s).is_some());
assert_eq!(item.label(&s).plain(), "Translucent Batching: Off");
assert_eq!(item.switch(&s), Some(false));
let r = row(&mut ui, &s, item);
let point = (r[0] + 4.0, r[1] + 4.0);
assert!(press(&mut ui, &mut s, point).is_empty());
@@ -3181,6 +3183,7 @@ mod tests {
// Where it can, a click switches it and keeps the preset.
crate::graphics::offer_translucent_batching(true);
assert_eq!(item.label(&s).plain(), "Translucent Batching: On");
assert_eq!(item.switch(&s), Some(true));
assert_eq!(
press(&mut ui, &mut s, point),
[Action::ButtonClick, Action::Changed]
+42 -10
View File
@@ -796,10 +796,10 @@ impl HudAtlas {
let g = self.font.glyph(ch);
g.rect.map(|rect| (rect, g.advance))
}
/// A font glyph's atlas cell (none for blank glyphs such as the space) and advance.
pub fn glyph_cell(&self, ch: char) -> (Option<Rect>, f32) {
let g = self.font.glyph(ch);
(g.rect, g.advance)
/// A font glyph in the default font: its atlas cell (none for blank
/// glyphs such as the space), its box and advance.
pub fn glyph_cell(&self, ch: char) -> crate::font::Glyph {
self.font.glyph(ch)
}
pub fn build(root: &Path) -> Result<Self> {
Self::build_over(root, root)
@@ -887,13 +887,39 @@ impl HudAtlas {
));
}
/// Only the font's metrics, for a game that draws nothing (the
/// integrated server): text measures as with the full atlas, without
/// holding its textures. The pixels are dropped once the glyph widths are
/// read, since nothing uploads them.
/// integrated or dedicated server): text measures as with the full atlas,
/// without packing textures or rasterizing glyphs.
pub fn fonts(root: &Path) -> Result<Self> {
let mut atlas = Self::pack(root, root, &["font"], false, false)?;
atlas.pixels = Vec::new();
Ok(atlas)
let white = Rect {
u0: 0.0,
v0: 0.0,
u1: 1.0,
v1: 1.0,
w: 1,
h: 1,
};
Ok(Self {
revision: Self::next_revision(),
pixels: Vec::new(),
packed: None,
galactic_widths: [0; 128],
width: 1,
height: 1,
sprites: HashMap::new(),
object_skins: HashMap::new(),
translucent: HashSet::new(),
flags: HashMap::new(),
sprite_animations: Vec::new(),
font: crate::font::Font::metrics(root),
glint: (Vec::new(), 1, 1),
glint_armor: (Vec::new(), 1, 1),
text_shader: None,
lines_shader: None,
random_textures: Default::default(),
emissive: Arc::default(),
blinks: Default::default(),
white,
})
}
/// The boot screen's atlas, before any pack loads: the theme's run-time
/// glyphs (`horizon/ui-text`), a white sprite and the glyph rows over
@@ -1274,6 +1300,12 @@ impl HudAtlas {
edition.3 = pixels;
}
images.push(("horizon/white".into(), 4, 4, vec![255; 64]));
images.push((
crate::pad_cursor::SPRITE.into(),
crate::pad_cursor::SIZE,
crate::pad_cursor::SIZE,
crate::pad_cursor::pixels(),
));
if full {
Self::slots(&mut images, crate::trims::slot_size(root), mod_image);
// MissingTextureAtlasSprite.generateMissingImage: 16x16, magenta
+1
View File
@@ -74,6 +74,7 @@ pub mod overlay;
pub mod pack_browser;
pub mod pack_screen;
pub mod packs;
pub mod pad_cursor;
pub mod par;
pub mod particles;
pub mod poi_sections;
+341 -48
View File
@@ -77,6 +77,110 @@ const HELD_SAMPLE_STEP: std::time::Duration = std::time::Duration::from_millis(5
/// How long a tile whose sampling failed (`LodSampler::grid` gave no
/// columns: a server refused or lost it) waits before it is asked again.
const RETRY: std::time::Duration = std::time::Duration::from_secs(2);
/// The most a tile a zoomed view magnifies may split past the plan it would
/// have had ([`ZoomAim`]): cells three times smaller, so nine times the tiles
/// where it applies. A spyglass covers about a sixteenth of the circle, so at
/// the 4078 tiles and ~1500 quads a tile measured at `lodDistance` 256 this
/// asks for roughly 45% more far tiles while the zoom is held, and gives them
/// back when it is released. Four would be about 85%: raise it only with a
/// measurement, because the cost grows with the square.
const MAX_ZOOM_BOOST: i32 = 3;
/// Yaw is rounded to this many degrees before it reaches the plan, so turning
/// the mouse does not rebuild the quadtree every frame. The cone is widened by
/// as much, so rounding never swings it off a tile being looked at.
const ZOOM_YAW_STEP: f32 = 5.0;
/// Where a zoomed view asks for finer far tiles (the `lodZoomDetail` extra).
///
/// `Plan::visit`'s falloff keeps a tile's cells roughly the same size on
/// screen, which silently assumes the field of view never changes. A spyglass
/// (`SpyglassItem`, a tenth of it) or the zoom mod breaks that: the same tile
/// covers many times more pixels, so its cells look as coarse as a tile ten
/// times nearer would. This asks the tiles the zoom actually magnifies to
/// split further.
///
/// Only those: lifting the whole circle would ask for about a hundred times
/// the tiles, which at the quads measured above is gigabytes of vertices. The
/// cone is the narrowed view's, and `MAX_ZOOM_BOOST` caps how far it goes.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ZoomAim {
/// The view direction in x/z, in thousandths of a unit vector.
dir: [i32; 2],
/// The cosine of the cone's half-angle, in thousandths.
cos_half: i32,
/// How many times further than the unzoomed plan a tile inside the cone
/// may split. Two or more; [`ZoomAim::new`] gives `None` for less.
boost: i32,
}
impl ZoomAim {
/// `fov_degrees` is the zoomed field of view and `magnification` how many
/// times the view is magnified (1 when it is not). `None` when the zoom is
/// too slight to be worth replanning for.
pub fn new(yaw: f32, fov_degrees: f32, magnification: f32) -> Option<Self> {
if !magnification.is_finite() || !fov_degrees.is_finite() || fov_degrees <= 0.0 {
return None;
}
let boost = (magnification.round() as i64).clamp(1, i64::from(MAX_ZOOM_BOOST)) as i32;
if boost < 2 {
return None;
}
// Half the field of view spans the screen's width; three quarters of
// it reaches the corners, and the rounding step is added on top.
let half = (fov_degrees * 0.75 + ZOOM_YAW_STEP).clamp(5.0, 90.0);
let radians = ((yaw / ZOOM_YAW_STEP).round() * ZOOM_YAW_STEP).to_radians();
// `view_vector`'s x and z: yaw 0 looks toward +z.
let thousandths = |v: f32| (v * 1000.0).round() as i32;
Some(Self {
dir: [thousandths(-radians.sin()), thousandths(radians.cos())],
cos_half: thousandths(half.to_radians().cos()),
boost,
})
}
/// Whether the cone reaches `key`, so the zoom magnifies it.
///
/// The tile is taken as the circle around its centre that covers it, and
/// that circle's own angular radius widens the cone for the test, so a
/// tile straddling the edge counts in rather than out. A tile whose circle
/// reaches the camera is always in: there is no direction to compare, and
/// it fills the view whichever way the player faces.
fn covers(&self, key: TileKey, camera: [i32; 2]) -> bool {
let [ox, _, oz] = key.origin();
let size = key.size();
// The camera stands in the tile: it is in view whichever way the
// player faces, and there is no direction to compare it with. Tested
// on the box rather than on the circle below, whose radius a camera
// on a corner matches exactly -- a float coin-toss.
// In i64: a level 22 tile is 67 million blocks wide, so the sum
// at the far edge of the world overflows an i32.
let (ox64, oz64, size64) = (i64::from(ox), i64::from(oz), i64::from(size));
if (ox64..ox64 + size64).contains(&i64::from(camera[0]))
&& (oz64..oz64 + size64).contains(&i64::from(camera[1]))
{
return true;
}
let half = f64::from(size) / 2.0;
let cx = f64::from(ox) + half - f64::from(camera[0]);
let cz = f64::from(oz) + half - f64::from(camera[1]);
let distance = (cx * cx + cz * cz).sqrt();
let radius = half * std::f64::consts::SQRT_2;
if distance <= radius {
return true;
}
let dir = [
f64::from(self.dir[0]) / 1000.0,
f64::from(self.dir[1]) / 1000.0,
];
let cos = (cx * dir[0] + cz * dir[1]) / distance;
let sin = (1.0 - cos * cos).max(0.0).sqrt();
// cos(angle - tile radius), expanded so no inverse trigonometry runs
// per tile: the quadtree asks this of every node it visits.
let radius_sin = (radius / distance).min(1.0);
let radius_cos = (1.0 - radius_sin * radius_sin).max(0.0).sqrt();
cos * radius_cos + sin * radius_sin >= f64::from(self.cos_half) / 1000.0
}
}
/// The coarsest tile level: `Plan` roots cover at most `MAX_CHUNKS` chunks
/// around (a level-22 tile is 67 million blocks wide: the whole world).
pub(crate) const MAX_LEVEL: u8 = 22;
@@ -598,6 +702,8 @@ struct Plan {
/// The squared distance within which planned tiles load before the
/// coarse backdrop (`wanted_tiles`).
first: i64,
/// Where a zoomed view asks for finer tiles, if it does ([`ZoomAim`]).
aim: Option<ZoomAim>,
}
/// Chunks past the render distance where only planned tiles are drawn (two
/// past the last near chunk, `streaming::in_view_distance`), and where they
@@ -607,7 +713,7 @@ const FIRST_CHUNKS: i32 = 4;
impl Plan {
#[cfg(test)]
fn new(camera: [i32; 2], detail: i32, distance: u32) -> Self {
Self::with_previous(camera, detail, distance, &Self::default(), 0, false)
Self::with_previous(camera, detail, distance, &Self::default(), 0, false, None)
}
/// `reduced`: Reduced far detail (`graphics::FarDetail`), each level from
/// half the distance.
@@ -618,6 +724,7 @@ impl Plan {
previous: &Self,
elevation: i32,
reduced: bool,
aim: Option<ZoomAim>,
) -> Self {
let range = distance.min(MAX_CHUNKS) as i32 * 16;
let mut level = 0;
@@ -630,6 +737,7 @@ impl Plan {
camera,
near: i64::from((detail + SEAM_CHUNKS) * 16).pow(2),
first: i64::from((detail + FIRST_CHUNKS) * 16).pow(2),
aim,
..Self::default()
};
for z in (camera[1] - range).div_euclid(size)..=(camera[1] + range).div_euclid(size) {
@@ -665,6 +773,14 @@ impl Plan {
// Beyond the old 1024-chunk limit the cells are as large as `reduced`'s:
// the whole world is 22 levels, not all of them worth full cells.
let world = key.distance(camera) >= WORLD_START;
// A zoomed view magnifies the tiles its cone covers, so those split
// further; every other tile keeps the plan it would have had
// ([`ZoomAim`]). The world-wide threshold is boosted too: the horizon is
// where a spyglass shows the coarsest cells of all.
let boost = match self.aim {
Some(aim) if aim.covers(key, camera) => i64::from(aim.boost),
_ => 1,
};
let threshold = i64::from(key.size())
* i64::from(if world {
WORLD_SPLIT
@@ -672,7 +788,8 @@ impl Plan {
split / 2
} else {
split
});
})
* boost;
// The 8 to 10 band above is wide already. The ring of full detail
// around the camera and the world-wide threshold have none: a tile
// split last plan stays split until it is 5% past them, or it
@@ -875,7 +992,16 @@ struct MeshInput {
}
struct ReducedColumns {
columns: Arc<[Column; 256]>,
levels: [Vec<Column>; 4],
/// The chunk reduced to cells 2, 4, 8 and 16 blocks wide, each built the
/// first time a tile of that width asks for it.
///
/// One chunk is covered by one far tile, so one of the four is what gets
/// read, and a chunk whose detail arrives while full-detail terrain still
/// draws it reads none. Building all four up front cost 405 us of a far
/// terrain thread for every chunk the world sent (`Column::reduced` walks
/// every span of the footprint four times over), and held 85 columns
/// beside the chunk's 256.
levels: [std::sync::OnceLock<Vec<Column>>; 4],
}
impl MeshInput {
/// The column of every cell: the tile's own samples unless the snapshot
@@ -905,25 +1031,26 @@ impl MeshInput {
}
impl ReducedColumns {
fn new(columns: Arc<[Column; 256]>) -> Self {
let levels = std::array::from_fn(|level| {
let width = 2 << level;
Self {
columns,
levels: std::array::from_fn(|_| std::sync::OnceLock::new()),
}
}
fn get(&self, x: usize, z: usize, width: usize) -> &Column {
if width == 1 {
return &self.columns[z * 16 + x];
}
let level = width.trailing_zeros() as usize - 1;
let reduced = self.levels[level].get_or_init(|| {
let columns = &self.columns;
(0..16 / width)
.flat_map(|z| {
let columns = &columns;
(0..16 / width)
.map(move |x| Column::reduced(columns, x * width, z * width, width))
})
.collect()
});
Self { columns, levels }
}
fn get(&self, x: usize, z: usize, width: usize) -> &Column {
if width == 1 {
&self.columns[z * 16 + x]
} else {
&self.levels[width.trailing_zeros() as usize - 1]
[(z / width) * (16 / width) + x / width]
}
&reduced[(z / width) * (16 / width) + x / width]
}
}
enum Job {
@@ -1049,7 +1176,7 @@ pub struct Lod {
context: Option<Arc<WorkerContext>>,
/// A finished mesh job may have changed the selection of its ancestors.
reselect: bool,
area: Option<([i32; 2], i32, u32, i32, bool)>,
area: Option<([i32; 2], i32, u32, i32, bool, Option<ZoomAim>)>,
plan: Plan,
/// Reduced far detail (`set_reduced_detail`).
reduced_detail: bool,
@@ -2210,29 +2337,35 @@ impl Lod {
self.shared.wake.notify_all();
}
}
/// `aim` asks the tiles a zoomed view magnifies for finer detail
/// ([`ZoomAim`]); `None` plans exactly as before.
pub fn update(
&mut self,
camera: [f64; 3],
detail: i32,
distance: u32,
covered: Option<FxHashSet<(i32, i32)>>,
aim: Option<ZoomAim>,
) -> Vec<(TileKey, SectionMesh)> {
let elevation =
(((camera[1] - f64::from(MIN_Y + crate::chunk::HEIGHT)).clamp(0., 16384.)) as i32 / 32)
* 32;
let camera = [camera[0].floor() as i32, camera[2].floor() as i32];
// Replan at chunk boundaries, not on every camera movement.
// `ZoomAim` rounds its yaw, so a zoom held still keeps one area and the
// quadtree is not rebuilt as the player nudges the mouse.
let area = (
[camera[0].div_euclid(16), camera[1].div_euclid(16)],
detail,
distance,
elevation,
self.reduced_detail,
aim,
);
if self.area != Some(area) {
crate::spans::time("lod_plan", || {
self.area = Some(area);
self.replan(camera, detail, distance, elevation);
self.replan(camera, detail, distance, elevation, aim);
});
}
let mut meshes = Vec::new();
@@ -2255,7 +2388,14 @@ impl Lod {
}
/// Rebuilds the quadtree and the selection it implies. Only runs when the
/// planned area changes (a chunk boundary), never per finished sample.
fn replan(&mut self, camera: [i32; 2], detail: i32, distance: u32, elevation: i32) {
fn replan(
&mut self,
camera: [i32; 2],
detail: i32,
distance: u32,
elevation: i32,
aim: Option<ZoomAim>,
) {
let plan = Plan::with_previous(
camera,
detail,
@@ -2263,6 +2403,7 @@ impl Lod {
&self.plan,
elevation,
self.reduced_detail,
aim,
);
// A tile that leaves the range before it was drawn stops being
// unknown to its neighbours (`Snapshot::planned_leaf`): they remesh
@@ -2952,6 +3093,104 @@ fn retiring_detail<'a>(
}
#[cfg(test)]
mod tests {
/// A spyglass or the zoom mod magnifies only what the narrowed view
/// covers, so `ZoomAim` asks for finer tiles there and nowhere else. Without
/// that cone the same request would reach the whole circle, which at the
/// measured quads a tile is gigabytes of vertices.
#[test]
fn a_zoomed_view_asks_for_finer_tiles_only_where_it_looks() {
// Yaw 0 looks toward +z (`view_vector`), ten times magnified.
let aim = ZoomAim::new(0.0, 7.0, 10.0).expect("a tenth of the field of view zooms");
// Capped, however far the zoom goes.
assert_eq!(aim.boost, MAX_ZOOM_BOOST);
let camera = [0, 0];
let tile = |x, z, level| TileKey { x, z, level };
// Straight ahead, far enough that the direction decides it.
assert!(aim.covers(tile(0, 8, 4), camera), "ahead");
// Behind, beside, and the opposite diagonal: the zoom does not
// magnify these, so they keep the plan they would have had.
assert!(!aim.covers(tile(0, -9, 4), camera), "behind");
assert!(!aim.covers(tile(8, 0, 4), camera), "beside");
assert!(!aim.covers(tile(-9, -9, 4), camera), "diagonally behind");
// A tile the camera stands in is in view whichever way it faces.
assert!(aim.covers(tile(0, 0, 4), camera), "underfoot");
}
/// A zoom too slight to change which level a tile wants is not worth
/// rebuilding the quadtree for, and an unzoomed view must plan exactly as
/// it did before this feature.
#[test]
fn a_slight_zoom_does_not_replan() {
assert_eq!(ZoomAim::new(0.0, 70.0, 1.0), None, "not zoomed");
assert_eq!(
ZoomAim::new(0.0, 60.0, 1.2),
None,
"a fifth is under one level"
);
assert!(ZoomAim::new(0.0, 35.0, 2.0).is_some(), "twice is worth it");
// Nothing finite comes of a broken field of view.
assert_eq!(ZoomAim::new(0.0, f32::NAN, 4.0), None);
assert_eq!(ZoomAim::new(0.0, 0.0, 4.0), None);
assert_eq!(ZoomAim::new(0.0, 70.0, f32::INFINITY), None);
}
/// The cone turns with the player, and its yaw is rounded so that a
/// mouse-twitch does not rebuild the quadtree: two aims within the
/// rounding step compare equal, and `Lod::update` replans on that.
#[test]
fn the_cone_turns_and_rounds_with_the_view() {
let camera = [0, 0];
let east = ZoomAim::new(-90.0, 7.0, 10.0).expect("zoomed");
let tile = |x, z| TileKey { x, z, level: 4 };
// Yaw -90 looks toward +x.
assert!(east.covers(tile(8, 0), camera), "ahead when facing +x");
assert!(!east.covers(tile(0, 8), camera), "+z is no longer ahead");
// Within the rounding step: the same aim, so no replan.
assert_eq!(ZoomAim::new(0.0, 7.0, 10.0), ZoomAim::new(1.0, 7.0, 10.0));
// Past it: a different aim, so the cone follows.
assert_ne!(ZoomAim::new(0.0, 7.0, 10.0), ZoomAim::new(20.0, 7.0, 10.0));
}
/// Inside the cone a tile splits further than it would unzoomed, and
/// outside it the plan is unchanged, tile for tile.
#[test]
fn the_plan_is_finer_ahead_and_untouched_behind() {
let camera = [0, 0];
let plain = Plan::with_previous(camera, 12, 256, &Plan::default(), 0, false, None);
let zoomed = Plan::with_previous(
camera,
12,
256,
&Plan::default(),
0,
false,
ZoomAim::new(0.0, 7.0, 10.0),
);
// Finer somewhere: the zoomed plan holds tiles the plain one split
// no further.
assert!(
zoomed.nodes.len() > plain.nodes.len(),
"zoomed {} tiles, plain {}",
zoomed.nodes.len(),
plain.nodes.len()
);
// Behind the camera the two agree. Tiles there are the same level in
// both, so every plain node well behind is still a node when zoomed.
let behind = |key: &&TileKey| {
let [_, _, oz] = key.origin();
oz + key.size() < -1024
};
let plain_behind: std::collections::BTreeSet<_> =
plain.nodes.iter().filter(behind).copied().collect();
let zoomed_behind: std::collections::BTreeSet<_> =
zoomed.nodes.iter().filter(behind).copied().collect();
assert!(!plain_behind.is_empty(), "nothing behind to compare");
assert_eq!(
plain_behind, zoomed_behind,
"the zoom changed the plan behind the player"
);
}
use super::*;
use crate::mesher::TerrainVertex;
#[test]
@@ -3023,7 +3262,8 @@ mod tests {
for camera in [[0, 0], [100, -40]] {
for distance in [64, 128, 512, 1024] {
let full = Plan::new(camera, 12, distance);
let reduced = Plan::with_previous(camera, 12, distance, &Plan::default(), 0, true);
let reduced =
Plan::with_previous(camera, 12, distance, &Plan::default(), 0, true, None);
// Never finer than Full anywhere, and as fine next to the
// full-detail area, whose edge the finest tiles match.
assert!(reduced.split.is_subset(&full.split));
@@ -3083,10 +3323,24 @@ mod tests {
([115, 135], 49_000_000),
([-30_000_000, 29_999_000], 90),
] {
let plan =
Plan::with_previous(camera, 12, MAX_CHUNKS, &Plan::default(), elevation, false);
let reduced =
Plan::with_previous(camera, 12, MAX_CHUNKS, &Plan::default(), elevation, true);
let plan = Plan::with_previous(
camera,
12,
MAX_CHUNKS,
&Plan::default(),
elevation,
false,
None,
);
let reduced = Plan::with_previous(
camera,
12,
MAX_CHUNKS,
&Plan::default(),
elevation,
true,
None,
);
let top = plan.nodes.iter().map(|k| k.level).max().unwrap();
assert_eq!(top, MAX_LEVEL, "{camera:?} y={elevation}: roots stop short");
assert!(
@@ -3113,7 +3367,7 @@ mod tests {
#[test]
fn aerial_distance_avoids_unseen_full_detail_outside_the_near_area() {
let ground = Plan::new([0, 0], 12, 1024);
let high = Plan::with_previous([0, 0], 12, 1024, &Plan::default(), 1024, false);
let high = Plan::with_previous([0, 0], 12, 1024, &Plan::default(), 1024, false, None);
assert!(high.nodes.len() < ground.nodes.len());
assert!(
high.nodes.contains(&TileKey {
@@ -3168,7 +3422,7 @@ mod tests {
// stand-in for a covered column, and only the roots are sampled.
let camera = [8, 8];
let before = Plan::new(camera, 64, 64);
let after = Plan::with_previous(camera, 8, 64, &before, 0, false);
let after = Plan::with_previous(camera, 8, 64, &before, 0, false, None);
let mut ready: FxHashMap<TileKey, ()> = before
.nodes
.iter()
@@ -3207,7 +3461,7 @@ mod tests {
#[test]
fn detail_survives_movement_across_a_split_threshold() {
let before = Plan::new([0, 0], 2, 256);
let after = Plan::with_previous([16, 0], 2, 256, &before, 0, false);
let after = Plan::with_previous([16, 0], 2, 256, &before, 0, false, None);
let mut retained = 0;
let range = i64::from(256 * 16);
for key in &before.split {
@@ -3232,12 +3486,12 @@ mod tests {
let before = Plan::new([0, 0], 40, 256);
assert!(before.split.contains(&tile));
// 670 blocks: past the ring, inside 5% of it.
let held = Plan::with_previous([-30, 0], 40, 256, &before, 0, false);
let held = Plan::with_previous([-30, 0], 40, 256, &before, 0, false, None);
assert!(held.split.contains(&tile));
// Not held without having been split: it was never in the ring.
assert!(!Plan::new([-30, 0], 40, 256).split.contains(&tile));
// 700 blocks: past the margin.
let gone = Plan::with_previous([-60, 0], 40, 256, &before, 0, false);
let gone = Plan::with_previous([-60, 0], 40, 256, &before, 0, false, None);
assert!(!gone.split.contains(&tile));
}
#[test]
@@ -3348,6 +3602,44 @@ mod tests {
"a crown over one corner of a wide cell still sits on its trunk"
);
}
/// A chunk is reduced to the one cell width that reads it, when it does.
///
/// Reducing to all four widths up front cost 405 us of a far terrain
/// thread for every chunk the world sent, where at most one is read.
#[test]
fn a_chunk_is_reduced_only_to_the_width_that_reads_it() {
let built = |reduced: &ReducedColumns| {
reduced
.levels
.iter()
.enumerate()
.filter(|(_, level)| level.get().is_some())
.map(|(level, _)| 2usize << level)
.collect::<Vec<_>>()
};
let reduced = ReducedColumns::new(Arc::new(std::array::from_fn(|i| Column {
surface: None,
spans: vec![Span {
min: 0,
max: 4 + (i % 3) as i16,
state: BlockStateId(1),
biome: 0,
light: 240,
}]
.into(),
})));
assert!(built(&reduced).is_empty(), "nothing is reduced unread");
// A tile of full-detail cells reads the chunk's own columns.
reduced.get(3, 5, 1);
assert!(built(&reduced).is_empty());
reduced.get(4, 8, 4);
assert_eq!(built(&reduced), [4]);
// Read again, and the reduction is the one already built.
reduced.get(0, 0, 4);
assert_eq!(built(&reduced), [4]);
reduced.get(0, 0, 16);
assert_eq!(built(&reduced), [4, 16]);
}
/// Detail read before a refresh over its chunk is dropped (asked
/// again); elsewhere, or read after, it is kept.
#[test]
@@ -3701,7 +3993,7 @@ mod tests {
// Exercise the actual worker/update seam: a parent stays displayed
// until all four child meshes can replace it in the same update.
let camera = [-256.0, 100.0, -256.0];
lod.area = Some(([-16, -16], 2, 64, 0, false));
lod.area = Some(([-16, -16], 2, 64, 0, false, None));
lod.plan.roots = vec![key];
lod.plan.nodes.insert(key);
// As a replan does (`update` only selects after a change).
@@ -3709,7 +4001,7 @@ mod tests {
let await_meshes = |lod: &mut Lod| {
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5);
loop {
let meshes = lod.update(camera, 2, 64, None);
let meshes = lod.update(camera, 2, 64, None, None);
if !meshes.is_empty() {
return meshes;
}
@@ -3727,7 +4019,7 @@ mod tests {
lod.ready.insert(child, lod.ready[&key].clone());
}
lod.reselect = true;
assert!(lod.update(camera, 2, 64, None).is_empty());
assert!(lod.update(camera, 2, 64, None, None).is_empty());
assert!(
lod.active.contains(&key),
"parent retired before child meshes"
@@ -3756,7 +4048,7 @@ mod tests {
lod.reselect = true;
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5);
loop {
lod.update(camera, 2, 64, None);
lod.update(camera, 2, 64, None, None);
if lod.outstanding_meshes == 0 && lod.active.is_empty() {
break;
}
@@ -3796,7 +4088,7 @@ mod tests {
.collect();
lod.detail_request_pending = true;
lod.wanted_stale = true;
lod.update(camera, 2, 64, Some(FxHashSet::default()));
lod.update(camera, 2, 64, Some(FxHashSet::default()), None);
let Some(Job::Sample(nearby)) = lod.shared.jobs.lock().unwrap().pop_front() else {
panic!("nearby detail was not scheduled");
};
@@ -3807,7 +4099,7 @@ mod tests {
"the nearest tiles load first"
);
lod.finish_sample(nearby, vec![Column::default(); 256]);
lod.update(camera, 2, 64, Some(FxHashSet::default()));
lod.update(camera, 2, 64, Some(FxHashSet::default()), None);
let Some(Job::Sample(next)) = lod.shared.jobs.lock().unwrap().pop_front() else {
panic!("the next ring was not scheduled");
};
@@ -3846,7 +4138,7 @@ mod tests {
level: 0,
})
.collect();
lod.update(camera, 2, 64, Some(FxHashSet::default()));
lod.update(camera, 2, 64, Some(FxHashSet::default()), None);
let Some(Job::Sample(first)) = lod.shared.jobs.lock().unwrap().pop_front() else {
panic!("no final tile scheduled")
};
@@ -3890,7 +4182,7 @@ mod tests {
lod.shared.jobs.lock().unwrap().clear();
lod.pending.clear();
lod.wanted_stale = true;
lod.update([-256., 64., -256.], 2, 64, Some(FxHashSet::default()));
lod.update([-256., 64., -256.], 2, 64, Some(FxHashSet::default()), None);
let jobs = lod.shared.jobs.lock().unwrap();
jobs.iter()
.filter(|job| matches!(job, Job::Sample(_)))
@@ -3906,7 +4198,7 @@ mod tests {
// Leaves' cells: 1 block to 256 blocks out, 2 to 512 (Full); Reduced
// keeps 1 to the near terrain's edge and 2 to 256.
for (reduced, [one, two]) in [(false, [256, 512]), (true, [208, 256])] {
let plan = Plan::with_previous([8, 8], 12, 128, &Plan::default(), 0, reduced);
let plan = Plan::with_previous([8, 8], 12, 128, &Plan::default(), 0, reduced, None);
for key in plan.nodes.iter().filter(|key| !plan.split.contains(key)) {
let distance = key.distance([8, 8]);
if distance < one {
@@ -3936,7 +4228,8 @@ mod tests {
}
}
}
let plan = Plan::with_previous(camera, detail, distance, &Plan::default(), 0, reduced);
let plan =
Plan::with_previous(camera, detail, distance, &Plan::default(), 0, reduced, None);
let leaf = |x: i32, z: i32| {
(0..=MAX_LEVEL)
.map(|level| TileKey {
@@ -4202,7 +4495,7 @@ mod tests {
lod.finish_sample(a, vec![Column::default(); CELLS * CELLS]);
lod.dirty.clear();
// A range of one chunk around (-10, 0) keeps `a` and drops `b`.
lod.replan([-10, 0], 0, 1, 0);
lod.replan([-10, 0], 0, 1, 0, None);
assert!(lod.plan.nodes.contains(&a) && !lod.plan.nodes.contains(&b));
assert!(lod.dirty.contains_key(&a), "its east wall is drawn now");
}
@@ -4257,7 +4550,7 @@ mod tests {
keys
};
let none = || Some(FxHashSet::default());
lod.update([8., 64., 8.], 2, 64, none());
lod.update([8., 64., 8.], 2, 64, none(), None);
let before = queued(&lod);
assert_eq!(before.len(), 16, "a full queue once the near terrain is in");
lod.shared
@@ -4267,7 +4560,7 @@ mod tests {
.push_back(Job::Observe((5, 5)));
// A chunk's move: the tiles still planned keep their place in line.
lod.update([24., 64., 8.], 2, 64, none());
lod.update([24., 64., 8.], 2, 64, none(), None);
let moved = queued(&lod);
let kept: Vec<_> = before
.iter()
@@ -4280,7 +4573,7 @@ mod tests {
assert!(observing(&lod), "only sample jobs are dropped");
// A teleport: none of the old view's tiles is sampled any more.
lod.update([100_000., 64., 100_000.], 2, 64, none());
lod.update([100_000., 64., 100_000.], 2, 64, none(), None);
let after = queued(&lod);
assert_eq!(after.len(), 16);
assert!(after.iter().all(|key| !moved.contains(key)));
@@ -4298,7 +4591,7 @@ mod tests {
assert!(lod.pending.is_empty());
assert!(observing(&lod));
lod.set_near_loading(false);
lod.update([100_000., 64., 100_000.], 2, 64, none());
lod.update([100_000., 64., 100_000.], 2, 64, none(), None);
assert_eq!(queued(&lod), after);
// Larger machines keep the first `SAMPLE_JOBS` of the queue.
@@ -4706,7 +4999,7 @@ mod tests {
};
for _ in 0..2 {
for _ in 0..4000 {
lod.update(camera, 2, 64, Some(FxHashSet::default()));
lod.update(camera, 2, 64, Some(FxHashSet::default()), None);
check(&lod);
if !finish_queued(&mut lod) {
break;
@@ -4717,11 +5010,11 @@ mod tests {
check(&lod);
// Move a chunk: retained samples stay selected, new ones join.
camera[0] += 16.0;
lod.update(camera, 2, 64, Some(FxHashSet::default()));
lod.update(camera, 2, 64, Some(FxHashSet::default()), None);
check(&lod);
}
// A covered level-0 stand-in completes its parent but is not drawn.
lod.update(camera, 2, 64, Some([(0, 0)].into_iter().collect()));
lod.update(camera, 2, 64, Some([(0, 0)].into_iter().collect()), None);
check(&lod);
assert!(lod.ready.contains_key(&TileKey {
x: 0,
@@ -4734,7 +5027,7 @@ mod tests {
level: 0
}));
// Shrinking the distance retires tiles and reports them once.
lod.update(camera, 0, 32, Some(FxHashSet::default()));
lod.update(camera, 0, 32, Some(FxHashSet::default()), None);
check(&lod);
let _ = lod.removed();
}
@@ -4818,7 +5111,7 @@ mod tests {
let mut covered = Some(covered.clone());
let mut quiet = 0;
while quiet < if settle { 50 } else { 3 } {
lod.update(camera, detail, distance, covered.take());
lod.update(camera, detail, distance, covered.take(), None);
lod.removed();
// Every tile sampled, but those waiting for their data.
let unsampled = lod.plan.nodes.iter().any(|key| {
+5 -2
View File
@@ -1405,7 +1405,8 @@ fn pack_coords(coords: u32) -> u16 {
pub struct Mesher<'a> {
/// CardinalLighting.NETHER: brighter up/down faces.
pub nether_shading: bool,
/// A shader pack lights the faces (its `oldLighting` off): no face shading in the colours.
/// A shader pack lights the faces (its `oldLighting` off): no face shading in the colours,
/// only the dimension's up-face brightness (`lighting::pack_flat`).
pub shader_pack_lighting: bool,
/// OptiFine/Iris separate ambient occlusion: vertex RGB does not include AO, and AO factor is passed in vertex alpha.
pub separate_ao: bool,
@@ -1660,8 +1661,10 @@ impl Mesher<'_> {
None => ao,
};
let shade_face = q.shade_direction_override.unwrap_or(q.facing);
let directional = if self.shader_pack_lighting || !q.shade {
let directional = if !q.shade {
1.0
} else if self.shader_pack_lighting {
lighting::pack_flat(self.nether_shading)
} else {
lighting::cardinal(self.nether_shading, shade_face)
};
@@ -189,8 +189,9 @@ impl FluidFlowWorld for FlowView<'_> {
}
}
/// Emits one fluid cell at section-relative `[x, y, z]`.
/// `(nether, shader_pack_lighting, separate_ao)`: the Nether's face shading, none when a shader pack
/// lights the faces (`Mesher::shader_pack_lighting`), and whether to keep pure alpha for separate AO.
/// `(nether, shader_pack_lighting, separate_ao)`: the Nether's face shading, only the up-face value
/// when a shader pack lights the faces (`Mesher::shader_pack_lighting`), and whether to keep pure
/// alpha for separate AO.
pub(super) fn tesselate(
tables: &MeshTables,
(nether, shader_pack_lighting, separate_ao): (bool, bool, bool),
@@ -202,7 +203,7 @@ pub(super) fn tesselate(
use horizon_assets::model::Direction as D;
let cardinal = |d: D| {
if shader_pack_lighting {
1.0
lighting::pack_flat(nether)
} else {
lighting::cardinal(nether, d)
}
@@ -447,7 +448,10 @@ pub(super) fn tesselate(
Some(layer) if face_state.overlay => (layer, true),
_ => (flow, false),
};
let side = if matches!(d, Direction::North | Direction::South) {
// Vanilla's side is the up value times the side's; a shader pack's has no side factor.
let side = if shader_pack_lighting {
1.0
} else if matches!(d, Direction::North | Direction::South) {
cardinal(D::North)
} else {
cardinal(D::West)
@@ -19,6 +19,12 @@ pub(super) fn cardinal(nether: bool, face: D) -> f32 {
D::West | D::East => 0.6,
}
}
/// A face's brightness when a shader pack lights the faces (`Mesher::shader_pack_lighting`): no
/// directional shading, every face at the dimension's up-face value (1.0, the Nether's 0.9).
/// Our probe pack's Iris 1.11.7 values fit this: 0.9 on every Nether face, 1.0 in the overworld.
pub(super) fn pack_flat(nether: bool) -> f32 {
cardinal(nether, D::Up)
}
fn normal(face: D) -> [i32; 3] {
match face {
D::Down => [0, -1, 0],
@@ -159,6 +159,60 @@ fn a_shader_pack_lights_the_faces_itself() {
assert_eq!(colours(true).len(), 1);
}
/// Run explicitly with prepared assets. With a shader pack lighting the faces, every face of a
/// block and of a lava source takes the dimension's up-face brightness, as Iris 1.11.7 gives our
/// probe pack: 0.9 in the Nether (`CardinalLighting.NETHER`'s up, 229 of 255), 1.0 elsewhere.
#[test]
#[ignore = "requires prepared 26.3 resources (HORIZON_MESH_TEST_RESOURCES or .local/horizon-client/26.3)"]
fn a_shader_pack_lights_every_nether_face_like_an_up_face() {
let root = std::env::var_os("HORIZON_MESH_TEST_RESOURCES")
.map(std::path::PathBuf::from)
.unwrap_or_else(|| {
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../../.local/horizon-client/26.3")
});
let assets = Assets::load(&root).unwrap();
let facts = BlockFacts::load(&root, assets.registry).unwrap();
let biomes = Biomes::load(&root).unwrap();
let render = assets.reload(&root).unwrap();
let visibility = crate::models::load_visibility(&root, assets.registry).unwrap();
let tables = MeshTables::new(&assets, &render, &facts, visibility.clone()).unwrap();
let state = |name| assets.registry.default_state(name).unwrap().0;
let mut chunk = Chunk::empty(0, 0);
chunk.set(4, MIN_Y + 8, 4, state("minecraft:netherrack"));
chunk.set(11, MIN_Y + 8, 11, state("minecraft:lava"));
let mut region = [[None; 3]; 3];
region[1][1] = Some(&chunk);
let colours = |nether_shading| {
let mut mesher = Mesher {
nether_shading,
shader_pack_lighting: true,
separate_ao: false,
assets: &assets,
render: &render,
tables: &tables,
biomes: &biomes,
visibility: visibility.clone(),
zoom: BiomeZoom::new(0),
blend_radius: 0,
smooth_lighting: false,
cutout_leaves: true,
translucent_layers: &[],
leaf_culling: LeafCullingMode::Off,
};
let mesh = mesher.mesh(&region, 0);
// Both blocks' six faces: 2 × 6 quads of 4 vertices.
let all: Vec<_> = mesh.solid.iter().chain(&mesh.translucent).collect();
assert!(all.len() >= 48, "{} vertices", all.len());
let mut colours: Vec<u32> = all.iter().map(|v| v.color & 0xFF_FFFF).collect();
colours.sort_unstable();
colours.dedup();
colours
};
assert_eq!(colours(true), [0xE5_E5E5]);
assert_eq!(colours(false), [0xFF_FFFF]);
}
#[test]
#[ignore = "requires prepared 26.3 resources (HORIZON_MESH_TEST_RESOURCES or .local/horizon-client/26.3)"]
fn separate_ao_passes_ao_in_alpha_without_darkening_rgb() {
@@ -263,9 +263,6 @@ static TABLE: LazyLock<Vec<Def>> = LazyLock::new(|| {
for part in MODEL_PARTS {
t.push(pb(&format!("modelPart_{part}"), true));
}
// Horizon's own: the outer skin layer drawn as slabs (Options → Skin
// Customization), read by the renderer when a skin is resolved.
t.push(pb("threeDSkinLayers", false));
t
});
+299 -28
View File
@@ -272,13 +272,16 @@ impl Opt {
Opt::PadAdaptiveTriggers => "horizon.options.adaptive_triggers",
Opt::PadLightbar => "horizon.options.lightbar",
Opt::PadLookSensitivity => "horizon.options.look_sensitivity",
Opt::PadPointerSpeed => "horizon.options.pointer_speed",
Opt::PadScrollSpeed => "horizon.options.scroll_speed",
Opt::PadRepeatDelay => "horizon.options.repeat_delay",
Opt::PadRepeatInterval => "horizon.options.repeat_interval",
Opt::PadInvertX => "horizon.options.invert_x",
Opt::PadInvertY => "horizon.options.invert_y",
Opt::PadToggleSneak => "horizon.options.sneak_button",
Opt::PadMoveDeadzone => "horizon.options.move_deadzone",
Opt::PadLookDeadzone => "horizon.options.look_deadzone",
Opt::PadGlyphs => "horizon.options.button_glyphs",
Opt::PadMenuNav => "horizon.options.menu_navigation",
Opt::PadKeyboard => "horizon.options.on_screen_keyboard",
Opt::ModelPart(i) => [
"options.modelPart.cape",
@@ -352,6 +355,10 @@ impl Opt {
Opt::ChatHeightUnfocused => Kind::Slider(s.chat_height_unfocused),
Opt::ChatLineSpacing => Kind::Slider(s.chat_line_spacing),
Opt::PadLookSensitivity => Kind::Slider(s.controller.look_sensitivity),
Opt::PadPointerSpeed => unit(s.controller.pointer_speed, 0.25, 3.0),
Opt::PadScrollSpeed => unit(s.controller.scroll_speed, 1.0, 36.0),
Opt::PadRepeatDelay => unit(s.controller.repeat_delay, 0.1, 1.0),
Opt::PadRepeatInterval => unit(s.controller.repeat_interval, 0.03, 0.3),
Opt::PadMoveDeadzone => unit(s.controller.move_deadzone, 0.0, 0.5),
Opt::PadLookDeadzone => unit(s.controller.look_deadzone, 0.0, 0.5),
Opt::SprintWindow => unit(s.sprint_window as f32, 0.0, 10.0),
@@ -610,6 +617,23 @@ impl Opt {
),
),
Opt::PadLookSensitivity => percent(c, 2.0 * s.controller.look_sensitivity),
Opt::PadPointerSpeed => percent(c, s.controller.pointer_speed),
Opt::PadScrollSpeed => percent(c, s.controller.scroll_speed / 12.0),
Opt::PadRepeatDelay | Opt::PadRepeatInterval => generic(
c,
Text::tr(
"horizon.options.milliseconds",
&[&format!(
"{:.0}",
1000.0
* if self == Opt::PadRepeatDelay {
s.controller.repeat_delay
} else {
s.controller.repeat_interval
}
)],
),
),
Opt::PadMoveDeadzone | Opt::PadLookDeadzone => {
let v = if self == Opt::PadMoveDeadzone {
s.controller.move_deadzone
@@ -730,7 +754,7 @@ impl Opt {
} else {
"options.key.hold"
}),
Opt::SkinLayers3d => tr(if s.three_d_skin_layers {
Opt::SkinLayers3d => tr(if s.extras.three_d_skin_layers {
"options.on"
} else {
"options.off"
@@ -796,10 +820,6 @@ impl Opt {
GlyphStyle::PlayStation => "horizon.options.glyphs.playstation",
GlyphStyle::Nintendo => "horizon.options.glyphs.nintendo",
}),
Opt::PadMenuNav => tr(match s.controller.menu_nav {
gamepad::MenuNav::Focus => "horizon.options.menu_focus",
gamepad::MenuNav::Pointer => "horizon.options.menu_pointer",
}),
Opt::PadKeyboard => tr(match s.controller.keyboard {
gamepad::TextKeyboard::Auto => "horizon.options.keyboard.auto",
gamepad::TextKeyboard::Horizon => "horizon.options.keyboard.horizon",
@@ -871,6 +891,10 @@ impl Opt {
Opt::ChatHeightUnfocused => s.chat_height_unfocused = v,
Opt::ChatLineSpacing => s.chat_line_spacing = v,
Opt::PadLookSensitivity => s.controller.look_sensitivity = v,
Opt::PadPointerSpeed => s.controller.pointer_speed = step(25.0, 300.0) / 100.0,
Opt::PadScrollSpeed => s.controller.scroll_speed = step(1.0, 36.0),
Opt::PadRepeatDelay => s.controller.repeat_delay = step(100.0, 1000.0) / 1000.0,
Opt::PadRepeatInterval => s.controller.repeat_interval = step(30.0, 300.0) / 1000.0,
Opt::PadMoveDeadzone => s.controller.move_deadzone = step(0.0, 50.0) / 100.0,
Opt::PadLookDeadzone => s.controller.look_deadzone = step(0.0, 50.0) / 100.0,
Opt::SprintWindow => s.sprint_window = step(0.0, 10.0) as i32,
@@ -1019,7 +1043,7 @@ impl Opt {
Opt::ChunkBuilder => s.prioritize_chunk_updates = next(s.prioritize_chunk_updates, 3),
Opt::ToggleCrouch => s.toggle_crouch = !s.toggle_crouch,
Opt::ToggleSprint => s.toggle_sprint = !s.toggle_sprint,
Opt::SkinLayers3d => s.three_d_skin_layers = !s.three_d_skin_layers,
Opt::SkinLayers3d => s.extras.three_d_skin_layers = !s.extras.three_d_skin_layers,
Opt::ToggleAttack => s.toggle_attack = !s.toggle_attack,
Opt::ToggleUse => s.toggle_use = !s.toggle_use,
Opt::AutoJump => s.auto_jump = !s.auto_jump,
@@ -1094,12 +1118,6 @@ impl Opt {
.unwrap_or(0) as u8;
s.controller.glyphs = GlyphStyle::ALL[usize::from(next(i, 4))];
}
Opt::PadMenuNav => {
s.controller.menu_nav = match s.controller.menu_nav {
gamepad::MenuNav::Focus => gamepad::MenuNav::Pointer,
gamepad::MenuNav::Pointer => gamepad::MenuNav::Focus,
}
}
Opt::PadKeyboard => {
let all = gamepad::TextKeyboard::ALL;
let i = all
@@ -1344,6 +1362,14 @@ enum WKind {
value: f32,
name: String,
},
/// A float or int option `.option` of the Horizon pack `s.shader_packs[.pack]`: where its
/// value sits between its minimum and maximum.
PackSlider {
label: Text,
value: f32,
pack: usize,
option: usize,
},
Text {
text: Text,
color: u32,
@@ -1372,6 +1398,8 @@ struct Widget {
enum FocusId {
Search,
Slider(Opt),
/// A Horizon pack's option slider: (pack, option).
PackSlider(usize, usize),
Cycle(Opt),
Act(Act),
Lang(String),
@@ -1381,6 +1409,7 @@ enum FocusId {
fn focus_id(i: usize, wd: &Widget) -> FocusId {
match &wd.kind {
WKind::Slider { opt, .. } => FocusId::Slider(*opt),
WKind::PackSlider { pack, option, .. } => FocusId::PackSlider(*pack, *option),
WKind::Button {
act: Act::Cycle(opt),
..
@@ -1394,6 +1423,9 @@ fn focus_id(i: usize, wd: &Widget) -> FocusId {
fn matches_focus(id: &FocusId, i: usize, wd: &Widget) -> bool {
match (id, &wd.kind) {
(FocusId::Slider(a), WKind::Slider { opt, .. }) => a == opt,
(FocusId::PackSlider(p, o), WKind::PackSlider { pack, option, .. }) => {
(p, o) == (pack, option)
}
(
FocusId::Cycle(a),
WKind::Button {
@@ -1512,6 +1544,9 @@ pub struct OptionsUi {
/// The shader pack option whose slider is held, and the handle's own position: it follows
/// the pointer while the option takes the nearest of its values.
shader_dragging: Option<(String, f32)>,
/// The same for a Horizon pack's option: (pack, option, the handle's position, whether its
/// value changed since the press). The pack saves its values, and reloads, on release.
pack_dragging: Option<(usize, usize, f32, bool)>,
/// Shift is held during the click (it resets a shader pack option to its default).
pub shift: bool,
/// The pressed slider's own position (`AbstractSliderButton.value`) and
@@ -1602,6 +1637,7 @@ impl Default for OptionsUi {
scroll: 0.0,
dragging: None,
shader_dragging: None,
pack_dragging: None,
shift: false,
slider_value: None,
scrollbar_drag_y: None,
@@ -1746,7 +1782,23 @@ fn native_pack_option_rows(s: &Settings, i: usize, at: u8) -> Vec<Row> {
let value = crate::shader_pack_list::show_value(&option.kind, pack.values[o]);
let label =
Text::option_name_value(Text::literal(option.name.clone()), Text::literal(value));
button(0.0, 0.0, width, label, Act::NativePackOption(i, o))
match pack.slider_position(o) {
Some(value) => Widget {
x: 0.0,
y: 0.0,
w: width,
h: 20.0,
kind: WKind::PackSlider {
label,
value,
pack: i,
option: o,
},
list: false,
row: 0,
},
None => button(0.0, 0.0, width, label, Act::NativePackOption(i, o)),
}
}
PageItem::Page(p) if p < pack.pages.len() => {
let label = Text::literal(format!("{}...", pack.pages[p].name));
@@ -1845,7 +1897,10 @@ fn tooltip_text(s: &Settings, wd: &Widget) -> Option<Text> {
WKind::Button {
act: Act::NativePackOption(pack, option),
..
} => Some(&s.shader_packs.get(*pack)?.options.get(*option)?.description),
}
| WKind::PackSlider { pack, option, .. } => {
Some(&s.shader_packs.get(*pack)?.options.get(*option)?.description)
}
WKind::Button {
act: Act::Push(Page::NativePackOptions(pack, at)),
..
@@ -2043,6 +2098,13 @@ fn pairs(opts: &[Opt]) -> Vec<Row> {
.collect()
}
impl OptionsUi {
/// No widget keeps the highlight a click gave it; a focus the keyboard
/// moved stays.
pub fn clear_click_focus(&mut self) {
if !self.focus_from_keyboard {
self.focus = None;
}
}
/// A switch of `horizon-extras.txt` as the Extras page draws it: "Caption: On".
fn extra_button(&self, s: &Settings, item: crate::extras::Item) -> Widget {
let label = Text::option_name_value(
@@ -2163,6 +2225,18 @@ impl OptionsUi {
}
events
}
/// Steps a Horizon pack's option slider one step forward or backward, and saves it.
fn step_pack_slider(s: &mut Settings, pack: usize, option: usize, forward: bool) -> Vec<Event> {
let stepped = s
.shader_packs
.get_mut(pack)
.is_some_and(|entry| entry.nudge_option(option, !forward));
if stepped {
vec![Event::NativePackOptions(pack)]
} else {
Vec::new()
}
}
/// Open a page whose Done/Escape returns directly to the calling screen.
pub fn open_at(&mut self, in_world: bool, page: Page) {
self.stack.clear();
@@ -2689,8 +2763,11 @@ impl OptionsUi {
Opt::PadMoveDeadzone,
Opt::PadLookDeadzone,
Opt::PadGlyphs,
Opt::PadMenuNav,
Opt::PadKeyboard,
Opt::PadPointerSpeed,
Opt::PadScrollSpeed,
Opt::PadRepeatDelay,
Opt::PadRepeatInterval,
]));
rows
}
@@ -3716,6 +3793,34 @@ impl OptionsUi {
dragging,
);
}
WKind::PackSlider {
label,
value,
pack,
option,
} => {
let held = self
.pack_dragging
.filter(|&(p, o, ..)| (p, o) == (*pack, *option));
let dragging = held.is_some();
ui::slider_ext(
batch,
atlas,
wd.x,
wd.y,
wd.w,
label,
held.map_or(*value, |h| h.2),
hover || dragging,
is_focused,
self.can_change_value && is_focused,
true,
dragging,
);
crate::widgets::last_narratable(|n| {
narrate_widget(n, s, wd, hover, list_key, framebuffer);
});
}
WKind::Text {
text,
color,
@@ -4015,6 +4120,16 @@ impl OptionsUi {
*at = ui::slider_value(wd.x, wd.w, x);
options.set_position(name, *at);
}
if let Some((pack, option, at, changed)) = &mut self.pack_dragging
&& let Some(wd) = widgets.iter().find(|wd| {
matches!(wd.kind, WKind::PackSlider { pack: p, option: o, .. }
if (p, o) == (*pack, *option))
})
&& let Some(entry) = s.shader_packs.get_mut(*pack)
{
*at = ui::slider_value(wd.x, wd.w, x);
*changed |= entry.set_slider_position(*option, *at);
}
if let Some(opt) = self.dragging
&& let Some(wd) = widgets
.iter()
@@ -4251,6 +4366,21 @@ impl OptionsUi {
}
self.shader_dragging = Some((name, at));
}
WKind::PackSlider { pack, option, .. } => {
self.can_change_value = true;
let Some(entry) = s.shader_packs.get_mut(pack) else {
break;
};
if self.shift {
// Its default, as a shift-click on a pack's button does; no drag follows.
entry.step_option(option, false, true);
events.extend([Event::ButtonClick, Event::NativePackOptions(pack)]);
break;
}
let at = ui::slider_value(wd.x, wd.w, x);
let changed = entry.set_slider_position(option, at);
self.pack_dragging = Some((pack, option, at, changed));
}
WKind::Slider { opt, .. } => {
self.can_change_value = true;
self.dragging = Some(opt);
@@ -4287,12 +4417,19 @@ impl OptionsUi {
}
// AbstractSliderButton suppresses the press sound and plays once on
// release, even if the pointer has left the widget or its value stayed.
let slider = self.dragging.take().is_some() | self.shader_dragging.take().is_some();
let pack = self.pack_dragging.take();
let slider =
self.dragging.take().is_some() | self.shader_dragging.take().is_some() | pack.is_some();
let graphics_slider = self.graphics.release();
let calibration_slider = self.calibration.as_mut().is_some_and(|c| c.release());
let split_drop = self.split_setup.release();
if slider || graphics_slider || calibration_slider {
vec![Event::ButtonClick, Event::Changed]
let mut events = vec![Event::ButtonClick, Event::Changed];
// A pack's option is saved, and the pack rebuilt, once per drag.
if let Some((pack, _, _, true)) = pack {
events.push(Event::NativePackOptions(pack));
}
events
} else if split_drop {
vec![Event::ButtonClick]
} else {
@@ -4390,6 +4527,10 @@ impl OptionsUi {
.map_or(150.0, |wd| wd.w);
return self.step_slider_option(s, opt, amount > 0.0, width);
}
Some(FocusId::PackSlider(pack, option)) => {
let (pack, option) = (*pack, *option);
return Self::step_pack_slider(s, pack, option, amount > 0.0);
}
Some(FocusId::Cycle(opt)) => {
let opt = *opt;
let mut events = vec![Event::ButtonClick];
@@ -4855,6 +4996,7 @@ impl OptionsUi {
match wd.kind {
WKind::Button { active: true, .. }
| WKind::Slider { .. }
| WKind::PackSlider { .. }
| WKind::LanguageRow { .. } => {
focusables.push((focus_id(i, wd), [wd.x, wd.y, wd.w, wd.h], wd.list));
}
@@ -4890,7 +5032,7 @@ impl OptionsUi {
let (next_id, rect, in_list) = &focusables[next_idx];
self.focus = Some(next_id.clone());
self.focus_from_keyboard = true;
self.can_change_value = matches!(next_id, FocusId::Slider(_));
self.can_change_value = matches!(next_id, FocusId::Slider(_) | FocusId::PackSlider(..));
if *in_list {
if rect[1] < top {
self.scroll -= (top - rect[1]) + 4.0;
@@ -4915,6 +5057,11 @@ impl OptionsUi {
.map_or(150.0, |wd| wd.w);
return self.step_slider_option(s, opt, forward, width);
}
if let Some(FocusId::PackSlider(pack, option)) = self.focus
&& self.can_change_value
{
return Self::step_pack_slider(s, pack, option, forward);
}
// Otherwise, navigate horizontally using 2D focus stepping
if let Some(fid) = &self.focus {
if let Some(cur_pos) = focusables.iter().position(|(id, ..)| id == fid) {
@@ -4992,7 +5139,7 @@ impl OptionsUi {
// 4. Enter / Space selection keys
if matches!(key, K::Enter | K::NumpadEnter | K::Space) {
match &self.focus {
Some(FocusId::Slider(_)) => {
Some(FocusId::Slider(_) | FocusId::PackSlider(..)) => {
self.can_change_value = !self.can_change_value;
return Vec::new();
}
@@ -5505,11 +5652,9 @@ mod tests {
id: "a".into(),
name: "A".into(),
description: String::new(),
kind: horizon_shader_api::OptionKind::Float {
default: 1.0,
min: 0.0,
max: 2.0,
step: 0.5,
kind: horizon_shader_api::OptionKind::Choice {
values: vec!["x".into(), "y".into(), "z".into()],
default: 1,
},
}],
values: vec![1.0],
@@ -5550,10 +5695,10 @@ mod tests {
let (x, y) = find(&mut ui, &s, &Act::NativePackOption(0, 0));
let events = ui.click(&mut s, &atlas, x, y, size, [854, 480], false, false);
assert!(events.contains(&Event::NativePackOptions(0)));
assert_eq!(s.shader_packs[0].values, [1.5]);
assert_eq!(s.shader_packs[0].values, [2.0]);
ui.click(&mut s, &atlas, x, y, size, [854, 480], false, true);
ui.click(&mut s, &atlas, x, y, size, [854, 480], false, true);
assert_eq!(s.shader_packs[0].values, [0.5]);
assert_eq!(s.shader_packs[0].values, [0.0]);
// Shift-click: the option's default.
ui.shift = true;
ui.click(&mut s, &atlas, x, y, size, [854, 480], false, false);
@@ -5567,6 +5712,116 @@ mod tests {
assert_eq!(ui.page(), Page::NativePackOptions(0, 0));
}
#[test]
fn a_horizon_packs_float_and_int_options_are_sliders_saved_on_release() {
use horizon_shader_api::{OptionKind, PackOption};
use winit::keyboard::KeyCode;
let atlas = atlas();
let size = (960.0, 540.0);
let option = |id: &str, kind| PackOption {
id: id.into(),
name: id.into(),
description: String::new(),
kind,
};
let pack = crate::shader_pack_list::Entry {
file: "p".into(),
title: "P".into(),
kind: crate::shader_pack_list::Kind::Horizon,
options: vec![
option(
"a",
OptionKind::Float {
default: 1.0,
min: 0.0,
max: 2.0,
step: 0.5,
},
),
option(
"b",
OptionKind::Int {
default: 2,
min: 0,
max: 4,
},
),
option("c", OptionKind::Bool { default: true }),
],
values: vec![1.0, 2.0, 1.0],
pages: Vec::new(),
};
let mut s = Settings {
shader_packs: vec![pack],
..Settings::default()
};
let mut ui = OptionsUi::default();
ui.show(Page::NativePackOptions(0, 0));
// A slider's track, from its left end to its right: (x of 0, x of 1, y).
let track = |ui: &mut OptionsUi, s: &Settings, want: usize| {
let (widgets, ..) = ui.layout(s, size.0, size.1);
widgets
.into_iter()
.find(|w| matches!(w.kind, WKind::PackSlider { option, .. } if option == want))
.map(|w| (w.x + 4.0, w.x + w.w - 4.0, w.y + w.h / 2.0))
};
assert!(track(&mut ui, &s, 2).is_none(), "a bool stays a button");
let (left, right, y) = track(&mut ui, &s, 0).expect("the float's slider");
let at = |f: f32| left + f * (right - left);
// Pressed a fifth of the way: 0.4 of 2, the nearest step 0.5. Nothing is saved yet.
let events = ui.click(&mut s, &atlas, at(0.2), y, size, [854, 480], false, false);
assert!(!events.contains(&Event::NativePackOptions(0)));
assert_eq!(s.shader_packs[0].values[0], 0.5);
// Dragged past the right end: the maximum.
ui.click(
&mut s,
&atlas,
right + 30.0,
y,
size,
[854, 480],
true,
false,
);
assert_eq!(s.shader_packs[0].values[0], 2.0);
// Released: saved once, which reloads the pack.
let events = ui.release();
let saves = events.iter().filter(|e| **e == Event::NativePackOptions(0));
assert_eq!(saves.count(), 1);
// A press and release that leave the value as it was save nothing.
ui.click(&mut s, &atlas, right, y, size, [854, 480], false, false);
assert!(!ui.release().contains(&Event::NativePackOptions(0)));
// Shift-click: the default at once.
ui.shift = true;
let events = ui.click(&mut s, &atlas, at(0.9), y, size, [854, 480], false, false);
ui.shift = false;
assert!(events.contains(&Event::NativePackOptions(0)));
assert_eq!(s.shader_packs[0].values[0], 1.0);
assert!(!ui.release().contains(&Event::NativePackOptions(0)));
// The int takes whole numbers: 0.6 of 0..4 is 2.4, so 2, unchanged; 0.7 is 3.
let (left, right, y) = track(&mut ui, &s, 1).expect("the int's slider");
let at = |f: f32| left + f * (right - left);
ui.click(&mut s, &atlas, at(0.6), y, size, [854, 480], false, false);
assert_eq!(s.shader_packs[0].values[1], 2.0);
assert!(!ui.release().contains(&Event::NativePackOptions(0)));
ui.click(&mut s, &atlas, at(0.7), y, size, [854, 480], false, false);
assert_eq!(s.shader_packs[0].values[1], 3.0);
assert!(ui.release().contains(&Event::NativePackOptions(0)));
// From the keyboard: the arrows step a focused slider, saving each step, up to its end.
ui.focus = Some(FocusId::PackSlider(0, 1));
ui.focus_from_keyboard = true;
ui.can_change_value = true;
let events = ui.key(&mut s, &atlas, KeyCode::ArrowRight, None);
assert_eq!(events, [Event::NativePackOptions(0)]);
assert_eq!(s.shader_packs[0].values[1], 4.0);
assert!(ui.key(&mut s, &atlas, KeyCode::ArrowRight, None).is_empty());
assert_eq!(s.shader_packs[0].values[1], 4.0);
ui.key(&mut s, &atlas, KeyCode::ArrowLeft, None);
assert_eq!(s.shader_packs[0].values[1], 3.0);
}
#[test]
fn resource_reload_error_blocks_options_until_dismissed() {
use winit::keyboard::KeyCode;
@@ -8039,7 +8294,7 @@ mod tests {
// Also verify Act::Extra on Page::Extras
ui.show(Page::Extras);
let (widgets, _, _, _) = ui.layout(&s, size.0, size.1);
let (widgets, top, _, _) = ui.layout(&s, size.0, size.1);
if let Some(extra_btn) = widgets.iter().find(|wd| {
matches!(
wd.kind,
@@ -8050,6 +8305,22 @@ mod tests {
}
)
}) {
// Extras has enough rows to scroll: click the cycle button inside
// the list's viewport, as a player would after scrolling to it.
ui.scroll = (extra_btn.y - top).max(0.0);
let (widgets, _, _, _) = ui.layout(&s, size.0, size.1);
let extra_btn = widgets
.iter()
.find(|wd| {
matches!(
wd.kind,
WKind::Button {
act: Act::Extra(crate::extras::Item::CosmeticsOthers),
..
}
)
})
.unwrap();
let (ex, ey) = (extra_btn.x + 1.0, extra_btn.y + 1.0);
use horizon_cosmetics::prefs::Others;
assert_eq!(s.extras.cosmetics_others, Others::Shown);
+96 -3
View File
@@ -170,8 +170,17 @@ fn last_pre_minor(current: Format) -> i64 {
/// `PackFormat.fullCodec(default_minor)`: an int, or a list of 1 to 256
/// non-negative ints whose second is the minor. None: it does not parse.
fn format_codec(v: &serde_json::Value, default_minor: i64) -> Option<Format> {
let non_negative =
|v: &serde_json::Value| v.as_i64().filter(|n| *n >= 0 && *n <= i64::from(i32::MAX));
// `Codec.INT` reads any JSON number through `Number.intValue`: a decimal such as `97.1`
// counts as its whole part (97), as it does for the game.
let non_negative = |v: &serde_json::Value| {
v.as_i64()
.or_else(|| {
v.as_f64()
.filter(|f| f.is_finite())
.map(|f| f.trunc() as i64)
})
.filter(|n| *n >= 0 && *n <= i64::from(i32::MAX))
};
match v {
serde_json::Value::Array(a) if (1..=256).contains(&a.len()) => {
let list: Option<Vec<i64>> = a.iter().map(non_negative).collect();
@@ -636,7 +645,7 @@ fn build_overlay_uncached(
}
// FallbackResourceManager: a file's `.mcmeta` counts only from the pack that
// supplies the file or one above it; vanilla's own sits below every pack.
let hidden: BTreeSet<String> = files
let mut hidden: BTreeSet<String> = files
.iter()
.filter(|(name, _)| !name.ends_with(".mcmeta"))
.filter_map(|(name, (owner, _))| {
@@ -647,6 +656,25 @@ fn build_overlay_uncached(
}
})
.collect();
// A companion from below the winning colour would describe another surface.
// A map-only pack above it is intentional and remains valid.
for (name, (owner, _)) in &files {
let Some(stem) = name.strip_suffix(".png") else {
continue;
};
if stem.ends_with("_n") || stem.ends_with("_s") {
continue;
}
for suffix in ["_n.png", "_s.png"] {
let map = format!("{stem}{suffix}");
if files
.get(&map)
.is_none_or(|(map_owner, _)| map_owner < owner)
{
hidden.insert(map);
}
}
}
for name in &hidden {
files.remove(name);
}
@@ -1257,6 +1285,16 @@ mod tests {
.unwrap_err()
.contains("deprecated")
);
// A decimal reads as its whole part, in a list or alone: `[97.1]` is format 97 (a pack
// that meant `[97, 1]` still loads and counts as compatible).
assert_eq!(
check(r#"{"min_format":[97.1],"max_format":[97.1]}"#),
Ok(((97, 0), (97, i64::from(i32::MAX))))
);
assert_eq!(
check(r#"{"min_format":97.9,"max_format":98.5}"#),
Ok(((97, 0), (98, i64::from(i32::MAX))))
);
assert_eq!(
check(r#"{"min_format":97,"max_format":98}"#),
Ok(((97, 0), (98, i64::from(i32::MAX))))
@@ -1469,6 +1507,61 @@ mod tests {
assert_eq!(merge_kind("assets/minecraft/models/block/stone.json"), None);
}
#[test]
fn companion_maps_follow_the_winning_colour_or_a_higher_map_only_pack() {
let root =
std::env::temp_dir().join(format!("horizon-material-owner-{}", std::process::id()));
let vanilla = root.join("vanilla");
let relative = "assets/minecraft/textures/block";
fs::create_dir_all(vanilla.join(relative)).unwrap();
let names = ["stone.png", "stone_n.png", "stone_s.png"];
for name in names {
fs::write(vanilla.join(relative).join(name), b"base").unwrap();
}
for (pack, files) in [
("low", vec!["stone_n.png", "stone_s.png"]),
("colour", vec!["stone.png"]),
("maps", vec!["stone_n.png"]),
] {
let dir = root.join("packs").join(pack);
fs::create_dir_all(dir.join(relative)).unwrap();
fs::write(
dir.join("pack.mcmeta"),
br#"{"pack":{"description":"fixture","pack_format":97}}"#,
)
.unwrap();
for name in files {
fs::write(dir.join(relative).join(name), pack.as_bytes()).unwrap();
}
}
let all = discover(&vanilla, &root.join("packs"));
let order = |names: &[&str]| {
names
.iter()
.map(|name| {
all.iter()
.find(|p| p.id == format!("file/{name}"))
.unwrap()
.clone()
})
.collect::<Vec<_>>()
};
let out = root.join("out");
build_overlay(&vanilla, &order(&["low", "colour"]), &out).unwrap();
assert_eq!(
fs::read(out.join(relative).join("stone.png")).unwrap(),
b"colour"
);
assert!(!out.join(relative).join("stone_n.png").exists());
assert!(!out.join(relative).join("stone_s.png").exists());
build_overlay(&vanilla, &order(&["low", "colour", "maps"]), &out).unwrap();
assert_eq!(
fs::read(out.join(relative).join("stone_n.png")).unwrap(),
b"maps"
);
assert!(!out.join(relative).join("stone_s.png").exists());
fs::remove_dir_all(root).unwrap();
}
#[test]
fn overlay_layers_packs_over_vanilla() {
// `&` would split an unquoted `cmd /C mklink` line.
let base = std::env::temp_dir().join(format!("horizon-packs-&{}", std::process::id()));
@@ -13,7 +13,7 @@ use std::{collections::BTreeSet, fs, io, path::Path, path::PathBuf};
// Version 5 read every vanilla file to stamp it on Windows.
// Version 6 stamps vanilla's files by their metadata on Windows too.
// Version 7 stacks blockstate files (every pack's definition, bottom first).
const VERSION: u32 = 7;
const VERSION: u32 = 8;
#[derive(Default, Serialize, Deserialize)]
pub(super) struct Dependencies {
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1,67 @@
//! The controller's menu cursor in inventories: a crosshair whose centre is
//! the point it clicks. Horizon's own art (pad_cursor.png).
/// The atlas sprite.
pub const SPRITE: &str = "horizon/pad_cursor";
/// The texture's size in texels, and the size it's drawn at in GUI pixels
/// (an item's).
pub const SIZE: u32 = 32;
pub const DRAWN: f32 = 16.0;
const PNG: &[u8] = include_bytes!("pad_cursor.png");
/// The texture as `SIZE` square RGBA texels.
pub fn pixels() -> Vec<u8> {
decode(PNG).expect("pad_cursor.png is a 32x32 PNG")
}
fn decode(bytes: &[u8]) -> Option<Vec<u8>> {
let mut decoder = png::Decoder::new(std::io::Cursor::new(bytes));
decoder.set_transformations(png::Transformations::EXPAND | png::Transformations::STRIP_16);
let mut reader = decoder.read_info().ok()?;
let info = reader.info();
if (info.width, info.height) != (SIZE, SIZE) {
return None;
}
let mut buf = vec![0; reader.output_buffer_size()?];
let frame = reader.next_frame(&mut buf).ok()?;
let channels = frame.color_type.samples();
Some(
buf.chunks(channels)
.take((SIZE * SIZE) as usize)
.flat_map(|p| match channels {
4 => [p[0], p[1], p[2], p[3]],
3 => [p[0], p[1], p[2], 255],
2 => [p[0], p[0], p[0], p[1]],
_ => [p[0], p[0], p[0], 255],
})
.collect(),
)
}
/// Where to draw the cursor (top-left, GUI pixels) so its centre lands on
/// the GUI point `at`.
pub fn origin(at: (f32, f32)) -> (f32, f32) {
(at.0 - DRAWN / 2.0, at.1 - DRAWN / 2.0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_cursor_is_a_crosshair_centred_on_its_point() {
let px = pixels();
assert_eq!(px.len(), (SIZE * SIZE * 4) as usize);
let alpha = |x: u32, y: u32| px[((y * SIZE + x) * 4 + 3) as usize];
let middle = (SIZE / 2 - 1)..=(SIZE / 2);
assert!(
middle
.clone()
.any(|x| middle.clone().any(|y| alpha(x, y) > 0)),
"the clicked point sits on the crosshair"
);
assert_eq!(alpha(0, 0), 0, "corners are clear: a plus, not a square");
assert_eq!(origin((100.0, 50.0)), (92.0, 42.0));
}
}
@@ -26,6 +26,8 @@ use std::{
pub use horizon_client_core::particle_type::ParticleType;
pub use horizon_items::firework::{DYE_FIREWORK_COLORS, FireworkExplosion, FireworkShape};
mod native;
pub use native::{ParticleController, ParticleUpdate};
/// `ParticleOptions` with the parameters of the option classes used here.
#[derive(Clone, Debug)]
@@ -212,6 +214,9 @@ enum Light {
#[derive(Clone, Copy, Debug, PartialEq)]
enum Behaviour {
Base,
Controlled {
translucent: bool,
},
Mod {
size: [f32; 2],
alpha: [f32; 2],
@@ -505,6 +510,7 @@ pub struct Particle {
pre_tick: bool,
/// `Particle.random`: the particle's own generator (`RandomSource.create()`).
random: LegacyRandom,
controller: Option<Box<dyn native::Controller>>,
}
fn aabb_at(p: [f64; 3], w: f32, h: f32) -> Aabb {
let w = f64::from(w) / 2.0;
@@ -841,8 +847,37 @@ impl Particle {
pre_tick: false,
frame_ticks: 0,
random: LegacyRandom::new(0),
controller: None,
}
}
/// The normal age/removal test and previous pose, without vanilla motion.
/// Returns false when this tick expires the particle.
pub fn advance_age(&mut self) -> bool {
self.previous = self.position;
self.o_roll = self.roll;
let alive = self.age < self.lifetime;
self.age = self.age.saturating_add(1);
self.removed |= !alive;
alive
}
pub fn remove(&mut self) {
self.removed = true;
}
/// Hide geometry while retaining the particle's lifetime and controller.
pub fn set_visible(&mut self, visible: bool) {
self.no_render = !visible;
}
/// The particle's own random, distinct from the engine and level generators.
pub fn random(&mut self) -> &mut LegacyRandom {
&mut self.random
}
pub fn set_full_bright(&mut self, full_bright: bool) {
self.light = if full_bright {
Light::FullBright
} else {
Light::World
};
}
/// `Particle(level, x, y, z, xa, ya, za)`: jittered, normalized initial velocity.
fn moving(random: &mut LegacyRandom, ty: ParticleType, p: [f64; 3], v: [f64; 3]) -> Self {
let mut particle = Self::base_moving(random, ty, p, v);
@@ -1420,6 +1455,10 @@ impl ParticleEngine {
atlas: &HashMap<String, Rect>,
translucent: &HashSet<String>,
) {
// Controllers' numeric sprite-set handles belong to this resource
// generation. Discard them rather than selecting another set after reload.
self.particles.retain(|p| p.controller.is_none());
self.pending.retain(|p| p.controller.is_none());
self.atlas = atlas
.iter()
.map(|(name, &rect)| (name.clone(), (rect, translucent.contains(name))))
@@ -1584,6 +1623,45 @@ impl ParticleEngine {
pub fn push(&mut self, particle: Particle) {
self.pending.push(particle);
}
/// Create a named billboard with custom compiled behavior and no vanilla
/// motion. Like `add`, respects camera distance and the Particles setting.
/// Configure its public fields, then `push` it to the ordinary engine queue.
/// Missing sprite sets return None. The constructor has the ordinary
/// stationary SingleQuadParticle random size/lifetime and no velocity draws.
pub fn controlled<C: ParticleController>(
&mut self,
name: &str,
position: [f64; 3],
translucent: bool,
controller: C,
) -> Option<Particle> {
let minimal = self.calculate_particle_level(false) == 2;
let distance: f64 = position
.iter()
.zip(self.camera)
.map(|(p, c)| (p - c).powi(2))
.sum();
if minimal || !distance.is_finite() || distance > 1024.0 {
return None;
}
let (set, _) = *self.mod_sets.get(name)?;
let mut random = LegacyRandom::new(self.seeds.next_long());
let mut particle = Particle::at(&mut random, ParticleType::Smoke, position);
particle.random = random;
particle.set = Some(set);
particle.sprite = Sprite::Index(*self.set_sprites.get(set as usize)?.first()?);
particle.behaviour = Behaviour::Controlled { translucent };
particle.controller = Some(Box::new(controller));
Some(particle)
}
/// Choose a frame from this particle's declared sprite set.
pub fn set_sprite_frame(&self, particle: &mut Particle, frame: usize) {
if let Some(sprites) = particle.set.and_then(|s| self.set_sprites.get(s as usize))
&& !sprites.is_empty()
{
particle.sprite = Sprite::Index(sprites[frame.min(sprites.len() - 1)]);
}
}
/// Random generator shared by particle constructors and spawners.
pub fn random(&mut self) -> &mut LegacyRandom {
&mut self.random
@@ -3710,6 +3788,20 @@ impl ParticleEngine {
fn tick_particle(&mut self, p: &mut Particle, level: &ParticleLevel) {
match p.behaviour {
Behaviour::Base => p.base_tick(level),
Behaviour::Controlled { .. } => {
if let Some(mut controller) = p.controller.take() {
let update = controller.tick(p, level);
p.controller = Some(controller);
if let Some(frame) = update.frame {
self.set_sprite_frame(p, frame);
}
for (options, pos, velocity) in update.spawned {
self.add(&options, pos, velocity);
}
} else {
p.remove();
}
}
Behaviour::Mod { size, alpha } => {
p.base_tick(level);
p.size = mod_curve(size, p.age, p.lifetime);
@@ -4627,6 +4719,7 @@ impl ParticleEngine {
Sprite::Index(i) => (
self.rects.get(*i as usize).copied().flatten(),
p.ty.translucent_layer()
|| matches!(p.behaviour, Behaviour::Controlled { translucent: true })
|| matches!(p.behaviour, Behaviour::Mod { alpha, .. } if alpha != [1.0; 2]),
),
Sprite::Named(name) => match self.atlas.get(name) {
@@ -0,0 +1,179 @@
//! Opt-in, compiled particle behavior. The ordinary engine still owns the
//! particle, its sprite set, interpolation, rendering and total count limits.
use super::{Particle, ParticleLevel, ParticleOptions};
use std::fmt::Debug;
/// Requests made by one particle tick, submitted through normal spawn limits.
#[derive(Default)]
pub struct ParticleUpdate {
/// An index in this particle's declared sprite set. Out-of-range is clamped.
pub frame: Option<usize>,
pub spawned: Vec<(ParticleOptions, [f64; 3], [f64; 3])>,
}
/// Owned behavior for a compiled native particle. Cloning a particle clones its
/// controller state. Do not retain world references; inspect `level` each tick.
pub trait ParticleController: Clone + Debug + Send + Sync + 'static {
/// Call `particle.advance_age()` for ordinary lifetime/interpolation, or
/// explicitly maintain those fields for an overridden source tick.
fn tick(&mut self, particle: &mut Particle, level: &ParticleLevel<'_>) -> ParticleUpdate;
}
pub(super) trait Controller: Debug + Send + Sync {
fn tick(&mut self, particle: &mut Particle, level: &ParticleLevel<'_>) -> ParticleUpdate;
fn clone_box(&self) -> Box<dyn Controller>;
}
impl<T: ParticleController> Controller for T {
fn tick(&mut self, particle: &mut Particle, level: &ParticleLevel<'_>) -> ParticleUpdate {
ParticleController::tick(self, particle, level)
}
fn clone_box(&self) -> Box<dyn Controller> {
Box::new(self.clone())
}
}
impl Clone for Box<dyn Controller> {
fn clone(&self) -> Self {
self.clone_box()
}
}
#[cfg(test)]
mod tests {
use super::super::*;
use horizon_world::{
DATA_VERSION,
collision::CachedCollisionRegistry,
nbt::Tag,
registry::{BlockRegistry, BlockStateId},
};
#[derive(Clone, Debug)]
struct Moving(u32);
impl ParticleController for Moving {
fn tick(&mut self, p: &mut Particle, _: &ParticleLevel<'_>) -> ParticleUpdate {
if p.advance_age() {
self.0 += 1;
p.position[0] += f64::from(self.0);
}
ParticleUpdate {
frame: Some(self.0 as usize),
..Default::default()
}
}
}
fn engine() -> ParticleEngine {
let mut e = ParticleEngine::new(Path::new(""), &HashMap::new(), &HashSet::new());
e.mod_sets.insert("example:orbit".into(), (0, None));
e.set_sprites.push(vec![0, 1]);
e.rects = vec![
Some(Rect {
u0: 0.0,
v0: 0.0,
u1: 1.0,
v1: 1.0,
w: 16,
h: 16
});
2
];
e
}
#[test]
fn controlled_particle_keeps_owned_motion_sprite_interpolation_and_expiry() {
let registry = BlockRegistry::from_nbt(&Tag::compound([
("DataVersion", Tag::Int(DATA_VERSION)),
(
"states",
Tag::List(vec![Tag::String("minecraft:air".into())]),
),
("defaults", Tag::compound([("minecraft:air", Tag::Int(0))])),
]))
.unwrap();
let cached = CachedCollisionRegistry::from_nbt(
&Tag::compound([
("DataVersion", Tag::Int(DATA_VERSION)),
(
"states",
Tag::List(vec![Tag::String("minecraft:air".into())]),
),
("shapes", Tag::List(vec![])),
("cached_shapes", Tag::IntArray(vec![-1])),
]),
&registry,
)
.unwrap();
let collision = BlockCollisionRegistry::new(std::sync::Arc::new(cached)).unwrap();
let facts = BlockFacts {
states: vec![],
air: BlockStateId(0),
woods: HashMap::new(),
};
let world = World::new(BlockStateId(0), 0);
let level = ParticleLevel {
world: &world,
facts: &facts,
collision: &collision,
player: None,
};
let mut e = engine();
let mut p = e
.controlled("example:orbit", [0.0; 3], true, Moving(0))
.unwrap();
p.lifetime = 2;
p.rgba[3] = 0.5;
p.velocity = [99.0; 3]; // no vanilla drift may leak into the custom tick
let mut copy = p.clone();
e.tick_particle(&mut p, &level);
e.tick_particle(&mut p, &level);
e.tick_particle(&mut copy, &level);
assert_eq!((p.position, p.previous), ([3.0, 0.0, 0.0], [1.0, 0.0, 0.0]));
assert_eq!(copy.position, [1.0, 0.0, 0.0]); // controller state cloned independently
assert!(matches!(p.sprite, Sprite::Index(1))); // clamped frame
p.size = 0.1;
e.particles.push(p.clone());
let mut vertices = Vec::new();
e.render(DVec3::ZERO, 0.0, 0.0, 0.5, &|_| 0, &mut vertices);
assert_eq!(vertices.len(), 4);
assert!(vertices.iter().all(
|v| v.color >> 24 == 127 && v.layer & crate::gfx::TRANSLUCENT_PARTICLE_OVERLAY != 0
));
e.particles[0].set_visible(false);
vertices.clear();
e.render(DVec3::ZERO, 0.0, 0.0, 0.5, &|_| 0, &mut vertices);
assert!(vertices.is_empty());
assert_eq!(e.particles[0].age, 2);
e.pending.push(copy);
e.reload(Path::new(""), &HashMap::new(), &HashSet::new());
assert!(e.pending.is_empty());
vertices.clear();
e.render(DVec3::ZERO, 0.0, 0.0, 0.5, &|_| 0, &mut vertices);
assert!(vertices.is_empty());
e.tick_particle(&mut p, &level);
assert!(p.removed);
assert_eq!(p.position, [3.0, 0.0, 0.0]);
}
#[test]
fn controlled_spawn_respects_distance_settings_and_declared_sprite_sets() {
let mut e = engine();
assert!(
e.controlled("example:missing", [0.0; 3], true, Moving(0))
.is_none()
);
assert!(
e.controlled("example:orbit", [33.0, 0.0, 0.0], true, Moving(0))
.is_none()
);
assert!(
e.controlled("example:orbit", [f64::NAN, 0.0, 0.0], true, Moving(0))
.is_none()
);
assert!(
e.controlled("example:orbit", [32.0, 0.0, 0.0], true, Moving(0))
.is_some()
);
e.particle_level = 2;
assert!(
e.controlled("example:orbit", [0.0; 3], true, Moving(0))
.is_none()
);
}
}
+46 -17
View File
@@ -144,9 +144,6 @@ pub struct Settings {
pub discrete_scroll: bool,
pub toggle_crouch: bool,
pub toggle_sprint: bool,
/// 3D skin layers (`threeDSkinLayers`): each painted texel of a player's
/// outer layer drawn as a slab (`crate::...`, Options → Skin).
pub three_d_skin_layers: bool,
pub auto_jump: bool,
pub language: String,
pub force_unicode: bool,
@@ -251,7 +248,6 @@ impl Default for Settings {
allow_cursor_changes: true,
toggle_crouch: false,
toggle_sprint: false,
three_d_skin_layers: false,
auto_jump: false,
language: "en_us".into(),
force_unicode: false,
@@ -341,23 +337,28 @@ impl Settings {
.parent()
.map(crate::gamepad::PadSettings::load)
.unwrap_or_default();
let extras = path
let extras_text = path
.parent()
.map(crate::extras::Extras::load)
.and_then(|dir| fs::read_to_string(dir.join(crate::extras::FILE)).ok())
.unwrap_or_default();
let mut extras = crate::extras::Extras::parse(&extras_text);
// The enabled mods are the game folder's, shared by its players (`--player-dir`).
let mods = path
.parent()
.map(|dir| crate::mod_list::ModList::load(&crate::logging::shared_dir(dir)))
.unwrap_or_default();
let settings = match fs::read(path) {
Ok(bytes) => Self {
horizon,
controller,
extras,
mods,
..Self::parse(&String::from_utf8_lossy(&bytes))
},
Ok(bytes) => {
let text = String::from_utf8_lossy(&bytes);
extras.read_old_options(&extras_text, &text);
Self {
horizon,
controller,
extras,
mods,
..Self::parse(&text)
}
}
Err(_) => Self {
horizon,
controller,
@@ -463,7 +464,6 @@ impl Settings {
allow_cursor_changes: f.bool("allowCursorChanges"),
toggle_crouch: f.bool("toggleCrouch"),
toggle_sprint: f.bool("toggleSprint"),
three_d_skin_layers: f.bool("threeDSkinLayers"),
toggle_attack: f.bool("toggleAttack"),
toggle_use: f.bool("toggleUse"),
sprint_window: f.int("sprintWindow"),
@@ -575,7 +575,6 @@ impl Settings {
put_b(&mut f, "allowCursorChanges", self.allow_cursor_changes);
put_b(&mut f, "toggleCrouch", self.toggle_crouch);
put_b(&mut f, "toggleSprint", self.toggle_sprint);
put_b(&mut f, "threeDSkinLayers", self.three_d_skin_layers);
put_b(&mut f, "toggleAttack", self.toggle_attack);
put_b(&mut f, "toggleUse", self.toggle_use);
put_i(&mut f, "sprintWindow", self.sprint_window);
@@ -652,7 +651,9 @@ impl Settings {
}
pub fn save(&self, path: &Path) {
self.describe_for_crashes();
let _ = fs::write(path, self.serialize());
if let Err(e) = fs::write(path, self.serialize()) {
log::warn!("Horizon: options.txt is not saved: {e}");
}
if let Some(dir) = path.parent() {
self.horizon.save(dir);
self.controller.save(dir);
@@ -804,13 +805,16 @@ pub enum Opt {
PadAdaptiveTriggers,
PadLightbar,
PadLookSensitivity,
PadPointerSpeed,
PadScrollSpeed,
PadRepeatDelay,
PadRepeatInterval,
PadInvertX,
PadInvertY,
PadToggleSneak,
PadMoveDeadzone,
PadLookDeadzone,
PadGlyphs,
PadMenuNav,
PadKeyboard,
/// `options.modelPart.*`: one skin layer (index into `options_file::MODEL_PARTS`).
ModelPart(usize),
@@ -852,6 +856,31 @@ mod tests {
);
}
#[test]
fn three_d_skin_layers_move_from_options_txt_to_the_extras_file() {
// #8091 wrote the switch into options.txt; a player who had it on keeps
// it, and the next options.txt is vanilla's again.
let dir = std::env::temp_dir().join(format!(
"horizon-skin-layers-{}-{:?}",
std::process::id(),
std::thread::current().id()
));
fs::create_dir_all(&dir).unwrap();
fs::write(dir.join("options.txt"), "fov:0.5\nthreeDSkinLayers:true\n").unwrap();
let s = Settings::load(&dir.join("options.txt"));
assert!(s.extras.three_d_skin_layers);
assert_eq!(s.fov, 90.0);
assert!(!s.serialize().contains("threeDSkinLayers"));
assert!(crate::extras::Extras::parse(&s.extras.serialize()).three_d_skin_layers);
// Once horizon-extras.txt has its own line, a stale options.txt one is ignored.
fs::write(dir.join(crate::extras::FILE), "threeDSkinLayers:false\n").unwrap();
assert!(
!Settings::load(&dir.join("options.txt"))
.extras
.three_d_skin_layers
);
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn an_option_set_keeps_horizon_files() {
let mut s = Settings::parse("fov:70\n");
s.mods.enabled.insert("zoom".into());
@@ -53,6 +53,47 @@ impl Entry {
true
}
/// Where option `i` sits on its slider, 0 to 1: float and int options are sliders.
pub fn slider_position(&self, i: usize) -> Option<f32> {
slider_position(&self.options.get(i)?.kind, *self.values.get(i)?)
}
/// Option `i` at the value nearest the slider position `at`. Whether its value changed.
pub fn set_slider_position(&mut self, i: usize, at: f32) -> bool {
let (Some(o), Some(v)) = (self.options.get(i), self.values.get_mut(i)) else {
return false;
};
let Some(new) = slider_value(&o.kind, at) else {
return false;
};
let changed = new != *v;
*v = new;
changed
}
/// Option `i`'s slider one step on (`back`: one before), stopping at its ends. Whether its
/// value changed.
pub fn nudge_option(&mut self, i: usize, back: bool) -> bool {
let (Some(o), Some(v)) = (self.options.get(i), self.values.get_mut(i)) else {
return false;
};
let dir = if back { -1.0 } else { 1.0 };
let new = match &o.kind {
OptionKind::Float { min, max, step, .. } => {
let (min, max, step) = (f64::from(*min), f64::from(*max), f64::from(*step));
let n = ((*v - min) / step).round() + dir;
on_f32((min + n * step).clamp(min, max))
}
OptionKind::Int { min, max, .. } => {
(v.round() + dir).clamp(f64::from(*min), f64::from(*max))
}
_ => return false,
};
let changed = new != *v;
*v = new;
changed
}
/// Every option back to its default.
pub fn reset_options(&mut self) {
let defaults = self.options.iter().map(|o| o.kind.default_constant());
@@ -209,6 +250,42 @@ fn step_value(kind: &OptionKind, v: f64, back: bool) -> f64 {
}
}
/// Where `v` sits between the option's minimum and maximum, 0 to 1; `None` for the options
/// that are buttons.
fn slider_position(kind: &OptionKind, v: f64) -> Option<f32> {
let (min, max) = match kind {
OptionKind::Float { min, max, .. } => (f64::from(*min), f64::from(*max)),
OptionKind::Int { min, max, .. } => (f64::from(*min), f64::from(*max)),
_ => return None,
};
Some(((v - min) / (max - min)).clamp(0.0, 1.0) as f32)
}
/// The option's value nearest the slider position `at`: a float on its steps from the
/// minimum (the last one the maximum, when the range isn't a whole number of steps), an int
/// whole.
fn slider_value(kind: &OptionKind, at: f32) -> Option<f64> {
let at = f64::from(at.clamp(0.0, 1.0));
match kind {
OptionKind::Float { min, max, step, .. } => {
let (min, max, step) = (f64::from(*min), f64::from(*max), f64::from(*step));
let steps = ((max - min) / step - 1e-6).ceil();
Some(on_f32((min + (at * steps).round() * step).min(max)))
}
OptionKind::Int { min, max, .. } => {
let (min, max) = (f64::from(*min), f64::from(*max));
Some((min + at * (max - min)).round())
}
_ => None,
}
}
/// `v` as the `f32` the pack's `override` gets: a step of 0.05 is no f32, and 20 of them would
/// otherwise miss a default of 1 by a hair.
fn on_f32(v: f64) -> f64 {
f64::from(v as f32)
}
/// The value as the settings page shows it.
pub fn show_value(kind: &OptionKind, v: f64) -> String {
match kind {
@@ -324,4 +401,65 @@ mod tests {
assert_eq!(load_values(&pack, &options), [1.0, 0.0]);
std::fs::remove_dir_all(&dir).unwrap();
}
#[test]
fn sliders_take_the_nearest_step_and_stop_at_the_ends() {
let float = |default, max, step| OptionKind::Float {
default,
min: 0.0,
max,
step,
};
let int = OptionKind::Int {
default: 2,
min: 0,
max: 10,
};
let mut entry = Entry {
file: "p".into(),
title: "P".into(),
kind: Kind::Horizon,
options: vec![
option("a", float(1.0, 2.0, 0.05)),
option("b", int),
option("c", OptionKind::Bool { default: true }),
option("d", float(0.0, 1.0, 0.3)),
],
values: vec![1.0, 2.0, 1.0, 0.0],
pages: Vec::new(),
};
assert_eq!(entry.slider_position(0), Some(0.5));
assert_eq!(entry.slider_position(1), Some(0.2));
// A bool (and a choice) stays a button.
assert_eq!(entry.slider_position(2), None);
assert!(!entry.set_slider_position(2, 0.0));
assert_eq!(entry.values[2], 1.0);
// 0.26 of 0..2 is 0.52: the step below.
assert!(entry.set_slider_position(0, 0.26));
assert_eq!(show_value(&entry.options[0].kind, entry.values[0]), "0.50");
// Twenty steps of 0.05 are the default of 1 exactly, so it isn't saved.
assert!(entry.set_slider_position(0, 0.5));
assert_eq!(entry.values[0], 1.0);
assert!(!entry.set_slider_position(0, 0.505));
// 5.7 of 0..10 is 6.
assert!(entry.set_slider_position(1, 0.57));
assert_eq!(entry.values[1], 6.0);
// A range that isn't a whole number of steps still reaches its maximum.
entry.set_slider_position(3, 1.0);
assert_eq!(entry.values[3], 1.0);
entry.set_slider_position(3, 0.6);
assert_eq!(entry.values[3], f64::from(0.6_f32));
// The arrow keys: a step at a time, not round past the ends.
entry.values[0] = 2.0;
assert!(!entry.nudge_option(0, false));
assert_eq!(entry.values[0], 2.0);
assert!(entry.nudge_option(0, true));
assert_eq!(entry.values[0], f64::from(1.95_f32));
entry.values[1] = 0.0;
assert!(!entry.nudge_option(1, true));
assert!(entry.nudge_option(1, false));
assert_eq!(entry.values[1], 1.0);
assert!(!entry.nudge_option(2, false));
}
}
+74 -17
View File
@@ -68,27 +68,48 @@ fn cpu_if_enabled() -> Option<Duration> {
/// Run `f`, recording its duration under `name` when spans are enabled.
#[inline]
pub fn time<T>(name: &'static str, f: impl FnOnce() -> T) -> T {
let _span = guard(name);
f()
}
/// A span that ends where it is dropped: [`time`] for a step with several ways out (early
/// returns). Nothing is read while spans are off.
#[must_use = "the span ends when the guard is dropped"]
pub struct Guard {
open: Option<(&'static str, Instant, Option<Duration>, u8)>,
_heap: Option<horizon_threads::heap_census::Tag>,
}
/// Start a span named `name` that ends when the returned [`Guard`] is dropped.
#[inline]
pub fn guard(name: &'static str) -> Guard {
let _heap = horizon_threads::heap_tag(name);
if !recording_here() {
return f();
return Guard { open: None, _heap };
}
let depth = DEPTH.with(|d| d.replace(d.get().saturating_add(1)));
let (started, cpu) = (Instant::now(), cpu_if_enabled());
let out = f();
let ms = started.elapsed().as_secs_f32() * 1000.0;
let cpu_ms = cpu
.and_then(|start| Some((thread_cpu()?, start)))
.map_or(ms, |(end, start)| (end - start).as_secs_f32() * 1000.0);
DEPTH.with(|d| d.set(depth));
SPANS.with(|s| {
s.borrow_mut().push(Span {
name,
ms,
cpu_ms,
depth,
})
});
out
Guard {
open: Some((name, Instant::now(), cpu_if_enabled(), depth)),
_heap,
}
}
impl Drop for Guard {
fn drop(&mut self) {
let Some((name, started, cpu, depth)) = self.open.take() else {
return;
};
let ms = started.elapsed().as_secs_f32() * 1000.0;
let cpu_ms = cpu
.and_then(|start| Some((thread_cpu()?, start)))
.map_or(ms, |(end, start)| (end - start).as_secs_f32() * 1000.0);
DEPTH.with(|d| d.set(depth));
SPANS.with(|s| {
s.borrow_mut().push(Span {
name,
ms,
cpu_ms,
depth,
})
});
}
}
/// Notes that one step inside a span took `ms` milliseconds doing `what` (one player's
/// packets, say), when this thread records spans and the step took at least [`NOTE_MS`]: a
@@ -551,6 +572,42 @@ mod tests {
.unwrap();
}
/// A guarded span ends on every way out of its step, an early return's too, and holds the
/// spans that ran inside it until then; the next span outside it is back at its depth.
#[test]
fn a_guarded_span_ends_where_its_step_returns() {
fn step(early: bool) -> u32 {
let _span = super::guard("use_block");
super::time("open_loot", || ());
if early {
return 1;
}
super::time("open_maps", || ());
2
}
std::thread::spawn(|| {
super::record_here(true);
assert_eq!(step(true), 1);
assert_eq!(step(false), 2);
super::time("use_item", || ());
let raw = super::take_raw();
let seen: Vec<_> = raw.iter().map(|s| (s.name, s.depth)).collect();
assert_eq!(
seen,
[
("open_loot", 1),
("use_block", 0),
("open_loot", 1),
("open_maps", 1),
("use_block", 0),
("use_item", 0)
]
);
})
.join()
.unwrap();
}
/// A slow frame is put down to the slowest outer span, then to the span inside it that took
/// at least half of it; repeated spans (several ticks a frame) add up.
#[test]
+137 -51
View File
@@ -8,6 +8,7 @@ pub struct SpriteAnimation {
pub layer: u32,
frames: Arc<Vec<Vec<u8>>>,
clip: Arc<AnimationClip>,
materials: [Option<Arc<Vec<Vec<u8>>>>; 2],
}
/// What an animated sprite shows: `frame`, mixed `millis` thousandths of the
/// way towards `next` (0 for a single frame).
@@ -18,6 +19,14 @@ pub struct FrameBlend {
pub millis: u32,
}
impl SpriteAnimation {
pub(crate) fn has_materials(&self) -> bool {
self.materials.iter().any(Option::is_some)
}
/// Material categories follow the selected colour frame, without colour interpolation.
pub(crate) fn material_pixels(&self, kind: usize, tick: u64) -> Option<&[u8]> {
let frames = self.materials[kind].as_ref()?;
Some(&frames[self.frame(tick) % frames.len()])
}
pub fn frame(&self, tick: u64) -> usize {
self.clip.sample(tick).frame as usize
}
@@ -88,9 +97,9 @@ pub struct Sprites {
/// Each layer's frames at the texture's own resolution (square), and the
/// frame shown first.
native: Vec<(u32, Arc<Vec<Vec<u8>>>, usize)>,
/// Each layer's LabPBR maps (`_n` normals, `_s` specular) beside its texture: their first
/// frame at their own resolution (square).
pbr: Vec<[Option<(u32, Vec<u8>)>; 2]>,
/// Each layer's LabPBR maps (`_n` normals, `_s` specular) beside its texture: their
/// frames at their own resolution (square).
pbr: Vec<[Option<(u32, Vec<Vec<u8>>)>; 2]>,
/// The layers' normal and specular maps at `pbr_size`, layer after layer, set by
/// [`Sprites::finish`]: empty when no texture has either.
pub normals: Vec<u8>,
@@ -126,7 +135,7 @@ pub fn set_texture_budget(mib: u32) {
/// Whether resource loads read the resource packs' LabPBR maps (`set_labpbr_maps`).
static LABPBR_MAPS: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
/// Whether resource loads from now on read the resource packs' LabPBR maps beside their block
/// textures. Only a native shader pack's material stage reads them, so without one they cost
/// textures. Native material stages and terrain shader packs read them; without a pack they cost
/// neither loading time nor video memory.
pub fn set_labpbr_maps(on: bool) {
LABPBR_MAPS.store(on, std::sync::atomic::Ordering::Relaxed);
@@ -135,7 +144,7 @@ pub fn set_labpbr_maps(on: bool) {
pub fn labpbr_maps() -> bool {
LABPBR_MAPS.load(std::sync::atomic::Ordering::Relaxed)
}
/// Largest array of each LabPBR map, which only a native shader pack reads.
/// Budget for either complete normal or specular image set.
const MAX_MAP_BYTES: u64 = DEFAULT_TEXTURE_BUDGET / 4;
/// Fewer layers than any game has (vanilla's are about 1300): what a map is kept at while
/// loading assumes at least these many share the array.
@@ -153,30 +162,12 @@ fn side_within(start: u32, layers: u64, bytes: u64) -> u32 {
}
size
}
/// A LabPBR map's square `frame` of `side` averaged down by whole powers of two to at most
/// `max`, channel by channel: they are data, not colour.
/// Reduce data maps by selecting whole texels: material IDs and sentinel bytes cannot be averaged.
fn shrink_map(frame: Vec<u8>, side: u32, max: u32) -> (u32, Vec<u8>) {
if side <= max || !side.is_power_of_two() || !max.is_power_of_two() {
return (side, frame);
}
let (out_side, f, side) = (max as usize, (side / max) as usize, side as usize);
let mut out = Vec::with_capacity(out_side * out_side * 4);
for y in 0..out_side {
for x in 0..out_side {
let mut sum = [0u32; 4];
for dy in 0..f {
let row = ((y * f + dy) * side + x * f) * 4;
for texel in frame[row..row + f * 4].as_chunks::<4>().0 {
for (s, &v) in sum.iter_mut().zip(texel) {
*s += u32::from(v);
}
}
}
let n = (f * f) as u32;
out.extend(sum.map(|s| ((s + n / 2) / n) as u8));
}
}
(max, out)
(max, resample(&frame, side, max))
}
/// Nearest-resample a square RGBA frame to another square size.
pub(crate) fn resample(frame: &[u8], from: u32, to: u32) -> Vec<u8> {
@@ -289,14 +280,28 @@ impl Sprites {
if largest_map.is_some() {
let pbr_size = self.pbr_size;
log::info!("Horizon: LabPBR maps at {pbr_size} px, {layers} layers");
for maps in std::mem::take(&mut self.pbr) {
for animation in &mut self.animations {
animation.materials = self.pbr[animation.layer as usize].each_ref().map(|map| {
map.as_ref().map(|(from, frames)| {
Arc::new(
frames
.iter()
.map(|frame| resample(frame, *from, pbr_size))
.collect(),
)
})
});
}
for (layer, maps) in std::mem::take(&mut self.pbr).into_iter().enumerate() {
for (map, (out, none)) in maps.into_iter().zip([
(&mut self.normals, FLAT_NORMAL),
(&mut self.specular, NO_SPECULAR),
]) {
match map {
Some((from, pixels)) => {
let (from, pixels) = shrink_map(pixels, from, pbr_size);
Some((from, frames)) => {
let initial = self.native[layer].2 % frames.len();
let (from, pixels) =
shrink_map(frames[initial].clone(), from, pbr_size);
out.extend(resample(&pixels, from, pbr_size));
}
None => out.extend(none.repeat((pbr_size * pbr_size) as usize)),
@@ -638,8 +643,10 @@ impl Sprites {
let pbr = match name.split_once(':') {
Some(_) if labpbr_maps() => {
let path = crate::atlas_sources::texture_path(root, name, "textures");
["_n", "_s"].map(|suffix| pbr_map(&path, suffix, count))
["_n", "_s"].map(|suffix| pbr_map(&path, suffix, count, animation.as_ref()))
}
None if labpbr_maps() && !name.starts_with("fusion/") => ["_n", "_s"]
.map(|suffix| pbr_map(&root.join(name), suffix, count, animation.as_ref())),
_ => [None, None],
};
let settings = crate::mipmap::Settings {
@@ -691,6 +698,7 @@ impl Sprites {
layer,
frames: frames.clone(),
clip: Arc::new(clip),
materials: [None, None],
});
}
self.mips.push(settings);
@@ -734,32 +742,64 @@ fn read_rgba(path: &Path) -> Result<(u32, u32, Vec<u8>)> {
Ok((info.width, info.height, pixels))
}
/// The LabPBR map with `suffix` beside the texture at `path`, whose frames lie in a column of
/// `count`: its first frame, square, no larger than the maps' array allows any game's
/// textures (a 512x pack's maps would otherwise fill the memory while loading).
fn pbr_map(path: &Path, suffix: &str, count: u32) -> Option<(u32, Vec<u8>)> {
/// Companion frames use the colour image's grid; a single-frame map stays static.
fn pbr_map(
path: &Path,
suffix: &str,
count: u32,
animation: Option<&AnimationMetadata>,
) -> Option<(u32, Vec<Vec<u8>>)> {
let stem = path.file_stem()?.to_str()?;
let map = path.with_file_name(format!("{stem}{suffix}.png"));
let (map_width, map_height, pixels) = read_rgba(&map).ok()?;
let (w, h) = (map_width, map_height / count.max(1));
let (width, height, _) = read_rgba(path).ok()?;
let [frame_width, frame_height] = animation.map_or([width as i32, height as i32], |a| {
a.frame_size(width as i32, height as i32)
});
let (frame_width, frame_height) = (
u32::try_from(frame_width).ok()?,
u32::try_from(frame_height).ok()?,
);
if frame_width == 0 || frame_height == 0 {
return None;
}
let (columns, rows) = if count <= 1 || (map_width == frame_width && map_height == frame_height)
{
(1, 1)
} else if u64::from(map_width) * u64::from(height) == u64::from(map_height) * u64::from(width) {
(width / frame_width, height / frame_height)
} else if u64::from(map_width) * u64::from(frame_height)
== u64::from(map_height) * u64::from(frame_width)
{
(1, 1)
} else {
return None;
};
if columns == 0 || rows == 0 || map_width % columns != 0 || map_height % rows != 0 {
return None;
}
let (w, h) = (map_width / columns, map_height / rows);
if w == 0 || h == 0 {
return None;
}
let side = w.max(h);
let mut frame = Vec::with_capacity((side * side * 4) as usize);
for row in 0..side {
for column in 0..side {
let offset = (((row * h / side) * map_width + column * w / side) * 4) as usize;
frame.extend_from_slice(&pixels[offset..offset + 4]);
}
}
Some(shrink_map(
frame,
side,
side_within(side, LAYERS_AT_LEAST, MAX_MAP_BYTES),
))
let output_side = side_within(side, LAYERS_AT_LEAST, MAX_MAP_BYTES).min(side);
let frames = (0..columns * rows)
.map(|index| {
let (x, y) = (index % columns * w, index / columns * h);
let mut frame = Vec::with_capacity((side * side * 4) as usize);
for row in 0..side {
for column in 0..side {
let offset =
(((y + row * h / side) * map_width + x + column * w / side) * 4) as usize;
frame.extend_from_slice(&pixels[offset..offset + 4]);
}
}
resample(&frame, side, output_side)
})
.collect();
Some((output_side, frames))
}
/// SpriteContents: the `.mcmeta` `texture` section (TextureMetadataSection's
/// mipmap strategy and alpha cutoff bias).
fn texture_section(
@@ -790,6 +830,7 @@ pub(crate) fn test_frames(layer: u32, frames: Vec<Vec<u8>>, metadata: &str) -> S
layer,
clip: Arc::new(metadata.clip(frames.len() as u32).unwrap().unwrap()),
frames: Arc::new(frames),
materials: [None, None],
}
}
@@ -815,8 +856,8 @@ mod tests {
assert_eq!(side_within(512, 1300, 1), SPRITE_SIZE);
}
#[test]
fn a_labpbr_map_shrinks_by_plain_means_and_a_layer_without_one_gets_the_default() {
// Alpha is data here: it does not weigh the other channels.
fn a_labpbr_map_selects_whole_texels_and_missing_layers_get_defaults() {
// Selecting a texel preserves encoded channels, including categorical values.
let frame = [
[200, 100, 0, 255],
[0, 0, 0, 0],
@@ -824,14 +865,14 @@ mod tests {
[0, 0, 0, 0],
]
.concat();
assert_eq!(shrink_map(frame, 2, 1), (1, vec![100, 50, 0, 128]));
assert_eq!(shrink_map(frame, 2, 1), (1, vec![200, 100, 0, 255]));
let mut sprites = Sprites {
native: vec![
(SPRITE_SIZE, Arc::new(vec![vec![0; 16 * 16 * 4]]), 0),
(SPRITE_SIZE, Arc::new(vec![vec![0; 16 * 16 * 4]]), 0),
],
pbr: vec![
[Some((16, [9, 8, 7, 6].repeat(16 * 16))), None],
[Some((16, vec![[9, 8, 7, 6].repeat(16 * 16)])), None],
[None, None],
],
..Default::default()
@@ -844,6 +885,50 @@ mod tests {
assert_eq!(sprites.specular.len(), 2 * 16 * 16 * 4);
}
#[test]
fn companion_grid_and_static_map_follow_colour_frame_selection() {
let root =
std::env::temp_dir().join(format!("horizon-companion-grid-{}", std::process::id()));
fs::create_dir_all(&root).unwrap();
let write = |name: &str, width, height, pixels: &[u8]| {
let mut encoder =
png::Encoder::new(File::create(root.join(name)).unwrap(), width, height);
encoder.set_color(png::ColorType::Rgba);
encoder.set_depth(png::BitDepth::Eight);
encoder
.write_header()
.unwrap()
.write_image_data(pixels)
.unwrap();
};
write("grid.png", 4, 4, &[200, 200, 200, 255].repeat(16));
let maps: Vec<u8> = (0..16)
.flat_map(|i| {
let cell = (i / 8) * 2 + (i % 4) / 2;
[10 + cell as u8, 230, 65, 255]
})
.collect();
write("grid_s.png", 4, 4, &maps);
write("grid_n.png", 2, 2, &[128, 128, 255, 255].repeat(4));
let metadata =
r#"{"width":2,"height":2,"frametime":2,"interpolate":true,"frames":[3,1,2,0]}"#;
let animation = AnimationMetadata::from_json(metadata).unwrap();
let (side, frames) = pbr_map(&root.join("grid.png"), "_s", 4, Some(&animation)).unwrap();
assert_eq!(side, 2);
assert_eq!(
frames.iter().map(|f| f[0]).collect::<Vec<_>>(),
[10, 11, 12, 13]
);
let (_, normals) = pbr_map(&root.join("grid.png"), "_n", 4, Some(&animation)).unwrap();
assert_eq!(normals.len(), 1);
let mut sprite = test_frames(0, vec![vec![0; 16]; 4], metadata);
sprite.materials = [Some(Arc::new(normals)), Some(Arc::new(frames))];
for (tick, expected) in [(0, 13), (1, 13), (2, 11), (4, 12), (6, 10)] {
assert_eq!(sprite.material_pixels(1, tick).unwrap()[0], expected);
assert_eq!(&sprite.material_pixels(0, tick).unwrap()[..4], &FLAT_NORMAL);
}
fs::remove_dir_all(root).unwrap();
}
#[test]
fn textures_without_labpbr_maps_keep_none() {
let mut sprites = Sprites {
native: vec![(SPRITE_SIZE, Arc::new(vec![vec![0; 16 * 16 * 4]]), 0)],
@@ -1284,6 +1369,7 @@ mod tests {
fn animation_boundaries_and_repeated_frames_do_not_depend_on_render_rate() {
let animation = SpriteAnimation {
layer: 0,
materials: [None, None],
frames: Arc::new(vec![vec![], vec![], vec![]]),
clip: Arc::new(AnimationMetadata::from_json(r#"{"frames":[{"index":2,"time":8},{"index":1,"time":3},{"index":1,"time":2},0]}"#).unwrap().clip(3).unwrap().unwrap()),
};
@@ -386,6 +386,7 @@ impl World {
chunk.motion_blocking(x & 15, z & 15, |v| blocks(BlockStateId(v)))
})
}
#[inline]
pub fn block(&self, x: i32, y: i32, z: i32) -> BlockStateId {
self.chunks
.get(&(x >> 4, z >> 4))
@@ -0,0 +1,162 @@
//! An ordinary read of a struct or buffer-block member that an atomic function also uses gets
//! the member's current value (#9081), on a host GPU through the translated program.
use horizon_shaders::{Macros, Pack, preprocess, translate_compute};
/// Runs compute program `source` once (one work group), with `probe_buffer` (set 4, binding 2)
/// starting at `start`, and returns `probe_result.result` (set 4, binding 1).
fn run(device: &wgpu::Device, queue: &wgpu::Queue, source: &str, start: [u32; 2]) -> u32 {
use std::io::Write;
use wgpu::util::DeviceExt;
let mut zip = zip::ZipWriter::new(std::io::Cursor::new(Vec::new()));
zip.start_file(
"shaders/deferred.csh",
zip::write::SimpleFileOptions::default(),
)
.unwrap();
zip.write_all(source.as_bytes()).unwrap();
let pack =
Pack::from_zip_bytes("atomic-values.zip", zip.finish().unwrap().into_inner()).unwrap();
let program = translate_compute(&preprocess(&pack, "deferred.csh", &Macros::standard()))
.unwrap_or_else(|e| panic!("{e:?}"));
let module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Naga(std::borrow::Cow::Owned(program.module)),
});
let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: None,
layout: None,
module: &module,
entry_point: Some("main"),
compilation_options: Default::default(),
cache: None,
});
let storage = wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC;
let uniforms = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: u64::from(program.interface.uniform_size.max(48)),
usage: wgpu::BufferUsages::UNIFORM,
mapped_at_creation: false,
});
let result = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None,
contents: &[0; 4],
usage: storage,
});
let probe: Vec<u8> = start.iter().flat_map(|v| v.to_le_bytes()).collect();
let probe = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None,
contents: &probe,
usage: storage,
});
let read = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 4,
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
mapped_at_creation: false,
});
let groups: Vec<wgpu::BindGroup> = (0..5)
.map(|set| {
let entries: Vec<wgpu::BindGroupEntry> = match set {
0 => vec![wgpu::BindGroupEntry {
binding: 0,
resource: uniforms.as_entire_binding(),
}],
4 if source.contains("probe_buffer") => vec![
wgpu::BindGroupEntry {
binding: 1,
resource: result.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 2,
resource: probe.as_entire_binding(),
},
],
4 => vec![wgpu::BindGroupEntry {
binding: 1,
resource: result.as_entire_binding(),
}],
_ => Vec::new(),
};
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &pipeline.get_bind_group_layout(set),
entries: &entries,
})
})
.collect();
let mut encoder = device.create_command_encoder(&Default::default());
{
let mut pass = encoder.begin_compute_pass(&Default::default());
pass.set_pipeline(&pipeline);
for (set, group) in groups.iter().enumerate() {
pass.set_bind_group(set as u32, group, &[]);
}
pass.dispatch_workgroups(1, 1, 1);
}
encoder.copy_buffer_to_buffer(&result, 0, &read, 0, 4);
queue.submit([encoder.finish()]);
read.slice(..)
.map_async(wgpu::MapMode::Read, |r| r.unwrap());
device.poll(wgpu::PollType::wait_indefinitely()).unwrap();
let bytes = read.slice(..).get_mapped_range().unwrap();
u32::from_le_bytes(bytes[..4].try_into().unwrap())
}
#[test]
#[ignore = "requires a host GPU; run with --ignored --nocapture"]
fn reads_of_atomic_members_see_the_atomic_updates() {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle());
let adapter = pollster::block_on(instance.request_adapter(&Default::default())).unwrap();
eprintln!("Atomic member GPU: {}", adapter.get_info().name);
// Pack buffers sit in bind group 4, as in the game.
let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
required_limits: wgpu::Limits {
max_bind_groups: 5,
..Default::default()
},
..Default::default()
}))
.unwrap();
let result = "layout(std430, binding = 1) buffer probe_result { uint result; };\n";
// Eight invocations each add 1 to a zeroed shared member, then read it: 8.
for (host, member) in [
("shared struct { uint other; uint count; } sh;", "sh.count"),
(
"struct Counter { uint count; uint other; };\nshared Counter counters[2];",
"counters[1].count",
),
] {
for (read, expected) in [
("result = M * 10u + 1u;", 81),
("uint r = M; result = r;", 8),
("result = uint(float(M) * 0.5);", 4),
("result = min(M, 5u);", 5),
] {
let source = format!(
"#version 460 core\nlayout(local_size_x = 8) in;\n{host}\n{result}\
void main() {{\n {member} = 0u;\n barrier();\n \
atomicAdd({member}, 1u);\n barrier();\n {}\n}}\n",
read.replace('M', member)
);
assert_eq!(
run(&device, &queue, &source, [0, 0]),
expected,
"{member}: {read}"
);
}
}
// One invocation adds 2 to a buffer member that starts at 5, then reads it: 7.
for (read, expected) in [
("result = pc.count * 10u + 1u;", 71),
("uint r = pc.count; result = r;", 7),
("result = uint(int(pc.count) - 10 + 100);", 97),
] {
let source = format!(
"#version 460 core\nlayout(local_size_x = 1) in;\n\
layout(std430, binding = 2) buffer probe_buffer {{ uint count; uint other; }} pc;\n\
{result}void main() {{\n atomicAdd(pc.count, 2u);\n {read}\n}}\n"
);
assert_eq!(run(&device, &queue, &source, [5, 0]), expected, "{read}");
}
}
@@ -0,0 +1,208 @@
//! Synthetic rendering checks (two output shapes mirror RenderPearl v2.7.2 @86de79c, MIT; see the
//! PR): a fragment output's `component` layout qualifier (GLSL 4.60, 4.4.2) picks the channels of
//! its location that it writes.
use horizon_shaders::{Origin, Preprocessed, Target, translate_composite};
fn source(code: &str) -> Preprocessed {
Preprocessed {
code: code.into(),
files: vec!["components.glsl".into()],
lines: code
.lines()
.enumerate()
.map(|(i, _)| Origin {
file: 0,
line: i as u32 + 1,
})
.collect(),
version: Some((440, None)),
..Default::default()
}
}
/// Draws `fragment` over a 1×1 target of `format` and reads back its 16 bytes.
fn draw(
device: &wgpu::Device,
queue: &wgpu::Queue,
fragment: &str,
format: wgpu::TextureFormat,
) -> [u8; 16] {
let program = translate_composite(
&source("void main() { gl_Position = ftransform(); }"),
&source(fragment),
Target::Buffers,
)
.unwrap_or_else(|e| panic!("{fragment}: {e:?}"));
let module = |module| {
device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Naga(std::borrow::Cow::Owned(module)),
})
};
let (vertex, fragment_module) = (module(program.vertex), module(program.fragment));
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
layout: None,
vertex: wgpu::VertexState {
module: &vertex,
entry_point: Some("main"),
compilation_options: Default::default(),
buffers: &[],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleStrip,
..Default::default()
},
depth_stencil: None,
multisample: Default::default(),
fragment: Some(wgpu::FragmentState {
module: &fragment_module,
entry_point: Some("main"),
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
format,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview_mask: None,
cache: None,
});
let extent = wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
};
let target = device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: extent,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
view_formats: &[],
});
let readback = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 256,
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
mapped_at_creation: false,
});
let view = target.create_view(&Default::default());
let mut encoder = device.create_command_encoder(&Default::default());
{
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
depth_slice: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: wgpu::StoreOp::Store,
},
})],
..Default::default()
});
pass.set_pipeline(&pipeline);
// The composite program's uniform block, if it kept one, reads zero.
if program.interface.uniform_size > 0 {
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: u64::from(program.interface.uniform_size).max(16),
usage: wgpu::BufferUsages::UNIFORM,
mapped_at_creation: false,
});
let group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &pipeline.get_bind_group_layout(0),
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: buffer.as_entire_binding(),
}],
});
pass.set_bind_group(0, &group, &[]);
}
pass.draw(0..4, 0..1);
}
encoder.copy_texture_to_buffer(
target.as_image_copy(),
wgpu::TexelCopyBufferInfo {
buffer: &readback,
layout: wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(256),
rows_per_image: Some(1),
},
},
extent,
);
queue.submit([encoder.finish()]);
readback
.slice(..)
.map_async(wgpu::MapMode::Read, |r| r.unwrap());
device.poll(wgpu::PollType::wait_indefinitely()).unwrap();
let bytes = readback.slice(..).get_mapped_range().unwrap()[..16]
.try_into()
.unwrap();
readback.unmap();
bytes
}
#[test]
#[ignore = "requires a host GPU; run with --ignored --nocapture"]
fn component_qualified_outputs_write_their_own_channels() {
let instance =
wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle().with_env());
let adapter = pollster::block_on(instance.request_adapter(&Default::default()))
.expect("GPU adapter required; missing adapter is not a pass");
eprintln!("Output components GPU: {:?}", adapter.get_info());
assert_ne!(adapter.get_info().device_type, wgpu::DeviceType::Cpu);
let (device, queue) = pollster::block_on(adapter.request_device(&Default::default())).unwrap();
// The channels no output writes keep the fill of a narrow output: 0, and 1 in alpha.
for (name, fragment, expected) in [
(
"uvec3 at component 1",
"layout(location = 0, component = 1) out uvec3 words;\n\
void main() { words = uvec3(7u, 8u, 9u); }",
[0u32, 7, 8, 9],
),
(
"uint at component 2",
"layout(location = 0, component = 2) out uint word;\n\
void main() { word = 5u; }",
[0, 0, 5, 1],
),
(
"two outputs share location 0",
"layout(location = 0, component = 0) out uint low;\n\
layout(location = 0, component = 1) out uvec3 high;\n\
void main() { low = 3u; high = uvec3(4u, 5u, 6u); }",
[3, 4, 5, 6],
),
(
"the component 1 output declared first",
"layout(location = 0, component = 1) out uvec2 middle;\n\
layout(location = 0) out uint low;\n\
void main() { low = 11u; middle = uvec2(12u, 13u); }",
[11, 12, 13, 1],
),
] {
let bytes = draw(&device, &queue, fragment, wgpu::TextureFormat::Rgba32Uint);
let actual: [u32; 4] = bytemuck::cast(bytes);
eprintln!("{name}: {actual:?}");
assert_eq!(actual, expected, "{name}");
}
let bytes = draw(
&device,
&queue,
"layout(location = 0, component = 0) out float red;\n\
layout(location = 0, component = 1) out vec3 rest;\n\
void main() { red = 0.0; rest = vec3(1.0, 0.0, 1.0); }",
wgpu::TextureFormat::Rgba32Float,
);
let actual: [f32; 4] = bytemuck::cast(bytes);
eprintln!("float outputs share location 0: {actual:?}");
assert_eq!(actual, [0.0, 1.0, 0.0, 1.0]);
}
@@ -0,0 +1,191 @@
//! Original synthetic rendering controls for ordinary uniform initial values.
use horizon_client_render::gfx::pack_uniforms::Value;
use horizon_shaders::{Origin, Preprocessed, Target, translate_composite};
use wgpu::util::DeviceExt;
fn source(code: &str) -> Preprocessed {
Preprocessed {
code: code.into(),
files: vec!["original-render.glsl".into()],
lines: code
.lines()
.enumerate()
.map(|(i, _)| Origin {
file: 0,
line: i as u32 + 1,
})
.collect(),
version: Some((430, None)),
..Default::default()
}
}
#[test]
#[ignore = "requires a host GPU; run with --ignored --nocapture"]
fn authored_defaults_render_and_runtime_values_override_including_zero() {
let program = translate_composite(
&source("void main() { gl_Position = ftransform(); }"),
&source("uniform float gain = 0.375; uniform mat3 basis = mat3(2.0); uniform mat4 transform = mat4(1.0); uniform vec3 tint = vec3(0.125,0.25,0.5); uniform float points[2] = float[](0.0625,0.125); uniform int mode = 3; uniform bool enabled = true; out vec4 color; void main() { color = vec4(gain * basis[0][0] * transform[2][2], tint.g + points[1], float(mode), enabled ? 1.0 : 0.0); }"),
Target::Buffers,
).unwrap();
let instance =
wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle().with_env());
let adapter = pollster::block_on(instance.request_adapter(&Default::default()))
.expect("GPU adapter required; missing adapter is not a pass");
eprintln!("Uniform defaults GPU: {:?}", adapter.get_info());
assert_ne!(adapter.get_info().device_type, wgpu::DeviceType::Cpu);
let (device, queue) = pollster::block_on(adapter.request_device(&Default::default())).unwrap();
let vertex = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Naga(std::borrow::Cow::Owned(program.vertex)),
});
let fragment = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Naga(std::borrow::Cow::Owned(program.fragment)),
});
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
layout: None,
vertex: wgpu::VertexState {
module: &vertex,
entry_point: Some("main"),
compilation_options: Default::default(),
buffers: &[],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleStrip,
..Default::default()
},
depth_stencil: None,
multisample: Default::default(),
fragment: Some(wgpu::FragmentState {
module: &fragment,
entry_point: Some("main"),
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba32Float,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview_mask: None,
cache: None,
});
let target = device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba32Float,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
view_formats: &[],
});
let readback = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 256,
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
mapped_at_creation: false,
});
for (name, zero_buffer, overrides, expected) in [
("authored defaults", false, vec![], [0.75, 0.375, 3.0, 1.0]),
(
"runtime precedence",
false,
vec![
("gain", Value::F([0.125; 16])),
("transform", Value::F([0.5; 16])),
("mode", Value::I([7, 0, 0])),
("enabled", Value::I([0; 3])),
],
[0.125, 0.375, 7.0, 0.0],
),
(
"runtime explicit zero",
false,
vec![("gain", Value::F([0.; 16])), ("mode", Value::I([0; 3]))],
[0.0, 0.375, 0.0, 1.0],
),
("zero upload negative control", true, vec![], [0.; 4]),
] {
let mut bytes = if zero_buffer {
vec![0; program.interface.uniform_size as usize]
} else {
program.interface.initial_values.clone()
};
for (name, value) in overrides {
let uniform = program
.interface
.uniforms
.iter()
.find(|u| u.name == name)
.unwrap();
value.write(&uniform.ty, &mut bytes, uniform.offset as usize);
}
let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(name),
contents: &bytes,
usage: wgpu::BufferUsages::UNIFORM,
});
let group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &pipeline.get_bind_group_layout(0),
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: buffer.as_entire_binding(),
}],
});
let view = target.create_view(&Default::default());
let mut encoder = device.create_command_encoder(&Default::default());
{
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
depth_slice: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: wgpu::StoreOp::Store,
},
})],
..Default::default()
});
pass.set_pipeline(&pipeline);
pass.set_bind_group(0, &group, &[]);
pass.draw(0..4, 0..1);
}
encoder.copy_texture_to_buffer(
target.as_image_copy(),
wgpu::TexelCopyBufferInfo {
buffer: &readback,
layout: wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(256),
rows_per_image: Some(1),
},
},
wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
);
queue.submit([encoder.finish()]);
readback
.slice(..)
.map_async(wgpu::MapMode::Read, |r| r.unwrap());
device.poll(wgpu::PollType::wait_indefinitely()).unwrap();
{
let mapped = readback.slice(..).get_mapped_range().unwrap();
let actual: &[f32] = bytemuck::cast_slice(&mapped[..16]);
eprintln!("{name}: {actual:?}");
assert_eq!(actual, expected, "{name}");
}
readback.unmap();
}
}
@@ -0,0 +1,479 @@
{
"horizon.options.controller": "Beheerder",
"horizon.options.vibration": "Vibrasie",
"horizon.options.adaptive_triggers": "Aanpasbare snellers",
"horizon.options.lightbar": "Ligbalk",
"horizon.options.look_sensitivity": "Kyksensitiwiteit",
"horizon.options.invert_x": "Omgekeerde kyk X",
"horizon.options.invert_y": "Omgekeerde kyk Y",
"horizon.options.sneak_button": "Sluipknoppie",
"horizon.options.move_deadzone": "Bewegingsdooiesone",
"horizon.options.look_deadzone": "Kykdooiesone",
"horizon.options.button_glyphs": "Knoppie Glyphs",
"horizon.options.on_screen_keyboard": "Sleutelbord op die skerm",
"horizon.options.chat_delay": "Chat Vertraging",
"horizon.options.lightbar.health": "Gesondheid",
"horizon.options.lightbar.dimension": "Dimensie",
"horizon.options.glyphs.auto": "Outo",
"horizon.options.keyboard.auto": "Outo",
"horizon.options.unavailable": "Hierdie kenmerk is nog nie in Horizon beskikbaar nie.",
"horizon.options.search": "Soek instellings...",
"horizon.pages.shader_packs": "Shader Pakke",
"horizon.pages.shader_options": "Shader Opsies",
"horizon.pages.controller": "Beheerderinstellings",
"horizon.pages.controller_binds": "Kontroleerder bind",
"horizon.pages.extras": "Horizon-ekstras",
"horizon.pages.settings": "Horizon-instellings",
"horizon.pages.agent": "KI Agent",
"horizon.split.leave": "Los",
"horizon.pages.split_settings": "Verdeelskerm-instellings",
"horizon.pages.split_profiles": "Wie speel?",
"horizon.pages.split_setup": "Verdeelskerm-opstelling",
"horizon.pages.music": "Musiek",
"horizon.pages.music.menu": "Spyskaart",
"horizon.pages.music.overworld": "Oorwêreld",
"horizon.pages.music.creative": "Kreatief",
"horizon.pages.music.underwater": "Onderwater",
"horizon.pages.music.end": "Die Einde",
"horizon.pages.music.boss": "Baas",
"horizon.pages.music.credits": "Krediete",
"horizon.pages.music.overworld_biomes": "Oorwêreld biome",
"horizon.pages.music.nether_biomes": "Onder biome",
"horizon.pages.music.end_biomes": "Eindig biome",
"horizon.pages.music.other": "Ander musiek",
"horizon.pages.music.empty": "Geen musieksnitte nie: die speletjie se klanke is nie gelaai nie.",
"horizon.pages.music.tooltip": "Die ingeboude snitte, volgens situasie: speel nou een, of skakel enige af sodat die speletjie ophou om dit te kies.",
"horizon.pages.settings.tooltip": "Horizon se eie instellings: grafika, beheerders, ekstras en mods.",
"horizon.pages.graphics.tooltip": "Grafika, eenvoudig: elke instelling sê wat dit kos.",
"horizon.pages.graphics_advanced.tooltip": "Elke grafiese instelling, Horizon s'n ingesluit, met wat elkeen kos.",
"horizon.pages.controller.tooltip": "Beheerders: hul knoppies, stokke, vibrasie en spyskaartnavigasie.",
"horizon.pages.extras.tooltip": "Opsionele toevoegings, alles by verstek af: die speletjie bly vanielje totdat jy een aanskakel.",
"horizon.pages.agent.tooltip": "Laat jou eie KI-agent speel: horizon-ctl en MCP-kliënte op hierdie rekenaar.",
"horizon.pages.split_profiles.tooltip": "Wie speel: 'n profiel wat in hierdie speletjie-lêergids gehou word.",
"horizon.pages.split_setup.tooltip": "Die vensters en monitors, en wie speel waar.",
"horizon.pages.shader_packs.tooltip": "Shader-pakke vanaf die shaderpacks-lêergids: Horizon se eie en dié in die shaders/formaat. Een hardloop op 'n slag.",
"horizon.pages.mods.tooltip": "Mods gemaak vir Horizon, vanaf die horizon-mods-lêergids. Elkeen loop in 'n sandbox en bly af totdat jy dit aanskakel.",
"horizon.pages.open.graphics": "Grafika...",
"horizon.pages.open.graphics_advanced": "Gevorderde grafika...",
"horizon.pages.open.shader_packs": "Shader-pakke...",
"horizon.pages.open.controller": "Beheerderinstellings...",
"horizon.pages.open.split_setup": "Verdeelskerm...",
"horizon.pages.open.extras": "Ekstras...",
"horizon.pages.open.mods": "Modusse...",
"horizon.pages.open.agent": "KI Agent...",
"horizon.pages.open.horizon_graphics": "Horizon-grafika...",
"horizon.pages.open.controller_binds": "Beheerder bind...",
"horizon.pages.open.horizon_settings": "Horizon-instellings...",
"horizon.pages.open.music": "Musiek...",
"horizon.mods.empty": "Geen mods: plaas .hmod-lêers in die horizon-mods-lêergids",
"horizon.mods.stopped": " (Gestop)",
"horizon.mods.not_built": " (Nie gebou nie)",
"horizon.mods.dev": " (Ontwikkelaar)",
"horizon.mods.settings": "Instellings",
"horizon.mods.in_world": " (los die wêreld om te verander)",
"horizon.mods.open_folder": "Maak Mod-lêergids oop",
"horizon.shader_packs.empty": "Geen shader packs: plaas dit in die shaderpacks-lêergids",
"horizon.shader_options.open": "Shader-opsies...",
"horizon.shader_options.profile": "Profiel",
"horizon.shader_options.loading": "Lees tans die shader-pakket se opsies...",
"horizon.agent.copy_details": "Kopieer verbindingbesonderhede",
"horizon.multiplayer.join": "Sluit aan",
"horizon.multiplayer.cancel_reconnecting": "Kanselleer Herkoppel",
"horizon.multiplayer.join_friend": "Sluit aan by 'n Vriend",
"horizon.multiplayer.invite_code": "Uitnodigingskode",
"horizon.multiplayer.relay": "Aflos (opsioneel)",
"Preset": "Vooraf ingesteld",
"LOD Distance": "LOD Afstand",
"Far Detail": "Verre Detail",
"Cave Culling": "Grotuitdunning",
"Entity Culling": "Entiteit Uitdunning",
"Upload Budget": "Laai begroting op",
"Sky": "Hemel",
"Fog": "Mis",
"Weather": "Weer",
"Animated Textures": "Geanimeerde teksture",
"Present Mode": "Aanbiedingsmodus",
"Frame Pacing": "Raamtempo",
"Worker Threads": "Werkerdrade",
"Resolution Scale": "Resolusieskaal",
"Upscaling": "Opskaling",
"Paper White": "Papier Wit",
"Peak Brightness": "Piek helderheid",
"Black Level": "Swart vlak",
"UI Brightness": "UI Helderheid",
"Battery Mode": "Batterymodus",
"Graphics API": "Grafiese API",
"Translucent Batching": "Deurskynende Batching",
"Performance Overlay": "Prestasie-oorleg",
"Full Detail Distance": "Volledige detail Afstand",
"On": "Aan",
"Off": "Af",
"Unlimited": "Onbeperk",
"Auto": "Outo",
"Custom": "Pasgemaak",
"Performance": "Prestasie",
"Balanced": "Gebalanseerd",
"Quality": "Kwaliteit",
"Ultra Performance": "Ultra prestasie",
"Native": "Inheems",
"Full": "Vol",
"Reduced": "Verminder",
"Efficient": "Doeltreffend",
"Precise": "Presies",
"Auto (VSync)": "Outo (VSync)",
"FIFO": "EIEU",
"Mailbox": "Posbus",
"Immediate": "Onmiddellik",
"FPS and Graph": "FPS en grafiek",
"No cost": "Geen koste nie",
"Low cost": "Lae koste",
"Medium cost": "Medium koste",
"High cost": "Hoë koste",
"Saves time": "Spaar tyd",
"Graphics": "Grafika",
"Done": "Klaar",
"This feature is not yet available in Horizon.": "Hierdie kenmerk is nog nie in Horizon beskikbaar nie.",
"Your display or system doesn't offer HDR output.": "Jou skerm of stelsel bied nie HDR-uitvoer nie.",
"Turn HDR on to use it.": "Skakel HDR aan om dit te gebruik.",
"Your GPU or graphics API doesn't support it.": "Jou GPU of grafiese API ondersteun dit nie.",
"%s chunks": "%s stukke",
"%s per frame": "%s per raam",
"%s MiB/frame": "%s MiB/raam",
"%s KiB/frame": "%s KiB/raam",
"Auto (%s)": "Outo (%s)",
"Full-detail terrain range. LOD Distance can extend local worlds farther.": "Volledige detail terreinreeks. LOD Distance kan plaaslike wêrelde verder uitbrei.",
"Coarse distant terrain in local worlds. Integrated GPUs stop at 128 chunks.": "Growwe verre terrein in plaaslike wêrelde. Geïntegreerde GPU's stop by 128 stukke.",
"Reduced draws far terrain coarser: faster on this integrated GPU.": "Verminder trek veel terrein growwer: vinniger op hierdie geïntegreerde GPU.",
"Reduced draws far terrain coarser, for GPUs it slows down.": "Verminder trek veel terrein growwer, vir GPU's word dit stadiger.",
"Field of view. Wider shows more terrain at once.": "Gesigsveld. Wyer wys meer terrein op een slag.",
"Caps frames per second; a cap saves power and heat.": "Caps rame per sekonde; 'n pet spaar krag en hitte.",
"Waits for the display's refresh: no tearing, a little latency.": "Wag vir die verversing van die skerm: geen skeur nie, 'n bietjie vertraging.",
"Size of menus and the HUD.": "Grootte van spyskaarte en die HUD.",
"Sets the options below together. Changing one makes it Custom.": "Stel die opsies hieronder saam. Om een te verander, maak dit Pasgemaak.",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "Lava, vuur en sonlig skyn helderder as wit. Benodig 'n HDR-skerm.",
"Your display's darkest black. 0 for OLED; LCDs glow a little.": "Jou skerm is die donkerste swart. 0 vir OLED; LCD's gloei 'n bietjie.",
"How bright highlights get. Auto: the peak your system reports.": "Hoe helder hoogtepunte word. Outo: die hoogtepunt wat jou stelsel rapporteer.",
"How bright white paper and menus look. Auto: your desktop's white.": "Hoe helder wit papier en spyskaarte lyk. Outo: jou lessenaar is wit.",
"How bright the HUD and menus are, against paper white.": "Hoe helder die HUD en spyskaarte is, teenoor papierwit.",
"How far mobs, crops and machines keep working. CPU time.": "Hoe ver skare, gewasse en masjiene aanhou werk. SVE tyd.",
"How far away mobs and items are drawn, as a share of normal.": "Hoe ver weg skares en items getrek word, as 'n deel van normaal.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Slaan terrein oor wat agter geslote gedeeltes versteek is. Af kos baie.",
"Hides entities behind opaque blocks.": "Versteek entiteite agter ondeursigtige blokke.",
"No entity occlusion culling.": "Geen entiteit-okklusion uitskot nie.",
"CPU occlusion test runs this frame.": "SVE-okklusietoets loop hierdie raam.",
"Occlusion checks run on a worker thread.": "Okklusiekontroles loop op 'n werkersdraad.",
"Meshed sections uploaded per frame. Lower is smoother while loading.": "Maasafdelings per raam opgelaai. Laer is gladder tydens laai.",
"Soft shadows in corners. Costs chunk building time only.": "Sagte skaduwees in hoeke. Dit kos slegs 'n stuk boutyd.",
"Smaller copies of block textures for distant terrain: less shimmer.": "Kleiner kopieë van blokteksture vir verafgeleë terrein: minder glans.",
"Blends grass and water colours across biomes when building chunks.": "Vermeng gras en waterkleure oor biome wanneer stukke gebou word.",
"Cloud layer: off, flat (fast) or 3D (fancy).": "Wolklaag: af, plat (vinnig) of 3D (fancy).",
"How far clouds reach, in chunks.": "Hoe ver wolke reik, in stukke.",
"Draws the sky, sun, moon and stars.": "Teken die lug, son, maan en sterre.",
"Distance fog. Off shows the edge of the loaded world.": "Afstand mis. Af wys die rand van die gelaaide wêreld.",
"Draws rain and snow.": "Trek reën en sneeu.",
"How far rain and snow fall around you.": "Hoe ver val reën en sneeu om jou.",
"How many particles effects spawn.": "Hoeveel deeltjies effekte kuit.",
"Round shadows under mobs and items.": "Ronde skaduwees onder skares en items.",
"Leaves with see-through gaps. Off draws them solid.": "Blare met deursigtige gapings. Off trek hulle stewig.",
"Sorts water and glass more exactly behind each other.": "Sorteer water en glas meer presies agter mekaar.",
"Which chunk rebuilds wait for the frame: none, nearby or yours.": "Watter stuk herbou wag vir die raam: geen, naby of joune.",
"Animates water, lava, fire and other moving textures.": "Animeer water, lawa, vuur en ander bewegende teksture.",
"World pixels per screen pixel. Below 100% is faster on the GPU.": "Wêreldpixels per skermpiksel. Onder 100% is vinniger op die GPU.",
"Smooths jagged edges. FXAA: softer. TAA: smoothest, moving things may trail.": "Maak gekartelde rande glad. FXAA: sagter. TAA: gladste, bewegende dinge kan loop.",
"Renders the world smaller and rebuilds it with TAA. Faster, softer.": "Maak die wêreld kleiner en herbou dit met TAA. Vinniger, sagter.",
"HDR output on an HDR display: lava, fire and sunlight shine brighter.": "HDR-uitset op 'n HDR-skerm: lawa, vuur en sonlig skyn helderder.",
"How frames reach the screen while VSync is off.": "Hoe rame die skerm bereik terwyl VSync af is.",
"Efficient sleeps until the next frame; Precise spins, like vanilla.": "Doeltreffende slaap tot die volgende raam; Presiese draaie, soos vanielje.",
"Fills the screen (borderless).": "Vul die skerm (randloos).",
"Brightens dark places.": "Verhelder donker plekke.",
"Lowers the frame rate when minimised, or idle too.": "Verlaag die raamtempo wanneer dit geminimaliseer word, of ook ledig.",
"Sways the view while walking.": "Swaai die uitsig terwyl jy stap.",
"Fades newly loaded chunks in.": "Vervaag nuutgelaaide stukke in.",
"Darkens the screen's edges.": "Maak die skerm se rande donkerder.",
"Shows when the world saves.": "Wys wanneer die wêreld red.",
"Shows attack cooldown.": "Toon aanvalafkoeling.",
"60 FPS cap (30 in menus or unfocused), half the workers: less power.": "60 FPS pet (30 in spyskaarte of ongefokus), die helfte van die werkers: minder krag.",
"Threads that generate, light and mesh chunks. Next start.": "Drade wat stukke genereer, lig en maas. Volgende begin.",
"Vulkan, then DirectX 12 (Windows) or OpenGL (Linux) where it fails. Next start.": "Vulkan, dan DirectX 12 (Windows) of OpenGL (Linux) waar dit misluk. Volgende begin.",
"Water and glass in one buffer: fewer draw calls, a bit more VRAM. Next start.": "Water en glas in een buffer: minder trekoproepe, 'n bietjie meer VRAM. Volgende begin.",
"Mesh data sent to the GPU per frame. Lower is smoother while loading.": "Maasdata per raam na die GPU gestuur. Laer is gladder tydens laai.",
"Frame rate and frame times over the game, with a graph.": "Raamtempo en raamtye oor die spel, met 'n grafiek.",
"horizon.multiplayer.finding_friend": "Soek jou vriend se wêreld...",
"horizon.multiplayer.not_signed_in": "Hierdie bediener is in aanlynmodus. Meld aan met jou Microsoft-rekening in die Horizon-lanseerder om daarby aan te sluit.",
"horizon.multiplayer.invalid_address": "Ongeldige bedieneradres: %s",
"horizon.multiplayer.connected_friend_direct": "Reguit gekoppel aan jou vriend...",
"horizon.multiplayer.connected_friend_relay": "Gekoppel aan jou vriend deur die aflos...",
"General": "Algemeen",
"Advanced": "Gevorderd",
"Undo": "Ontdoen",
"Search options": "Soek opsies",
"Shortcut": "Kortpad",
"Distance": "Afstand",
"Display": "Vertoon",
"Interface": "Koppelvlak",
"Sky and weather": "Lug en weer",
"Lighting and entities": "Beligting en entiteite",
"Textures": "Teksture",
"Image": "Beeld",
"Chunk loading": "Chunk laai",
"Culling": "Uitskot",
"Power": "Krag",
"Frames": "Rame",
"Overlay": "Oorleg",
"Output": "Uitset",
"Brightness": "Helderheid",
"How far you see, the window and the HUD.": "Hoe ver jy sien, die venster en die HUD.",
"The sky and weather, lighting, textures and the image.": "Die lug en weer, beligting, teksture en die beeld.",
"Chunk loading, culling and power use.": "Chunk laai, uitskot en kraggebruik.",
"How frames reach the screen, and the performance overlay.": "Hoe rame die skerm bereik, en die prestasie-oorleg.",
"Brighter-than-white output on an HDR display.": "Helderder-as-wit uitset op 'n HDR-skerm.",
"Results for \"%s\"": "Resultate vir \"%s\"",
"No settings match.": "Geen instellings pas nie.",
"horizon.title.not_affiliated": "Nie geaffilieer met Mojang of Microsoft nie",
"horizon.extras.header.lighting": "Beligting",
"horizon.extras.header.third_person": "Derde persoon",
"horizon.extras.header.particles": "Deeltjies",
"horizon.extras.header.inventory": "Voorraad",
"horizon.extras.header.chat": "Gesels",
"horizon.extras.header.multiplayer": "Multispeler",
"horizon.extras.header.sound": "Klank",
"horizon.extras.header.menus": "Spyskaarte",
"horizon.extras.fullbright": "Vol helder",
"horizon.extras.dynamic_lights": "Dinamiese ligte",
"horizon.extras.own_name_tag": "Eie naamplaatjie",
"horizon.extras.hide_block_particles": "Versteek blokbreekdeeltjies",
"horizon.extras.auto_reconnect": "Herkoppel outomaties",
"horizon.extras.ui_sound_reserve": "Hou kanale vir UI-klanke",
"horizon.extras.music_page": "Musiek bladsy",
"horizon.extras.mouse_tweaks": "Muis Tweaks kontroles",
"horizon.extras.mouse_tweaks_wheel": "Rolwiel beweeg items",
"horizon.extras.sort": "Sorteer knoppies",
"horizon.extras.sort_key": "Sorteer sleutel",
"horizon.extras.no_ai": "Geen KI",
"horizon.extras.agent_control": "Agent beheer",
"horizon.extras.discord_presence": "Ryk teenwoordigheid op Discord",
"horizon.extras.splitscreen": "Verdeelskerm",
"horizon.extras.chat_animation": "Glad gesels",
"horizon.extras.chat_indicator": "Boodskap aanwyser",
"horizon.extras.wheel_cycles_settings": "Rolwiel verander instellings",
"horizon.extras.pack_browser": "Hulpbronpakketblaaier",
"horizon.extras.fullbright.tooltip": "Wys blokke en entiteite in volle lig, selfs in donker areas.",
"horizon.extras.dynamic_lights.tooltip": "Fakkels en ander ligbronne wat vasgehou, laat val word of aan die brand steek, steek die blokke om hulle aan.",
"horizon.extras.own_name_tag.tooltip": "Wys jou naam bo jou kop in derde persoon, soos ander spelers dit sien. Versteek wanneer die bediener jou span se name versteek of jy onsigbaar is.",
"horizon.extras.hide_block_particles.tooltip": "Versteek die puindeeltjies wat uit 'n gebreekte blok bars en verkrummel van die een wat jy myn. Ander deeltjies bly.",
"horizon.extras.auto_reconnect.tooltip": "Sluit weer by 'n bediener aan na 'n verlore verbinding of 'n herbegin, nooit na 'n skop of verbod nie.",
"horizon.extras.ui_sound_reserve.tooltip": "Klanke in die wêreld laat 16 van die 247 klankkanale vry, so 'n vloed van hulle ('n groot ontploffing) kan nie kieslysklikke stilmaak nie.",
"horizon.extras.music_page.tooltip": "Musiek en Klanke kry 'n Musiek...-bladsy: speel nou enige ingeboude snit, of skakel een af sodat die speletjie ophou om dit te kies. Niks verander totdat jy 'n snit afskakel nie.",
"horizon.extras.mouse_tweaks.tooltip": "Regs-sleep plaas een per gleuf, links-sleep versamel die gehou item, Shift + links-sleep skuif elke gleuf.",
"horizon.extras.mouse_tweaks_wheel.tooltip": "Af stuur een item van die gleuf na die ander kant, op bring een terug.",
"horizon.extras.sort.tooltip": "Sorteer die voorraad, kiste, vate, eindkiste en shulkerbokse in kreatiewe oortjievolgorde, en voeg stapels saam.",
"horizon.extras.sort_key.tooltip": "Sorteer die deel onder die wyser. Klik en druk dan 'n sleutel; Ontvlugting ontbind dit.",
"horizon.extras.no_ai.tooltip": "Versteek en skakel elke KI-kenmerk af: agentbeheer, horizon-ctl en die MCP-bedieners. Die lanseerder se skakelaar ook.",
"horizon.extras.agent_control.tooltip": "Jou eie KI-agent kan hierdie speletjie deur horizon-ctl of 'n MCP-kliënt speel, slegs op hierdie rekenaar.",
"horizon.extras.discord_presence.tooltip": "Wys Playing Horizon op jou Discord-profiel terwyl Discord loop: spyskaarte, enkelspeler of multispeler, die dimensie en die speeltyd. Nooit 'n wêreld se naam of 'n bediener se adres nie.",
"Calibrate...": "Kalibreer...",
"Calibrate Anyway...": "Kalibreer in elk geval...",
"Use Automatic Setup": "Gebruik outomatiese opstelling",
"Shortcut: %s": "Kortpad: %s",
"Next": "Volgende",
"HDR Calibration %s/%s: %s": "HDR-kalibrasie %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Stel die drie hieronder volskerm, een toetsprent op 'n slag.",
"A key that opens this screen from the game. Not bound at first.": "'n Sleutel wat hierdie skerm vanaf die speletjie oopmaak. Eers nie gebonde nie.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Jou skerm het nie gesê hoe helder dit word nie: kalibreer om dit op te stel.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Reeds opgestel vir jou vertoning. Kalibreer slegs as HDR verkeerd lyk.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Plaas Peak Brightness en Paper White terug na jou skerm se opstelling.",
"Set up for your display. Leave on Auto unless it looks wrong.": "Stel op vir jou vertoning. Los op Auto tensy dit verkeerd lyk.",
"Lower it until the logo just disappears into the black.": "Laat sak dit totdat die logo net in die swart verdwyn.",
"Raise it until the logo just disappears into the bright square.": "Lig dit op totdat die logo net in die helder vierkant verdwyn.",
"Make the card a white that's comfortable to look at for a long time, in the light you play in.": "Maak die kaart 'n wit wat gemaklik is om lank na te kyk, in die lig waarin jy speel.",
"horizon.splitscreen.profiles.forget": "Vergeet",
"horizon.splitscreen.profiles.create": "Skep",
"horizon.splitscreen.profiles.you": " - jy",
"horizon.splitscreen.profiles.forget_named": "Vergeet %s",
"horizon.splitscreen.profiles.previous": "Vorige",
"horizon.splitscreen.profiles.next": "Volgende",
"horizon.splitscreen.profiles.new_button": "Nuwe profiel...",
"horizon.splitscreen.profiles.forget_title": "Vergeet profiel?",
"horizon.splitscreen.profiles.forget_leaves": "Dit verlaat hierdie instansie se lys.",
"horizon.splitscreen.profiles.forget_kept": "Wêreldvordering en instellings word behou.",
"horizon.splitscreen.profiles.new_title": "Nuwe profiel",
"horizon.splitscreen.profiles.name_hint": "Naam (3 tot 16 letters, syfers, _)",
"horizon.splitscreen.profiles.switch_title": "Wissel profiel",
"horizon.splitscreen.profiles.offline_note": "Vanlyn profiele behoort aan hierdie geval",
"horizon.splitscreen.profiles.move": "Beweeg waar jy speel",
"horizon.splitscreen.profiles.move_place": "Beweeg waar jy speel: %s",
"horizon.friends.title": "Speel met vriende",
"horizon.friends.hint": "Vriende plak die kode na die titelskerm se Vriende-knoppie, in Direkte verbinding, of op die lanseerder se Speel-bladsy.",
"horizon.friends.copied": "Gekopieer",
"horizon.friends.copy_code": "Kopieer kode",
"horizon.friends.stop_sharing": "Hou op deel",
"horizon.friends.none_yet": "Nog geen vriende hier nie.",
"horizon.friends.here_now": "Hier nou: %s",
"horizon.friends.share_intro": "Deel hierdie wêreld agter 'n uitnodigingskode: dit maak oop na LAN, en vriende op die internet sluit aan met die kode. Geen bediener van ons is betrokke nie.",
"horizon.friends.start_sharing": "Begin deel",
"horizon.friends.troubleshooting": "Foutsporing …",
"horizon.friends.stopped": "Het opgehou om die uitnodigingkode te deel.",
"horizon.friends.short_code_changed": "Iemand het aanhou om 'n verkeerde kort kode te probeer, so dit het verander.",
"horizon.friends.wrong_code": "Iemand het 'n verkeerde kode probeer.",
"horizon.friends.code_ready": "Jou uitnodigingskode is gereed: Speletjie-kieslys, Speel met vriende. Kopieer dit en stuur dit aan jou vriende.",
"horizon.friends.share_failed": "Kon nie die wêreld deel nie: %s",
"horizon.friends.finding_route": "Vind tans hoe vriende jou kan bereik …",
"horizon.osk.shift": "Verskuiwing",
"horizon.osk.space": "Ruimte",
"horizon.osk.delete": "Vee uit",
"horizon.osk.type": "Tik",
"horizon.osk.symbols": "Simbole",
"horizon.osk.close": "Maak toe",
"horizon.extras.chat_animation.tooltip": "'n Nuwe kletslyn gly van links af terwyl dit vervaag, in plaas daarvan om heel te lyk.",
"horizon.extras.chat_indicator.tooltip": "Die gekleurde balk aan die linkerkant van 'n kletslyn.",
"horizon.extras.wheel_cycles_settings.tooltip": "Die rolwiel oor 'n knoppie in die instellings verander die waarde daarvan, soos in vanielje. Af, die wiel blaai net deur die lys.",
"horizon.extras.pack_browser.tooltip": "Select Resource Packs kry 'n Voeg by-knoppie wat Modrinth deursoek en 'n pakket in jou pakke-lêergids installeer. Af, die speletjie kontak nooit Modrinth nie.",
"horizon.extras.splitscreen.tooltip": "In 'n wêreld kan 'n ander beheerder as 'n nuwe speler aansluit: druk A wanneer dit vra, kies dan 'n profiel en waar om te speel.",
"horizon.splitscreen.setup.monitor_unknown": "'n monitor",
"horizon.splitscreen.setup.window": "Venster %s",
"horizon.splitscreen.setup.new_window": "Nuwe venster",
"horizon.splitscreen.setup.fill_monitor": "Vul %s",
"horizon.splitscreen.setup.done": "Klaar",
"horizon.splitscreen.setup.on": "Aan",
"horizon.splitscreen.setup.off": "Af",
"horizon.splitscreen.setup.player": "Speler %s",
"horizon.splitscreen.setup.revert": "Keer terug",
"horizon.splitscreen.setup.apply": "Doen aansoek",
"horizon.splitscreen.setup.warning.empty_main": "Niemand speel in Venster 1 nie: dit skuil",
"horizon.splitscreen.setup.warning.empty_close": "Niemand speel in Venster %s nie: dit sluit op Toepassing",
"horizon.splitscreen.setup.warning.empty_new": "Niemand speel in Venster %s nie: dit sal nie oopmaak nie",
"horizon.splitscreen.setup.warning.on_main": "Venster %s vul Venster 1 se monitor: sy spelers speel in Venster 1",
"horizon.splitscreen.setup.warning.unplugged": "Venster %s se monitor is nie gekoppel nie: sy spelers speel in Venster 1",
"horizon.splitscreen.setup.status.off": "Verdeelskerm is af.",
"horizon.splitscreen.setup.status.enable_hint": "Skakel dit aan en druk dan 'n knoppie op 'n ander beheerder in 'n wêreld.",
"horizon.splitscreen.setup.status.join_hint": "Druk A om aan te sluit en kies 'n profiel.",
"horizon.splitscreen.setup.status.idle": "Gedeelskerm ledig",
"horizon.splitscreen.setup.status.alone": "Geen ander plaaslike spelers nie.",
"horizon.splitscreen.setup.status.solo_controls": "Solo-kontroles en kontroleerder-terugvoer is onveranderd.",
"horizon.splitscreen.setup.window_empty": "Venster %s: leeg",
"horizon.splitscreen.setup.note.select": "Klik 'n speler, of sleep dit na 'n venster of 'n monitor.",
"horizon.splitscreen.setup.note.apply": "Doen aansoek om op hierdie manier te speel, Keer terug om te ontdoen.",
"horizon.splitscreen.setup.note.place": "Waar moet %s speel?",
"horizon.splitscreen.setup.this_player": "hierdie speler",
"horizon.splitscreen.setup.switch_disabled": "Splitscreen skakel af sodra die ander vertrek het (hul pousekieslys).",
"horizon.splitscreen.setup.window_place": "Venster %s (%s van %s)",
"horizon.splitscreen.setup.names": "Name",
"horizon.splitscreen.setup.identify": "Identifiseer",
"horizon.splitscreen.setup.layout.two": "2 deel: %s",
"horizon.splitscreen.setup.layout.three": "3 deel: %s",
"horizon.splitscreen.setup.layout.two.0": "Outo",
"horizon.splitscreen.setup.layout.two.1": "Sy aan sy",
"horizon.splitscreen.setup.layout.two.2": "Bo en onder",
"horizon.splitscreen.setup.layout.three.0": "Een breed bo-op",
"horizon.splitscreen.setup.layout.three.1": "Een lang aan die linkerkant",
"horizon.splitscreen.setup.layout.three.2": "Sy aan sy",
"horizon.splitscreen.setup.help.player": "Klik of sleep %s om hulle te skuif.",
"horizon.splitscreen.setup.help.layout_auto": "%s (naas mekaar wanneer die venster wyd is). Druk om te verander.",
"horizon.splitscreen.setup.help.layout": "%s. Druk om te verander.",
"horizon.splitscreen.setup.help.switch": "Verdeelskerm vir hierdie rekenaar, aan of af.",
"horizon.splitscreen.setup.help.identify": "Elke speler se aansig wys in watter venster dit is.",
"horizon.splitscreen.setup.help.names": "Elke speler se naam in die hoek van hul aansig.",
"horizon.splitscreen.setup.help.revert": "Ontdoen die veranderinge wat nog nie toegepas is nie.",
"horizon.splitscreen.setup.help.apply": "Speel soos die kaart wys.",
"horizon.splitscreen.setup.help.unchanged": "Niks het nog verander nie.",
"horizon.splitscreen.setup.give_up_pad": "Gee op Pad",
"horizon.splitscreen.prompt.controller_connected": "Beheerder gekoppel",
"horizon.splitscreen.prompt.controller_disconnected": "Beheerder ontkoppel",
"horizon.splitscreen.prompt.controller_disconnected.hint": "Koppel dit weer, of druk 'n knoppie op 'n ander",
"horizon.splitscreen.prompt.join.one": "%s wil aansluit",
"horizon.splitscreen.prompt.join.many": "%s beheerders wil aansluit",
"horizon.splitscreen.prompt.join.yes": "Sluit aan",
"horizon.splitscreen.prompt.join.as_p1": "Speel as P1",
"horizon.splitscreen.prompt.join.no": "Nee",
"horizon.splitscreen.prompt.lost": "%s: beheerder ontkoppel",
"horizon.splitscreen.prompt.lost.use_another": "Gebruik 'n ander",
"horizon.splitscreen.prompt.lost.leave": "Los",
"horizon.splitscreen.setup.help.give_up_pad": "Jou beheerder hou op om jou te bestuur en word aangebied om aan te sluit (A).",
"horizon.splitscreen.setup.note.held_at": "%s tot %s: A laat val dit hier, B sit dit terug.",
"horizon.splitscreen.setup.note.held": "%s opgetel: links/regs kies waar, A laat val, B sit dit terug.",
"Hides models behind opaque blocks. Async checks off-thread; entities tick.": "Versteek modelle agter ondeursigtige blokke. Async kontroleer af-draad; entiteite merk.",
"Block Entities": "Blok Entiteite",
"Reuses settled boxes; lid movement stays animated.": "Hergebruik afgewikkelde bokse; dekselbeweging bly geanimeerd.",
"Signs": "Tekens",
"Reuses sign text until text, light, or font changes.": "Hergebruik tekenteks totdat teks, lig of lettertipe verander.",
"Decorated Pots": "Versierde potte",
"Reuses still pots; break wobble and sherd changes redraw.": "Hergebruik stilpotte; breek wankel en skeur veranderinge herteken.",
"Bells": "Klokke",
"Reuses still bells; ringing stays animated.": "Hergebruik stilstaande klokke; lui bly geanimeerd.",
"Chests": "Kiste",
"Reuses closed chests; moving lids stay animated.": "Hergebruik toe kiste; bewegende deksels bly geanimeerd.",
"Banners": "Baniere",
"Reuses poles; flags and pattern layers still update.": "Hergebruik pale; vlae en patroonlae werk steeds by.",
"Copper Golem Statues": "Koper Golem-standbeelde",
"Reuses statue geometry until its block state changes.": "Hergebruik standbeeldgeometrie totdat die bloktoestand daarvan verander.",
"Leaf Culling": "Blaaruitsny",
"Every leaf face. Vanilla's fancy leaves.": "Elke blaar gesig. Vanielje se spoggerige blare.",
"Distant Plant Dither": "Verre Plant Dither",
"Keeps fine plants in high-res packs from fading far away.": "Hou fyn plante in hoë-resolusie-pakke om ver weg te vervaag.",
"Screenshot Quality": "Skermkiekie Kwaliteit",
"F2 screenshots at this many times the game's resolution. Higher takes longer.": "F2 skermkiekies teen hierdie baie keer die spel se resolusie. Hoër neem langer.",
"Frame Generation": "Raam Generasie",
"Shows a made-up frame between each two real ones: smoother, a little more lag.": "Toon 'n opgemaakte raam tussen elke twee regte: gladder, 'n bietjie meer vertraging.",
"Shadow Cache": "Skadu-kas",
"Reuses the pack shadow map when sun, view and world match. Off by default.": "Hergebruik die pakskadukaart wanneer son, uitsig en wêreld ooreenstem. Af by verstek.",
"Find BlueMap": "Vind BlueMap",
"On servers, finds their BlueMap web map for far terrain. Next join.": "Op bedieners, vind hul BlueMap-webkaart vir ver terrein. Sluit volgende aan.",
"BlueMap Radius": "Blou Kaart Radius",
"How far around you BlueMap tiles are downloaded. Next join.": "Hoe ver om jou is BlueMap-teëls afgelaai. Sluit volgende aan.",
"Synchronous": "Sinchronies",
"Asynchronous": "Asinchronies",
"Solid": "Solied",
"Solid Aggressive": "Stewige aggressiewe",
"Hollow": "Hol",
"Leaves fully inside a canopy draw solid: fewer see-through layers.": "Blare volledig binne-in 'n blaredak trek solied: minder deurskynende lae.",
"Leaves with one open side draw solid too: faster, chunkier edges.": "Blare met een oop kant trek ook solied: vinniger, bonkiger rande.",
"Drops faces between leaves: faster, but gaps look through.": "Laat val gesigte tussen blare: vinniger, maar gapings kyk deur.",
"Hotbar: Previous Slot": "Hotbar: Vorige Slot",
"Hotbar: Next Slot": "Hotbar: Volgende Slot",
"horizon.agent.connected.one": "Gekoppel: 1 agentverbinding",
"horizon.agent.connected.many": "Gekoppel: %s agentverbindings",
"horizon.agent.connected.one_ago": "Gekoppel: 1 agentverbinding, laaste opdrag %s s gelede",
"horizon.agent.connected.many_ago": "Gekoppel: %s agentverbindings, laaste opdrag %s s gelede",
"horizon.agent.waiting": "Wag vir 'n agent.",
"horizon.agent.off": "Af: agente kan nie hierdie speletjie bereik nie.",
"horizon.agent.session_opened": "Geopen vir hierdie sessie deur die program wat die speletjie begin het.",
"horizon.agent.socket": "Sok: %s (kopieer die besonderhede vir sy volledige pad)",
"horizon.agent.local_only": "Slegs plaaslik: agente op hierdie rekenaar, nooit die netwerk nie.",
"%s nits": "%s nete",
"Auto (%s nits)": "Outo (%s nete)",
"%.2f nits": "%.2f nete",
"Hides models behind opaque blocks. Entities update. Off with custom packs.": "Versteek modelle agter ondeursigtige blokke. Entiteite-opdatering. Af met pasgemaakte pakke.",
"horizon.extras.repro_keys": "Opname kortpaaie",
"horizon.extras.repro_keys.tooltip": "F3+R begin of stop 'n repro-opname en F3+E skryf sy bundel. Af by verstek: 'n opname kopieer die wêreld.",
"horizon.agent.record_session": "Begin opnamesessie",
"horizon.agent.record_stop": "Stop opnamesessie",
"horizon.agent.export_repro": "Voer Repro-bundel uit",
"horizon.repro.started": "Opname het begin; F3+R stop dit en skryf die bundel.",
"horizon.repro.stopped": "Opname gestop: %s",
"horizon.repro.bundle": "Repro-bundel: %s",
"horizon.repro.no_recording": "Daar is nog geen opname om uit te voer nie.",
"horizon.repro.replay_started": "Herhaling het begin: %s",
"horizon.pack.addInfo": "Voeg 'n hulpbronpakket van Modrinth by",
"horizon.pack.addWorking": "Vra vir Modrinth...",
"horizon.pack.addNone": "Geen hulpbronpakke gevind nie",
"horizon.pack.addTitle": "Voeg 'n hulpbronpakket by",
"horizon.pack.install": "Installeer",
"horizon.pack.installing": "Installeer tans",
"horizon.pack.installed": "Geïnstalleer",
"horizon.pack.search": "Soek",
"horizon.pack.searchHint": "Soek Modrinth...",
"horizon.pack.filter.realistic": "Realisties",
"horizon.pack.filter.simplistic": "Simplistiese",
"horizon.pack.filter.vanillaLike": "Vanielje-agtig",
"horizon.pack.addFailed": "Die pakkie kon nie bygevoeg word nie: %s",
"horizon.extras.header.cosmetics": "Skoonheidsmiddels",
"horizon.extras.cosmetics_mine": "My skoonheidsmiddels",
"horizon.extras.cosmetics_mine.tooltip": "Versteek: elke Horizon-speler sien jou sonder jou skoonheidsmiddels, jy ingesluit. Dieselfde skakelaar as die lanseerder se klerekas.",
"horizon.extras.cosmetics_others": "Ander spelers se skoonheidsmiddels",
"horizon.extras.cosmetics_others.tooltip": "Wat ander Horizon-spelers dra: alles, net hul naamplaatjiestyle, of niks. Versteek, die speletjie vra nooit wat hulle dra nie.",
"horizon.extras.cosmetics_auras": "Ander se aura's en roetes",
"horizon.extras.cosmetics_auras.tooltip": "Auras en roetes wat ander spelers dra. Af by verstek.",
"horizon.extras.cosmetics.shown": "Getoon",
"horizon.extras.cosmetics.hidden": "Versteek",
"horizon.extras.cosmetics.name_tags": "Slegs naamplaatjies"
}
@@ -10,7 +10,6 @@
"horizon.options.move_deadzone": "المنطقة الميتة للحركة",
"horizon.options.look_deadzone": "المنطقة الميتة للنظر",
"horizon.options.button_glyphs": "الحروف الرسومية للأزرار",
"horizon.options.menu_navigation": "التنقل في القائمة",
"horizon.options.on_screen_keyboard": "لوحة المفاتيح على الشاشة",
"horizon.options.chat_delay": "تأخير الدردشة",
"horizon.options.lightbar.health": "الصحة",
@@ -19,8 +18,6 @@
"horizon.options.glyphs.xbox": "اكس بوكس",
"horizon.options.glyphs.playstation": "بلاي ستيشن",
"horizon.options.glyphs.nintendo": "نينتندو",
"horizon.options.menu_focus": "التركيز",
"horizon.options.menu_pointer": "المؤشر",
"horizon.options.keyboard.auto": "تلقائي",
"horizon.options.keyboard.steam": "البخار",
"horizon.options.unavailable": "هذه الميزة غير متوفرة بعد في Horizon.",
@@ -0,0 +1,470 @@
{
"horizon.options.controller": "Nəzarətçi",
"horizon.options.vibration": "Vibrasiya",
"horizon.options.adaptive_triggers": "Adaptiv tətiklər",
"horizon.options.lightbar": "İşıq çubuğu",
"horizon.options.look_sensitivity": "Baxış həssaslığı",
"horizon.options.invert_x": "Tərs baxış X",
"horizon.options.invert_y": "Tərs baxış Y",
"horizon.options.sneak_button": "Gizlənmə düyməsi",
"horizon.options.move_deadzone": "Hərəkət ölü zonası",
"horizon.options.look_deadzone": "Baxış ölü zonası",
"horizon.options.button_glyphs": "Düymə Qlifləri",
"horizon.options.on_screen_keyboard": "Ekran Klaviaturası",
"horizon.options.chat_delay": "Söhbət Gecikməsi",
"horizon.options.lightbar.health": "Sağlamlıq",
"horizon.options.lightbar.dimension": "Ölçü",
"horizon.options.glyphs.auto": "Avtomatik",
"horizon.options.keyboard.auto": "Avtomatik",
"horizon.options.unavailable": "Bu funksiya hələ Horizon ərazisində mövcud deyil.",
"horizon.options.search": "Axtarış parametrləri...",
"horizon.pages.mods": "Modlar",
"horizon.pages.shader_packs": "Shader Paketləri",
"horizon.pages.shader_options": "Shader Seçimləri",
"horizon.pages.controller": "Nəzarətçi Parametrləri",
"horizon.pages.controller_binds": "Nəzarətçi Bağlayır",
"horizon.pages.extras": "Horizon Əlavələr",
"horizon.pages.settings": "Horizon Parametrlər",
"horizon.pages.agent": "AI agenti",
"horizon.split.leave": "tərk et",
"horizon.pages.split_settings": "Splitscreen Parametrləri",
"horizon.pages.split_profiles": "Kim oynayır?",
"horizon.pages.split_setup": "Splitscreen Quraşdırma",
"horizon.pages.music": "Musiqi",
"horizon.pages.music.menu": "Menyu",
"horizon.pages.music.overworld": "Yerüstü dünya",
"horizon.pages.music.creative": "Yaradıcı",
"horizon.pages.music.underwater": "Sualtı",
"horizon.pages.music.end": "Son",
"horizon.pages.music.boss": "patron",
"horizon.pages.music.credits": "Kreditlər",
"horizon.pages.music.overworld_biomes": "Yerüstü biomlar",
"horizon.pages.music.nether_biomes": "Nether biomları",
"horizon.pages.music.end_biomes": "Biomları bitir",
"horizon.pages.music.other": "Digər musiqi",
"horizon.pages.music.empty": "Musiqi parçaları yoxdur: oyunun səsləri yüklənmir.",
"horizon.pages.music.tooltip": "Vəziyyətə görə quraşdırılmış treklər: birini indi oynayın və ya hər hansı birini söndürün ki, oyun onu seçməyi dayandırsın.",
"horizon.pages.settings.tooltip": "Horizon öz parametrləri: qrafika, nəzarətçilər, əlavələr və modlar.",
"horizon.pages.graphics.tooltip": "Qrafika, sadəcə olaraq: hər parametr nəyə başa gəldiyini bildirir.",
"horizon.pages.graphics_advanced.tooltip": "Hər bir qrafik parametri, Horizon daxil edilir, hər birinin dəyəri ilə.",
"horizon.pages.controller.tooltip": "Nəzarətçilər: onların düymələri, çubuqları, vibrasiya və menyu naviqasiyası.",
"horizon.pages.extras.tooltip": "Könüllü əlavələr, hamısı defolt olaraq söndürülür: siz birini yandırana qədər oyun vanil olaraq qalır.",
"horizon.pages.agent.tooltip": "Öz AI agentinizin oynamasına icazə verin: bu kompüterdə horizon-ctl və MCP müştəriləri.",
"horizon.pages.split_profiles.tooltip": "Kim oynayır: bu oyun qovluğunda saxlanılan profil.",
"horizon.pages.split_setup.tooltip": "Pəncərələr və monitorlar və kim harada oynayır.",
"horizon.pages.shader_packs.tooltip": "shaderpacks qovluğundan şader paketləri: Horizon öz və shaders/formatında olanlar. Bir dəfə qaçır.",
"horizon.pages.mods.tooltip": "Horizon-mods qovluğundan Horizon üçün hazırlanmış modlar. Hər biri sandboxda işləyir və siz onu yandırana qədər sönür.",
"horizon.pages.open.graphics": "Qrafika...",
"horizon.pages.open.graphics_advanced": "Qabaqcıl Qrafika...",
"horizon.pages.open.shader_packs": "Shader Paketləri...",
"horizon.pages.open.controller": "Nəzarətçi Parametrləri...",
"horizon.pages.open.split_setup": "Ayrılmış ekran...",
"horizon.pages.open.extras": "Əlavələr...",
"horizon.pages.open.mods": "Modlar...",
"horizon.pages.open.horizon_graphics": "Horizon Qrafika...",
"horizon.pages.open.controller_binds": "Nəzarətçi Bağlayır...",
"horizon.pages.open.horizon_settings": "Horizon Parametrlər...",
"horizon.pages.open.music": "Musiqi...",
"horizon.mods.empty": "Mod yoxdur: .hmod fayllarını horizon-mods qovluğuna qoyun",
"horizon.mods.stopped": " (Dayandı)",
"horizon.mods.not_built": " (tikilməyib)",
"horizon.mods.settings": "Parametrlər",
"horizon.mods.in_world": " (Dəyişmək üçün dünyanı tərk et)",
"horizon.mods.open_folder": "Mod Qovluğunu açın",
"horizon.shader_packs.empty": "Şeyder paketləri yoxdur: onları shaderpacks qovluğuna qoyun",
"horizon.shader_options.open": "Shader Seçimləri...",
"horizon.shader_options.profile": "Profil",
"horizon.shader_options.loading": "Şeyder paketinin seçimləri oxunur...",
"horizon.agent.copy_details": "Bağlantı təfərrüatlarını kopyalayın",
"horizon.multiplayer.join": "Qoşulun",
"horizon.multiplayer.cancel_reconnecting": "Yenidən qoşulmanı ləğv edin",
"horizon.multiplayer.join_friend": "Dosta Qoşulun",
"horizon.multiplayer.invite_code": "Dəvət Kodu",
"horizon.multiplayer.relay": "Rele (isteğe bağlı)",
"Preset": "Əvvəlcədən təyin",
"LOD Distance": "LOD məsafəsi",
"Far Detail": "Uzaq Detal",
"Cave Culling": "Mağaraların kəsilməsi",
"Entity Culling": "Müəssisə Culling",
"Chunk Updates": "Parça Yeniləmələri",
"Upload Budget": "Büdcə yükləyin",
"Sky": "Göy",
"Fog": "Duman",
"Weather": "Hava",
"Animated Textures": "Animasiyalı Dokular",
"Present Mode": "İndiki rejim",
"Worker Threads": "İşçi ipləri",
"Resolution Scale": "Qətnamə Ölçüsü",
"Upscaling": "Təkmilləşdirmə",
"Paper White": "Kağız Ağ",
"Peak Brightness": "Pik Parlaqlıq",
"Black Level": "Qara Səviyyə",
"UI Brightness": "UI Parlaqlığı",
"Battery Mode": "Batareya rejimi",
"Graphics API": "Qrafik API",
"Translucent Batching": "Şəffaf Batching",
"Performance Overlay": "Performans örtüyü",
"Full Detail Distance": "Tam Detal Məsafə",
"On": "Aktiv",
"Off": "Söndür",
"Unlimited": "Limitsiz",
"Auto": "Avtomatik",
"Custom": "Xüsusi",
"Performance": "Performans",
"Balanced": "Balanslı",
"Quality": "Keyfiyyət",
"Ultra Performance": "Ultra Performans",
"Native": "Yerli",
"Full": "Tam",
"Reduced": "Azaldılmış",
"Efficient": "Səmərəli",
"Precise": "Dəqiq",
"Auto (VSync)": "Avtomatik (VSync)",
"FIFO": "FİFO",
"Mailbox": "Poçt qutusu",
"Immediate": "Dərhal",
"FPS and Graph": "FPS və Qrafik",
"No cost": "Xərc yoxdur",
"Low cost": "Aşağı qiymət",
"Medium cost": "Orta qiymət",
"High cost": "Yüksək qiymət",
"Saves time": "Vaxta qənaət edir",
"Graphics": "Qrafika",
"Done": "Bitdi",
"This feature is not yet available in Horizon.": "Bu funksiya hələ Horizon ərazisində mövcud deyil.",
"Your display or system doesn't offer HDR output.": "Ekranınız və ya sisteminiz HDR çıxışı təklif etmir.",
"Turn HDR on to use it.": "İstifadə etmək üçün HDR-ni yandırın.",
"Your GPU or graphics API doesn't support it.": "GPU və ya qrafik API onu dəstəkləmir.",
"%s chunks": "%s parçalar",
"%s per frame": "%s hər çərçivə üçün",
"%s MiB/frame": "%s MiB/çərçivə",
"%s KiB/frame": "%s KiB/çərçivə",
"Auto (%s)": "Avtomatik (%s)",
"Full-detail terrain range. LOD Distance can extend local worlds farther.": "Tam detallı ərazi diapazonu. LOD məsafəsi yerli dünyaları daha da uzada bilər.",
"Coarse distant terrain in local worlds. Integrated GPUs stop at 128 chunks.": "Yerli dünyalarda kobud uzaq ərazi. İnteqrasiya edilmiş GPU-lar 128 hissədə dayanır.",
"Reduced draws far terrain coarser: faster on this integrated GPU.": "Azaldılmış ərazini daha qaba çəkir: bu inteqrasiya olunmuş GPU-da daha sürətli.",
"Reduced draws far terrain coarser, for GPUs it slows down.": "Azaldılmış ərazini daha qaba çəkir, GPU-lar üçün isə yavaşlayır.",
"Field of view. Wider shows more terrain at once.": "Baxış sahəsi. Daha geniş bir anda daha çox ərazini göstərir.",
"Caps frames per second; a cap saves power and heat.": "Saniyədə kadrların sayı; qapaq enerjiyə və istiliyə qənaət edir.",
"Waits for the display's refresh: no tearing, a little latency.": "Ekranın təzələnməsini gözləyir: cırılma, bir az gecikmə.",
"Size of menus and the HUD.": "Menyuların ölçüsü və HUD.",
"Sets the options below together. Changing one makes it Custom.": "Aşağıdakı seçimləri birlikdə təyin edir. Birinin dəyişdirilməsi onu Xüsusi edir.",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "Lava, od və günəş işığı ağdan daha parlaq parlayır. HDR displey lazımdır.",
"Your display's darkest black. 0 for OLED; LCDs glow a little.": "Ekranınız ən tünd qaradır. OLED üçün 0; LCD-lər bir az parlayır.",
"How bright highlights get. Auto: the peak your system reports.": "Parlaq məqamlar necə əldə edilir. Avtomatik: sisteminizin ən yüksək hesabatı.",
"How bright white paper and menus look. Auto: your desktop's white.": "Ağ kağız və menyular necə parlaq görünür. Avtomatik: masaüstünüz ağ rəngdədir.",
"How bright the HUD and menus are, against paper white.": "HUD və menyular ağ kağıza qarşı nə qədər parlaqdır.",
"How far mobs, crops and machines keep working. CPU time.": "Kütlələr, məhsullar və maşınlar nə qədər işləməyə davam edir. CPU vaxtı.",
"How far away mobs and items are drawn, as a share of normal.": "Nə qədər uzağa mob və əşyalar çəkilir, normal bir pay olaraq.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Qapalı hissələrin arxasında gizlənmiş ərazini ötür. Sıxılma çox baha başa gəlir.",
"Hides entities behind opaque blocks.": "Obyektləri qeyri-şəffaf blokların arxasında gizlədir.",
"No entity occlusion culling.": "Heç bir obyektin okklyuziyası məhv edilmir.",
"CPU occlusion test runs this frame.": "CPU tıxanma testi bu çərçivəni idarə edir.",
"Occlusion checks run on a worker thread.": "Tıxanma yoxlanışı işçi ipində aparılır.",
"Meshed sections uploaded per frame. Lower is smoother while loading.": "Meshed bölmələr hər çərçivəyə yüklənir. Yükləyərkən aşağı daha hamar olur.",
"Soft shadows in corners. Costs chunk building time only.": "Künclərdə yumşaq kölgələr. Yalnız tikinti vaxtı xərcləri.",
"Smaller copies of block textures for distant terrain: less shimmer.": "Uzaq ərazilər üçün blok dokuların daha kiçik nüsxələri: daha az parıltı.",
"Blends grass and water colours across biomes when building chunks.": "Parçalar qurarkən biomlar arasında ot və su rənglərini qarışdırır.",
"Cloud layer: off, flat (fast) or 3D (fancy).": "Bulud təbəqəsi: sönük, düz (sürətli) və ya 3D (fantastik).",
"How far clouds reach, in chunks.": "Buludlar nə qədər uzağa çatır, parça-parça.",
"Draws the sky, sun, moon and stars.": "Göyü, günəşi, ayı və ulduzları çəkir.",
"Distance fog. Off shows the edge of the loaded world.": "Məsafə dumanı. Off yüklənmiş dünyanın kənarını göstərir.",
"Draws rain and snow.": "Yağış və qar çəkir.",
"How far rain and snow fall around you.": "Ətrafınıza yağış və qar nə qədər yağır.",
"How many particles effects spawn.": "Nə qədər hissəcik effektləri kürü verir.",
"Round shadows under mobs and items.": "Kütlələrin və əşyaların altında dairəvi kölgələr.",
"Leaves with see-through gaps. Off draws them solid.": "Yarpaqları göz qabağındadır. Off onları möhkəm çəkir.",
"Sorts water and glass more exactly behind each other.": "Suyu və şüşəni bir-birinin arxasında daha dəqiq şəkildə sıralayır.",
"Which chunk rebuilds wait for the frame: none, nearby or yours.": "Hansı hissənin yenidən qurulması çərçivəni gözləyir: heç biri, yaxınlıqda və ya sizin.",
"Animates water, lava, fire and other moving textures.": "Su, lava, od və digər hərəkətli dokuları canlandırır.",
"Smooths jagged edges. FXAA: softer. TAA: smoothest, moving things may trail.": "Kəsik kənarları hamarlayır. FXAA: daha yumşaq. TAA: ən hamar, hərəkətli şeylər cığır çəkə bilər.",
"Renders the world smaller and rebuilds it with TAA. Faster, softer.": "Dünyanı daha kiçik edir və TAA ilə yenidən qurur. Daha sürətli, daha yumşaq.",
"HDR output on an HDR display: lava, fire and sunlight shine brighter.": "HDR displeydə HDR çıxışı: lava, yanğın və günəş işığı daha parlaq parlayır.",
"How frames reach the screen while VSync is off.": "VSync söndürüldükdə çərçivələr ekrana necə çatır.",
"Efficient sleeps until the next frame; Precise spins, like vanilla.": "Növbəti kadra qədər səmərəli yuxular; Vanil kimi dəqiq spinlər.",
"Fills the screen (borderless).": "Ekranı doldurur (sərhədsiz).",
"Brightens dark places.": "Qaranlıq yerləri işıqlandırır.",
"Lowers the frame rate when minimised, or idle too.": "Minimumlaşdırıldıqda və ya boş olduqda kadr sürətini azaldır.",
"Sways the view while walking.": "Gəzərkən mənzərəni yelləyir.",
"Fades newly loaded chunks in.": "Yeni yüklənmiş parçaları sönür.",
"Darkens the screen's edges.": "Ekranın kənarlarını qaraldır.",
"Shows when the world saves.": "Dünyanın nə vaxt xilas etdiyini göstərir.",
"Shows attack cooldown.": "Hücumun soyumasını göstərir.",
"60 FPS cap (30 in menus or unfocused), half the workers: less power.": "60 FPS qapağı (menyularda 30 və ya diqqətsiz), işçilərin yarısı: daha az güc.",
"Threads that generate, light and mesh chunks. Next start.": "Yaradan iplər, yüngül və mesh parçaları. Növbəti başlanğıc.",
"Vulkan, then DirectX 12 (Windows) or OpenGL (Linux) where it fails. Next start.": "Vulkan, sonra uğursuz olduğu DirectX 12 (Windows) və ya OpenGL (Linux). Növbəti başlanğıc.",
"Water and glass in one buffer: fewer draw calls, a bit more VRAM. Next start.": "Bir buferdə su və şüşə: daha az çəkiliş zəngi, bir az daha çox VRAM. Növbəti başlanğıc.",
"Mesh data sent to the GPU per frame. Lower is smoother while loading.": "Hər kadra GPU-ya göndərilən mesh datası. Yükləyərkən aşağı daha hamar olur.",
"Frame rate and frame times over the game, with a graph.": "Qrafik ilə oyun üzrə çərçivə sürəti və kadr vaxtları.",
"horizon.multiplayer.finding_friend": "Dostunuzun dünyasını tapmaq...",
"horizon.multiplayer.not_signed_in": "Bu server onlayn rejimdədir. Qoşulmaq üçün Horizon başlatma cihazında Microsoft hesabınızla daxil olun.",
"horizon.multiplayer.invalid_address": "Yanlış server ünvanı: %s",
"horizon.multiplayer.connected_friend_direct": "Dostunuza birbaşa qoşulun...",
"horizon.multiplayer.connected_friend_relay": "Rele vasitəsilə dostunuza qoşulun...",
"Advanced": "Qabaqcıl",
"Undo": "Geri alın",
"Search options": "Axtarış seçimləri",
"Shortcut": "Qısayol",
"Distance": "Məsafə",
"Display": "Ekran",
"Interface": "İnterfeys",
"Sky and weather": "Səma və hava",
"Lighting and entities": "İşıqlandırma və obyektlər",
"Textures": "Dokular",
"Image": "Şəkil",
"Chunk loading": "Parça yükləmə",
"Power": "Güc",
"Frames": "Çərçivələr",
"Output": "Çıxış",
"Brightness": "Parlaqlıq",
"How far you see, the window and the HUD.": "Nə qədər görürsən, pəncərə və HUD.",
"The sky and weather, lighting, textures and the image.": "Səma və hava, işıqlandırma, faktura və görüntü.",
"Chunk loading, culling and power use.": "Parçaların yüklənməsi, kəsilməsi və enerji istifadəsi.",
"How frames reach the screen, and the performance overlay.": "Çərçivələrin ekrana necə çatması və performans örtüyü.",
"Brighter-than-white output on an HDR display.": "HDR displeydə ağdan daha parlaq çıxış.",
"Results for \"%s\"": "\"%s\" üçün nəticələr",
"No settings match.": "Heç bir parametr uyğun gəlmir.",
"horizon.title.not_affiliated": "Mojang və ya Microsoft ilə əlaqəli deyil",
"horizon.extras.header.lighting": "İşıqlandırma",
"horizon.extras.header.third_person": "Üçüncü şəxs",
"horizon.extras.header.particles": "hissəciklər",
"horizon.extras.header.inventory": "İnventar",
"horizon.extras.header.chat": "Söhbət",
"horizon.extras.header.sound": "Səs",
"horizon.extras.header.menus": "Menyular",
"horizon.extras.fullbright": "Tam parlaq",
"horizon.extras.dynamic_lights": "Dinamik işıqlar",
"horizon.extras.own_name_tag": "Öz ad etiketi",
"horizon.extras.hide_block_particles": "Blok qırılan hissəcikləri gizlədin",
"horizon.extras.auto_reconnect": "Avtomatik yenidən qoşulun",
"horizon.extras.ui_sound_reserve": "UI səsləri üçün kanalları saxlayın",
"horizon.extras.music_page": "Musiqi səhifəsi",
"horizon.extras.mouse_tweaks": "Mouse Tweaks nəzarətləri",
"horizon.extras.mouse_tweaks_wheel": "Sürüşdürmə çarxı elementləri hərəkət etdirir",
"horizon.extras.sort": "Çeşidləmə düymələri",
"horizon.extras.sort_key": "Sıralama açarı",
"horizon.extras.no_ai": "AI yoxdur",
"horizon.extras.agent_control": "Agent Nəzarəti",
"horizon.extras.chat_animation": "Rahat söhbət",
"horizon.extras.chat_indicator": "Mesaj göstəricisi",
"horizon.extras.wheel_cycles_settings": "Sürüşdürmə çarxı parametrləri dəyişir",
"horizon.extras.pack_browser": "Resurs paketi brauzeri",
"horizon.extras.fullbright.tooltip": "Blokları və obyektləri hətta qaranlıq yerlərdə də tam işıqda göstərin.",
"horizon.extras.dynamic_lights.tooltip": "Məşəllər və digər işıq mənbələri ətrafdakı blokları tutmuş, yerə atmış və ya odda yandırır.",
"horizon.extras.own_name_tag.tooltip": "Başqa oyunçuların gördüyü kimi adınızı başınızın üstündə üçüncü şəxsdə göstərir. Server komandanızın adlarını gizlətdikdə və ya siz görünməz olduğunuzda gizlidir.",
"horizon.extras.hide_block_particles.tooltip": "Sınıq blokdan qopan və mənimsədiyiniz blokdan parçalanan zibil hissəciklərini gizlədir. Digər hissəciklər qalır.",
"horizon.extras.auto_reconnect.tooltip": "Heç bir zərbə və ya qadağadan sonra itən əlaqə və ya yenidən başladıqdan sonra serverə yenidən qoşulur.",
"horizon.extras.ui_sound_reserve.tooltip": "Dünyadakı səslər 247 səs kanalından 16-nı pulsuz buraxır, buna görə də onların selinin (böyük partlayış) menyu kliklərini susdura bilməz.",
"horizon.extras.music_page.tooltip": "Musiqi və Səslər Musiqi... səhifəsi əldə edir: hər hansı daxili treki indi çalın və ya birini söndürün ki, oyun onu seçməyi dayandırsın. Siz treki söndürməyincə heç nə dəyişməyəcək.",
"horizon.extras.mouse_tweaks.tooltip": "Sağa sürükləmək hər yuvaya bir yerləşdirir, sola sürükləmək saxlanılan elementi toplayır, Shift + sola sürükləmək hər bir yuvaya köçür.",
"horizon.extras.mouse_tweaks_wheel.tooltip": "Aşağı yuvanın bir elementini digər tərəfə göndərir, yuxarı birini geri gətirir.",
"horizon.extras.sort.tooltip": "İnventar, sandıqlar, çəlləklər, ender sandıqlar və şulker qutularını yaradıcı nişan sırası ilə çeşidləyir, yığınları birləşdirin.",
"horizon.extras.sort_key.tooltip": "Kursorun altındakı hissəni çeşidləyir. Basın, sonra düyməni basın; Escape onu açır.",
"horizon.extras.no_ai.tooltip": "Hər bir AI funksiyasını gizlədir və söndürür: agent nəzarəti, horizon-ctl və MCP serverləri. Başlatıcının açarı da.",
"horizon.extras.agent_control.tooltip": "Öz AI agentiniz bu oyunu horizon-ctl və ya MCP müştəri vasitəsilə yalnız bu kompüterdə oynaya bilər.",
"horizon.extras.discord_presence.tooltip": "Discord işləyərkən Discord profilinizdə Horizon Playing göstərilir: menyular, tək oyunçu və ya multiplayer, ölçü və oynanılan vaxt. Heç vaxt dünyanın adı və ya server ünvanı.",
"Calibrate...": "Kalibrləmə...",
"Calibrate Anyway...": "İstənilən halda kalibrləyin...",
"Use Automatic Setup": "Avtomatik Quraşdırmadan istifadə edin",
"Shortcut: %s": "Qısayol: %s",
"Next": "Sonrakı",
"HDR Calibration %s/%s: %s": "HDR Kalibrləmə %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Aşağıdakı üç tam ekranı, hər dəfə bir sınaq şəklini təyin edir.",
"A key that opens this screen from the game. Not bound at first.": "Oyundan bu ekranı açan açar. Əvvəlcə bağlı deyil.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Ekranınız nə qədər parlaq olduğunu deməyib: onu qurmaq üçün kalibrləyin.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Ekranınız üçün artıq qurulub. Yalnız HDR səhv görünürsə kalibrləyin.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Pik Parlaqlığı və Kağız Ağı ekranınızın quraşdırılmasına qaytarır.",
"Set up for your display. Leave on Auto unless it looks wrong.": "Ekranınız üçün qurun. Səhv görünməsə, Auto-da buraxın.",
"Lower it until the logo just disappears into the black.": "Loqo qara rəngdə yox olana qədər onu endirin.",
"Raise it until the logo just disappears into the bright square.": "Loqo parlaq kvadratda yox olana qədər onu qaldırın.",
"Make the card a white that's comfortable to look at for a long time, in the light you play in.": "Kartı oynadığınız işıqda uzun müddət baxmaq üçün rahat olan ağ rəngə çevirin.",
"horizon.splitscreen.profiles.forget": "Unut",
"horizon.splitscreen.profiles.create": "Yaradın",
"horizon.splitscreen.profiles.you": " - sən",
"horizon.splitscreen.profiles.forget_named": "Unut %s",
"horizon.splitscreen.profiles.previous": "Əvvəlki",
"horizon.splitscreen.profiles.next": "Sonrakı",
"horizon.splitscreen.profiles.new_button": "Yeni Profil...",
"horizon.splitscreen.profiles.forget_title": "Profili unutmusunuz?",
"horizon.splitscreen.profiles.forget_leaves": "Bu nümunənin siyahısını tərk edir.",
"horizon.splitscreen.profiles.forget_kept": "Dünya tərəqqisi və parametrlər saxlanılır.",
"horizon.splitscreen.profiles.new_title": "Yeni Profil",
"horizon.splitscreen.profiles.name_hint": "Ad (3-16 hərf, rəqəm, _)",
"horizon.splitscreen.profiles.switch_title": "Profilə keçin",
"horizon.splitscreen.profiles.offline_note": "Oflayn profillər bu instansiyaya aiddir",
"horizon.splitscreen.profiles.move": "Oynadığınız yerdə hərəkət edin",
"horizon.splitscreen.profiles.move_place": "Oynadığınız yerə köçürün: %s",
"horizon.friends.title": "Dostlarla oynayın",
"horizon.friends.hint": "Dostlar kodu başlıq ekranının Dostlar düyməsindən sonra, Birbaşa Bağlantıda və ya başlatma qurğusunun Play səhifəsində yerləşdirin.",
"horizon.friends.copied": "Kopyalandı",
"horizon.friends.copy_code": "Kodu kopyalayın",
"horizon.friends.stop_sharing": "Paylaşmağı dayandırın",
"horizon.friends.none_yet": "Burada hələ dost yoxdur.",
"horizon.friends.here_now": "İndi burada: %s",
"horizon.friends.share_intro": "Bu dünyanı dəvət kodunun arxasında paylaşın: o, LAN-a açılır və internetdəki dostlar kodla qoşulur. Heç bir serverimiz iştirak etmir.",
"horizon.friends.start_sharing": "Paylaşmağa başlayın",
"horizon.friends.troubleshooting": "Problem həll olunur...",
"horizon.friends.stopped": "Dəvət kodunu paylaşmağı dayandırdı.",
"horizon.friends.short_code_changed": "Kimsə səhv qısa kodu sınamağa davam etdi, ona görə də dəyişdi.",
"horizon.friends.wrong_code": "Kimsə səhv kodu sınadı.",
"horizon.friends.code_ready": "Dəvət kodunuz hazırdır: Oyun Menyu, Dostlarla Oyna. Kopyalayın və dostlarınıza göndərin.",
"horizon.friends.share_failed": "Dünyanı paylaşmaq mümkün olmadı: %s",
"horizon.friends.finding_route": "Dostların sizinlə necə əlaqə saxlaya biləcəyi tapılır...",
"horizon.osk.space": "Kosmos",
"horizon.osk.delete": "Sil",
"horizon.osk.type": "Növ",
"horizon.osk.symbols": "Simvollar",
"horizon.osk.close": "Bağlayın",
"horizon.extras.chat_animation.tooltip": "Yeni söhbət xətti bütöv görünmək əvəzinə soldan içəri sürüşür.",
"horizon.extras.chat_indicator.tooltip": "Söhbət xəttinin solunda olan rəngli bar.",
"horizon.extras.wheel_cycles_settings.tooltip": "Parametrlərdəki düymənin üzərindəki sürüşdürmə çarxı vanildə olduğu kimi dəyərini dəyişir. Söndürülmüş, təkər yalnız siyahını sürüşdürür.",
"horizon.extras.pack_browser.tooltip": "Resurs Paketlərini seçin Modrinth-də axtarış aparan və paketlər qovluğuna paket quraşdıran Əlavə et düyməsini alır. Off, oyun Modrinthlə heç vaxt əlaqə saxlamır.",
"horizon.extras.splitscreen.tooltip": "Dünyada başqa bir nəzarətçi yeni oyunçu kimi qoşula bilər: soruşduqda A düyməsini basın, sonra profil və harada oynayacağınızı seçin.",
"horizon.splitscreen.setup.monitor_unknown": "monitor",
"horizon.splitscreen.setup.window": "Pəncərə %s",
"horizon.splitscreen.setup.new_window": "Yeni pəncərə",
"horizon.splitscreen.setup.fill_monitor": "%s doldurun",
"horizon.splitscreen.setup.done": "Bitdi",
"horizon.splitscreen.setup.on": "Aktiv",
"horizon.splitscreen.setup.off": "Söndür",
"horizon.splitscreen.setup.player": "Oyunçu %s",
"horizon.splitscreen.setup.revert": "Geri qaytarın",
"horizon.splitscreen.setup.apply": "Müraciət edin",
"horizon.splitscreen.setup.warning.empty_main": "Window 1-də heç kim oynamır: gizlənir",
"horizon.splitscreen.setup.warning.empty_close": "%s Pəncərəsində heç kim oynamır: Tətbiq edərkən bağlanır",
"horizon.splitscreen.setup.warning.empty_new": "%s pəncərəsində heç kim oynamır: açılmayacaq",
"horizon.splitscreen.setup.warning.on_main": "Pəncərə %s Pəncərə 1-in monitorunu doldurur: onun oyunçuları Pəncərə 1-də oynayır",
"horizon.splitscreen.setup.warning.unplugged": "Pəncərənin %s monitoru bağlı deyil: onun oyunçuları Window 1-də oynayır",
"horizon.splitscreen.setup.status.off": "Splitscreen deaktivdir.",
"horizon.splitscreen.setup.status.enable_hint": "Onu yandırın, sonra dünyanın başqa bir nəzarətçisində düyməni basın.",
"horizon.splitscreen.setup.status.join_hint": "Qoşulmaq və profil seçmək üçün A düyməsini basın.",
"horizon.splitscreen.setup.status.idle": "Splitscreen boş",
"horizon.splitscreen.setup.status.alone": "Başqa yerli oyunçu yoxdur.",
"horizon.splitscreen.setup.status.solo_controls": "Solo nəzarət və nəzarətçi rəyi dəyişməzdir.",
"horizon.splitscreen.setup.window_empty": "Pəncərə %s: boş",
"horizon.splitscreen.setup.note.select": "Oyunçuya klikləyin və ya onu pəncərəyə və ya monitora sürükləyin.",
"horizon.splitscreen.setup.note.apply": "Bu şəkildə oynamaq üçün müraciət edin, ləğv etmək üçün geri qaytarın.",
"horizon.splitscreen.setup.note.place": "%s harada oynamalıdır?",
"horizon.splitscreen.setup.this_player": "bu oyunçu",
"horizon.splitscreen.setup.switch_disabled": "Digərləri ayrıldıqdan sonra Splitscreen sönür (pauza menyusu).",
"horizon.splitscreen.setup.window_place": "Pəncərə %s (%s/%s)",
"horizon.splitscreen.setup.names": "Adlar",
"horizon.splitscreen.setup.identify": "Müəyyən etmək",
"horizon.splitscreen.setup.layout.two": "2 paylaşım: %s",
"horizon.splitscreen.setup.layout.three": "3 paylaşım: %s",
"horizon.splitscreen.setup.layout.two.0": "Avtomatik",
"horizon.splitscreen.setup.layout.two.1": "Yan-yana",
"horizon.splitscreen.setup.layout.two.2": "Üst və alt",
"horizon.splitscreen.setup.layout.three.0": "Üstündə biri geniş",
"horizon.splitscreen.setup.layout.three.1": "Solda biri hündür",
"horizon.splitscreen.setup.layout.three.2": "Yan-yana",
"horizon.splitscreen.setup.help.player": "Onları köçürmək üçün %s üzərinə klikləyin və ya dartın.",
"horizon.splitscreen.setup.help.layout_auto": "%s (pəncərə geniş olduqda yan-yana). Dəyişmək üçün basın.",
"horizon.splitscreen.setup.help.layout": "%s. Dəyişmək üçün basın.",
"horizon.splitscreen.setup.help.switch": "Bu kompüter üçün bölünmüş ekran, aktiv və ya söndürülür.",
"horizon.splitscreen.setup.help.identify": "Hər bir oyunçunun görünüşü onun hansı pəncərədə olduğunu göstərir.",
"horizon.splitscreen.setup.help.names": "Görünüşünün küncündə hər bir oyunçunun adı.",
"horizon.splitscreen.setup.help.revert": "Hələ tətbiq olunmamış dəyişiklikləri geri alın.",
"horizon.splitscreen.setup.help.apply": "Xəritənin göstərdiyi şəkildə oynayın.",
"horizon.splitscreen.setup.help.unchanged": "Hələ heç nə dəyişməyib.",
"horizon.splitscreen.setup.give_up_pad": "Pasxadan imtina et",
"horizon.splitscreen.prompt.controller_connected": "Nəzarətçi qoşulub",
"horizon.splitscreen.prompt.controller_disconnected": "Nəzarətçi ayrıldı",
"horizon.splitscreen.prompt.controller_disconnected.hint": "Onu yenidən birləşdirin və ya digərində düyməni basın",
"horizon.splitscreen.prompt.join.one": "%s qoşulmaq istəyir",
"horizon.splitscreen.prompt.join.many": "%s nəzarətçilər qoşulmaq istəyirlər",
"horizon.splitscreen.prompt.join.yes": "Qoşulun",
"horizon.splitscreen.prompt.join.as_p1": "P1 kimi oynayın",
"horizon.splitscreen.prompt.join.no": "yox",
"horizon.splitscreen.prompt.lost": "%s: nəzarətçi ayrıldı",
"horizon.splitscreen.prompt.lost.use_another": "Başqasını istifadə edin",
"horizon.splitscreen.prompt.lost.leave": "tərk et",
"horizon.splitscreen.setup.help.give_up_pad": "Nəzarətçiniz sizi idarə etməyi dayandırır və ona qoşulmaq təklif olunur (A).",
"horizon.splitscreen.setup.note.held_at": "%s to %s: A onu bura düşür, B onu geri qoyur.",
"horizon.splitscreen.setup.note.held": "%s götürdü: sol/sağ yeri seçir, A düşür, B onu geri qoyur.",
"Hides models behind opaque blocks. Async checks off-thread; entities tick.": "Modelləri qeyri-şəffaf blokların arxasında gizlədir. Async-dən kənar yoxlayır; qurumlar işarələyir.",
"Block Entities": "Blok obyektləri",
"Shulker Boxes": "Shulker qutuları",
"Reuses settled boxes; lid movement stays animated.": "Yerləşmiş qutuları təkrar istifadə edir; qapaq hərəkəti canlı qalır.",
"Signs": "İşarələr",
"Reuses sign text until text, light, or font changes.": "Mətn, işıq və ya şrift dəyişənə qədər işarə mətnindən təkrar istifadə edir.",
"Decorated Pots": "Bəzəkli qablar",
"Reuses still pots; break wobble and sherd changes redraw.": "Qazan qabları təkrar istifadə edir; yırğalanmaq və parçalanma dəyişiklikləri yenidən çəkmək.",
"Bells": "Zənglər",
"Reuses still bells; ringing stays animated.": "Hərəkətsiz zəngləri təkrar istifadə edir; Zəng animasiyalı qalır.",
"Chests": "Sinələr",
"Reuses closed chests; moving lids stay animated.": "Qapalı sandıqları təkrar istifadə edir; hərəkət edən qapaqlar canlı qalır.",
"Banners": "Bannerlər",
"Reuses poles; flags and pattern layers still update.": "Dirəkləri təkrar istifadə edir; bayraqlar və naxış qatları hələ də yenilənir.",
"Copper Golem Statues": "Mis Qolem heykəlləri",
"Reuses statue geometry until its block state changes.": "Blok vəziyyəti dəyişənə qədər heykəlin həndəsəsindən təkrar istifadə edir.",
"Leaf Culling": "Yarpaqların kəsilməsi",
"Every leaf face. Vanilla's fancy leaves.": "Hər yarpaq üzü. Vanilin gözəl yarpaqları.",
"Distant Plant Dither": "Uzaq Bitki Dither",
"Keeps fine plants in high-res packs from fading far away.": "Yüksək dəqiqlikli paketlərdəki incə bitkilərin uzaqdan solmasını qarşısını alır.",
"Screenshot Quality": "Ekran Şotunun Keyfiyyəti",
"F2 screenshots at this many times the game's resolution. Higher takes longer.": "F2 ekran görüntüləri bu dəfələrlə oyunun həlli. Yüksək daha uzun çəkir.",
"Frame Generation": "Çərçivə Yaradılması",
"Shows a made-up frame between each two real ones: smoother, a little more lag.": "Hər iki real arasında düzəldilmiş çərçivəni göstərir: daha hamar, bir az daha gecikmə.",
"Shadow Cache": "Kölgə önbelleği",
"Reuses the pack shadow map when sun, view and world match. Off by default.": "Günəş, görünüş və dünya uyğunlaşdıqda paket kölgə xəritəsindən təkrar istifadə edir. Defolt olaraq söndürülür.",
"Find BlueMap": "BlueMap tapın",
"On servers, finds their BlueMap web map for far terrain. Next join.": "Serverlərdə uzaq ərazilər üçün BlueMap veb xəritəsini tapır. Növbəti qoşulun.",
"How far around you BlueMap tiles are downloaded. Next join.": "Ətrafınızda BlueMap plitələri nə qədər yüklənir. Növbəti qoşulun.",
"Synchronous": "Sinxron",
"Asynchronous": "Asinxron",
"Solid": "Möhkəm",
"Solid Aggressive": "Möhkəm Aqressiv",
"Hollow": "Boş",
"Leaves fully inside a canopy draw solid: fewer see-through layers.": "Tam örtünün içindəki yarpaqlar möhkəm çəkilir: daha az keçirici təbəqə.",
"Leaves with one open side draw solid too: faster, chunkier edges.": "Bir tərəfi açıq olan yarpaqlar da bərk çəkir: daha sürətli, daha sıx kənarlar.",
"Drops faces between leaves: faster, but gaps look through.": "Yarpaqlar arasında üzləri düşür: daha sürətli, lakin boşluqlar içəri baxır.",
"Hotbar: Previous Slot": "İsti panel: Əvvəlki yuva",
"Hotbar: Next Slot": "İsti panel: Növbəti Slot",
"horizon.agent.connected.one": "Qoşuldu: 1 agent bağlantısı",
"horizon.agent.connected.many": "Qoşuldu: %s agent əlaqələri",
"horizon.agent.connected.one_ago": "Qoşuldu: 1 agent bağlantısı, son əmr %s s əvvəl",
"horizon.agent.connected.many_ago": "Qoşuldu: %s agent əlaqələri, son əmr %s s əvvəl",
"horizon.agent.waiting": "Agent gözləyir.",
"horizon.agent.off": "Off: agentlər bu oyuna daxil ola bilməz.",
"horizon.agent.session_opened": "Oyuna başlayan proqram tərəfindən bu sessiya üçün açıldı.",
"horizon.agent.socket": "Soket: %s (tam yolu üçün təfərrüatları kopyalayın)",
"horizon.agent.local_only": "Yalnız yerli: bu kompüterdəki agentlər, heç vaxt şəbəkə.",
"Auto (%s nits)": "Avtomatik (%s nits)",
"Hides models behind opaque blocks. Entities update. Off with custom packs.": "Modelləri qeyri-şəffaf blokların arxasında gizlədir. Müəssisələrin yenilənməsi. Fərdi paketlərlə endirim.",
"horizon.extras.repro_keys": "Qısayolların qeyd edilməsi",
"horizon.extras.repro_keys.tooltip": "F3+R repro qeydini başlayır və ya dayandırır və F3+E öz paketini yazır. Defolt olaraq söndürülür: yazı dünyanı kopyalayır.",
"horizon.agent.record_session": "Səs yazmağa başlayın",
"horizon.agent.record_stop": "Səs yazmağı dayandırın",
"horizon.agent.export_repro": "Repro paketini ixrac edin",
"horizon.repro.started": "Qeydiyyat başladı; F3+R onu dayandırır və paketi yazır.",
"horizon.repro.stopped": "Qeydiyyat dayandırıldı: %s",
"horizon.repro.bundle": "Repro paketi: %s",
"horizon.repro.error": "Reproduksiya: %s",
"horizon.repro.no_recording": "İxrac etmək üçün hələ heç bir qeyd yoxdur.",
"horizon.repro.replay_started": "Təkrar başladı: %s",
"horizon.pack.addInfo": "Modrinth-dən resurs paketi əlavə edin",
"horizon.pack.addWorking": "Modrinthdən soruşur...",
"horizon.pack.addNone": "Heç bir resurs paketi tapılmadı",
"horizon.pack.addTitle": "Resurs Paketi əlavə edin",
"horizon.pack.install": "Quraşdırın",
"horizon.pack.installing": "Quraşdırılır",
"horizon.pack.installed": "Quraşdırılıb",
"horizon.pack.search": "Axtar",
"horizon.pack.searchHint": "Modrinth axtar...",
"horizon.pack.filters": "Filtrlər",
"horizon.pack.filter.realistic": "Realist",
"horizon.pack.filter.simplistic": "Sadə",
"horizon.pack.filter.vanillaLike": "Vanil kimi",
"horizon.pack.addFailed": "Paketi əlavə etmək mümkün olmadı: %s",
"horizon.extras.header.cosmetics": "Kosmetika",
"horizon.extras.cosmetics_mine": "Kosmetikam",
"horizon.extras.cosmetics_mine.tooltip": "Gizli: hər Horizon oyunçu sizi kosmetikanız olmadan görür. Başlatma şkafı ilə eyni açar.",
"horizon.extras.cosmetics_others": "Digər oyunçuların kosmetikası",
"horizon.extras.cosmetics_others.tooltip": "Digər Horizon oyunçular nə geyinirlər: hər şey, yalnız ad etiketi üslubları və ya heç nə. Gizli, oyun heç vaxt nə geyindiklərini soruşmur.",
"horizon.extras.cosmetics_auras": "Başqalarının auraları və izləri",
"horizon.extras.cosmetics_auras.tooltip": "Digər oyunçuların geydiyi auralar və izlər. Defolt olaraq söndürülür.",
"horizon.extras.cosmetics.shown": "Göstərildi",
"horizon.extras.cosmetics.hidden": "Gizli",
"horizon.extras.cosmetics.name_tags": "Yalnız ad etiketləri"
}
@@ -0,0 +1,496 @@
{
"horizon.options.controller": "Контролер",
"horizon.options.three_d_skin_layers": "3D скин ҡатламдары",
"horizon.options.vibration": "Вибрация",
"horizon.options.adaptive_triggers": "Адаптив триггерҙар",
"horizon.options.lightbar": "Лайтбар",
"horizon.options.look_sensitivity": "Ҡарау һиҙгерлеге",
"horizon.options.invert_x": "Ҡараш инверсияһы X",
"horizon.options.invert_y": "Ҡараш инверсияһы Y",
"horizon.options.sneak_button": "Боҫоу төймәһе",
"horizon.options.move_deadzone": "Хәрәкәт үле зонаһы",
"horizon.options.look_deadzone": "Ҡараш үле зонаһы",
"horizon.options.button_glyphs": "Төймә глифтары",
"horizon.options.on_screen_keyboard": "Экрандағы клавиатура",
"horizon.options.chat_delay": "Чат тотҡарланыуы",
"horizon.options.lightbar.health": "Һаулыҡ",
"horizon.options.lightbar.dimension": "Үлсәм",
"horizon.options.glyphs.auto": "Авто",
"horizon.options.keyboard.auto": "Авто",
"horizon.options.unavailable": "Был функция Horizon-ла әлегә юҡ.",
"horizon.options.search": "Эҙләү параметрҙары...",
"horizon.pages.mods": "Модтар",
"horizon.pages.shader_packs": "Шейдер пакеттары",
"horizon.pages.shader_options": "Шейдер параметрҙары",
"horizon.pages.controller": "Контроллер параметрҙары",
"horizon.pages.controller_binds": "Контроллер бәйләй",
"horizon.pages.extras": "Horizon Өҫтәмә",
"horizon.pages.settings": "Horizon Көйләүҙәр",
"horizon.pages.agent": "ЯИ агенты",
"horizon.split.leave": "Ҡалдырырға",
"horizon.pages.split_settings": "Сплит-экран параметрҙары",
"horizon.pages.split_profiles": "Кем уйнай?",
"horizon.pages.split_setup": "Сплит-экранды ҡуйыу",
"horizon.pages.music": "Музыка",
"horizon.pages.music.menu": "Меню",
"horizon.pages.music.overworld": "Өҫтөн донъя",
"horizon.pages.music.creative": "Ижади .",
"horizon.pages.music.underwater": "Һыу аҫтында .",
"horizon.pages.music.end": "Аҙағы",
"horizon.pages.music.boss": "Етәксе",
"horizon.pages.music.credits": "Кредиттар",
"horizon.pages.music.overworld_biomes": "Донъя өҫтөндәге биомдар",
"horizon.pages.music.nether_biomes": "Недер биомдары",
"horizon.pages.music.end_biomes": "Аҙаҡҡы биомдар",
"horizon.pages.music.other": "Башҡа музыка .",
"horizon.pages.music.empty": "Музыка тректары юҡ: уйындың тауыштары тейәлмәгән.",
"horizon.pages.music.tooltip": "Встроенный тректар, хәл буйынса: хәҙер бер уйнай, йәки теләһә ниндәй һүндерергә, шулай итеп, уйын уны йыйыу туҡтата.",
"horizon.pages.settings.tooltip": "Horizon үҙ параметрҙары: графика, контроллерҙар, өҫтәмәләр һәм модтар.",
"horizon.pages.graphics.tooltip": "Графика, ябай: һәр параметр нимәгә төшкәнен әйтә.",
"horizon.pages.graphics_advanced.tooltip": "Һәр график параметр, Horizon&#8217;s инә, һәр береһе нимә менән.",
"horizon.pages.controller.tooltip": "Контроллерҙар: уларҙың төймәләре, таяҡсалар, вибрация һәм меню навигацияһы.",
"horizon.pages.extras.tooltip": "Факультатив өҫтәүҙәр, бөтәһе лә өҫтөндә ғәҙәттәгесә: уйын ваниль ҡала, тик һеҙ бер өҫтөндә.",
"horizon.pages.agent.tooltip": "Һеҙҙең үҙ AI агент уйнай: horizon-ctl һәм MCP клиенттар был компьютерҙа.",
"horizon.pages.split_profiles.tooltip": "Кем уйнай: был уйын папкаһында һаҡланған профиль.",
"horizon.pages.split_setup.tooltip": "Тәҙрәләр һәм мониторҙар, ә кем ҡайҙа уйнай.",
"horizon.pages.shader_packs.tooltip": "Шейдер пакеттары шейдерпакеттар папкаһынан: Horizon үҙ һәм шейдерҙар/ форматындағылар. Берәм-берәм йүгерә.",
"horizon.pages.mods.tooltip": "Horizon өсөн эшләнгән модтар, горизонт-модтар папкаһынан. Һәр береһе ҡом йәшниктә йүгерә һәм һеҙ уны тоҡандырғансы һүндерелә.",
"horizon.pages.open.graphics": "Графика...",
"horizon.pages.open.graphics_advanced": "Алдынғы графика...",
"horizon.pages.open.shader_packs": "Шейдер пакеттары...",
"horizon.pages.open.controller": "Контроллер параметрҙары...",
"horizon.pages.open.split_setup": "Бүленгән экран...",
"horizon.pages.open.extras": "Өҫтәмәләр...",
"horizon.pages.open.mods": "Модтар...",
"horizon.pages.open.agent": "ЯИ агенты...",
"horizon.pages.open.horizon_graphics": "Horizon Графика...",
"horizon.pages.open.controller_binds": "Контроллер бәйләй...",
"horizon.pages.open.horizon_settings": "Horizon Көйләүҙәр...",
"horizon.pages.open.music": "Музыка...",
"horizon.mods.empty": "Модтар юҡ: .hmod файлдарын горизонт-модтар папкаһына ҡуйығыҙ",
"horizon.mods.stopped": " (Туҡтатылған)",
"horizon.mods.not_built": " (Төҙөлмәгән)",
"horizon.mods.dev": " (Дев)",
"horizon.mods.settings": "Көйләүҙәр",
"horizon.mods.in_world": " (донъяны үҙгәртергә ҡалдырырға)",
"horizon.mods.open_folder": "Мод папкаһын асыу",
"horizon.shader_packs.empty": "Шейдер пакеттары юҡ: уларҙы шейдерпакеттар папкаһына һалығыҙ",
"horizon.shader_options.open": "Шейдер варианттары...",
"horizon.shader_options.profile": "Профиль",
"horizon.shader_options.loading": "Шейдер пакетының варианттарын уҡыу...",
"horizon.agent.copy_details": "Тоташыу тураһында мәғлүмәтте күсерергә",
"horizon.multiplayer.join": "Ҡушылыу",
"horizon.multiplayer.cancel_reconnecting": "Яңынан тоташтырыуҙы юҡҡа сығарыу",
"horizon.multiplayer.join_friend": "Дуҫҡа ҡушыл",
"horizon.multiplayer.invite_code": "Саҡырыу коды",
"horizon.multiplayer.relay": "Реле (теләк буйынса)",
"Preset": "Алдан ҡуйылған",
"LOD Distance": "LOD арауығы",
"Far Detail": "Алыҫ деталь",
"Cave Culling": "Мәмерйә ҡырҡыу",
"Entity Culling": "Субъектты ҡырҡыу",
"Chunk Updates": "Яңыртыу өлөштәре",
"Upload Budget": "Бюджет тейәү",
"Sky": "Күк",
"Fog": "Томан",
"Weather": "Һауа торошо",
"Animated Textures": "Йәнле текстуралар",
"Present Mode": "Хәҙерге режим",
"Frame Pacing": "Кадр темпы",
"Worker Threads": "Эшсе ептәр",
"Resolution Scale": "Асыҡлыҡ шкалаһы",
"Anti-aliasing": "Анти-псевдоним",
"Upscaling": "Масштаблаштырыу",
"Paper White": "Аҡ ҡағыҙ",
"Peak Brightness": "Иң юғары яҡтылыҡ",
"Black Level": "Ҡара кимәл",
"UI Brightness": "Яҡтылыҡ",
"Battery Mode": "Аккумулятор режимы",
"Graphics API": "Графика API",
"Translucent Batching": "Ярым үтә күренмәле партиялау",
"Performance Overlay": "Һөҙөмтәлелек өҫтөнлөктәре",
"Full Detail Distance": "Тулы ентекле арауыҡ",
"On": "Өҫтөндә",
"Off": "Һүндерелгән",
"Unlimited": "Сикләнмәгән",
"Auto": "Авто",
"Custom": "Ҡулланыусы",
"Performance": "Башҡарыу",
"Balanced": "Баланслы",
"Quality": "Сифат",
"Ultra": "Ультра",
"Ultra Performance": "Ультра етештереүсәнлеге",
"Native": "Натив",
"Full": "Тулы",
"Reduced": "Кәметелгән",
"Efficient": "Һөҙөмтәле",
"Precise": "Теүәл",
"Auto (VSync)": "Авто (ВСинхронизация)",
"FIFO": "ФИФО",
"Mailbox": "Почта йәшниге",
"Immediate": "Шунда уҡ",
"FPS and Graph": "FPS һәм график",
"TAA": "ТАА",
"FXAA": "ФХАА",
"No cost": "Хаҡы юҡ",
"Low cost": "Түбән хаҡ",
"Medium cost": "Уртаса хаҡ",
"High cost": "Юғары хаҡ",
"Saves time": "Ваҡытты экономиялай",
"Graphics": "Графика",
"Done": "Бөттө",
"This feature is not yet available in Horizon.": "Был функция Horizon-ла әлегә юҡ.",
"Your display or system doesn't offer HDR output.": "Һеҙҙең дисплей йәки система тәҡдим итмәй HDR сығыш.",
"Turn HDR on to use it.": "Уны ҡулланыу өсөн HDR ҡабыҙығыҙ.",
"Your GPU or graphics API doesn't support it.": "Һеҙҙең GPU йәки график API уны ҡабул итмәй.",
"%s chunks": "%s өлөштәр",
"%s per frame": "%s бер кадрға",
"%s MiB/frame": "%s МиБ/кадр",
"%s KiB/frame": "%s КиБ/кадр",
"Auto (%s)": "Авто (%s)",
"Full-detail terrain range. LOD Distance can extend local worlds farther.": "Тулы деталь ер диапазоны. LOD Дистанция урындағы донъяларҙы алыҫҡараҡ һуҙырға мөмкин.",
"Coarse distant terrain in local worlds. Integrated GPUs stop at 128 chunks.": "Урындағы донъяларҙа ҡырыҫ алыҫ ерҙәр. Интеграцияланған график процессорҙар 128 өлөштә туҡтай.",
"Reduced draws far terrain coarser: faster on this integrated GPU.": "Ҡыҫҡартылған тарта алыҫ ер ҡырыҫ: тиҙерәк был интеграцияланған GPU.",
"Reduced draws far terrain coarser, for GPUs it slows down.": "Кәметелгән тарта алыҫ ер ҡырыҫыраҡ, GPUs өсөн ул яйлатып.",
"Field of view. Wider shows more terrain at once.": "Күренеш өлкәһе. Киңерәк бер юлы күберәк ерҙе күрһәтә.",
"Caps frames per second; a cap saves power and heat.": "Ҡапҡастар кадрҙар секундына; ҡапҡас энергияны һәм йылылыҡты һаҡлай.",
"Waits for the display's refresh: no tearing, a little latency.": "Дисплейҙы яңыртыуҙы көтә: йыртыу юҡ, бер аҙ тотҡарлыҡ.",
"Size of menus and the HUD.": "Размер меню һәм HUD.",
"Sets the options below together. Changing one makes it Custom.": "Түбәндәге варианттарҙы бергә ҡуя. Бер үҙгәртеү уны Ҡулланыусы итә.",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "Лава, ут һәм ҡояш нурҙары аҡтан да сағыуыраҡ балҡый. HDR дисплей кәрәк.",
"Your display's darkest black. 0 for OLED; LCDs glow a little.": "Һеҙҙең дисплей&#8217;ы иң ҡара ҡара. 0 өсөн OLED; ЖК-дисплейҙар бер аҙ балҡый.",
"How bright highlights get. Auto: the peak your system reports.": "Нисек сағыу өҫтөнлөктәр ала. Авто: пик һеҙҙең система отчеттар.",
"How bright white paper and menus look. Auto: your desktop's white.": "Нисек сағыу аҡ ҡағыҙ, менюлар күренә. Авто: һеҙҙең өҫтәл&#8217;ы аҡ.",
"How bright the HUD and menus are, against paper white.": "Нисек сағыу HUD һәм менюлар, ҡағыҙ аҡ ҡаршы.",
"How far mobs, crops and machines keep working. CPU time.": "Күпме алыҫҡа мобтар, культуралар һәм машиналар эшләп тора. Процессор ваҡыты.",
"How far away mobs and items are drawn, as a share of normal.": "Нисек алыҫҡа мобтар һәм әйберҙәр тартыла, өлөшө булараҡ нормаль.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Ябыҡ участкалар артында йәшеренгән ерҙәрҙе һикерә. Офф күп сығымдар талап итә.",
"Hides entities behind opaque blocks.": "Һүҙ асыҡ булмаған блоктар артында субъекттарҙы йәшерә.",
"No entity occlusion culling.": "Юҡ субъект окклюзия ҡырҡыу.",
"CPU occlusion test runs this frame.": "Процессор окклюзия һынау был кадрҙа эшләй.",
"Occlusion checks run on a worker thread.": "Окклюзия тикшерелеүҙәре эшсе ептә эшләй.",
"Meshed sections uploaded per frame. Lower is smoother while loading.": "Бер кадрға тейәлгән селтәрле бүлектәр. Түбәнге тейәгәндә шымараҡ.",
"Soft shadows in corners. Costs chunk building time only.": "Мөйөштәрҙә йомшаҡ күләгәләр. Сығымдар өлөшө төҙөү ваҡыты ғына.",
"Smaller copies of block textures for distant terrain: less shimmer.": "Алыҫ ерҙәр өсөн блок текстураларының бәләкәйерәк күсермәләре: ялтырап тороу аҙыраҡ.",
"Blends grass and water colours across biomes when building chunks.": "Үлән һәм һыу төҫтәрен биомдар буйынса ҡатнаштырып, өлөштәр төҙөгәндә.",
"Cloud layer: off, flat (fast) or 3D (fancy).": "Болот ҡатламы: өҙөлгән, яҫы (тиҙ) йәки 3D (фантазия).",
"How far clouds reach, in chunks.": "Болоттар ни тиклем алыҫҡа етә, өлөшләп.",
"Draws the sky, sun, moon and stars.": "Күкте, ҡояшты, айҙы, йондоҙҙарҙы һүрәтләй.",
"Distance fog. Off shows the edge of the loaded world.": "Дистанция томан. Off йөкләнгән донъяның ситен күрһәтә.",
"Draws rain and snow.": "Ямғыр һәм ҡар тарта.",
"How far rain and snow fall around you.": "Тирә-яғыңда ни тиклем алыҫҡа ямғыр, ҡар яуа.",
"How many particles effects spawn.": "Күпме киҫәксә эффекттары үрсетә.",
"Round shadows under mobs and items.": "Мобтар һәм әйберҙәр аҫтында түңәрәк күләгәләр.",
"Leaves with see-through gaps. Off draws them solid.": "Япраҡтары үтә күренмәле тишектәр менән. Офф уларҙы ныҡлы тарта.",
"Sorts water and glass more exactly behind each other.": "Һыу менән быяланы бер-береһе артында анығыраҡ сорттарға айыра.",
"Which chunk rebuilds wait for the frame: none, nearby or yours.": "Ниндәй өлөшө яңынан төҙөй көтә рамка: бер ниндәй ҙә, яҡында йәки һеҙҙең.",
"Animates water, lava, fire and other moving textures.": "Һыу, лава, ут һәм башҡа хәрәкәт итеүсе текстураларҙы йәнләндерә.",
"World pixels per screen pixel. Below 100% is faster on the GPU.": "Донъя пикселдәре экран пикселе. 100% түбән тиҙерәк GPU.",
"Smooths jagged edges. FXAA: softer. TAA: smoothest, moving things may trail.": "Тешле ситтәрен тигеҙләй. ФХАА: йомшағыраҡ. ТАА: иң шыма, хәрәкәт итеүсе әйберҙәр эҙҙәре булыуы мөмкин.",
"Renders the world smaller and rebuilds it with TAA. Faster, softer.": "Донъяны бәләкәйерәк итә һәм уны ТАА менән яңынан төҙөй. Тиҙерәк, йомшағыраҡ.",
"HDR output on an HDR display: lava, fire and sunlight shine brighter.": "HDR дисплейында HDR сығышы: лава, ут һәм ҡояш нурҙары сағыуыраҡ балҡый.",
"How frames reach the screen while VSync is off.": "Нисек кадрҙар экранға етә, ә VSync өҙөлгән.",
"Efficient sleeps until the next frame; Precise spins, like vanilla.": "Һөҙөмтәле йоҡлай сираттағы кадрға тиклем; Теүәл әйләнештәр, ваниль кеүек.",
"Fills the screen (borderless).": "Экранды тултыра (сикһеҙ).",
"Brightens dark places.": "Ҡараңғы урындарҙы яҡтырта.",
"Lowers the frame rate when minimised, or idle too.": "Кадрҙар йышлығын түбәнәйтә, ҡасан минималь, йәки буш ла.",
"Sways the view while walking.": "Йөрөгәндә ҡарашты һелкетә.",
"Fades newly loaded chunks in.": "Яңы тейәлгән өлөштәрҙе һүнә.",
"Darkens the screen's edges.": "Экрандың ситтәрен ҡарайта.",
"Shows when the world saves.": "Донъя ҡотҡарғанда күрһәтә.",
"Shows attack cooldown.": "Һөжүм итеү ваҡытын күрһәтә.",
"60 FPS cap (30 in menus or unfocused), half the workers: less power.": "60 FPS ҡапҡас (30 менюларҙа йәки нефокусированный), яртыһы эшселәр: ҡөҙрәте аҙыраҡ.",
"Threads that generate, light and mesh chunks. Next start.": "Ептәр, генерациялау, яҡтылыҡ һәм селтәр өлөштәре. Киләһе старт.",
"Vulkan, then DirectX 12 (Windows) or OpenGL (Linux) where it fails. Next start.": "Vulkan, унан һуң DirectX 12 (Windows) йәки OpenGL (Linux) ҡайҙа уңышһыҙлыҡҡа осрай. Киләһе старт.",
"Water and glass in one buffer: fewer draw calls, a bit more VRAM. Next start.": "Һыу һәм быяла бер буферҙа: низағ шылтыратыуҙар аҙыраҡ, бер аҙ күберәк VRAM. Киләһе старт.",
"Mesh data sent to the GPU per frame. Lower is smoother while loading.": "Селтәр мәғлүмәттәре ебәрелгән GPU бер кадр. Түбәнге тейәгәндә шымараҡ.",
"Frame rate and frame times over the game, with a graph.": "Кадрҙар тиҙлеге һәм кадрҙар ваҡыты өҫтөндә уйын, график менән.",
"horizon.multiplayer.finding_friend": "Дуҫыңдың донъяһын табыу...",
"horizon.multiplayer.not_signed_in": "Был сервер онлайн режимда. Уға ҡушылыу өсөн Microsoft иҫәп яҙмағыҙ менән Horizon стартерында инегеҙ.",
"horizon.multiplayer.invalid_address": "Дөрөҫ булмаған сервер адресы: %s",
"horizon.multiplayer.connected_friend_direct": "Һеҙҙең дуҫығыҙға туранан-тура тоташтырылған...",
"horizon.multiplayer.connected_friend_relay": "Һеҙҙең дуҫығыҙға реле аша тоташтырылған...",
"General": "Дөйөм",
"Advanced": "Алдынғы",
"Undo": "Кире ҡайтарыу",
"Search options": "Эҙләү варианттары",
"Shortcut": "Ярлыҡ",
"Distance": "Арауыҡ",
"Display": "Дисплей",
"Interface": "Интерфейс",
"Sky and weather": "Күк йөҙө һәм һауа торошо",
"Lighting and entities": "Яҡтыртыу һәм субъекттар",
"Textures": "Текстуралар",
"Image": "Һүрәт",
"Chunk loading": "Ҡыҫҡа тейәү",
"Culling": "Куллинг",
"Power": "Власть",
"Frames": "Рамкалар",
"Overlay": "Ҡаплау",
"Output": "Сығыш",
"Brightness": "Яҡтылыҡ",
"How far you see, the window and the HUD.": "Күпме алыҫ күрәһең, тәҙрә һәм HUD.",
"The sky and weather, lighting, textures and the image.": "Күк һәм һауа торошо, яҡтыртыу, текстуралар һәм образ.",
"Chunk loading, culling and power use.": "Ҡырҡылған тейәү, ҡырҡыу һәм ҡөҙрәт ҡулланыу.",
"How frames reach the screen, and the performance overlay.": "Нисек кадрҙар экранға етә, һәм башҡарыу өҫтәү.",
"Brighter-than-white output on an HDR display.": "HDR дисплейында аҡтан сағыуыраҡ сығыш.",
"Results for \"%s\"": "\"%s\" өсөн һөҙөмтәләр.",
"No settings match.": "Бер ниндәй ҙә параметрҙар тап килмәй.",
"horizon.title.not_affiliated": "Mojang йәки Microsoft менән бәйле түгел",
"horizon.extras.header.lighting": "Яҡтыртыу",
"horizon.extras.header.third_person": "Өсөнсө зат .",
"horizon.extras.header.particles": "Киҫәксәләр",
"horizon.extras.header.inventory": "Инвентаризация",
"horizon.extras.header.chat": "Чат",
"horizon.extras.header.multiplayer": "Күп уйынсылар",
"horizon.extras.header.sound": "Тауыш .",
"horizon.extras.header.menus": "Менюлар",
"horizon.extras.fullbright": "Фуллбрайт",
"horizon.extras.dynamic_lights": "Динамик яҡтылыҡ",
"horizon.extras.own_name_tag": "Үҙ исем билдәһе",
"horizon.extras.hide_block_particles": "Блок-өҙөү киҫәксәләрен йәшерергә",
"horizon.extras.auto_reconnect": "Авто-яңынан тоташтырыу",
"horizon.extras.ui_sound_reserve": "Ҡулланыусылар интерфейсы тауыштары өсөн каналдар һаҡлау",
"horizon.extras.music_page": "Музыка бите .",
"horizon.extras.mouse_tweaks": "Сысҡандың идара итеү элементтарын үҙгәртә",
"horizon.extras.mouse_tweaks_wheel": "Прокрутка тәгәрмәсе әйберҙәрҙе күсерә",
"horizon.extras.sort": "Сортлау төймәләре",
"horizon.extras.sort_key": "Сортлау асҡысы",
"horizon.extras.no_ai": "ЯИ юҡ",
"horizon.extras.agent_control": "Агент контроле",
"horizon.extras.discord_presence": "Discord Бай булыу",
"horizon.extras.splitscreen": "Бүлгән экран",
"horizon.extras.chat_animation": "Тигеҙ һөйләшеү",
"horizon.extras.chat_indicator": "Хәбәр күрһәткесе",
"horizon.extras.wheel_cycles_settings": "Прокрутка тәгәрмәсе параметрҙарҙы үҙгәртә",
"horizon.extras.pack_browser": "Ресурс пакеты браузеры",
"horizon.extras.fullbright.tooltip": "Блоктарҙы һәм субъекттарҙы тулы яҡтылыҡта, хатта ҡараңғы урындарҙа ла күрһәтегеҙ.",
"horizon.extras.dynamic_lights.tooltip": "Факелдар һәм башҡа яҡтылыҡ сығанаҡтары тотолған, төшөрөлгән йәки ут өҫтөндә улар тирәһендәге блоктарҙы яҡтырта.",
"horizon.extras.own_name_tag.tooltip": "Һеҙҙең исемде баш өҫтөндә өсөнсө заттан күрһәтә, башҡа уйынсылар күргәнсә. Йәшерен ҡасан сервер һеҙҙең команданың исемдәрен йәшерә йәки һеҙ&#8217;күренмәй.",
"horizon.extras.hide_block_particles.tooltip": "Йәшерә сүп-сар киҫәксәләре, улар өҙөлгән блоктан ярыла һәм һеҙ шахта өҫтөндә емерек. Башҡа киҫәксәләр ҡала.",
"horizon.extras.auto_reconnect.tooltip": "Серверға тоташыу юғалғандан йәки ҡабаттан башлағандан һуң ҡабаттан ҡушыла, бер ҡасан да тибеүҙән йәки тыйыуҙан һуң.",
"horizon.extras.ui_sound_reserve.tooltip": "Донъялағы тауыштар 247 тауыш каналының 16-һын бушлай ҡалдыра, шуға күрә уларҙы ташҡын (ҙур шартлау) меню баҫыуҙарын тындыра алмай.",
"horizon.extras.music_page.tooltip": "Музыка & Тауыштар ала Музыка... бит: уйнау теләһә ниндәй встроенный трек хәҙер, йәки бер һүндереү, шулай итеп, уйын уны йыйыу туҡтата. Тректы һүндермәйенсә бер нәмә лә үҙгәрмәй.",
"horizon.extras.mouse_tweaks.tooltip": "Уң-һөйрәп урындар бер слот, һул-һөйрәп йыя үткәрелгән әйбер, Shift + һул-һөйрәп һәр слот күсә.",
"horizon.extras.mouse_tweaks_wheel.tooltip": "Аҫҡа слоттың бер әйберен икенсе яҡҡа ебәрә, өҫкә бер кире килтерә.",
"horizon.extras.sort.tooltip": "Сортировать инвентарь, һандыҡтар, бочкалар, эндер һандыҡтар һәм шулкер йәшниктәр ижади вкладка тәртибендә, стека берләштереү.",
"horizon.extras.sort_key.tooltip": "Курсор аҫтындағы өлөштө сорттарға бүлә. Баҫығыҙ, һуңынан бер төймәгә баҫығыҙ; Ҡасыу уны сисеп ебәрә.",
"horizon.extras.no_ai.tooltip": "Йәшерә һәм һүндерә һәр AI функцияһы: агент менән идара итеү, горизонт-ctl һәм MCP серверҙары. Пускатель&#8217;ы переключатель да.",
"horizon.extras.agent_control.tooltip": "Һеҙҙең үҙ AI агент был уйынды уйнай ала аша horizon-ctl йәки MCP клиент, был компьютерҙа ғына.",
"horizon.extras.discord_presence.tooltip": "Һеҙҙең Discord профилендә Horizon уйнауҙы күрһәтә, ә Discord йүгерә: менюлар, бер уйынсы йәки күп уйынсы, үлсәм һәм уйнаған ваҡыт. Бер ҡасан да донъяның исеме йәки сервер адресы.",
"Calibrate...": "Калибрлау...",
"Calibrate Anyway...": "Калибрлау барыбер...",
"Use Automatic Setup": "Автоматик ҡуйыуҙы ҡулланыу",
"Shortcut: %s": "Ҡыҫҡа юл: %s",
"Next": "Артабан",
"HDR Calibration %s/%s: %s": "HDR калибровкаһы %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Өс аҫтында тулы экран ҡуя, бер һынау һүрәте бер юлы.",
"A key that opens this screen from the game. Not bound at first.": "Уйындан был экранды асҡан асҡыс. Тәүҙә бәйләнмәгән.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Һеҙҙең дисплей нисек яҡты ала, тип әйтмәне: калибровка, уны ҡуйырға.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Һеҙҙең дисплей өсөн инде ҡуйылған. Тик калибровка, әгәр HDR дөрөҫ түгел күренә.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Ҡуя Пик Яркость һәм Ҡағыҙ Аҡ кире һеҙҙең дисплей&#8217;ы ҡороу.",
"Set up for your display. Leave on Auto unless it looks wrong.": "Һеҙҙең дисплей өсөн ҡуйырға. Авто өҫтөндә ҡалдырырға, әгәр ҙә ул дөрөҫ күренмәһә.",
"Lower it until the logo just disappears into the black.": "Түбән уны тиклем логотип ҡара ғына юғала.",
"Raise it until the logo just disappears into the bright square.": "Логотип яҡты квадратҡа ғына юғалғансы уны күтәрегеҙ.",
"Make the card a white that's comfortable to look at for a long time, in the light you play in.": "Картаны аҡ төҫтә эшләгеҙ, оҙаҡ ҡарарға уңайлы, һеҙ уйнаған яҡтылыҡта.",
"horizon.splitscreen.profiles.forget": "Онот .",
"horizon.splitscreen.profiles.create": "Ижад итеү",
"horizon.splitscreen.profiles.you": " - һин",
"horizon.splitscreen.profiles.forget_named": "Онот %s .",
"horizon.splitscreen.profiles.previous": "Алдағы",
"horizon.splitscreen.profiles.next": "Артабан",
"horizon.splitscreen.profiles.new_button": "Яңы профиль...",
"horizon.splitscreen.profiles.forget_title": "Профильде оноттоғоҙмо?",
"horizon.splitscreen.profiles.forget_leaves": "Ул был инстанцияның исемлегенән сыға.",
"horizon.splitscreen.profiles.forget_kept": "Донъя прогресс һәм параметрҙар һаҡлана.",
"horizon.splitscreen.profiles.new_title": "Яңы профиль",
"horizon.splitscreen.profiles.name_hint": "Исем (3-тән 16-ға тиклем хәреф, һан, _)",
"horizon.splitscreen.profiles.switch_title": "Профиль алмаштырыу",
"horizon.splitscreen.profiles.offline_note": "Офлайн профилдәр был данаға ҡарай",
"horizon.splitscreen.profiles.move": "Уйнаған ергә күсерегеҙ .",
"horizon.splitscreen.profiles.move_place": "Уйнаған урынға күсегеҙ: %s .",
"horizon.friends.title": "Дуҫтар менән уйнау .",
"horizon.friends.hint": "Дуҫтар кодты титул экранының «Дуҫтар» төймәһенән һуң, туранан-тура бәйләнештә йәки стартердың «Уйнау» битендә йәбештерә.",
"horizon.friends.copied": "Күсерелгән",
"horizon.friends.copy_code": "Код күсермәһе",
"horizon.friends.stop_sharing": "Бүлешеүҙе туҡтатығыҙ",
"horizon.friends.none_yet": "Бында әлегә дуҫтар юҡ.",
"horizon.friends.here_now": "Бында хәҙер: %s",
"horizon.friends.share_intro": "Был донъя менән бүлешергә артында саҡырыу коды: ул LAN асыла, һәм дуҫтар интернетта код менән ҡушыла. Беҙҙең бер сервер ҙа ҡатнашмай.",
"horizon.friends.start_sharing": "Бүлешергә башлағыҙ",
"horizon.friends.troubleshooting": "Неисправность...",
"horizon.friends.stopped": "Саҡырыу коды менән бүлешеүҙе туҡтатты.",
"horizon.friends.short_code_changed": "Кемдер дөрөҫ булмаған ҡыҫҡа кодты һынап ҡараны, шуға күрә ул үҙгәрҙе.",
"horizon.friends.wrong_code": "Кемдер дөрөҫ булмаған кодты ҡулланып ҡараған.",
"horizon.friends.code_ready": "Һеҙҙең саҡырыу коды әҙер: Уйын Менюһы, Дуҫтар менән уйнау. Күсереп яҙып, дуҫтарығыҙға ебәрегеҙ.",
"horizon.friends.share_failed": "Донъяны бүлешә алманым: %s",
"horizon.friends.finding_route": "Һеҙгә дуҫтар нисек барып етә алыуын табыу...",
"horizon.osk.shift": "Смена",
"horizon.osk.space": "Йыһан",
"horizon.osk.delete": "Юйырға",
"horizon.osk.type": "Тип .",
"horizon.osk.symbols": "Символдар",
"horizon.osk.close": "Ябыҡ",
"horizon.extras.chat_animation.tooltip": "Яңы чат һыҙығы һулдан шыуып инә, ә ул һүнә, урынына бөтөн күренә.",
"horizon.extras.chat_indicator.tooltip": "Чат һыҙығының һул яғында төҫлө һыҙыҡ.",
"horizon.extras.wheel_cycles_settings.tooltip": "Көйләүҙәрҙәге төймә өҫтөндәге прокрутка тәгәрмәсе үҙенең ҡиммәтен үҙгәртә, ванильдәге кеүек. Һүндерелгән, тәгәрмәс исемлекте генә йөрөтә.",
"horizon.extras.pack_browser.tooltip": "Һайлау ресурс пакеттары ала өҫтәү төймәһе, эҙләү Modrinth һәм һеҙҙең пакеттар папкаһына пакет ҡуя. Һүндерелгән, уйын бер ҡасан да бәйләнешкә инә Modrinth.",
"horizon.extras.splitscreen.tooltip": "Донъяла, икенсе контроллер яңы уйынсы булараҡ ҡушыла ала: баҫығыҙ А, ҡасан ул һорай, һуңынан профиль һайлап һәм ҡайҙа уйнау.",
"horizon.splitscreen.setup.monitor": "Монитор %s",
"horizon.splitscreen.setup.monitor_unknown": "монитор",
"horizon.splitscreen.setup.window": "Тәҙрә %s",
"horizon.splitscreen.setup.new_window": "Яңы тәҙрә",
"horizon.splitscreen.setup.fill_monitor": "Тултырырға %s",
"horizon.splitscreen.setup.done": "Бөттө",
"horizon.splitscreen.setup.on": "Өҫтөндә",
"horizon.splitscreen.setup.off": "Һүндерелгән",
"horizon.splitscreen.setup.player": "Уйынсы %s",
"horizon.splitscreen.setup.revert": "Кире ҡайтарыу",
"horizon.splitscreen.setup.apply": "Ҡулланыу",
"horizon.splitscreen.setup.warning.empty_main": "1-се тәҙрәлә бер кем дә уйнамай: ул йәшенә",
"horizon.splitscreen.setup.warning.empty_close": "Тәҙрәлә бер кем дә уйнамай %s: ул Ҡулланыуҙа ябыла",
"horizon.splitscreen.setup.warning.empty_new": "Тәҙрәлә бер кем дә уйнамай %s: ул асылмай",
"horizon.splitscreen.setup.warning.on_main": "%s тәҙрә 1-се тәҙрә мониторын тултыра: уның уйынсылары 1-се тәҙрәлә уйнай",
"horizon.splitscreen.setup.warning.unplugged": "Тәҙрә %s мониторы тоташтырылмаған: уның уйынсылары 1-се тәҙрәлә уйнай",
"horizon.splitscreen.setup.status.off": "Сплитскрин һүндерелгән.",
"horizon.splitscreen.setup.status.enable_hint": "Уны тоҡандырып, һуңынан бер донъяла икенсе контроллерҙағы төймәгә баҫығыҙ.",
"horizon.splitscreen.setup.status.join_hint": "А баҫып, ҡушылырға һәм профиль һайларға.",
"horizon.splitscreen.setup.status.idle": "Сплит-экран буш тора",
"horizon.splitscreen.setup.status.alone": "Башҡа урындағы уйынсылар юҡ.",
"horizon.splitscreen.setup.status.solo_controls": "Яңғыҙ идара итеү һәм контроллер кире бәйләнеш үҙгәрешһеҙ.",
"horizon.splitscreen.setup.window_empty": "Тәҙрә %s: буш",
"horizon.splitscreen.setup.note.select": "Плеерға баҫығыҙ, йәки уны тәҙрәгә йәки мониторға һөйрәгеҙ.",
"horizon.splitscreen.setup.note.apply": "Ҡулланыу өсөн уйнау өсөн был ысул, Ҡайтарыу өсөн кире ҡайтарыу.",
"horizon.splitscreen.setup.note.place": "%s ҡайҙа уйнарға тейеш?",
"horizon.splitscreen.setup.this_player": "был уйынсы",
"horizon.splitscreen.setup.switch_disabled": "Splitscreen һүнә бер тапҡыр башҡа ҡалдырҙы (уларҙы пауза менюһы).",
"horizon.splitscreen.setup.window_place": "Тәҙрә %s (%s %s-нан)",
"horizon.splitscreen.setup.names": "Исемдәр .",
"horizon.splitscreen.setup.identify": "Билдәләү",
"horizon.splitscreen.setup.layout.two": "2 бүлешеү: %s",
"horizon.splitscreen.setup.layout.three": "3 бүлешеү: %s",
"horizon.splitscreen.setup.layout.two.0": "Авто",
"horizon.splitscreen.setup.layout.two.1": "Ян-яҡтан .",
"horizon.splitscreen.setup.layout.two.2": "Өҫкө һәм аҫҡы .",
"horizon.splitscreen.setup.layout.three.0": "Өҫтөндә бер киң",
"horizon.splitscreen.setup.layout.three.1": "Һул яҡта бер бейек",
"horizon.splitscreen.setup.layout.three.2": "Ян-яҡтан .",
"horizon.splitscreen.setup.help.player": "Уларҙы күсерер өсөн %s баҫығыҙ йәки һөйрәгеҙ.",
"horizon.splitscreen.setup.help.layout_auto": "%s (тәҙрә киң булғанда йәнәш). Үҙгәртергә баҫығыҙ.",
"horizon.splitscreen.setup.help.layout": "%s. Үҙгәртергә баҫығыҙ.",
"horizon.splitscreen.setup.help.switch": "Был компьютер өсөн экран бүленгән, өҫтөндә йәки өҫтөндә.",
"horizon.splitscreen.setup.help.identify": "Һәр уйынсының ҡарашы ниндәй тәҙрәлә икәнен күрһәтә.",
"horizon.splitscreen.setup.help.names": "Һәр уйынсының исеме мөйөшөндә уларҙы ҡарау.",
"horizon.splitscreen.setup.help.revert": "Әле ҡулланылмаған үҙгәрештәрҙе кире ҡайтарығыҙ.",
"horizon.splitscreen.setup.help.apply": "Картала күрһәтелгәнсә уйнағыҙ.",
"horizon.splitscreen.setup.help.unchanged": "Әлегә бер нәмә лә үҙгәрмәне.",
"horizon.splitscreen.setup.give_up_pad": "Падтан баш тартығыҙ",
"horizon.splitscreen.prompt.controller_connected": "Контроллер тоташтырылған",
"horizon.splitscreen.prompt.controller_disconnected": "Контроллер өҙөлгән",
"horizon.splitscreen.prompt.controller_disconnected.hint": "Яңынан уны тоташтырырға, йәки икенсе төймәгә баҫығыҙ",
"horizon.splitscreen.prompt.join.one": "%s ҡушылырға теләй",
"horizon.splitscreen.prompt.join.many": "%s контроллерҙар ҡушылырға теләй",
"horizon.splitscreen.prompt.join.yes": "Ҡушылыу",
"horizon.splitscreen.prompt.join.as_p1": "Р1 булып уйнай.",
"horizon.splitscreen.prompt.join.no": "Юҡ",
"horizon.splitscreen.prompt.lost": "%s: контроллер өҙөлгән",
"horizon.splitscreen.prompt.lost.use_another": "Икенсе ҡулланыу .",
"horizon.splitscreen.prompt.lost.leave": "Ҡалдырырға",
"horizon.splitscreen.setup.help.give_up_pad": "Һеҙҙең контролер һеҙҙе водитель туҡтата һәм ҡушылырға тәҡдим ителә (А).",
"horizon.splitscreen.setup.note.held_at": "%s %s тиклем: А уны бында төшөрә, В кире һала.",
"horizon.splitscreen.setup.note.held": "%s йыйып алынған: һул/уң ҡайҙа йыйып ала, А төшә, В кире һала.",
"horizon.splitscreen.online_only": "Был сервер онлайн режимда: офлайн профилдәр уға ҡушыла алмай, һәр уйынсыға үҙ Minecraft иҫәбе кәрәк.",
"horizon.splitscreen.own_account": "Был серверға һәр уйынсының үҙ Minecraft иҫәбе кәрәк",
"horizon.splitscreen.local_only": "Башҡа уйынсылар был серверға үҙҙәренең Minecraft иҫәбенә генә ҡушыла ала: офлайн профилдәр һеҙҙең донъяла, LAN йәки һеҙҙең урындағы селтәрҙәге серверҙа уйнай.",
"Hides models behind opaque blocks. Async checks off-thread; entities tick.": "Моделдәрҙе асыҡ булмаған блоктар артында йәшерә. Ассинхрон тикшерә тышҡы еп; субъекттар галочкой.",
"Block Entities": "Блок субъекттары",
"Shulker Boxes": "Шулкер йәшниктәре",
"Reuses settled boxes; lid movement stays animated.": "Ҡабаттан ҡуллана урынлаштырылған йәшниктәр; ҡапҡас хәрәкәте йәнле ҡала.",
"Signs": "Билдәләре",
"Reuses sign text until text, light, or font changes.": "Текст, яҡтылыҡ йәки шрифт үҙгәргәнсе билдә тексын ҡабаттан ҡуллана.",
"Decorated Pots": "Биҙәкле һауыттар .",
"Reuses still pots; break wobble and sherd changes redraw.": "Һаман да ҡаҙандарҙы ҡабаттан ҡуллана; өҙөү качаться һәм шерд үҙгәрештәр перерисовать.",
"Bells": "Ҡыңғырауҙар",
"Reuses still bells; ringing stays animated.": "Ҡабаттан ҡуллана әле ҡыңғырауҙар; шылтыратыу йәнле ҡала.",
"Chests": "Һандыҡтар",
"Reuses closed chests; moving lids stay animated.": "Ябыҡ һандыҡтарҙы ҡабаттан ҡуллана; хәрәкәт итеүсе ҡапҡастар йәнле булып ҡала.",
"Banners": "Баннерҙар",
"Reuses poles; flags and pattern layers still update.": "Бағаналарҙы ҡабаттан ҡуллана; флагтар һәм өлгө ҡатламдары һаман да яңыртыла.",
"Copper Golem Statues": "Баҡыр Голем статуялары",
"Reuses statue geometry until its block state changes.": "Блок хәле үҙгәргәнсе статуя геометрияһын ҡабаттан ҡуллана.",
"Leaf Culling": "Япраҡ ҡырҡыу",
"Every leaf face. Vanilla's fancy leaves.": "Һәр япраҡ йөҙө. Ванильдың фантазия япраҡтары.",
"Distant Plant Dither": "Алыҫ үҫемлек ҡалтыраныуы",
"Keeps fine plants in high-res packs from fading far away.": "Юғары асыҡлыҡтағы пакеттарҙағы ваҡ үҫемлектәрҙе алыҫҡа һүнеүҙән һаҡлай.",
"Screenshot Quality": "Скриншот сифаты",
"F2 screenshots at this many times the game's resolution. Higher takes longer.": "F2 скриншоттар был күп тапҡыр уйын&#8217;ы ҡарар. Юғары оҙағыраҡ ваҡыт ала.",
"Frame Generation": "Кадр быуын",
"Shows a made-up frame between each two real ones: smoother, a little more lag.": "Һәр ике ысын араһында ҡоролған кадр күрһәтә: шымараҡ, бер аҙ күберәк лаг.",
"Shadow Cache": "Күләгә кэш",
"Reuses the pack shadow map when sun, view and world match. Off by default.": "Ҡояш, ҡараш һәм донъя тап килгәндә пакет күләгә картаһын ҡабаттан ҡуллана. Һүндерелгән по умолчанию.",
"Find BlueMap": "Зәңгәр картаны табығыҙ",
"On servers, finds their BlueMap web map for far terrain. Next join.": "Серверҙарҙа, алыҫ ерҙәр өсөн уларҙы BlueMap веб-картаһын таба. Киләһе ҡушылыу.",
"BlueMap Radius": "Зәңгәр карта радиусы",
"How far around you BlueMap tiles are downloaded. Next join.": "Һеҙҙең тирәләй ни тиклем алыҫ BlueMap плиталар скачать. Киләһе ҡушылыу.",
"Synchronous": "Синхрон",
"Asynchronous": "Ассинхрон",
"Solid": "Ҡаты",
"Solid Aggressive": "Ҡаты агрессив",
"Hollow": "Ҡыуыш",
"Leaves fully inside a canopy draw solid: fewer see-through layers.": "Япраҡтар тулыһынса навес эсендә ҡаты тарта: үтә күренмәле ҡатламдар аҙыраҡ.",
"Leaves with one open side draw solid too: faster, chunkier edges.": "Япраҡтар менән бер асыҡ яғы ҡаты тарта ла: тиҙерәк, chunkier ситтәре.",
"Drops faces between leaves: faster, but gaps look through.": "Япраҡтар араһында йөҙҙәрен тамсылай: тиҙерәк, әммә тишектәр аша ҡарай.",
"Hotbar: Previous Slot": "Ҡыҙыу панелдә: Алдағы слот",
"Hotbar: Next Slot": "Ҡыҙыу панелдә: Киләһе слот",
"horizon.agent.connected.one": "Тоташтырылған: 1 агент тоташыуы",
"horizon.agent.connected.many": "Тоташтырылған: %s агент тоташыуҙары",
"horizon.agent.connected.one_ago": "Тоташтырылған: 1 агент тоташыу, һуңғы команда %s с элек",
"horizon.agent.connected.many_ago": "Тоташтырылған: %s агент тоташыуҙары, һуңғы команда %s с элек",
"horizon.agent.waiting": "Агент көтөп.",
"horizon.agent.off": "Һүндерелгән: агенттар был уйынға етә алмай.",
"horizon.agent.session_opened": "Был сессия өсөн уйынды башлаған программа асты.",
"horizon.agent.socket": "Розетка: %s (уның тулы юлы өсөн реквизиттарҙы күсерергә)",
"horizon.agent.local_only": "Урындағы ғына: был компьютерҙа агенттар, бер ҡасан да селтәр.",
"%s nits": "%s нит",
"Auto (%s nits)": "Авто (%s нит)",
"%.2f nits": "%.2f нит",
"Hides models behind opaque blocks. Entities update. Off with custom packs.": "Моделдәрҙе асыҡ булмаған блоктар артында йәшерә. Яңыртыу субъекттары. Ҡулланыусылар пакеттары менән өҙөлгән.",
"horizon.extras.repro_keys": "Яҙма ярлыҡтар",
"horizon.extras.repro_keys.tooltip": "F3+R репро яҙманы башлай йәки туҡтата һәм F3+E уның өйөмөн яҙа. Ғәҙәттәгесә һүндерелгән: яҙма донъяны күсерә.",
"horizon.agent.record_session": "Яҙҙырыу сеансын башлау",
"horizon.agent.record_stop": "Яҙҙырыуҙы туҡтатыу",
"horizon.agent.export_repro": "Экспортлау Repro йыйылмаһы",
"horizon.repro.started": "Яҙҙырыу башланды; F3+R уны туҡтата һәм өйөмдө яҙа.",
"horizon.repro.stopped": "Яҙма туҡтатылды: %s",
"horizon.repro.bundle": "Репро пакеты: %s",
"horizon.repro.error": "Ҡабатлау: %s",
"horizon.repro.no_recording": "Әлегә экспортҡа сығарырлыҡ яҙма юҡ.",
"horizon.repro.replay_started": "Ҡабатлау башланды: %s",
"horizon.pack.addInfo": "Modrinth ресурстар пакетын өҫтәү",
"horizon.pack.addWorking": "Һорау Modrinth...",
"horizon.pack.addNone": "Ресурс пакеттары табылмаған",
"horizon.pack.addTitle": "Ресурстар пакетын өҫтәү",
"horizon.pack.install": "Ҡуйыу",
"horizon.pack.installing": "Ҡуйыу",
"horizon.pack.installed": "Ҡуйылған",
"horizon.pack.search": "Эҙләү",
"horizon.pack.searchHint": "Эҙләү Modrinth...",
"horizon.pack.filters": "Фильтрҙар",
"horizon.pack.filter.realistic": "Ысынбарлыҡҡа ярашлы",
"horizon.pack.filter.simplistic": "Ябайлаштырылған",
"horizon.pack.filter.vanillaLike": "Ваниль һымаҡ",
"horizon.pack.filter.x16": "16х",
"horizon.pack.filter.x32": "32х",
"horizon.pack.filter.x64": "64х",
"horizon.pack.addFailed": "Пакетты өҫтәп булманы: %s",
"horizon.extras.header.cosmetics": "Косметика",
"horizon.extras.cosmetics_mine": "Минең косметика",
"horizon.extras.cosmetics_mine.tooltip": "Йәшерен: һәр Horizon уйынсы һеҙҙең косметикаһыҙ күрә, һеҙ ҙә индерелгән. Шул уҡ переключатель стартер&#8217;s Гардероб.",
"horizon.extras.cosmetics_others": "Башҡа уйынсыларҙың косметикаһы",
"horizon.extras.cosmetics_others.tooltip": "Башҡа ниндәй Horizon уйынсылар кейә: барыһы ла, тик уларҙы исем биркаһы стилдәре, йәки бер нәмә лә. Йәшерен, уйын бер ҡасан да һорамай, улар нимә кейә.",
"horizon.extras.cosmetics_auras": "Башҡаларҙың ауралары һәм эҙҙәре",
"horizon.extras.cosmetics_auras.tooltip": "Аура һәм эҙҙәр башҡа уйынсылар кейә. Һүндерелгән по умолчанию.",
"horizon.extras.cosmetics.shown": "Күрһәтелгән",
"horizon.extras.cosmetics.hidden": "Йәшерен",
"horizon.extras.cosmetics.name_tags": "Исем билдәләре генә"
}
@@ -0,0 +1,482 @@
{
"horizon.options.controller": "Кантралёр",
"horizon.options.vibration": "Вібрацыя",
"horizon.options.adaptive_triggers": "Адаптыўныя трыгеры",
"horizon.options.lightbar": "Светлавая панэль",
"horizon.options.look_sensitivity": "Адчувальнасць агляду",
"horizon.options.invert_x": "Інверсія агляду па X",
"horizon.options.invert_y": "Інверсія агляду па Y",
"horizon.options.sneak_button": "Кнопка красціся",
"horizon.options.move_deadzone": "Мёртвая зона руху",
"horizon.options.look_deadzone": "Мёртвая зона агляду",
"horizon.options.button_glyphs": "Гліфы кнопак",
"horizon.options.on_screen_keyboard": "Экранная клавіятура",
"horizon.options.chat_delay": "Затрымка чата",
"horizon.options.lightbar.health": "здароўе",
"horizon.options.lightbar.dimension": "Вымярэнне",
"horizon.options.glyphs.auto": "Аўто",
"horizon.options.keyboard.auto": "Аўто",
"horizon.options.unavailable": "Гэтая функцыя яшчэ недаступная ў Horizon.",
"horizon.options.search": "Налады пошуку...",
"horizon.pages.mods": "Моды",
"horizon.pages.shader_packs": "Пакеты шэйдараў",
"horizon.pages.shader_options": "Параметры шэйдара",
"horizon.pages.controller": "Налады кантролера",
"horizon.pages.controller_binds": "Прывязкі кантролера",
"horizon.pages.extras": "Horizon Дадаткова",
"horizon.pages.settings": "Horizon Налады",
"horizon.pages.agent": "Агент штучнага інтэлекту",
"horizon.split.leave": "Сыходзь",
"horizon.pages.split_settings": "Налады падзеленага экрана",
"horizon.pages.split_profiles": "Хто гуляе?",
"horizon.pages.split_setup": "Налада падзеленага экрана",
"horizon.pages.music": "Музыка",
"horizon.pages.music.menu": "Меню",
"horizon.pages.music.overworld": "Надсвет",
"horizon.pages.music.creative": "Творчы",
"horizon.pages.music.underwater": "Пад вадой",
"horizon.pages.music.end": "Канец",
"horizon.pages.music.boss": "Бос",
"horizon.pages.music.credits": "Крэдыты",
"horizon.pages.music.overworld_biomes": "Сусветныя біёмы",
"horizon.pages.music.nether_biomes": "Ніжнія біёмы",
"horizon.pages.music.end_biomes": "Канчатковыя біёмы",
"horizon.pages.music.other": "Іншая музыка",
"horizon.pages.music.empty": "Няма музычных трэкаў: не загружаюцца гукі гульні.",
"horizon.pages.music.tooltip": "Убудаваныя трэкі ў залежнасці ад сітуацыі: прайграйце адзін зараз або адключыце любы, каб гульня перастала яго выбіраць.",
"horizon.pages.settings.tooltip": "Уласныя налады Horizon: графіка, кантролеры, дадатковыя функцыі і моды.",
"horizon.pages.graphics.tooltip": "Простая графіка: кожная налада паказвае, колькі яна каштуе.",
"horizon.pages.graphics_advanced.tooltip": "Кожная налада графікі, у тым ліку Horizon, з коштам кожнай.",
"horizon.pages.controller.tooltip": "Кантролеры: іх кнопкі, стыкі, вібрацыя і навігацыя па меню.",
"horizon.pages.extras.tooltip": "Неабавязковыя дадаткі, усе выключаны па змаўчанні: гульня застаецца яркай, пакуль вы не ўключыце адну.",
"horizon.pages.agent.tooltip": "Дазвольце вашаму ўласнаму агенту штучнага інтэлекту гуляць: кліенты horizon-ctl і MCP на гэтым кампутары.",
"horizon.pages.split_profiles.tooltip": "Хто гуляе: профіль захоўваецца ў гэтай тэчцы гульні.",
"horizon.pages.split_setup.tooltip": "Вокны і маніторы, і хто дзе гуляе.",
"horizon.pages.shader_packs.tooltip": "Пакеты шэйдараў з папкі shaderpacks: уласныя Horizon і тыя, якія знаходзяцца ў фармаце shaders/. Бяжыць адзін за адным.",
"horizon.pages.mods.tooltip": "Моды, зробленыя для Horizon, з папкі horizon-mods. Кожны працуе ў пясочніцы і застаецца выключаным, пакуль вы яго не ўключыце.",
"horizon.pages.open.graphics": "Графіка...",
"horizon.pages.open.graphics_advanced": "Пашыраная графіка...",
"horizon.pages.open.shader_packs": "Пакеты шэйдэраў...",
"horizon.pages.open.controller": "Налады кантролера...",
"horizon.pages.open.split_setup": "Раздзелены экран...",
"horizon.pages.open.extras": "Дадаткова...",
"horizon.pages.open.mods": "Моды...",
"horizon.pages.open.agent": "Агент штучнага інтэлекту...",
"horizon.pages.open.horizon_graphics": "Horizon Графіка...",
"horizon.pages.open.controller_binds": "Кантролер прывязвае...",
"horizon.pages.open.horizon_settings": "Horizon Налады...",
"horizon.pages.open.music": "музыка...",
"horizon.mods.empty": "Няма модаў: змясціце файлы .hmod у тэчку horizon-mods",
"horizon.mods.stopped": " (Спынена)",
"horizon.mods.not_built": " (Не пабудаваны)",
"horizon.mods.settings": "Налады",
"horizon.mods.in_world": " (пакінуць свет змяніцца)",
"horizon.mods.open_folder": "Адкрыць тэчку Mod",
"horizon.shader_packs.empty": "Пакетаў шэйдэраў няма: змесціце іх у тэчку шэйдэраў",
"horizon.shader_options.open": "Параметры шэйдара...",
"horizon.shader_options.profile": "Профіль",
"horizon.shader_options.loading": "Чытанне параметраў пакета шэйдараў...",
"horizon.agent.copy_details": "Скапіруйце звесткі аб падключэнні",
"horizon.multiplayer.join": "Далучайцеся",
"horizon.multiplayer.cancel_reconnecting": "Адмяніць паўторнае падключэнне",
"horizon.multiplayer.join_friend": "Далучайцеся да сяброў",
"horizon.multiplayer.invite_code": "Код запрашэння",
"horizon.multiplayer.relay": "Рэле (неабавязкова)",
"Preset": "Прадусталяваная",
"LOD Distance": "LOD Адлегласць",
"Far Detail": "Далёкая дэталь",
"Cave Culling": "Выбракоўка пячор",
"Entity Culling": "Выбракоўванне сутнасці",
"Chunk Updates": "Абнаўленні чанка",
"Upload Budget": "Бюджэт загрузкі",
"Sky": "неба",
"Fog": "Туман",
"Weather": "Надвор'е",
"Animated Textures": "Аніміраваныя тэкстуры",
"Present Mode": "Рэжым сучаснасці",
"Frame Pacing": "Тэмп кадраў",
"Worker Threads": "Працоўныя тэмы",
"Resolution Scale": "Шкала дазволу",
"Anti-aliasing": "Згладжванне",
"Upscaling": "Апскейлінг",
"Paper White": "Папера белая",
"Peak Brightness": "Пікавая яркасць",
"Black Level": "Узровень чорнага",
"UI Brightness": "Яркасць інтэрфейсу",
"Battery Mode": "Рэжым батарэі",
"Graphics API": "Графічны API",
"Translucent Batching": "Напаўпразрысты дазаванне",
"Performance Overlay": "Прадукцыйнасць Overlay",
"Full Detail Distance": "Поўная адлегласць",
"On": "Укл",
"Off": "Выкл",
"Unlimited": "Неабмежаваны",
"Auto": "Аўто",
"Custom": "Прыстасаваныя",
"Performance": "Прадукцыйнасць",
"Balanced": "Ураўнаважаны",
"Quality": "Якасць",
"Ultra": "Ультра",
"Ultra Performance": "Ультрапрадукцыйнасць",
"Native": "Натыўны",
"Full": "Поўны",
"Reduced": "Паменшаны",
"Efficient": "Эфектыўная",
"Precise": "Дакладны",
"Auto (VSync)": "Аўта (VSync)",
"Mailbox": "Паштовая скрыня",
"Immediate": "Неадкладны",
"FPS and Graph": "FPS і графік",
"No cost": "Без выдаткаў",
"Low cost": "Нізкі кошт",
"Medium cost": "Сярэдні кошт",
"High cost": "Высокі кошт",
"Saves time": "Эканоміць час",
"Graphics": "Графіка",
"Done": "Гатова",
"This feature is not yet available in Horizon.": "Гэтая функцыя яшчэ недаступная ў Horizon.",
"Your display or system doesn't offer HDR output.": "Ваш дысплэй або сістэма не забяспечвае выхад HDR.",
"Turn HDR on to use it.": "Уключыце HDR, каб выкарыстоўваць яго.",
"Your GPU or graphics API doesn't support it.": "Ваш GPU або графічны API не падтрымлівае гэта.",
"%s chunks": "%s кавалкаў",
"%s per frame": "%s за кадр",
"%s MiB/frame": "%s МіБ/кадр",
"%s KiB/frame": "%s КіБ/кадр",
"Auto (%s)": "Аўто (%s)",
"Full-detail terrain range. LOD Distance can extend local worlds farther.": "Поўны дэталёвы дыяпазон мясцовасці. LOD Адлегласць можа пашырыць мясцовыя светы далей.",
"Coarse distant terrain in local worlds. Integrated GPUs stop at 128 chunks.": "Грубая аддаленая мясцовасць у лакальных мірах. Убудаваныя графічныя працэсары спыняюцца на 128 кавалках.",
"Reduced draws far terrain coarser: faster on this integrated GPU.": "Паменшанае малюнак больш грубай мясцовасці: хутчэй на гэтым убудаваным графічным працэсары.",
"Reduced draws far terrain coarser, for GPUs it slows down.": "Зніжэнне малюе больш грубую мясцовасць, для графічных працэсараў гэта запавольваецца.",
"Field of view. Wider shows more terrain at once.": "Поле зроку. Шырэй паказвае адразу больш мясцовасці.",
"Caps frames per second; a cap saves power and heat.": "Колькасць кадраў у секунду; шапка эканоміць энергію і цяпло.",
"Waits for the display's refresh: no tearing, a little latency.": "Чакае абнаўлення дысплея: без разрыву, з невялікай затрымкай.",
"Size of menus and the HUD.": "Памер меню і HUD.",
"Sets the options below together. Changing one makes it Custom.": "Разам усталёўвае параметры ніжэй. Змена аднаго робіць яго карыстальніцкім.",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "Лава, агонь і сонечнае святло ззяюць ярчэй белага. Патрэбен дысплей HDR.",
"Your display's darkest black. 0 for OLED; LCDs glow a little.": "Самы цёмны чорны на вашым дысплеі. 0 для OLED; ВК-дысплеі трохі свецяцца.",
"How bright highlights get. Auto: the peak your system reports.": "Якія яркія моманты становяцца. Аўта: пік, пра які паведамляе ваша сістэма.",
"How bright white paper and menus look. Auto: your desktop's white.": "Як ярка выглядаюць белая папера і меню. Аўтаматычна: ваш працоўны стол белы.",
"How bright the HUD and menus are, against paper white.": "Наколькі яркія HUD і меню на фоне белай паперы.",
"How far mobs, crops and machines keep working. CPU time.": "Як далёка працягваюць працаваць мобы, ураджай і машыны. працэсарны час.",
"How far away mobs and items are drawn, as a share of normal.": "Наколькі далёка намаляваны мобы і прадметы ў долі звычайнага.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Прапускае мясцовасць, схаваную за закрытымі ўчасткамі. Off каштуе шмат.",
"Hides entities behind opaque blocks.": "Хавае аб'екты за непразрыстымі блокамі.",
"No entity occlusion culling.": "Няма выбракоўвання аклюзіі сутнасці.",
"CPU occlusion test runs this frame.": "Тэст аклюзіі ЦП запускае гэты кадр.",
"Occlusion checks run on a worker thread.": "Праверкі аклюзіі выконваюцца ў працоўным патоку.",
"Meshed sections uploaded per frame. Lower is smoother while loading.": "Сеткавыя раздзелы, загружаныя на кадр. Ніжняя больш плыўная падчас загрузкі.",
"Soft shadows in corners. Costs chunk building time only.": "Мяккія цені ў кутах. Каштуе толькі час зборкі кавалкаў.",
"Smaller copies of block textures for distant terrain: less shimmer.": "Меншыя копіі блочных тэкстур для далёкай мясцовасці: менш мігацення.",
"Blends grass and water colours across biomes when building chunks.": "Змешвае колеру травы і вады ў біёмах пры стварэнні кавалкаў.",
"Cloud layer: off, flat (fast) or 3D (fancy).": "Воблачны пласт: выключаны, плоскі (хуткі) або 3D (фантастычны).",
"How far clouds reach, in chunks.": "Наколькі дасягаюць воблакі, кавалкамі.",
"Draws the sky, sun, moon and stars.": "Малюе неба, сонца, месяц і зоркі.",
"Distance fog. Off shows the edge of the loaded world.": "Далёкі туман. Off паказвае край загружанага свету.",
"Draws rain and snow.": "Малюе дождж і снег.",
"How far rain and snow fall around you.": "Як далёка вакол цябе дождж і снег.",
"How many particles effects spawn.": "Колькі эфектаў часціц спараджае.",
"Round shadows under mobs and items.": "Круглыя цені пад мобамі і прадметамі.",
"Leaves with see-through gaps. Off draws them solid.": "Лісце з празрыстымі прасветамі. Off малюе іх цвёрда.",
"Sorts water and glass more exactly behind each other.": "Сартуе ваду і шкло больш дакладна адзін за адным.",
"Which chunk rebuilds wait for the frame: none, nearby or yours.": "Які кавалак перабудоўваецца ў чаканні кадра: ніякі, побач ці ваш.",
"Animates water, lava, fire and other moving textures.": "Ажыўляе ваду, лаву, агонь і іншыя рухомыя тэкстуры.",
"World pixels per screen pixel. Below 100% is faster on the GPU.": "Сусветныя пікселі на піксель экрана. Ніжэй за 100% хутчэй на графічным працэсары.",
"Smooths jagged edges. FXAA: softer. TAA: smoothest, moving things may trail.": "Згладжвае няроўныя краю. FXAA: мякчэй. TAA: самыя гладкія рэчы, якія рухаюцца, могуць цягнуцца.",
"Renders the world smaller and rebuilds it with TAA. Faster, softer.": "Памяншае свет і перабудоўвае яго з TAA. Хутчэй, мякчэй.",
"HDR output on an HDR display: lava, fire and sunlight shine brighter.": "Вывад HDR на дысплеі HDR: лава, агонь і сонечнае святло ззяюць ярчэй.",
"How frames reach the screen while VSync is off.": "Як кадры трапляюць на экран, калі VSync выключаны.",
"Efficient sleeps until the next frame; Precise spins, like vanilla.": "Эфектыўнае спіць да наступнага кадра; Дакладныя спіны, як ваніль.",
"Fills the screen (borderless).": "Запаўняе экран (без палёў).",
"Brightens dark places.": "Асвятляе цёмныя месцы.",
"Lowers the frame rate when minimised, or idle too.": "Зніжае частату кадраў пры згортванні або ў рэжыме чакання.",
"Sways the view while walking.": "Качае погляд падчас хады.",
"Fades newly loaded chunks in.": "Змяншае толькі што загружаныя кавалкі.",
"Darkens the screen's edges.": "Зацямняе краю экрана.",
"Shows when the world saves.": "Паказвае, калі свет ратуе.",
"Shows attack cooldown.": "Паказвае час аднаўлення атакі.",
"60 FPS cap (30 in menus or unfocused), half the workers: less power.": "Абмежаванне 60 кадраў у секунду (30 у меню або без увагі), палова рабочых: меншая магутнасць.",
"Threads that generate, light and mesh chunks. Next start.": "Ніткі, якія ствараюць, лёгкія і сеткаватыя кавалкі. Наступны старт.",
"Vulkan, then DirectX 12 (Windows) or OpenGL (Linux) where it fails. Next start.": "Vulkan, затым DirectX 12 (Windows) або OpenGL (Linux), дзе не атрымліваецца. Наступны старт.",
"Water and glass in one buffer: fewer draw calls, a bit more VRAM. Next start.": "Вада і шкло ў адным буферы: менш выклікаў на розыгрыш, крыху больш VRAM. Наступны старт.",
"Mesh data sent to the GPU per frame. Lower is smoother while loading.": "Даныя сеткі, якія адпраўляюцца ў графічны працэсар за кадр. Ніжняя больш плыўная падчас загрузкі.",
"Frame rate and frame times over the game, with a graph.": "Частата кадраў і час кадраў у гульні з графікам.",
"horizon.multiplayer.finding_friend": "Пошук свету вашага сябра...",
"horizon.multiplayer.not_signed_in": "Гэты сервер знаходзіцца ў онлайн-рэжыме. Увайдзіце ў свой уліковы запіс Microsoft у праграме запуску Horizon, каб далучыцца да яе.",
"horizon.multiplayer.invalid_address": "Няправільны адрас сервера: %s",
"horizon.multiplayer.connected_friend_direct": "Падключаны непасрэдна да вашага сябра...",
"horizon.multiplayer.connected_friend_relay": "Падключаны да вашага сябра праз рэле...",
"General": "Генерал",
"Advanced": "Пашыраны",
"Undo": "Адмяніць",
"Search options": "Параметры пошуку",
"Shortcut": "Ярлык",
"Distance": "Дыстанцыя",
"Display": "Дысплей",
"Interface": "Інтэрфейс",
"Sky and weather": "Неба і надвор'е",
"Lighting and entities": "Асвятленне і сутнасці",
"Textures": "Тэкстуры",
"Image": "Малюнак",
"Chunk loading": "Загрузка чанка",
"Culling": "Выбракоўка",
"Power": "Магутнасць",
"Frames": "Рамкі",
"Overlay": "Накладка",
"Output": "Выхад",
"Brightness": "Яркасць",
"How far you see, the window and the HUD.": "Як далёка вы бачыце, акно і HUD.",
"The sky and weather, lighting, textures and the image.": "Неба і надвор'е, асвятленне, тэкстуры і малюнак.",
"Chunk loading, culling and power use.": "Загрузка кавалкаў, выбракоўванне і выкарыстанне энергіі.",
"How frames reach the screen, and the performance overlay.": "Як кадры трапляюць на экран і накладанне прадукцыйнасці.",
"Brighter-than-white output on an HDR display.": "Ярчэйшы за белы выхад на HDR-дысплеі.",
"Results for \"%s\"": "Вынікі для \"%s\"",
"No settings match.": "Налады не супадаюць.",
"horizon.title.not_affiliated": "Не звязаны з Mojang або Microsoft",
"horizon.extras.header.lighting": "Асвятленне",
"horizon.extras.header.third_person": "Трэцяя асоба",
"horizon.extras.header.particles": "Часціцы",
"horizon.extras.header.inventory": "Інвентар",
"horizon.extras.header.chat": "Чат",
"horizon.extras.header.multiplayer": "Мультыплэер",
"horizon.extras.header.sound": "Гук",
"horizon.extras.header.menus": "Меню",
"horizon.extras.fullbright": "Фулбрайт",
"horizon.extras.dynamic_lights": "Дынамічныя фары",
"horizon.extras.own_name_tag": "Уласная бірка з імем",
"horizon.extras.hide_block_particles": "Схаваць часціцы, якія разбіваюць блок",
"horizon.extras.auto_reconnect": "Аўтаматычнае падключэнне",
"horizon.extras.ui_sound_reserve": "Захоўвайце каналы для гукаў інтэрфейсу",
"horizon.extras.music_page": "Музычная старонка",
"horizon.extras.mouse_tweaks": "Элементы кіравання Mouse Tweaks",
"horizon.extras.mouse_tweaks_wheel": "Кола пракруткі перамяшчае элементы",
"horizon.extras.sort": "Кнопкі сартавання",
"horizon.extras.sort_key": "Ключ сартавання",
"horizon.extras.agent_control": "Агентскі кантроль",
"horizon.extras.splitscreen": "Раздзелены экран",
"horizon.extras.chat_animation": "Плаўны чат",
"horizon.extras.chat_indicator": "Індыкатар паведамлення",
"horizon.extras.wheel_cycles_settings": "Кола пракруткі змяняе налады",
"horizon.extras.pack_browser": "Браўзэр Resource Pack",
"horizon.extras.fullbright.tooltip": "Паказвайце блокі і аб'екты пры поўным асвятленні, нават у цёмных месцах.",
"horizon.extras.dynamic_lights.tooltip": "Факелы і іншыя крыніцы святла, якія трымаюць, упускаюць або гараць, асвятляюць блокі вакол іх.",
"horizon.extras.own_name_tag.tooltip": "Паказвае ваша імя над галавой ад трэцяй асобы, як яго бачаць іншыя гульцы. Схаваны, калі сервер хавае назвы вашай каманды або вы нябачныя.",
"horizon.extras.hide_block_particles.tooltip": "Хавае часціцы смецця, якія вырываюцца з разбітага блока і рассыпаюцца на той, які вы здабываеце. Іншыя часціцы застаюцца.",
"horizon.extras.auto_reconnect.tooltip": "Паўторна далучаецца да сервера пасля страты злучэння або перазагрузкі, ніколі пасля кіку або забароны.",
"horizon.extras.ui_sound_reserve.tooltip": "Гукі ў свеце пакідаюць 16 з 247 гукавых каналаў свабоднымі, таму іх паток (вялікі выбух) не можа заглушыць націсканне меню.",
"horizon.extras.music_page.tooltip": "«Музыка і гукі» атрымлівае старонку «Музыка...»: прайграйце любы ўбудаваны трэк зараз або адключыце адзін, каб гульня перастала яго выбіраць. Нічога не зменіцца, пакуль вы не адключыце трэк.",
"horizon.extras.mouse_tweaks.tooltip": "Перацягванне правай кнопкай мышы змяшчае па адным у слот, перацягванне левай кнопкай збірае ўтрымоўваны прадмет, Shift + перацягванне левай кнопкай мышы перамяшчае кожны слот.",
"horizon.extras.mouse_tweaks_wheel.tooltip": "Уніз адпраўляе адзін элемент слота на другі бок, уверх вяртае адзін назад.",
"horizon.extras.sort.tooltip": "Сартуе інвентар, куфры, бочкі, эндэрскія куфры і шулкерныя скрыні ў творчым парадку ўкладак, аб'ядноўваючы стосы.",
"horizon.extras.sort_key.tooltip": "Сартуе частку пад курсорам. Націсніце, затым націсніце клавішу; Уцёкі развязвае яго.",
"horizon.extras.no_ai.tooltip": "Хавае і адключае ўсе функцыі штучнага інтэлекту: кіраванне агентамі, horizon-ctl і серверы MCP. Пераключальнік пускавы ўстаноўкі таксама.",
"horizon.extras.agent_control.tooltip": "Ваш уласны агент штучнага інтэлекту можа гуляць у гэтую гульню праз horizon-ctl або кліент MCP толькі на гэтым камп'ютары.",
"horizon.extras.discord_presence.tooltip": "Паказвае Playing Horizon у вашым профілі Discord падчас запуску Discord: меню, адзіночны або шматкарыстальніцкі рэжым, памер і час гульні. Ніколі не назва свету або адрас сервера.",
"Calibrate...": "Адкалібраваць...",
"Calibrate Anyway...": "Усё роўна адкалібраваць...",
"Use Automatic Setup": "Выкарыстоўвайце аўтаматычную наладу",
"Shortcut: %s": "Ярлык: %s",
"Next": "Далей",
"HDR Calibration %s/%s: %s": "Каліброўка HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Устанаўлівае тры ніжэй на ўвесь экран, па адным тэставым малюнку.",
"A key that opens this screen from the game. Not bound at first.": "Ключ, які адкрывае гэты экран з гульні. Спачатку не звязаны.",
"Your display didn't say how bright it gets: calibrate to set it up.": "На вашым дысплеі не пазначана, наколькі ён яркім: адкалібруйце, каб наладзіць яго.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Ужо наладжана для вашага дысплея. Калібруйце, толькі калі HDR выглядае няправільна.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Вяртае пікавую яркасць і папяровы белы ў налады вашага дысплея.",
"Set up for your display. Leave on Auto unless it looks wrong.": "Наладзьце для вашага дысплея. Пакіньце на Auto, калі гэта не выглядае няправільна.",
"Lower it until the logo just disappears into the black.": "Апускайце яго, пакуль лагатып проста не знікне ў чорным колеры.",
"Raise it until the logo just disappears into the bright square.": "Падымайце яго, пакуль лагатып не знікне ў яркім квадраце.",
"Make the card a white that's comfortable to look at for a long time, in the light you play in.": "Зрабіце карту белай, каб на яе было зручна глядзець на працягу доўгага часу пры святле, пры якім вы гуляеце.",
"horizon.splitscreen.profiles.forget": "Забыць",
"horizon.splitscreen.profiles.create": "Ствараць",
"horizon.splitscreen.profiles.you": " - вы",
"horizon.splitscreen.profiles.forget_named": "Забыць %s",
"horizon.splitscreen.profiles.previous": "Папярэдні",
"horizon.splitscreen.profiles.next": "Далей",
"horizon.splitscreen.profiles.new_button": "Новы профіль...",
"horizon.splitscreen.profiles.forget_title": "Забыць профіль?",
"horizon.splitscreen.profiles.forget_leaves": "Ён пакідае спіс гэтага экземпляра.",
"horizon.splitscreen.profiles.forget_kept": "Сусветны прагрэс і налады захоўваюцца.",
"horizon.splitscreen.profiles.new_title": "Новы профіль",
"horizon.splitscreen.profiles.name_hint": "Імя (ад 3 да 16 літар, лічбаў, _)",
"horizon.splitscreen.profiles.switch_title": "Пераключыць профіль",
"horizon.splitscreen.profiles.offline_note": "Афлайн-профілі належаць да гэтага асобніка",
"horizon.splitscreen.profiles.move": "Рухайцеся туды, дзе гуляеце",
"horizon.splitscreen.profiles.move_place": "Рухайцеся туды, дзе вы гуляеце: %s",
"horizon.friends.title": "Гуляць з сябрамі",
"horizon.friends.hint": "Сябры ўстаўце код пасля кнопкі \"Сябры\" на тытульным экране ў раздзеле \"Прамое злучэнне\" або на старонцы \"Прайграванне\" праграмы запуску.",
"horizon.friends.copied": "Скапіравана",
"horizon.friends.copy_code": "Скапіруйце код",
"horizon.friends.stop_sharing": "Спыніць абагульванне",
"horizon.friends.none_yet": "Тут пакуль няма сяброў.",
"horizon.friends.here_now": "Вось зараз: %s",
"horizon.friends.share_intro": "Падзяліцеся гэтым светам за кодам запрашэння: ён адкрываецца ў лакальнай сеткі, і сябры ў Інтэрнэце далучаюцца з кодам. Наш сервер не задзейнічаны.",
"horizon.friends.start_sharing": "Пачніце абагульваць",
"horizon.friends.troubleshooting": "Ліквідацыя непаладак...",
"horizon.friends.stopped": "Абагульванне кода запрашэння спынена.",
"horizon.friends.short_code_changed": "Хтосьці спрабаваў увесці няправільны кароткі код, таму ён змяніўся.",
"horizon.friends.wrong_code": "Хтосьці паспрабаваў увесці няправільны код.",
"horizon.friends.code_ready": "Ваш код запрашэння гатовы: меню гульні, гульня з сябрамі. Скапіруйце яго і адпраўце сваім сябрам.",
"horizon.friends.share_failed": "Не атрымалася падзяліцца светам: %s",
"horizon.friends.finding_route": "Пошук, як сябры могуць звязацца з вамі...",
"horizon.osk.shift": "зрух",
"horizon.osk.space": "космас",
"horizon.osk.delete": "Выдаліць",
"horizon.osk.type": "Тып",
"horizon.osk.symbols": "Сімвалы",
"horizon.osk.close": "Блізка",
"horizon.extras.chat_animation.tooltip": "Новы радок чата слізгае злева, пакуль ён знікае, замест таго, каб выглядаць цалкам.",
"horizon.extras.chat_indicator.tooltip": "Каляровая паласа злева ад радка чата.",
"horizon.extras.wheel_cycles_settings.tooltip": "Кола пракруткі над кнопкай у наладах змяняе яе значэнне, як у vanilla. Выключана, кола толькі пракручвае спіс.",
"horizon.extras.pack_browser.tooltip": "Select Resource Packs атрымлівае кнопку «Дадаць», якая выконвае пошук у Modrinth і ўсталёўвае пакет у вашу тэчку пакетаў. Выключана, гульня ніколі не звязваецца з Modrinth.",
"horizon.extras.splitscreen.tooltip": "У свеце іншы кантролер можа далучыцца як новы гулец: націсніце A, калі ён запытае, затым выберыце профіль і дзе гуляць.",
"horizon.splitscreen.setup.monitor": "Манітор %s",
"horizon.splitscreen.setup.monitor_unknown": "манітор",
"horizon.splitscreen.setup.window": "Акно %s",
"horizon.splitscreen.setup.new_window": "Новае акно",
"horizon.splitscreen.setup.fill_monitor": "Запоўніце %s",
"horizon.splitscreen.setup.done": "Гатова",
"horizon.splitscreen.setup.on": "Укл",
"horizon.splitscreen.setup.off": "Выкл",
"horizon.splitscreen.setup.player": "Гулец %s",
"horizon.splitscreen.setup.revert": "Вярнуць",
"horizon.splitscreen.setup.apply": "Ужыць",
"horizon.splitscreen.setup.warning.empty_main": "Ніхто не гуляе ў Window 1: яна хаваецца",
"horizon.splitscreen.setup.warning.empty_close": "Ніхто не гуляе ў Window %s: яно зачыняецца пры ўжыванні",
"horizon.splitscreen.setup.warning.empty_new": "Ніхто не гуляе ў Window %s: яно не адчыняецца",
"horizon.splitscreen.setup.warning.on_main": "Window %s запаўняе манітор Window 1: яго гульцы гуляюць у Window 1",
"horizon.splitscreen.setup.warning.unplugged": "Манітор Window %s не падключаны: яго гульцы гуляюць у Window 1",
"horizon.splitscreen.setup.status.off": "Splitscreen выключаны.",
"horizon.splitscreen.setup.status.enable_hint": "Уключыце яго, затым націсніце кнопку на іншым кантролеры ў свеце.",
"horizon.splitscreen.setup.status.join_hint": "Націсніце A, каб далучыцца і выбраць профіль.",
"horizon.splitscreen.setup.status.idle": "Раздзелены экран у рэжыме чакання",
"horizon.splitscreen.setup.status.alone": "Няма іншых мясцовых гульцоў.",
"horizon.splitscreen.setup.status.solo_controls": "Індывідуальныя элементы кіравання і зваротная сувязь кантролера не змяніліся.",
"horizon.splitscreen.setup.window_empty": "Акно %s: пустое",
"horizon.splitscreen.setup.note.select": "Пстрыкніце прайгравальнік або перацягніце яго ў акно або на манітор.",
"horizon.splitscreen.setup.note.apply": "Падайце заяўку, каб гуляць такім чынам, вярніце, каб адмяніць.",
"horizon.splitscreen.setup.note.place": "Дзе гуляць %s?",
"horizon.splitscreen.setup.this_player": "гэты гулец",
"horizon.splitscreen.setup.switch_disabled": "Раздзелены экран выключаецца, як толькі іншыя сыдуць (іх меню паўзы).",
"horizon.splitscreen.setup.window_place": "Акно %s (%s з %s)",
"horizon.splitscreen.setup.names": "Імёны",
"horizon.splitscreen.setup.identify": "Ідэнтыфікаваць",
"horizon.splitscreen.setup.layout.two": "2 абмен: %s",
"horizon.splitscreen.setup.layout.three": "3 абмен: %s",
"horizon.splitscreen.setup.layout.two.0": "Аўто",
"horizon.splitscreen.setup.layout.two.1": "Бок аб бок",
"horizon.splitscreen.setup.layout.two.2": "Зверху і знізу",
"horizon.splitscreen.setup.layout.three.0": "Адзін шырокі зверху",
"horizon.splitscreen.setup.layout.three.1": "Адзін высокі злева",
"horizon.splitscreen.setup.layout.three.2": "Бок аб бок",
"horizon.splitscreen.setup.help.player": "Націсніце або перацягніце %s, каб перамясціць іх.",
"horizon.splitscreen.setup.help.layout_auto": "%s (побач, калі акно шырокае). Націсніце, каб змяніць.",
"horizon.splitscreen.setup.help.layout": "%s. Націсніце, каб змяніць.",
"horizon.splitscreen.setup.help.switch": "Splitscreen для гэтага кампутара, уключаны або выключаны.",
"horizon.splitscreen.setup.help.identify": "Погляд кожнага гульца паказвае, у якім акне ён знаходзіцца.",
"horizon.splitscreen.setup.help.names": "Імя кожнага гульца ў куце іх агляду.",
"horizon.splitscreen.setup.help.revert": "Адмяніць яшчэ не прымененыя змены.",
"horizon.splitscreen.setup.help.apply": "Гуляйце так, як паказвае карта.",
"horizon.splitscreen.setup.help.unchanged": "Пакуль нічога не змянілася.",
"horizon.splitscreen.setup.give_up_pad": "Адмоўцеся ад Pad",
"horizon.splitscreen.prompt.controller_connected": "Кантролер падключаны",
"horizon.splitscreen.prompt.controller_disconnected": "Кантролер адключаны",
"horizon.splitscreen.prompt.controller_disconnected.hint": "Зноў падключыце яго або націсніце кнопку на іншым",
"horizon.splitscreen.prompt.join.one": "%s хоча далучыцца",
"horizon.splitscreen.prompt.join.many": "%s кантралёраў хочуць далучыцца",
"horizon.splitscreen.prompt.join.yes": "Далучайцеся",
"horizon.splitscreen.prompt.join.as_p1": "Гуляць як P1",
"horizon.splitscreen.prompt.join.no": "няма",
"horizon.splitscreen.prompt.lost": "%s: кантролер адключаны",
"horizon.splitscreen.prompt.lost.use_another": "Выкарыстоўвайце іншы",
"horizon.splitscreen.prompt.lost.leave": "Сыходзь",
"horizon.splitscreen.setup.help.give_up_pad": "Ваш кантролер перастае кіраваць вамі і прапануе далучыцца (A).",
"horizon.splitscreen.setup.note.held_at": "%s да %s: А кідае яго сюды, Б ставіць назад.",
"horizon.splitscreen.setup.note.held": "%s падняў: левы/правы выбіраюць, дзе, А кідае, Б ставіць назад.",
"Hides models behind opaque blocks. Async checks off-thread; entities tick.": "Хавае мадэлі за непразрыстымі блокамі. Async правярае па-за патокам; суб'ектаў цік.",
"Block Entities": "Блокавыя аб'екты",
"Shulker Boxes": "Скрынкі Шулкера",
"Reuses settled boxes; lid movement stays animated.": "Паўторна выкарыстоўвае адстойнікі; рух вечка застаецца ажыўленым.",
"Signs": "Знакі",
"Reuses sign text until text, light, or font changes.": "Паўторна выкарыстоўвае тэкст знака, пакуль не зменіцца тэкст, падсвятленне або шрыфт.",
"Decorated Pots": "Упрыгожаныя гаршкі",
"Reuses still pots; break wobble and sherd changes redraw.": "Паўторна выкарыстоўвае гаршкі; перапыніць хістанне і асколак змены перамаляваць.",
"Bells": "Званы",
"Reuses still bells; ringing stays animated.": "Паўторна выкарыстоўвае яшчэ званы; званок застаецца аніміраваным.",
"Chests": "Сундукі",
"Reuses closed chests; moving lids stay animated.": "Паўторна выкарыстоўвае закрытыя куфры; рухомыя павекі застаюцца ажыўленымі.",
"Banners": "Банеры",
"Reuses poles; flags and pattern layers still update.": "Паўторна выкарыстоўвае слупы; сцягі і ўзоры слаёў па-ранейшаму абнаўляюцца.",
"Copper Golem Statues": "Медныя статуі голема",
"Reuses statue geometry until its block state changes.": "Паўторна выкарыстоўвае геаметрыю статуі, пакуль не зменіцца яе стан блока.",
"Leaf Culling": "Выбракоўванне лісця",
"Every leaf face. Vanilla's fancy leaves.": "Кожны ліст твар. Вострыя лісце ванілі.",
"Distant Plant Dither": "Дыстантны завод",
"Keeps fine plants in high-res packs from fading far away.": "Захоўвае выдатныя расліны ў пакетах з высокім дазволам ад выцвітання.",
"Screenshot Quality": "Якасць скрыншота",
"F2 screenshots at this many times the game's resolution. Higher takes longer.": "Скрыншоты F2 з дазволам, які шмат разоў перавышае дазвол гульні. Вышэйшы займае больш часу.",
"Frame Generation": "Генерацыя фрэймаў",
"Shows a made-up frame between each two real ones: smoother, a little more lag.": "Паказвае прыдуманую рамку паміж двума сапраўднымі: больш гладкая, з крыху большым адставаннем.",
"Shadow Cache": "Ценявы кэш",
"Reuses the pack shadow map when sun, view and world match. Off by default.": "Паўторна выкарыстоўвае карту ценяў зграі, калі сонца, выгляд і свет супадаюць. Выключана па змаўчанні.",
"Find BlueMap": "Знайдзіце BlueMap",
"On servers, finds their BlueMap web map for far terrain. Next join.": "На серверах знаходзіць вэб-карту BlueMap для далёкай мясцовасці. Далей далучыцца.",
"BlueMap Radius": "Радыус BlueMap",
"How far around you BlueMap tiles are downloaded. Next join.": "Як далёка вакол вас загружаюцца пліткі BlueMap. Далей далучыцца.",
"Synchronous": "Сінхронныя",
"Asynchronous": "Асінхронны",
"Solid": "Цвёрды",
"Solid Aggressive": "Суцэльны агрэсіўны",
"Hollow": "Полая",
"Leaves fully inside a canopy draw solid: fewer see-through layers.": "Лісце цалкам унутры полага малююць суцэльна: менш празрыстых слаёў.",
"Leaves with one open side draw solid too: faster, chunkier edges.": "Лісце з адным адкрытым бокам таксама атрымліваюцца цвёрдымі: больш хуткія, каржакаватыя краю.",
"Drops faces between leaves: faster, but gaps look through.": "Апускае грані паміж лісцікамі: хутчэй, але шчыліны праглядваюць.",
"Hotbar: Previous Slot": "Гарачая панэль: папярэдні слот",
"Hotbar: Next Slot": "Гарачая панэль: наступны слот",
"horizon.agent.connected.one": "Падключана: 1 падключэнне агента",
"horizon.agent.connected.many": "Падключана: %s агентскіх злучэнняў",
"horizon.agent.connected.one_ago": "Падключана: 1 падключэнне агента, апошняя каманда %s с таму",
"horizon.agent.connected.many_ago": "Падключана: %s злучэнняў агента, апошняя каманда %s с таму",
"horizon.agent.waiting": "Чакаю агента.",
"horizon.agent.off": "Выключана: агенты не могуць атрымаць доступ да гэтай гульні.",
"horizon.agent.session_opened": "Адкрыты для гэтай сесіі праграмай, якая запусціла гульню.",
"horizon.agent.socket": "Сокет: %s (скапіруйце дэталі для поўнага шляху)",
"horizon.agent.local_only": "Толькі лакальна: агенты на гэтым кампутары, ніколі ў сетцы.",
"%s nits": "%s ніт",
"Auto (%s nits)": "Аўта (%s ніт)",
"%.2f nits": "%.2f ніт",
"Hides models behind opaque blocks. Entities update. Off with custom packs.": "Хавае мадэлі за непразрыстымі блокамі. Абнаўленне суб'ектаў. Выключыце спецыяльныя пакеты.",
"horizon.extras.repro_keys": "Ярлыкі для запісу",
"horizon.extras.repro_keys.tooltip": "F3+R запускае або спыняе прайгравальны запіс, а F3+E запісвае пакет. Выключана па змаўчанні: запіс капіруе свет.",
"horizon.agent.record_session": "Пачаць сеанс запісу",
"horizon.agent.record_stop": "Спыніць сеанс запісу",
"horizon.agent.export_repro": "Экспарт Repro Bundle",
"horizon.repro.started": "Запіс пачаўся; F3+R спыняе яго і запісвае пакет.",
"horizon.repro.stopped": "Запіс спынены: %s",
"horizon.repro.bundle": "Пакет рэпрадукцый: %s",
"horizon.repro.error": "Паўтор: %s",
"horizon.repro.no_recording": "Пакуль няма запісу для экспарту.",
"horizon.repro.replay_started": "Паўтор пачаўся: %s",
"horizon.pack.addInfo": "Дадайце пакет рэсурсаў з Modrinth",
"horizon.pack.addWorking": "Запыт да Modrinth...",
"horizon.pack.addNone": "Пакеты рэсурсаў не знойдзены",
"horizon.pack.addTitle": "Дадайце пакет рэсурсаў",
"horizon.pack.install": "Усталяваць",
"horizon.pack.installing": "Ўстаноўка",
"horizon.pack.installed": "Усталяваны",
"horizon.pack.search": "Пошук",
"horizon.pack.searchHint": "Пошук у Modrinth...",
"horizon.pack.filters": "Фільтры",
"horizon.pack.filter.realistic": "Рэалістычны",
"horizon.pack.filter.simplistic": "Спрошчаны",
"horizon.pack.filter.vanillaLike": "Ванільны",
"horizon.extras.header.cosmetics": "Касметыка",
"horizon.extras.cosmetics_mine": "Мая касметыка",
"horizon.extras.cosmetics_mine.tooltip": "Схаваны: кожны гулец Horizon бачыць вас без вашай касметыкі, у тым ліку і вас. The same switch as the launcher's Wardrobe.",
"horizon.extras.cosmetics_others": "Касметыка іншых гульцоў",
"horizon.extras.cosmetics_others.tooltip": "Што носяць іншыя гульцы Horizon: усё, толькі стылі тэгаў з імёнамі або нічога. Hidden, the game never asks what they wear.",
"horizon.extras.cosmetics_auras": "Чужыя аўры і шлейфы",
"horizon.extras.cosmetics_auras.tooltip": "Auras and trails other players wear. Выключана па змаўчанні.",
"horizon.extras.cosmetics.shown": "Паказана",
"horizon.extras.cosmetics.hidden": "Схаваны",
"horizon.extras.cosmetics.name_tags": "Толькі біркі з імёнамі"
}
@@ -10,14 +10,11 @@
"horizon.options.move_deadzone": "Мъртва зона на движението",
"horizon.options.look_deadzone": "Мъртва зона на погледа",
"horizon.options.button_glyphs": "Глифове на бутони",
"horizon.options.menu_navigation": "Навигация в менюто",
"horizon.options.on_screen_keyboard": "Екранна клавиатура",
"horizon.options.chat_delay": "Забавяне на чата",
"horizon.options.lightbar.health": "здраве",
"horizon.options.lightbar.dimension": "Измерение",
"horizon.options.glyphs.auto": "Авто",
"horizon.options.menu_focus": "Фокус",
"horizon.options.menu_pointer": "показалец",
"horizon.options.keyboard.auto": "Авто",
"horizon.options.keyboard.steam": "Пара",
"horizon.options.unavailable": "Тази функция все още не е налична в Horizon.",
@@ -0,0 +1,481 @@
{
"horizon.options.controller": "Reoler",
"horizon.options.three_d_skin_layers": "Gwiskadoù kroc'hen 3D",
"horizon.options.vibration": "Krenadenn",
"horizon.options.adaptive_triggers": "Gwaskerioù azasaus",
"horizon.options.lightbar": "Barrenn gouloù",
"horizon.options.look_sensitivity": "Sensibilitez ar sell",
"horizon.options.invert_x": "Distreiñ ar sell X",
"horizon.options.invert_y": "Distreiñ ar sell Y",
"horizon.options.sneak_button": "Bouton mont kuzh",
"horizon.options.move_deadzone": "Takad marv ar fiñval",
"horizon.options.look_deadzone": "Takad marv ar sell",
"horizon.options.button_glyphs": "Glifoù bouton",
"horizon.options.on_screen_keyboard": "Klavier war ar skramm",
"horizon.options.chat_delay": "Dale flapañ",
"horizon.options.lightbar.health": "Yec'hed",
"horizon.options.lightbar.dimension": "Ment",
"horizon.options.glyphs.auto": "Emgefreek",
"horizon.options.keyboard.auto": "Emgefreek",
"horizon.options.unavailable": "N'eo ket hegerz an elfenn-mañ c'hoazh e Horizon.",
"horizon.options.search": "Arventennoù klask...",
"horizon.pages.mods": "Modoù",
"horizon.pages.shader_packs": "Pakadoù skeudennoù",
"horizon.pages.shader_options": "Dibarzhioù skeudenniñ",
"horizon.pages.controller": "Arventennoù ar merour",
"horizon.pages.controller_binds": "Liammañ ar c'hontroller",
"horizon.pages.extras": "Horizon Ouzhpennoù",
"horizon.pages.settings": "Horizon Arventennoù",
"horizon.pages.agent": "Ajañs IA",
"horizon.split.leave": "Kuitaat",
"horizon.pages.split_settings": "Arventennoù ar skramm rannet",
"horizon.pages.split_profiles": "Piv a zo o c'hoari ?",
"horizon.pages.split_setup": "Arventennoù ar skramm rannet",
"horizon.pages.music": "Sonerezh",
"horizon.pages.music.overworld": "Bed dreist",
"horizon.pages.music.creative": "Krouiñ",
"horizon.pages.music.underwater": "Dindan an dour",
"horizon.pages.music.end": "Ar fin",
"horizon.pages.music.boss": "Patron",
"horizon.pages.music.credits": "Kredoù",
"horizon.pages.music.overworld_biomes": "Biomoù ar bed dreist",
"horizon.pages.music.nether_biomes": "Biomoù an izelvroioù",
"horizon.pages.music.end_biomes": "Biomoù dibenn",
"horizon.pages.music.other": "Sonerezh all",
"horizon.pages.music.empty": "Roudenn sonerezh ebet : n'eo ket karget sonioù ar c'hoari.",
"horizon.pages.music.tooltip": "An hentoù enkorfet, dre stad : c'hoari unan bremañ, pe diweredekaat unan anezho evit ma paouezfe ar c'hoari da zibab anezhañ.",
"horizon.pages.settings.tooltip": "arventennoù Horizon e-unan : grafikoù, kontrollerioù, ouzhpennadennoù ha kemmoù.",
"horizon.pages.graphics.tooltip": "Grafikoù, simpl : pep arventenn a lavar petra a goust.",
"horizon.pages.graphics_advanced.tooltip": "Pep arventenn grafek, Horizon en o zouez, gant ar pezh a goust pep hini.",
"horizon.pages.controller.tooltip": "Kontrollerien : o boutonoù, o bizhier, o c'hrennlavar hag o merdeiñ war al lañserioù.",
"horizon.pages.extras.tooltip": "Ouzhpennadennoù dibar, holl diweredekaet dre ziouer : chom a ra ar c'hoari vanilha betek ma vo lakaet unan war-sav.",
"horizon.pages.agent.tooltip": "Lakait hoc'h ajañs AI da c'hoari : ar c'hliented horizon-ctl ha MCP war an urzhiataer-mañ.",
"horizon.pages.split_profiles.tooltip": "Piv a c'hoari : ur profil miret er renkell c'hoari-mañ.",
"horizon.pages.split_setup.tooltip": "Ar prenestroù hag ar skrammoù, ha piv a c'hoari pelec'h.",
"horizon.pages.shader_packs.tooltip": "Pakadoù skeudennoù eus an teuliad pakadoù skeudennoù : Horizon o-unan hag ar re er stumm skeudennoù/. Unan a red d'an dro.",
"horizon.pages.mods.tooltip": "Modoù savet evit Horizon, eus an teuliad horizon-mods. Pep hini a ya en-dro en ur voest-traezh ha chom a ra diweredekaet betek ma vo lakaet war-sav.",
"horizon.pages.open.graphics": "Grafikoù...",
"horizon.pages.open.graphics_advanced": "Grafikoù araokaet...",
"horizon.pages.open.shader_packs": "Pakadoù skeudennoù...",
"horizon.pages.open.controller": "Arventennoù ar merour...",
"horizon.pages.open.split_setup": "Skramm rannet...",
"horizon.pages.open.extras": "Ouzhpenn...",
"horizon.pages.open.mods": "Modoù...",
"horizon.pages.open.agent": "Ajañs IA...",
"horizon.pages.open.horizon_graphics": "Horizon Grafikoù...",
"horizon.pages.open.controller_binds": "Stagañ a ra ar reoler...",
"horizon.pages.open.horizon_settings": "Horizon Arventennoù...",
"horizon.pages.open.music": "Sonerezh...",
"horizon.mods.empty": "Mod ebet : lakait restroù .hmod e teuliad horizon-mods",
"horizon.mods.stopped": " (Paouezet)",
"horizon.mods.not_built": " (N'eo ket bet savet)",
"horizon.mods.settings": "Arventennoù",
"horizon.mods.in_world": " (lezel ar bed da cheñch)",
"horizon.mods.open_folder": "Digeriñ an teuliad mod",
"horizon.shader_packs.empty": "Pakad skeudennoù ebet : lakait anezho e teuliad ar pakadoù skeudennoù",
"horizon.shader_options.open": "Dibarzhioù skeudenniñ...",
"horizon.shader_options.profile": "Profil",
"horizon.shader_options.loading": "O lenn dibaboù ar pakad skeudennoù...",
"horizon.agent.copy_details": "Eilañ munudoù ar c'hennask",
"horizon.multiplayer.join": "Kemer perzh",
"horizon.multiplayer.cancel_reconnecting": "Nullañ an adkennaskañ",
"horizon.multiplayer.join_friend": "Kemer perzh en ur mignon",
"horizon.multiplayer.invite_code": "Kod pediñ",
"horizon.multiplayer.relay": "Treuzkas (dibar)",
"Preset": "Raktreset",
"LOD Distance": "Pellder LOD",
"Far Detail": "Munudoù pell",
"Cave Culling": "Lazhañ ar c'hevoù",
"Entity Culling": "Distruj an entite",
"Chunk Updates": "Hizivadennoù tammoù",
"Upload Budget": "Budjed pellgargañ",
"Sky": "Oabl",
"Fog": "Brumenn",
"Weather": "Amzer",
"Animated Textures": "Teñzorioù bevaat",
"Present Mode": "Mod bremañ",
"Frame Pacing": "Pazadur ar framm",
"Worker Threads": "Neudennoù labourerien",
"Resolution Scale": "Skeul an diarunusted",
"Anti-aliasing": "Enep-aliañ",
"Upscaling": "Kreskiñ",
"Paper White": "Gwenn paper",
"Peak Brightness": "Sklêrijenn uhelañ",
"Black Level": "Live du",
"UI Brightness": "Sklêrijenn UI",
"Battery Mode": "Mod pilenn",
"Graphics API": "API grafikoù",
"Translucent Batching": "Lodennadur treuzwelus",
"Performance Overlay": "Dreistlakaat ar perzhded",
"Full Detail Distance": "Pellder munudoù klok",
"On": "War",
"Off": "Diweredekaet",
"Unlimited": "Didalvoud",
"Auto": "Emgefreek",
"Custom": "Personelaet",
"Performance": "Arvest",
"Balanced": "Kempouez",
"Quality": "Kalite",
"Native": "Natif",
"Full": "Leun",
"Reduced": "Digresket",
"Efficient": "Efedus",
"Precise": "Resis",
"Auto (VSync)": "Emgefre (VSync)",
"Mailbox": "Boest-postel",
"Immediate": "Diouzhtu",
"FPS and Graph": "FPS ha grafik",
"No cost": "Koust ebet",
"Low cost": "Koust izel",
"Medium cost": "Koust etre",
"High cost": "Koust uhel",
"Saves time": "Gounit a ra amzer",
"Graphics": "Grafikoù",
"Done": "Echu",
"This feature is not yet available in Horizon.": "N'eo ket hegerz an elfenn-mañ c'hoazh e Horizon.",
"Your display or system doesn't offer HDR output.": "Ho skramm pe ho reizhiad ne ginnig ket un disoc'h HDR.",
"Turn HDR on to use it.": "Lakait HDR war-sav evit e implijout.",
"Your GPU or graphics API doesn't support it.": "N'eo ket skoret gant ho GPU pe ho API grafikoù.",
"%s chunks": "%s tammoù",
"%s per frame": "%s dre framm",
"%s MiB/frame": "%s MiB/framm",
"%s KiB/frame": "%s KiB/framm",
"Auto (%s)": "Emgefre (%s)",
"Full-detail terrain range. LOD Distance can extend local worlds farther.": "Rannvro an douaroù leun a vunudoù. LOD Ar pellder a c'hell astenn ar bedoù lec'hel pelloc'h.",
"Coarse distant terrain in local worlds. Integrated GPUs stop at 128 chunks.": "Douaroù pell gros e bedoù lec'hel. Paouez a ra ar GPUoù kevredet da 128 tamm.",
"Reduced draws far terrain coarser: faster on this integrated GPU.": "Digreskiñ a ra an douaroù kalz grosoc'h : buanoc'h war ar GPU kevredet-mañ.",
"Reduced draws far terrain coarser, for GPUs it slows down.": "Ar reduset a denn an douaroù kalz grosoc'h, evit ar GPUoù e vez gorrekaet.",
"Field of view. Wider shows more terrain at once.": "Tachenn gwel. Ledanoc'h a ziskouez muioc'h a zouaroù war un dro.",
"Caps frames per second; a cap saves power and heat.": "Bep frammoù dre eilenn; ur c'hasketenn a espern energiezh ha tommder.",
"Waits for the display's refresh: no tearing, a little latency.": "Gortoz a ra freskaat ar skramm : tamm torr ebet, un tamm dale.",
"Size of menus and the HUD.": "Ment ar mennozhioù hag an HUD.",
"Sets the options below together. Changing one makes it Custom.": "Lakaat a ra an dibarzhioù amañ dindan asambles. Cheñch unan a ra anezhañ Personelaet.",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "Lava, tan ha gouloù an heol a lugern muioc'h eget ar gwenn. Ezhomm zo eus ur skramm HDR.",
"Your display's darkest black. 0 for OLED; LCDs glow a little.": "Du teñvalañ eo ho skramm. 0 evit OLED; LCDoù a lugern un tamm.",
"How bright highlights get. Auto: the peak your system reports.": "Pegen skedus e teu ar sklêrijennoù. Emgefre : ar pik a vez embannet gant ho reizhiad.",
"How bright white paper and menus look. Auto: your desktop's white.": "Pegen skedus e seblant ar paper gwenn hag ar mennozhioù. Emgefre : gwenn eo ho burev.",
"How bright the HUD and menus are, against paper white.": "Pegen skedus eo an HUD hag ar mennozhioù, a-enep gwenn ar paper.",
"How far mobs, crops and machines keep working. CPU time.": "Pegen pell e kendalc'h ar mobs, ar c'hoadoù hag ar mekanikoù da labourat. Amzer ar CPU.",
"How far away mobs and items are drawn, as a share of normal.": "Pegen pell e vez tennet ar mobs hag an traoù, evel ul lodenn eus ar pezh a vez graet normal.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Tremen a ra an douaroù kuzhet a-dreñv rannoù serr. Koustañ a ra kalz.",
"Hides entities behind opaque blocks.": "Kuzhat a ra an elfennoù a-dreñv bloc'hoù dister.",
"No entity occlusion culling.": "N'eus ket a zistruj entite.",
"CPU occlusion test runs this frame.": "Test stankañ ar CPU a gas ar framm-mañ da benn.",
"Occlusion checks run on a worker thread.": "Ar gwirioù stankañ a vez kaset da benn war un neudenn labourer.",
"Meshed sections uploaded per frame. Lower is smoother while loading.": "Rannoù rouedek pellgarget dre framm. Eeunoc'h eo an izelañ e-pad ar c'hargañ.",
"Soft shadows in corners. Costs chunk building time only.": "Skeudoù dous er c'hornioù. Koustañ a ra amzer sevel a-stroll hepken.",
"Smaller copies of block textures for distant terrain: less shimmer.": "Skouerennoù bihanoc'h eus teñzorioù bloc'hoù evit an tachennoù pell : nebeutoc'h a sked.",
"Blends grass and water colours across biomes when building chunks.": "Meskañ a ra livioù ar geot hag an dour dre ar biomoù pa vez savet tammoù.",
"Cloud layer: off, flat (fast) or 3D (fancy).": "Treuzfollenn ar c'hoad : diweredekaet, plaen (buan) pe 3D (fentus).",
"How far clouds reach, in chunks.": "Pegen pell e tizh ar c'houmoul, e tammoù.",
"Draws the sky, sun, moon and stars.": "Tresañ a ra an oabl, an heol, al loar hag ar stered.",
"Distance fog. Off shows the edge of the loaded world.": "Brumenn pellder. Diweredekaat a ziskouez ribl ar bed karget.",
"Draws rain and snow.": "Tennañ a ra glav hag erc'h.",
"How far rain and snow fall around you.": "Pegen pell e kouezh ar glav hag an erc'h en-dro deoc'h.",
"How many particles effects spawn.": "Peseurt efedoù rannigoù a vez ganet.",
"Round shadows under mobs and items.": "Skeudennoù ront dindan mobs hag elfennoù.",
"Leaves with see-through gaps. Off draws them solid.": "Delioù gant toulloù gwelus. Off a denn anezho start.",
"Sorts water and glass more exactly behind each other.": "Renkañ a ra an dour hag ar gwer an eil a-dreñv egile.",
"Which chunk rebuilds wait for the frame: none, nearby or yours.": "Peseurt rann a adsevel a c'hortoz ar framm : hini ebet, e-kichen pe ho hini.",
"Animates water, lava, fire and other moving textures.": "Bevaat a ra an dour, al lava, an tan hag an danvezioù fiñvus all.",
"World pixels per screen pixel. Below 100% is faster on the GPU.": "Pikseloù bed dre piksel skramm. Buanoc'h eo dindan 100% war ar GPU.",
"Smooths jagged edges. FXAA: softer. TAA: smoothest, moving things may trail.": "Eeunaat a ra ar c'hornioù ruz. FXAA: dousoc'h. TAA: an traoù eeunañ, o fiñval a c'hell heuliañ.",
"Renders the world smaller and rebuilds it with TAA. Faster, softer.": "Bihanaat a ra ar bed hag adsevel anezhañ gant TAA. Buanoc'h, dousoc'h.",
"HDR output on an HDR display: lava, fire and sunlight shine brighter.": "Disoc'h HDR war ur skramm HDR : lava, tan ha gouloù an heol a lugern muioc'h.",
"How frames reach the screen while VSync is off.": "Penaos e tistro ar frammoù war ar skramm pa vez diweredekaet VSync.",
"Efficient sleeps until the next frame; Precise spins, like vanilla.": "Kousket a ra efedus betek ar framm da heul; Troioù resis, evel ar vanilha.",
"Fills the screen (borderless).": "Leuniañ ar skramm (hep bevennoù).",
"Brightens dark places.": "Sklaeraat a ra al lec'hioù teñval.",
"Lowers the frame rate when minimised, or idle too.": "Digreskiñ a ra ar feur framm pa vez bihanaet, pe pa vez didalvoud ivez.",
"Sways the view while walking.": "Heñchañ a ra ar gwel e-pad ma kerzher.",
"Fades newly loaded chunks in.": "Diverkañ a ra an tammoù nevez karget.",
"Darkens the screen's edges.": "Teñvalaat a ra ribloù ar skramm.",
"Shows when the world saves.": "Diskouez a ra pa savete ar bed.",
"Shows attack cooldown.": "Diskouez a ra an amzer yenaat evit an taolioù.",
"60 FPS cap (30 in menus or unfocused), half the workers: less power.": "60 FPS (30 er mennozhioù pe diskoulmet), an hanter eus al labourerien : nebeutoc'h a c'halloud.",
"Threads that generate, light and mesh chunks. Next start.": "Neudennoù a grou, a luc'h hag a ro troc'hadoù. Kregiñ war-lerc'h.",
"Vulkan, then DirectX 12 (Windows) or OpenGL (Linux) where it fails. Next start.": "Vulkan, neuze DirectX 12 (Windows) pe OpenGL (Linux) ma c'hwit. Kregiñ war-lerc'h.",
"Water and glass in one buffer: fewer draw calls, a bit more VRAM. Next start.": "Dour ha gwer en ur buffer : nebeutoc'h a c'halvoù tennañ, un tamm muioc'h a VRAM. Kregiñ war-lerc'h.",
"Mesh data sent to the GPU per frame. Lower is smoother while loading.": "Roadennoù rouedad kaset d'ar GPU dre framm. Eeunoc'h eo an izelañ e-pad ar c'hargañ.",
"Frame rate and frame times over the game, with a graph.": "Feur ar framm hag an amzer framm e-pad ar c'hoari, gant ur grafik.",
"horizon.multiplayer.finding_friend": "O kavout bed ho mignon...",
"horizon.multiplayer.not_signed_in": "E mod enlinenn emañ an dafariad-mañ. En em lugañ gant ho kont Microsoft el lañser Horizon evit mont e-barzh.",
"horizon.multiplayer.invalid_address": "Chomlec'h servijer direizh : %s",
"horizon.multiplayer.connected_friend_direct": "Kevreet war-eeun ouzh ho mignon...",
"horizon.multiplayer.connected_friend_relay": "Kevreet ouzh ho mignon dre ar c'heflusker...",
"General": "Hollek",
"Advanced": "Araokaet",
"Undo": "Dizober",
"Search options": "Dibarzhioù klask",
"Shortcut": "Berradennoù",
"Distance": "Pellder",
"Display": "Diskouez",
"Interface": "Etrefas",
"Sky and weather": "Oabl hag amzer",
"Lighting and entities": "Gouloù hag ensavadurioù",
"Textures": "Teñzorioù",
"Image": "Skeudenn",
"Chunk loading": "Kargañ tammoù",
"Culling": "Lazhañ",
"Power": "Galloud",
"Frames": "Frammoù",
"Overlay": "Dreistlakaat",
"Output": "Disoc'h",
"Brightness": "Sklêrijenn",
"How far you see, the window and the HUD.": "Pegen pell e weler, ar prenestr hag an HUD.",
"The sky and weather, lighting, textures and the image.": "An oabl hag an amzer, ar gouloù, an teñzorioù hag ar skeudenn.",
"Chunk loading, culling and power use.": "Kargañ troc'hadoù, distrujañ hag implij ar galloud.",
"How frames reach the screen, and the performance overlay.": "Penaos e tistro ar frammoù d'ar skramm, hag an dreuzfollenn perzhded.",
"Brighter-than-white output on an HDR display.": "Disoc'h sklaeroc'h eget gwenn war ur skramm HDR.",
"Results for \"%s\"": "Disoc'hoù evit \"%s\"",
"No settings match.": "Arventennoù ebet a glot.",
"horizon.title.not_affiliated": "N'eo ket stag ouzh Mojang pe Microsoft",
"horizon.extras.header.lighting": "Gouloù",
"horizon.extras.header.third_person": "Trede gour",
"horizon.extras.header.particles": "Rannigoù",
"horizon.extras.header.inventory": "Enrolladenn",
"horizon.extras.header.chat": "Flapañ",
"horizon.extras.header.multiplayer": "Liesc'hoarier",
"horizon.extras.header.sound": "Son",
"horizon.extras.header.menus": "Mennozhioù",
"horizon.extras.dynamic_lights": "Gouloù dinamek",
"horizon.extras.own_name_tag": "Tikedenn anv e-unan",
"horizon.extras.hide_block_particles": "Kuzhat rannigoù bloc'h-torr",
"horizon.extras.auto_reconnect": "Adkevreañ emgefre",
"horizon.extras.ui_sound_reserve": "Mirout ar chadennoù evit sonioù an UI",
"horizon.extras.music_page": "Pajenn sonerezh",
"horizon.extras.mouse_tweaks": "Reolennoù kemmañ al logodenn",
"horizon.extras.mouse_tweaks_wheel": "Ar rod riklañ a zilec'h elfennoù",
"horizon.extras.sort": "Boutonoù renkañ",
"horizon.extras.sort_key": "Alc'hwez renkañ",
"horizon.extras.no_ai": "AI ebet",
"horizon.extras.agent_control": "Kontroll an ajañs",
"horizon.extras.discord_presence": "Discord Bezañs pinvidik",
"horizon.extras.splitscreen": "Skramm rannet",
"horizon.extras.chat_animation": "Flapañ eeun",
"horizon.extras.chat_indicator": "Merk kemennadenn",
"horizon.extras.wheel_cycles_settings": "Kemmañ a ra arventennoù ar rouedad riklañ",
"horizon.extras.pack_browser": "Merdeer pakadoù arc'hwelioù",
"horizon.extras.fullbright.tooltip": "Diskouez bloc'hoù hag elfennoù e-pad ar gouloù leun, zoken e lec'hioù teñval.",
"horizon.extras.dynamic_lights.tooltip": "Torchoù ha fontoù gouloù all dalc'het, taolet pe en tan a sklêrijenn ar bloc'hoù tro-dro dezho.",
"horizon.extras.own_name_tag.tooltip": "Diskouez a ra hoc'h anv a-us d'ho penn en trede gour, evel ma vez gwelet gant ar c'hoarierien all. Kuzhet pa vez kuzhet anvioù ho skipailh gant an dafariad pe pa vezit diwelus.",
"horizon.extras.hide_block_particles.tooltip": "Kuzhat a ra ar rannigoù lastez a darzh diwar ur bloc'h torret hag a zismantr diwar an hini a vez tennet ganeoc'h. Chom a ra rannigoù all.",
"horizon.extras.auto_reconnect.tooltip": "Adkemer perzh en ur servijer goude ur c'hennask kollet pe un adloc'hañ, biskoazh goude ur c'hrogad pe ur bann.",
"horizon.extras.ui_sound_reserve.tooltip": "Ar sonioù er bed a lez 16 eus ar 247 chadenn son digoust, setu ne c'hell ket ur bern anezho (ur splujadenn vras) sioulaat ar c'hlikoù war al lañser.",
"horizon.extras.music_page.tooltip": "Music & Sounds a gav ur bajenn Sonerezh... : seniñ un ton enkorfet bremañ, pe diweredekaat unan evit ma paouezfe ar c'hoari da zibab anezhañ. Ne cheñch netra betek ma vo lazhet un hent.",
"horizon.extras.mouse_tweaks.tooltip": "An riklañ a-zehou a laka unan dre lec'h, an riklañ a-gleiz a zastum an elfenn dalc'het, an riklañ Shift + an riklañ a-gleiz a zilec'h pep rann.",
"horizon.extras.mouse_tweaks_wheel.tooltip": "Down a gas un elfenn eus ar rann d'an tu all, up a zegas unan en-dro.",
"horizon.extras.sort.tooltip": "Urzhiañ a ra an enrolladennoù, ar c'hofoù, ar barilhoù, ar c'hofoù ender hag ar boestoù shulker en urzh ivinelloù krouiñ, o kendeuziñ ar bernoù.",
"horizon.extras.sort_key.tooltip": "Urzhiañ a ra al lodenn dindan ar merdeer. Klikañ, ha pouezañ war un alc'hwez ; Tec'hout a zistag anezhañ.",
"horizon.extras.no_ai.tooltip": "Kuzhat ha diweredekaat pep elfenn AI : kontroll an ajañs, horizon-ctl hag ar servijerioù MCP. Alc'hwez al lañser ivez.",
"horizon.extras.agent_control.tooltip": "Hoc'h ajañs IA a c'hall c'hoari ar c'hoari-mañ dre horizon-ctl pe dre ur c'hlient MCP, war an urzhiataer-mañ hepken.",
"horizon.extras.discord_presence.tooltip": "Diskouez a ra C'hoari Horizon war ho profil Discord e-keit ma vez Discord o vont en-dro : mennozhioù, c'hoarier unvan pe liesc'hoarier, ar vent hag an amzer c'hoariet. Biskoazh anv ur bed pe chomlec'h ur servijer.",
"Calibrate...": "Kalibrañ...",
"Calibrate Anyway...": "Kalibrañ koulskoude...",
"Use Automatic Setup": "Implijout arventennoù emgefre",
"Shortcut: %s": "Berradennoù : %s",
"Next": "Da heul",
"HDR Calibration %s/%s: %s": "Kalibradur HDR %s/%s : %s",
"Sets the three below full screen, one test picture at a time.": "Lakaat a ra an tri skramm leun a-is, ur skeudenn amprouiñ bep tro.",
"A key that opens this screen from the game. Not bound at first.": "Un alc'hwez a zigor ar skramm-mañ eus ar c'hoari. N'eo ket stag da gentañ.",
"Your display didn't say how bright it gets: calibrate to set it up.": "N'eo ket bet embannet gant ho skramm pegen sklaer e teu da vezañ : kalibret evit e lakaat war-sav.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Kefluniet dija evit ho skramm. Kalibret nemet ma seblant HDR bezañ fazius.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Lakaat a ra ar sked uhelañ hag ar gwenn paper en-dro e kefluniadur ho skramm.",
"Set up for your display. Leave on Auto unless it looks wrong.": "Kefluniañ evit ho skramm. Lezel war Auto nemet pa seblant fall.",
"Lower it until the logo just disappears into the black.": "Izelaat anezhañ betek ma vo kollet al logo en du.",
"Raise it until the logo just disappears into the bright square.": "Sevel anezhañ betek ma vo kollet al logo er c'harrez skedus.",
"Make the card a white that's comfortable to look at for a long time, in the light you play in.": "Lakaat ar gartenn da vezañ gwenn a vo aes da sellet outi e-pad pell, er gouloù ma c'hoarier.",
"horizon.splitscreen.profiles.forget": "Ankounac'haat",
"horizon.splitscreen.profiles.create": "Krouiñ",
"horizon.splitscreen.profiles.you": " - c'hwi",
"horizon.splitscreen.profiles.forget_named": "Ankouaat %s",
"horizon.splitscreen.profiles.previous": "Kent",
"horizon.splitscreen.profiles.next": "Da heul",
"horizon.splitscreen.profiles.new_button": "Profil nevez...",
"horizon.splitscreen.profiles.forget_title": "Ankounac'haat ho profil ?",
"horizon.splitscreen.profiles.forget_leaves": "Kuitaat a ra roll an ensavadur-mañ.",
"horizon.splitscreen.profiles.forget_kept": "Miret e vez an araokadennoù hag an arventennoù er bed.",
"horizon.splitscreen.profiles.new_title": "Profil nevez",
"horizon.splitscreen.profiles.name_hint": "Anv (3 da 16 lizherenn, sifroù, _)",
"horizon.splitscreen.profiles.switch_title": "Cheñch profil",
"horizon.splitscreen.profiles.offline_note": "Ar profiloù ezlinenn a ya d'ober an ensavadur-mañ",
"horizon.splitscreen.profiles.move": "Dilec'hiañ el lec'h ma c'hoarier",
"horizon.splitscreen.profiles.move_place": "Dilec'hiañ el lec'h ma c'hoarier : %s",
"horizon.friends.title": "C'hoari gant mignoned",
"horizon.friends.hint": "Pegañ a ra ar vignoned ar c'hod goude bouton Mignoned ar skramm titl, e Kevreadur war-eeun, pe war bajenn C'hoari al lañser.",
"horizon.friends.copied": "Eilet",
"horizon.friends.copy_code": "Eilañ ar c'hod",
"horizon.friends.stop_sharing": "Paouez da rannañ",
"horizon.friends.none_yet": "Mignon ebet amañ c'hoazh.",
"horizon.friends.here_now": "Setu bremañ : %s",
"horizon.friends.share_intro": "Rannañ ar bed-mañ a-dreñv ur c'hod pediñ : digeriñ a ra d'ar LAN, ha mignoned war internet a gemer perzh gant ar c'hod. N'eus servijer ebet ac'hanomp e-barzh.",
"horizon.friends.start_sharing": "Kregiñ da rannañ",
"horizon.friends.troubleshooting": "Diskoulmañ kudennoù...",
"horizon.friends.stopped": "Paouezet da rannañ ar c'hod pediñ.",
"horizon.friends.short_code_changed": "Unan bennak a gendalc'has da glask ur c'hod berr fazius, setu m'eo cheñchet.",
"horizon.friends.wrong_code": "Unan bennak en deus klasket ur c'hod fall.",
"horizon.friends.code_ready": "Prest eo ho kod pediñ : Menü ar c'hoari, C'hoari gant mignoned. Eilañ anezhañ ha kas anezhañ d'ho mignoned.",
"horizon.friends.share_failed": "N'hall ket rannañ ar bed : %s",
"horizon.friends.finding_route": "Kavout penaos e c'hall mignoned tizhout ac'hanoc'h...",
"horizon.osk.shift": "Dilec'hiañ",
"horizon.osk.space": "Egor",
"horizon.osk.delete": "Dilemel",
"horizon.osk.type": "Seurt",
"horizon.osk.symbols": "Arouezioù",
"horizon.osk.close": "Serriñ",
"horizon.extras.chat_animation.tooltip": "Ur linenn flapañ nevez a zeu eus an tu kleiz e-keit ma vez o vont da get, e-lec'h dont war wel a-bezh.",
"horizon.extras.chat_indicator.tooltip": "Ar varrenn livet a-gleiz d'ul linenn flapañ.",
"horizon.extras.wheel_cycles_settings.tooltip": "Ar rod riklañ war ur bouton en arventennoù a gemm e dalvoudegezh, evel e vanilha. Diweredekaet, ne ra ar rod nemet riklañ ar roll.",
"horizon.extras.pack_browser.tooltip": "Dibab pakadoù arc'hwelioù a resev ur bouton Ouzhpennañ a glask Modrinth hag a staliañ ur pakad en ho teuliad pakadoù. Diweredekaet, ne vez ket darempredet ar c'hoari gant Modrinth.",
"horizon.extras.splitscreen.tooltip": "En ur bed e c'hall ur c'hontroller all mont e-barzh evel c'hoarier nevez : pouezit war A pa c'houlenn, ha dibabit ur profil ha pelec'h c'hoari.",
"horizon.splitscreen.setup.monitor": "Heuliañ %s",
"horizon.splitscreen.setup.monitor_unknown": "ur skramm",
"horizon.splitscreen.setup.window": "Prenestr %s",
"horizon.splitscreen.setup.new_window": "Prenestr nevez",
"horizon.splitscreen.setup.fill_monitor": "Leuniañ %s",
"horizon.splitscreen.setup.done": "Echu",
"horizon.splitscreen.setup.on": "War",
"horizon.splitscreen.setup.off": "Diweredekaet",
"horizon.splitscreen.setup.player": "C'hoarier %s",
"horizon.splitscreen.setup.revert": "Distreiñ",
"horizon.splitscreen.setup.apply": "Goulenn",
"horizon.splitscreen.setup.warning.empty_main": "Den ne c'hoari e Prenestr 1 : kuzhat a ra",
"horizon.splitscreen.setup.warning.empty_close": "Den ne c'hoari e Prenestr %s : serriñ a ra war Apply",
"horizon.splitscreen.setup.warning.empty_new": "Den ne c'hoari e Prenestr %s : ne vo ket digoret",
"horizon.splitscreen.setup.warning.on_main": "Prenestr %s a leun skramm ar prenestr 1 : e c'hoarierien a c'hoari e prenestr 1",
"horizon.splitscreen.setup.warning.unplugged": "N'eo ket kevreet skramm ar Prenestr %s : c'hoari a ra e c'hoarierien e Prenestr 1",
"horizon.splitscreen.setup.status.off": "Diweredekaet eo ar skramm rannet.",
"horizon.splitscreen.setup.status.enable_hint": "Lakait anezhañ war enaou, ha pouezit war ur bouton war ur c'hontroller all en ur bed.",
"horizon.splitscreen.setup.status.join_hint": "Pouezit war A evit mont e-barzh ha dibab ur profil.",
"horizon.splitscreen.setup.status.idle": "Skramm rannet didalvoud",
"horizon.splitscreen.setup.status.alone": "C'hoarier lec'hel all ebet.",
"horizon.splitscreen.setup.status.solo_controls": "Ar c'hontrolloù solo hag an evezhiadennoù kontroller n'int ket cheñchet.",
"horizon.splitscreen.setup.window_empty": "Prenestr %s: goullo",
"horizon.splitscreen.setup.note.select": "Klikit war ur c'hoarier, pe riklañ anezhañ betek ur prenestr pe ur skramm.",
"horizon.splitscreen.setup.note.apply": "Arverañ evit c'hoari evel-se, Distreiñ evit distreiñ.",
"horizon.splitscreen.setup.note.place": "Pelec'h e tlefe %s c'hoari ?",
"horizon.splitscreen.setup.this_player": "ar c'hoarier-mañ",
"horizon.splitscreen.setup.switch_disabled": "Lazhañ a ra ar skramm rannet ur wech ma'z a ar re all kuit (o lañser paouez).",
"horizon.splitscreen.setup.window_place": "Prenestr %s (%s eus %s)",
"horizon.splitscreen.setup.names": "Anvioù",
"horizon.splitscreen.setup.identify": "Anavezout",
"horizon.splitscreen.setup.layout.two": "2 rannañ : %s",
"horizon.splitscreen.setup.layout.three": "3 rannañ : %s",
"horizon.splitscreen.setup.layout.two.0": "Emgefreek",
"horizon.splitscreen.setup.layout.two.1": "Kichen-ha-kichen",
"horizon.splitscreen.setup.layout.two.2": "Krec'h ha traoñ",
"horizon.splitscreen.setup.layout.three.0": "Unan ledan war-c'horre",
"horizon.splitscreen.setup.layout.three.1": "Unan uhel a-gleiz",
"horizon.splitscreen.setup.layout.three.2": "Kichen-ha-kichen",
"horizon.splitscreen.setup.help.player": "Klikañ pe riklañ %s evit o dilec'hiañ.",
"horizon.splitscreen.setup.help.layout_auto": "%s (kichen-ha-kichen pa vez ledan ar prenestr). Pouezit evit cheñch.",
"horizon.splitscreen.setup.help.layout": "%s. Pouezit evit cheñch.",
"horizon.splitscreen.setup.help.switch": "Skramm rannet evit an urzhiataer-mañ, war pe get.",
"horizon.splitscreen.setup.help.identify": "Gwel pep c'hoarier a ziskouez e peseurt prenestr emañ.",
"horizon.splitscreen.setup.help.names": "Anv pep c'hoarier e korn o gwel.",
"horizon.splitscreen.setup.help.revert": "Dilemel ar c'hemmoù n'int ket bet arveret c'hoazh.",
"horizon.splitscreen.setup.help.apply": "C'hoari evel ma tiskouez ar gartenn.",
"horizon.splitscreen.setup.help.unchanged": "N'eus netra cheñchet c'hoazh.",
"horizon.splitscreen.setup.give_up_pad": "Pad dilezel",
"horizon.splitscreen.prompt.controller_connected": "Reoler kevreet",
"horizon.splitscreen.prompt.controller_disconnected": "Diskennet eo ar c'hontroller",
"horizon.splitscreen.prompt.controller_disconnected.hint": "Adkevreit anezhañ, pe pouezit war ur bouton war unan all",
"horizon.splitscreen.prompt.join.one": "%s a fell dezhañ mont e-barzh",
"horizon.splitscreen.prompt.join.many": "%s kontrollerien a fell dezho kemer perzh",
"horizon.splitscreen.prompt.join.yes": "Kemer perzh",
"horizon.splitscreen.prompt.join.as_p1": "C'hoari evel P1",
"horizon.splitscreen.prompt.join.no": "Nann",
"horizon.splitscreen.prompt.lost": "%s: distaget eo ar c'hontroller",
"horizon.splitscreen.prompt.lost.use_another": "Implijout unan all",
"horizon.splitscreen.prompt.lost.leave": "Kuitaat",
"horizon.splitscreen.setup.help.give_up_pad": "Paouez a ra ho kontroller da gas ac'hanoc'h ha kinniget e vez deoc'h mont e-barzh (A).",
"horizon.splitscreen.setup.note.held_at": "%s da %s: A a laka anezhañ da gouezhañ amañ, B a laka anezhañ en-dro.",
"horizon.splitscreen.setup.note.held": "%s dastumet : dibaboù kleiz/dehou e-lec'h ma kouezh A, B a laka anezhañ en-dro.",
"horizon.splitscreen.online_only": "E mod enlinenn emañ ar servijer-mañ : n'hall ket ar profiloù ezlinenn mont e-barzh, pep c'hoarier en deus ezhomm eus e gont Minecraft.",
"horizon.splitscreen.own_account": "Ezhomm en deus ar servijer-mañ eus ur gont Minecraft evit pep c'hoarier",
"horizon.splitscreen.local_only": "Ar c'hoarierien all ne c'hallont mont e-barzh ar servijer-mañ nemet gant o c'hont Minecraft o-unan : ar profiloù ezlinenn a c'hoari en ho bed, war LAN pe war ur servijer war ho rouedad lec'hel.",
"Hides models behind opaque blocks. Async checks off-thread; entities tick.": "Kuzhat a ra ar patromoù a-dreñv bloc'hoù dister. Gwiriañ a ra an async e-maez an neudennad; tik an entiteoù.",
"Block Entities": "Entiteoù stankañ",
"Shulker Boxes": "Boestoù Shulker",
"Reuses settled boxes; lid movement stays animated.": "Adimplij a ra ar voestoù staliet; fiñv ar golo a chom bev.",
"Signs": "Sinoù",
"Reuses sign text until text, light, or font changes.": "Adlakaat a ra testenn ar sin betek ma cheñcho an destenn, ar gouloù pe ar font.",
"Decorated Pots": "Podoù kinklet",
"Reuses still pots; break wobble and sherd changes redraw.": "Adimplij a ra podoù c'hoazh; terriñ ar c'hrennlazh hag ar c'hemmoù strinkañ adtresañ.",
"Bells": "Klochennoù",
"Reuses still bells; ringing stays animated.": "Adimplij a ra kloc'hioù c'hoazh; chom a ra bevaat ar sonerezh.",
"Chests": "Kofoù",
"Reuses closed chests; moving lids stay animated.": "Adimplij ar c'houfr serr; ar goloioù o fiñval a chom bev.",
"Banners": "Bannieloù",
"Reuses poles; flags and pattern layers still update.": "Adimplij ar palioù; hizivaet c'hoazh ar bannieloù hag ar gwiskadoù patromoù.",
"Copper Golem Statues": "Delwennoù golem kouevr",
"Reuses statue geometry until its block state changes.": "Adlakaat a ra geometriezh an delwenn betek ma cheñcho he stad bloc'h.",
"Leaf Culling": "Lazhañ delioù",
"Every leaf face. Vanilla's fancy leaves.": "Pep delienn dremm. Delioù fentus ar vanilha.",
"Distant Plant Dither": "Tremen a ra ar plant pell",
"Keeps fine plants in high-res packs from fading far away.": "Mirout a ra ar plant fin e pakadoù uhel o resolution da vont da get pell.",
"Screenshot Quality": "Kalite ar skramm",
"F2 screenshots at this many times the game's resolution. Higher takes longer.": "Skeudennoù skramm F2 gant kement-se meur a wech diarunusted ar c'hoari. Uheloc'h a bad hiroc'h.",
"Frame Generation": "Krouiñ frammoù",
"Shows a made-up frame between each two real ones: smoother, a little more lag.": "Diskouez a ra ur framm savet etre pep daou hini gwirion : eeunoc'h, un tamm muioc'h a zilez.",
"Shadow Cache": "Grubuilh skeud",
"Reuses the pack shadow map when sun, view and world match. Off by default.": "Adimplij ar gartenn skeud pakad pa glot an heol, ar gwel hag ar bed. Diweredekaet dre ziouer.",
"Find BlueMap": "Kavout BlueMap",
"On servers, finds their BlueMap web map for far terrain. Next join.": "War ar servijerioù e kaver o gartenn web BlueMap evit an tachennoù pell. Kemer perzh da heul.",
"BlueMap Radius": "Radius ar gartenn glas",
"How far around you BlueMap tiles are downloaded. Next join.": "Pegen pell tro-dro deoc'h e vez pellgarget teulioù BlueMap. Kemer perzh da heul.",
"Synchronous": "Sinkron",
"Asynchronous": "Azinkron",
"Solid Aggressive": "Tagus kreñv",
"Hollow": "Toull",
"Leaves fully inside a canopy draw solid: fewer see-through layers.": "Delioù penn-da-benn e-barzh ur c'hoad a denn solut : nebeutoc'h a gwiskadoù treuzwelus.",
"Leaves with one open side draw solid too: faster, chunkier edges.": "Ar delioù gant un tu digor a denn ivez : bordoù buanoc'h ha stankoc'h.",
"Drops faces between leaves: faster, but gaps look through.": "Lakaat a ra fazioù etre ar delioù : buanoc'h, met toulloù a sell drezo.",
"Hotbar: Previous Slot": "Barrenn-dre-lien : Lec'h a-raok",
"Hotbar: Next Slot": "Barrenn-dre-lien : Lec'h war-lerc'h",
"horizon.agent.connected.one": "Kevreet : 1 kevreadenn ajañs",
"horizon.agent.connected.many": "Kevreet : %s kevreadennoù an ajañs",
"horizon.agent.connected.one_ago": "Kevreet : 1 kevreadenn ajañs, ur gemennadenn ziwezhañ %s s zo",
"horizon.agent.connected.many_ago": "Kevreet : %s kevreadennoù an ajañs, ar gemennadenn ziwezhañ %s s zo",
"horizon.agent.waiting": "O c'hortoz un ajañs.",
"horizon.agent.off": "Diweredekaet : n'hall ket an ajañsoù tizhout ar c'hoari-mañ.",
"horizon.agent.session_opened": "Digoret evit ar prantad-mañ gant ar programm en deus kroget ar c'hoari.",
"horizon.agent.socket": "Socket: %s (eilañ ar munudoù evit e hent klok)",
"horizon.agent.local_only": "Lec'hel hepken : an ajañsoù war an urzhiataer-mañ, biskoazh ar rouedad.",
"Auto (%s nits)": "Emgefre (%s nits)",
"Hides models behind opaque blocks. Entities update. Off with custom packs.": "Kuzhat a ra ar patromoù a-dreñv bloc'hoù dister. Hizivaat an ensavadurioù. Dilezet gant pakadoù personelaet.",
"horizon.extras.repro_keys": "Berradennoù enrollañ",
"horizon.extras.repro_keys.tooltip": "F3+R a grog pe a paouez gant un enrolladenn advouezhiañ ha F3+E a skriv e strollad. Diweredekaet dre ziouer : un enrolladenn a eil ar bed.",
"horizon.agent.record_session": "Kregiñ gant an enrollañ",
"horizon.agent.record_stop": "Paouez gant an enrollañ",
"horizon.agent.export_repro": "Ezporzhiañ ar pakad advouezhiañ",
"horizon.repro.started": "Kroget eo an enrollañ ; F3+R a harz anezhañ hag a skriv ar pakad.",
"horizon.repro.stopped": "Paouezet gant an enrollañ : %s",
"horizon.repro.bundle": "Pakad advouezhiañ : %s",
"horizon.repro.no_recording": "N'eus enrolladenn ebet da ezporzhiañ c'hoazh.",
"horizon.repro.replay_started": "Adc'hoari kroget : %s",
"horizon.pack.addInfo": "Ouzhpennañ ur pakad arc'hwelioù adalek Modrinth",
"horizon.pack.addWorking": "Goulenn Modrinth...",
"horizon.pack.addNone": "N'eus bet kavet pakad arc'hwel ebet",
"horizon.pack.addTitle": "Ouzhpennañ ur pakad arc'hwelioù",
"horizon.pack.install": "Staliañ",
"horizon.pack.installing": "O staliañ",
"horizon.pack.installed": "Staliet",
"horizon.pack.search": "Klask",
"horizon.pack.searchHint": "Klask Modrinth...",
"horizon.pack.filters": "Siloù",
"horizon.pack.filter.realistic": "Gwirvoudel",
"horizon.pack.filter.simplistic": "Eeun",
"horizon.pack.filter.vanillaLike": "Heñvel ouzh ar vanilha",
"horizon.pack.addFailed": "N'haller ket ouzhpennañ ar pakad : %s",
"horizon.extras.header.cosmetics": "Kosmetikoù",
"horizon.extras.cosmetics_mine": "Ma kosmetikoù",
"horizon.extras.cosmetics_mine.tooltip": "Kuzhet : pep c'hoarier Horizon a wel ac'hanoc'h hep ho kosmetikoù, c'hwi en o zouez. Ar memes treuzfurmadur hag armel al lañser.",
"horizon.extras.cosmetics_others": "Kosmetikoù ar c'hoarierien all",
"horizon.extras.cosmetics_others.tooltip": "Ar pezh a vez gwisket gant ar c'hoarierien Horizon all : pep tra, o doareoù tikedenn anv hepken, pe netra. Kuzhet, ar c'hoari ne c'houlenn ket biskoazh petra a wiskont.",
"horizon.extras.cosmetics_auras": "Auraoù ha roudoù tud all",
"horizon.extras.cosmetics_auras.tooltip": "Auraoù ha roudoù a vez gwisket gant c'hoarierien all. Diweredekaet dre ziouer.",
"horizon.extras.cosmetics.shown": "Diskouezet",
"horizon.extras.cosmetics.hidden": "Kuzhet",
"horizon.extras.cosmetics.name_tags": "Tikedennoù anvioù hepken"
}
@@ -0,0 +1,423 @@
{
"horizon.options.controller": "Kontroler",
"horizon.options.vibration": "Vibracije",
"horizon.options.adaptive_triggers": "Adaptivni okidači",
"horizon.options.look_sensitivity": "Osjetljivost pogleda",
"horizon.options.invert_x": "Obrnuti pogled po X",
"horizon.options.invert_y": "Obrnuti pogled po Y",
"horizon.options.sneak_button": "Dugme za šunjanje",
"horizon.options.move_deadzone": "Mrtva zona kretanja",
"horizon.options.look_deadzone": "Mrtva zona pogleda",
"horizon.options.button_glyphs": "Dugmad Glyphs",
"horizon.options.on_screen_keyboard": "Tastatura na ekranu",
"horizon.options.lightbar.health": "Zdravlje",
"horizon.options.lightbar.dimension": "Dimenzija",
"horizon.options.unavailable": "Ova funkcija još nije dostupna u Horizon.",
"horizon.options.search": "Postavke pretrage...",
"horizon.pages.controller": "Postavke kontrolera",
"horizon.pages.controller_binds": "Kontroler se vezuje",
"horizon.pages.extras": "Horizon Dodaci",
"horizon.pages.settings": "Horizon Postavke",
"horizon.split.leave": "Odlazi",
"horizon.pages.split_profiles": "ko igra?",
"horizon.pages.music": "Muzika",
"horizon.pages.music.menu": "Meni",
"horizon.pages.music.creative": "Kreativno",
"horizon.pages.music.underwater": "Pod vodom",
"horizon.pages.music.end": "Kraj",
"horizon.pages.music.boss": "Šefe",
"horizon.pages.music.credits": "Zasluge",
"horizon.pages.music.end_biomes": "Kraj bioma",
"horizon.pages.music.other": "Ostala muzika",
"horizon.pages.music.empty": "Nema muzičkih numera: zvuci igre se ne učitavaju.",
"horizon.pages.music.tooltip": "Ugrađene numere, po situaciji: igrajte jednu sada ili isključite bilo koju da igra prestane da je bira.",
"horizon.pages.settings.tooltip": "Horizon vlastite postavke: grafika, kontroleri, dodaci i modovi.",
"horizon.pages.graphics.tooltip": "Grafika, jednostavno: svaka postavka govori koliko košta.",
"horizon.pages.graphics_advanced.tooltip": "Svaka grafička postavka, Horizon je uključena, sa onim što svaka košta.",
"horizon.pages.controller.tooltip": "Kontroleri: njihova dugmad, štapići, vibracije i navigacija u meniju.",
"horizon.pages.extras.tooltip": "Opcioni dodaci, svi isključeni prema zadanim postavkama: igra ostaje vanila dok je ne uključite.",
"horizon.pages.agent.tooltip": "Neka vaš AI agent igra: horizon-ctl i MCP klijente na ovom računaru.",
"horizon.pages.split_profiles.tooltip": "Ko igra: profil koji se čuva u ovoj fascikli igre.",
"horizon.pages.split_setup.tooltip": "Prozori i monitori, i ko gde igra.",
"horizon.pages.shader_packs.tooltip": "Paketi shadera iz foldera shaderpacks: Horizon vlastiti i oni u shaders/ formatu. Jedan po jedan trči.",
"horizon.pages.mods.tooltip": "Modovi napravljeni za Horizon, iz foldera horizon-mods. Svaki radi u sandboxu i ostaje isključen dok ga ne uključite.",
"horizon.pages.open.graphics": "Grafika...",
"horizon.pages.open.graphics_advanced": "Napredna grafika...",
"horizon.pages.open.shader_packs": "Shader paketi...",
"horizon.pages.open.controller": "Postavke kontrolera...",
"horizon.pages.open.extras": "Dodaci...",
"horizon.pages.open.mods": "Modovi...",
"horizon.pages.open.agent": "AI agent...",
"horizon.pages.open.horizon_graphics": "Horizon Grafika...",
"horizon.pages.open.controller_binds": "Kontroler vezuje...",
"horizon.pages.open.horizon_settings": "Horizon Postavke...",
"horizon.pages.open.music": "muzika...",
"horizon.mods.empty": "Bez modova: stavite .hmod fajlove u folder horizon-mods",
"horizon.mods.stopped": " (zaustavljeno)",
"horizon.mods.not_built": " (nije izgrađeno)",
"horizon.mods.settings": "Postavke",
"horizon.mods.in_world": " (ostavi svijet da se promijeni)",
"horizon.mods.open_folder": "Otvorite Mod folder",
"horizon.shader_packs.empty": "Nema paketa shadera: stavite ih u mapu shaderpacks",
"horizon.shader_options.open": "Opcije shadera...",
"horizon.shader_options.profile": "Profil",
"horizon.shader_options.loading": "Čitanje opcija paketa shadera...",
"horizon.agent.copy_details": "Kopiraj detalje veze",
"horizon.multiplayer.join": "Pridružite se",
"horizon.multiplayer.cancel_reconnecting": "Otkaži ponovno povezivanje",
"horizon.multiplayer.join_friend": "Pridružite se prijatelju",
"horizon.multiplayer.relay": "Relej (opciono)",
"LOD Distance": "LOD Udaljenost",
"Far Detail": "Daleki detalj",
"Entity Culling": "Uništavanje entiteta",
"Fog": "Magla",
"Weather": "Vrijeme",
"Animated Textures": "Animirane teksture",
"Resolution Scale": "Skala rezolucije",
"Upscaling": "Povećanje",
"Paper White": "Papir bijeli",
"Graphics API": "Grafički API",
"Performance Overlay": "Preklapanje performansi",
"Full Detail Distance": "Puna udaljenost do detalja",
"Off": "Isključeno",
"Unlimited": "Neograničeno",
"Performance": "Performanse",
"Balanced": "Balansirano",
"Quality": "Kvaliteta",
"Native": "Izvorno",
"Full": "Pun",
"Reduced": "Smanjena",
"Efficient": "Efikasno",
"Precise": "Precizno",
"Auto (VSync)": "automatski (VSync)",
"Immediate": "Odmah",
"FPS and Graph": "FPS i Graf",
"No cost": "Bez troškova",
"Low cost": "Niska cijena",
"Medium cost": "Srednji trošak",
"High cost": "Visoka cijena",
"Saves time": "Štedi vrijeme",
"Graphics": "Grafika",
"Done": "Gotovo",
"This feature is not yet available in Horizon.": "Ova funkcija još nije dostupna u Horizon.",
"Your display or system doesn't offer HDR output.": "Vaš ekran ili sistem ne nude HDR izlaz.",
"Turn HDR on to use it.": "Uključite HDR da ga koristite.",
"Your GPU or graphics API doesn't support it.": "Vaš GPU ili grafički API to ne podržava.",
"%s chunks": "%s komada",
"%s per frame": "%s po kadru",
"%s MiB/frame": "%s MiB/okvir",
"%s KiB/frame": "%s KiB/okvir",
"Auto (%s)": "automatski (%s)",
"Full-detail terrain range. LOD Distance can extend local worlds farther.": "Opseg terena sa punim detaljima. LOD Udaljenost može proširiti lokalne svjetove dalje.",
"Coarse distant terrain in local worlds. Integrated GPUs stop at 128 chunks.": "Grubi udaljeni teren u lokalnim svjetovima. Integrisani GPU-ovi se zaustavljaju na 128 komada.",
"Reduced draws far terrain coarser: faster on this integrated GPU.": "Smanjeno crtanje dalekog terena grublje: brže na ovom integrisanom GPU-u.",
"Reduced draws far terrain coarser, for GPUs it slows down.": "Smanjeno crtanje dalekog terena je grublje, za GPU-ove usporava.",
"Field of view. Wider shows more terrain at once.": "Vidno polje. Šire prikazuje više terena odjednom.",
"Caps frames per second; a cap saves power and heat.": "Caps okvira u sekundi; kapa štedi energiju i toplotu.",
"Waits for the display's refresh: no tearing, a little latency.": "Čeka osvježavanje ekrana: nema kidanja, malo kašnjenja.",
"Size of menus and the HUD.": "Veličina menija i HUD-a.",
"Sets the options below together. Changing one makes it Custom.": "Zajedno postavlja donje opcije. Promjenom jednog postaje prilagođeno.",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "Lava, vatra i sunčeva svjetlost sijaju jače od bijele. Potreban je HDR ekran.",
"Your display's darkest black. 0 for OLED; LCDs glow a little.": "Vaš ekran je najtamnije crn. 0 za OLED; LCD-i malo svijetle.",
"How bright highlights get. Auto: the peak your system reports.": "Kako svetli naglasci postaju. Automatski: vrhunac o kojem vaš sistem prijavljuje.",
"How bright white paper and menus look. Auto: your desktop's white.": "Kako izgledaju svijetli bijeli papir i meniji. Auto: vaša radna površina je bijela.",
"How bright the HUD and menus are, against paper white.": "Koliko su svijetli HUD i meniji, naspram bijelog papira.",
"How far mobs, crops and machines keep working. CPU time.": "Koliko mobovi, usjevi i mašine rade. CPU vrijeme.",
"How far away mobs and items are drawn, as a share of normal.": "Koliko su udaljeni mobovi i predmeti, kao udio normalnog.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Preskače teren skriven iza zatvorenih dijelova. Isključenje košta puno.",
"Hides entities behind opaque blocks.": "Sakriva entitete iza neprozirnih blokova.",
"No entity occlusion culling.": "Nema uklanjanja okluzije entiteta.",
"CPU occlusion test runs this frame.": "CPU test okluzije pokreće ovaj okvir.",
"Occlusion checks run on a worker thread.": "Provjere okluzije se pokreću na radnoj niti.",
"Meshed sections uploaded per frame. Lower is smoother while loading.": "Mrežasti odjeljci otpremljeni po kadru. Donji dio je glatkiji tokom punjenja.",
"Soft shadows in corners. Costs chunk building time only.": "Meke senke u uglovima. Košta samo vrijeme izgradnje komada.",
"Smaller copies of block textures for distant terrain: less shimmer.": "Manje kopije blok tekstura za udaljeni teren: manje svjetlucanja.",
"Blends grass and water colours across biomes when building chunks.": "Miješa travu i vodene boje po biomima prilikom izgradnje komada.",
"Cloud layer: off, flat (fast) or 3D (fancy).": "Sloj oblaka: isključen, ravan (brzo) ili 3D (fensi).",
"How far clouds reach, in chunks.": "Koliko daleko sežu oblaci, u komadima.",
"Draws the sky, sun, moon and stars.": "Crta nebo, sunce, mjesec i zvijezde.",
"Distance fog. Off shows the edge of the loaded world.": "Distance magla. Isključeno pokazuje ivicu učitanog svijeta.",
"Draws rain and snow.": "Privlači kišu i snijeg.",
"How far rain and snow fall around you.": "Koliko kiša i snijeg padaju oko vas.",
"How many particles effects spawn.": "Koliko se efekata čestica pojavljuje.",
"Round shadows under mobs and items.": "Okrugle sjene ispod mobova i predmeta.",
"Leaves with see-through gaps. Off draws them solid.": "Listovi sa providnim prazninama. Off ih čvrsto privlači.",
"Sorts water and glass more exactly behind each other.": "Tačnije sortira vodu i staklo jedno iza drugog.",
"Which chunk rebuilds wait for the frame: none, nearby or yours.": "Koji komad obnavlja čeka na okvir: nijedan, u blizini ili vaš.",
"Animates water, lava, fire and other moving textures.": "Animira vodu, lavu, vatru i druge pokretne teksture.",
"World pixels per screen pixel. Below 100% is faster on the GPU.": "Svjetski pikseli po pikselu ekrana. Ispod 100% je brži na GPU-u.",
"Smooths jagged edges. FXAA: softer. TAA: smoothest, moving things may trail.": "Zaglađuje nazubljene ivice. FXAA: mekše. TAA: najglađe, pokretne stvari mogu zaostajati.",
"Renders the world smaller and rebuilds it with TAA. Faster, softer.": "Čini svijet manjim i obnavlja ga pomoću TAA. Brže, mekše.",
"HDR output on an HDR display: lava, fire and sunlight shine brighter.": "HDR izlaz na HDR displeju: lava, vatra i sunčeva svetlost sijaju jače.",
"How frames reach the screen while VSync is off.": "Kako okviri dolaze do ekrana dok je VSync isključen.",
"Efficient sleeps until the next frame; Precise spins, like vanilla.": "Efikasno spavanje do sljedećeg kadra; Precizno okretanje, poput vanile.",
"Fills the screen (borderless).": "Ispunjava ekran (bez ivica).",
"Brightens dark places.": "Osvjetljava tamna mjesta.",
"Lowers the frame rate when minimised, or idle too.": "Smanjuje brzinu kadrova kada je minimizirana ili u stanju mirovanja.",
"Sways the view while walking.": "Ljulja pogled dok hodate.",
"Fades newly loaded chunks in.": "Uklanja novoučitane komade.",
"Darkens the screen's edges.": "Zatamnjuje ivice ekrana.",
"Shows when the world saves.": "Prikazuje kada svijet spašava.",
"Shows attack cooldown.": "Prikazuje hlađenje napada.",
"60 FPS cap (30 in menus or unfocused), half the workers: less power.": "Ograničenje od 60 FPS (30 u menijima ili nefokusirano), pola radnika: manje energije.",
"Threads that generate, light and mesh chunks. Next start.": "Niti koje stvaraju, lagane i mrežaste komade. Sljedeći početak.",
"Vulkan, then DirectX 12 (Windows) or OpenGL (Linux) where it fails. Next start.": "Vulkan, zatim DirectX 12 (Windows) ili OpenGL (Linux) gdje ne uspije. Sljedeći početak.",
"Water and glass in one buffer: fewer draw calls, a bit more VRAM. Next start.": "Voda i staklo u jednom baferu: manje poziva za povlačenje, malo više VRAM-a. Sljedeći početak.",
"Mesh data sent to the GPU per frame. Lower is smoother while loading.": "Mrežni podaci se šalju GPU-u po okviru. Donji dio je glatkiji tokom punjenja.",
"Frame rate and frame times over the game, with a graph.": "Brzina kadrova i vrijeme kadrova u igri, sa grafikonom.",
"horizon.multiplayer.finding_friend": "Pronalaženje svijeta svog prijatelja...",
"horizon.multiplayer.not_signed_in": "Ovaj server je u online modu. Prijavite se sa svojim Microsoft nalogom u Horizon pokretaču da mu se pridružite.",
"horizon.multiplayer.invalid_address": "Nevažeća adresa servera: %s",
"horizon.multiplayer.connected_friend_direct": "Povezani direktno sa vašim prijateljem...",
"horizon.multiplayer.connected_friend_relay": "Povezani sa vašim prijateljem preko releja...",
"General": "Generale",
"Advanced": "Napredno",
"Undo": "Poništi",
"Search options": "Opcije pretraživanja",
"Shortcut": "Prečica",
"Distance": "Udaljenost",
"Sky and weather": "Nebo i vrijeme",
"Lighting and entities": "Rasvjeta i entiteti",
"Textures": "Teksture",
"Image": "Slika",
"Chunk loading": "Učitavanje komada",
"Culling": "Odbijanje",
"Power": "Snaga",
"Frames": "Okviri",
"Output": "Izlaz",
"Brightness": "Osvetljenost",
"How far you see, the window and the HUD.": "Koliko daleko vidite, prozor i HUD.",
"The sky and weather, lighting, textures and the image.": "Nebo i vrijeme, osvjetljenje, teksture i slika.",
"Chunk loading, culling and power use.": "Učitavanje komada, uklanjanje i korištenje energije.",
"How frames reach the screen, and the performance overlay.": "Kako okviri dolaze do ekrana i preklapanje performansi.",
"Brighter-than-white output on an HDR display.": "Izlaz svjetliji od bijelog na HDR ekranu.",
"Results for \"%s\"": "Rezultati za \"%s\"",
"No settings match.": "Nijedna postavka se ne podudara.",
"horizon.title.not_affiliated": "Nije povezan sa Mojangom ili Microsoftom",
"horizon.extras.header.lighting": "Osvetljenje",
"horizon.extras.header.third_person": "Treća osoba",
"horizon.extras.header.particles": "Čestice",
"horizon.extras.header.inventory": "Inventar",
"horizon.extras.header.sound": "Zvuk",
"horizon.extras.header.menus": "Meniji",
"horizon.extras.dynamic_lights": "Dinamička svjetla",
"horizon.extras.own_name_tag": "Oznaka s vlastitim imenom",
"horizon.extras.hide_block_particles": "Sakrijte čestice razbijanja bloka",
"horizon.extras.auto_reconnect": "Automatsko ponovno povezivanje",
"horizon.extras.ui_sound_reserve": "Zadržite kanale za zvukove korisničkog interfejsa",
"horizon.extras.music_page": "Muzička stranica",
"horizon.extras.mouse_tweaks": "Kontrole podešavanja miša",
"horizon.extras.mouse_tweaks_wheel": "Točkić za pomicanje pomiče stavke",
"horizon.extras.sort": "Dugmad za sortiranje",
"horizon.extras.sort_key": "Ključ za sortiranje",
"horizon.extras.no_ai": "Nema AI",
"horizon.extras.chat_animation": "Glatko ćaskanje",
"horizon.extras.chat_indicator": "Indikator poruke",
"horizon.extras.wheel_cycles_settings": "Točkić za pomicanje mijenja postavke",
"horizon.extras.pack_browser": "Pregledač paketa resursa",
"horizon.extras.fullbright.tooltip": "Prikažite blokove i entitete pri punom svjetlu, čak iu tamnim područjima.",
"horizon.extras.dynamic_lights.tooltip": "Baklje i drugi izvori svjetlosti koji su držani, ispušteni ili zapaljeni osvjetljavaju blokove oko sebe.",
"horizon.extras.own_name_tag.tooltip": "Prikazuje vaše ime iznad glave u trećem licu, kako ga vide drugi igrači. Skriveno kada server sakriva imena vašeg tima ili ste nevidljivi.",
"horizon.extras.hide_block_particles.tooltip": "Sakriva čestice krhotina koje su izbile iz slomljenog bloka i raspale se s onog koji ste iskopavali. Ostale čestice ostaju.",
"horizon.extras.auto_reconnect.tooltip": "Ponovo se pridružuje serveru nakon izgubljene veze ili ponovnog pokretanja, nikada nakon izbacivanja ili zabrane.",
"horizon.extras.ui_sound_reserve.tooltip": "Zvukovi u svijetu ostavljaju slobodnim 16 od 247 zvučnih kanala, tako da njihova poplava (velika eksplozija) ne može utišati klikove menija.",
"horizon.extras.music_page.tooltip": "Muzika i zvukovi dobijaju stranicu Music...: reproducirajte bilo koju ugrađenu numeru sada ili je isključite tako da igra prestane da je bira. Ništa se ne mijenja dok ne isključite numeru.",
"horizon.extras.mouse_tweaks.tooltip": "Prevlačenjem udesno postavlja jedan po utoru, povlačenjem ulijevo skuplja zadržanu stavku, Shift + prevlačenjem ulijevo pomiče svaki slot.",
"horizon.extras.mouse_tweaks_wheel.tooltip": "Dolje šalje jednu stavku utora na drugu stranu, gore vraća jednu nazad.",
"horizon.extras.sort.tooltip": "Sortira inventar, škrinje, bačve, ender škrinje i kutije za šulkere u kreativnom redoslijedu kartica, spajajući hrpe.",
"horizon.extras.sort_key.tooltip": "Sortira dio ispod kursora. Kliknite, a zatim pritisnite tipku; Escape ga odvezuje.",
"horizon.extras.no_ai.tooltip": "Sakriva i isključuje svaku AI funkciju: kontrolu agenta, horizon-ctl i MCP servere. Prekidač lansera takođe.",
"horizon.extras.agent_control.tooltip": "Vaš vlastiti AI agent može igrati ovu igru preko horizon-ctl ili MCP klijenta, samo na ovom računaru.",
"horizon.extras.discord_presence.tooltip": "Prikazuje igranje Horizon na vašem Discord profilu dok Discord radi: meniji, za jednog igrača ili za više igrača, dimenziju i vrijeme igranja. Nikada ime svijeta ili adresu servera.",
"Calibrate...": "Kalibriraj...",
"Calibrate Anyway...": "Kalibriraj svejedno...",
"Use Automatic Setup": "Koristite automatsko podešavanje",
"Shortcut: %s": "Prečica: %s",
"Next": "Sledeći",
"HDR Calibration %s/%s: %s": "HDR kalibracija %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Postavlja tri ispod preko celog ekrana, jednu po jednu probnu sliku.",
"A key that opens this screen from the game. Not bound at first.": "Taster koji otvara ovaj ekran iz igre. U početku nije vezan.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Vaš displej nije rekao koliko svetli: kalibrirajte da biste ga postavili.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Već postavljeno za vaš ekran. Kalibrirajte samo ako HDR izgleda pogrešno.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Vraća Peak Brightness i Paper White u postavke vašeg ekrana.",
"Set up for your display. Leave on Auto unless it looks wrong.": "Postavite za vaš ekran. Ostavite na Auto osim ako ne izgleda pogrešno.",
"Lower it until the logo just disappears into the black.": "Spuštajte ga dok logo ne nestane u crno.",
"Raise it until the logo just disappears into the bright square.": "Podignite ga dok logo ne nestane u svijetlom kvadratu.",
"Make the card a white that's comfortable to look at for a long time, in the light you play in.": "Neka karta bude bela da je udobna za gledanje dugo vremena, na svetlu u kojem igrate.",
"horizon.splitscreen.profiles.forget": "Zaboravi",
"horizon.splitscreen.profiles.create": "Kreiraj",
"horizon.splitscreen.profiles.you": " - ti",
"horizon.splitscreen.profiles.forget_named": "Zaboravi %s",
"horizon.splitscreen.profiles.previous": "Prethodno",
"horizon.splitscreen.profiles.next": "Sledeći",
"horizon.splitscreen.profiles.new_button": "Novi profil...",
"horizon.splitscreen.profiles.forget_title": "Zaboravili ste profil?",
"horizon.splitscreen.profiles.forget_leaves": "Napušta listu ove instance.",
"horizon.splitscreen.profiles.forget_kept": "Svjetski napredak i postavke se čuvaju.",
"horizon.splitscreen.profiles.new_title": "Novi profil",
"horizon.splitscreen.profiles.name_hint": "Ime (3 do 16 slova, cifara, _)",
"horizon.splitscreen.profiles.switch_title": "Promijeni profil",
"horizon.splitscreen.profiles.offline_note": "Offline profili pripadaju ovoj instanci",
"horizon.splitscreen.profiles.move": "Krećite se tamo gdje igrate",
"horizon.splitscreen.profiles.move_place": "Premjestite se gdje igrate: %s",
"horizon.friends.title": "Igrajte se s prijateljima",
"horizon.friends.hint": "Prijatelji zalijepe kod iza dugmeta Prijatelji na naslovnom ekranu, u Direktnu vezu ili na Play stranicu pokretača.",
"horizon.friends.copied": "Kopirano",
"horizon.friends.copy_code": "Kopiraj kod",
"horizon.friends.none_yet": "Ovdje još nema prijatelja.",
"horizon.friends.here_now": "Evo sada: %s",
"horizon.friends.share_intro": "Podijelite ovaj svijet iza pozivnog koda: otvara se na LAN, a prijatelji na internetu se pridružuju kodu. Nijedan naš server nije uključen.",
"horizon.friends.start_sharing": "Počnite dijeliti",
"horizon.friends.troubleshooting": "Rješavanje problema...",
"horizon.friends.stopped": "Zaustavljeno je dijeljenje pozivnog koda.",
"horizon.friends.short_code_changed": "Neko je nastavio pokušavati s pogrešnim kratkim kodom, pa se to promijenilo.",
"horizon.friends.wrong_code": "Neko je probao pogrešan kod.",
"horizon.friends.code_ready": "Vaš pozivni kod je spreman: Meni igre, Igrajte se sa prijateljima. Kopirajte ga i pošaljite svojim prijateljima.",
"horizon.friends.share_failed": "Ne mogu dijeliti svijet: %s",
"horizon.friends.finding_route": "Pronalaženje načina na koji prijatelji mogu doći do vas…",
"horizon.osk.delete": "Izbriši",
"horizon.osk.type": "Tip",
"horizon.osk.symbols": "Simboli",
"horizon.osk.close": "Zatvori",
"horizon.extras.chat_animation.tooltip": "Nova linija ćaskanja klizi s lijeve strane dok se gasi, umjesto da izgleda kao cijela.",
"horizon.extras.chat_indicator.tooltip": "Traka u boji lijevo od linije za ćaskanje.",
"horizon.extras.wheel_cycles_settings.tooltip": "Točkić za pomeranje preko dugmeta u podešavanjima menja svoju vrednost, kao u vaniliji. Isključeno, kotačić samo pomiče listu.",
"horizon.extras.pack_browser.tooltip": "Select Resource Packs dobija dugme Dodaj koje pretražuje Modrinth i instalira paket u fasciklu sa paketima. Isključeno, igra nikada ne kontaktira Modrinth.",
"horizon.extras.splitscreen.tooltip": "U svijetu se drugi kontrolor može pridružiti kao novi igrač: pritisnite A kada to zatraži, a zatim odaberite profil i gdje ćete igrati.",
"horizon.splitscreen.setup.monitor_unknown": "monitor",
"horizon.splitscreen.setup.window": "Prozor %s",
"horizon.splitscreen.setup.new_window": "Novi prozor",
"horizon.splitscreen.setup.fill_monitor": "Popuni %s",
"horizon.splitscreen.setup.done": "Gotovo",
"horizon.splitscreen.setup.off": "Isključeno",
"horizon.splitscreen.setup.player": "Igrač %s",
"horizon.splitscreen.setup.revert": "Vrati",
"horizon.splitscreen.setup.apply": "Prijavite se",
"horizon.splitscreen.setup.warning.empty_main": "Niko ne igra u prozoru 1: skriva se",
"horizon.splitscreen.setup.warning.empty_close": "Niko ne igra u prozoru %s: zatvara se na Apply",
"horizon.splitscreen.setup.warning.empty_new": "Niko ne igra u prozoru %s: neće se otvoriti",
"horizon.splitscreen.setup.warning.on_main": "Prozor %s ispunjava monitor prozora 1: njegovi igrači igraju u prozoru 1",
"horizon.splitscreen.setup.warning.unplugged": "Monitor prozora %s nije povezan: njegovi igrači igraju u prozoru 1",
"horizon.splitscreen.setup.status.off": "Splitscreen je isključen.",
"horizon.splitscreen.setup.status.enable_hint": "Uključite ga, a zatim pritisnite dugme na drugom kontroleru u svetu.",
"horizon.splitscreen.setup.status.join_hint": "Pritisnite A da se pridružite i odaberete profil.",
"horizon.splitscreen.setup.status.idle": "Splitscreen u mirovanju",
"horizon.splitscreen.setup.status.alone": "Nema drugih domaćih igrača.",
"horizon.splitscreen.setup.status.solo_controls": "Solo kontrole i povratne informacije kontrolera su nepromijenjene.",
"horizon.splitscreen.setup.window_empty": "Prozor %s: prazan",
"horizon.splitscreen.setup.note.select": "Kliknite na plejer ili ga prevucite na prozor ili monitor.",
"horizon.splitscreen.setup.note.apply": "Prijavite se da igrate na ovaj način, vratite se da poništite.",
"horizon.splitscreen.setup.note.place": "Gdje bi %s trebao igrati?",
"horizon.splitscreen.setup.this_player": "ovaj igrač",
"horizon.splitscreen.setup.switch_disabled": "Splitscreen se gasi kada drugi odu (njihov meni za pauzu).",
"horizon.splitscreen.setup.window_place": "Prozor %s (%s od %s)",
"horizon.splitscreen.setup.names": "Imena",
"horizon.splitscreen.setup.identify": "Identifikujte",
"horizon.splitscreen.setup.layout.two": "2 dijeljenja: %s",
"horizon.splitscreen.setup.layout.three": "3 dijeljenja: %s",
"horizon.splitscreen.setup.layout.two.1": "Rame uz rame",
"horizon.splitscreen.setup.layout.two.2": "Gornji i donji deo",
"horizon.splitscreen.setup.layout.three.0": "Jedan širok na vrhu",
"horizon.splitscreen.setup.layout.three.1": "Jedan visok sa leve strane",
"horizon.splitscreen.setup.layout.three.2": "Rame uz rame",
"horizon.splitscreen.setup.help.player": "Kliknite ili prevucite %s da ih premjestite.",
"horizon.splitscreen.setup.help.layout_auto": "%s (uporedo kada je prozor širok). Pritisnite za promjenu.",
"horizon.splitscreen.setup.help.layout": "%s. Pritisnite za promjenu.",
"horizon.splitscreen.setup.help.switch": "Splitscreen za ovaj računar, uključen ili isključen.",
"horizon.splitscreen.setup.help.identify": "Pogled svakog igrača pokazuje u kojem se prozoru nalazi.",
"horizon.splitscreen.setup.help.names": "Ime svakog igrača u uglu njegovog pogleda.",
"horizon.splitscreen.setup.help.revert": "Poništite promjene koje još nisu primijenjene.",
"horizon.splitscreen.setup.help.apply": "Igrajte onako kako je prikazano na karti.",
"horizon.splitscreen.setup.help.unchanged": "Još se ništa nije promijenilo.",
"horizon.splitscreen.prompt.controller_connected": "Kontroler povezan",
"horizon.splitscreen.prompt.controller_disconnected": "Kontroler je isključen",
"horizon.splitscreen.prompt.controller_disconnected.hint": "Ponovo ga povežite ili pritisnite dugme na drugom",
"horizon.splitscreen.prompt.join.one": "%s se želi pridružiti",
"horizon.splitscreen.prompt.join.many": "%s kontrolori se žele pridružiti",
"horizon.splitscreen.prompt.join.yes": "Pridružite se",
"horizon.splitscreen.prompt.join.as_p1": "Igrajte kao P1",
"horizon.splitscreen.prompt.join.no": "br",
"horizon.splitscreen.prompt.lost": "%s: kontroler isključen",
"horizon.splitscreen.prompt.lost.use_another": "Koristi drugu",
"horizon.splitscreen.prompt.lost.leave": "Odlazi",
"horizon.splitscreen.setup.help.give_up_pad": "Vaš kontrolor prestaje da vas vozi i nudi mu se pridruživanje (A).",
"horizon.splitscreen.setup.note.held_at": "%s do %s: A ispušta je ovdje, B vraća.",
"horizon.splitscreen.setup.note.held": "%s podignuto: lijevo/desno bira gdje, A pada, B vraća nazad.",
"Hides models behind opaque blocks. Async checks off-thread; entities tick.": "Skriva modele iza neprozirnih blokova. Async provjerava off-thread; entiteti otkucaju.",
"Block Entities": "Blokirajte entitete",
"Reuses settled boxes; lid movement stays animated.": "Ponovno koristi postavljene kutije; pokreti poklopca ostaju animirani.",
"Signs": "Znakovi",
"Reuses sign text until text, light, or font changes.": "Ponovo koristi potpisni tekst dok se ne promijeni tekst, svjetlo ili font.",
"Reuses still pots; break wobble and sherd changes redraw.": "Ponovno koristi lonce za mirovanje; prekinuti kolebanje i promjene u komadu ponovo nacrtati.",
"Bells": "Zvona",
"Reuses still bells; ringing stays animated.": "Ponovno koristi zvona za mirovanje; zvono ostaje animirano.",
"Chests": "Škrinje",
"Reuses closed chests; moving lids stay animated.": "Ponovno koristi zatvorene škrinje; pokretni poklopci ostaju animirani.",
"Banners": "Banneri",
"Reuses poles; flags and pattern layers still update.": "Ponovo koristi stubove; zastavice i slojevi uzoraka se i dalje ažuriraju.",
"Copper Golem Statues": "Statue od bakrenog golema",
"Reuses statue geometry until its block state changes.": "Ponovo koristi geometriju statue dok se stanje bloka ne promijeni.",
"Every leaf face. Vanilla's fancy leaves.": "Svaki list lice. Vanilini fensi listovi.",
"Keeps fine plants in high-res packs from fading far away.": "Čuva fine biljke u pakovanjima visoke rezolucije da ne izblijede.",
"Screenshot Quality": "Kvaliteta snimka zaslona",
"F2 screenshots at this many times the game's resolution. Higher takes longer.": "F2 screenshotovi na ovoj rezoluciji koja je mnogo puta veća od igre. Više traje duže.",
"Frame Generation": "Generacija okvira",
"Shows a made-up frame between each two real ones: smoother, a little more lag.": "Prikazuje napravljeni okvir između svaka dva stvarna: glatkiji, malo više zaostajanja.",
"Reuses the pack shadow map when sun, view and world match. Off by default.": "Ponovo koristi mapu senki paketa kada se poklope sunce, pogled i svijet. Podrazumevano isključeno.",
"Find BlueMap": "Pronađite BlueMap",
"On servers, finds their BlueMap web map for far terrain. Next join.": "Na serverima pronalazi njihovu BlueMap web kartu za daleki teren. Sljedeći spoj.",
"How far around you BlueMap tiles are downloaded. Next join.": "Koliko daleko oko vas se preuzimaju BlueMap pločice. Sljedeći spoj.",
"Synchronous": "Sinhroni",
"Asynchronous": "Asinhroni",
"Leaves fully inside a canopy draw solid: fewer see-through layers.": "Listovi u potpunosti unutar krošnje crtaju čvrsto: manje providnih slojeva.",
"Leaves with one open side draw solid too: faster, chunkier edges.": "Listovi s jednom otvorenom stranom također crtaju čvrsto: brže, zdepanije ivice.",
"Drops faces between leaves: faster, but gaps look through.": "Spušta lica između listova: brže, ali praznine gledaju kroz njih.",
"Hotbar: Previous Slot": "Hotbar: prethodni slot",
"horizon.agent.connected.one": "Povezano: 1 veza agenta",
"horizon.agent.connected.many": "Povezano: %s veze agenta",
"horizon.agent.connected.one_ago": "Povezano: 1 veza agenta, zadnja komanda prije %s s",
"horizon.agent.connected.many_ago": "Povezano: %s veze agenta, zadnja komanda prije %s s",
"horizon.agent.waiting": "Čekam agenta.",
"horizon.agent.off": "Isključeno: agenti ne mogu doći do ove igre.",
"horizon.agent.session_opened": "Otvoreno za ovu sesiju od strane programa koji je pokrenuo igru.",
"horizon.agent.socket": "Socket: %s (kopirajte detalje za njegovu punu putanju)",
"horizon.agent.local_only": "Samo lokalno: agenti na ovom računaru, nikada mreža.",
"%s nits": "%s gnjida",
"Auto (%s nits)": "automatski (%s gnjida)",
"%.2f nits": "%.2f gnjida",
"Hides models behind opaque blocks. Entities update. Off with custom packs.": "Skriva modele iza neprozirnih blokova. Ažuriranje entiteta. Isključeno sa prilagođenim paketima.",
"horizon.extras.repro_keys": "Prečice za snimanje",
"horizon.extras.repro_keys.tooltip": "F3+R pokreće ili zaustavlja repro snimanje, a F3+E upisuje njegov paket. Podrazumevano isključeno: snimak kopira svijet.",
"horizon.agent.record_session": "Započnite snimanje sesije",
"horizon.agent.record_stop": "Zaustavi snimanje sesije",
"horizon.agent.export_repro": "Izvezi Repro Bundle",
"horizon.repro.started": "Snimanje je počelo; F3+R ga zaustavlja i upisuje paket.",
"horizon.repro.stopped": "Snimanje zaustavljeno: %s",
"horizon.repro.bundle": "Repro paket: %s",
"horizon.repro.no_recording": "Još uvijek nema snimka za izvoz.",
"horizon.repro.replay_started": "Repriza je počela: %s",
"horizon.pack.addInfo": "Dodajte paket resursa iz Modrintha",
"horizon.pack.addWorking": "Pitam Modrintha...",
"horizon.pack.addNone": "Nije pronađen nijedan paket resursa",
"horizon.pack.addTitle": "Dodajte paket resursa",
"horizon.pack.install": "Instaliraj",
"horizon.pack.installing": "Instaliranje",
"horizon.pack.installed": "Instalirano",
"horizon.pack.search": "Traži",
"horizon.pack.searchHint": "Pretraži Modrinth...",
"horizon.pack.filters": "Filteri",
"horizon.pack.filter.realistic": "Realističan",
"horizon.pack.filter.simplistic": "Pojednostavljeno",
"horizon.pack.filter.vanillaLike": "Nalik vaniliji",
"horizon.pack.addFailed": "Paket se nije mogao dodati: %s",
"horizon.extras.header.cosmetics": "Kozmetika",
"horizon.extras.cosmetics_mine": "Moja kozmetika",
"horizon.extras.cosmetics_mine.tooltip": "Skriveno: svaki Horizon igrač vas vidi bez vaše kozmetike, uključujući i vas. Isti prekidač kao Ormar lansera.",
"horizon.extras.cosmetics_others": "Kozmetika drugih igrača",
"horizon.extras.cosmetics_others.tooltip": "Šta drugi Horizon igrači nose: sve, samo stilove oznaka sa imenom ili ništa. Skriveno, igra nikad ne pita šta nose.",
"horizon.extras.cosmetics_auras": "Aure i tragovi drugih",
"horizon.extras.cosmetics_auras.tooltip": "Aure i staze koje drugi igrači nose. Podrazumevano isključeno.",
"horizon.extras.cosmetics.shown": "Prikazano",
"horizon.extras.cosmetics.hidden": "Skriveno",
"horizon.extras.cosmetics.name_tags": "Samo oznake sa imenom"
}
@@ -0,0 +1,471 @@
{
"horizon.options.controller": "Controlador",
"horizon.options.vibration": "Vibració",
"horizon.options.adaptive_triggers": "Disparadors adaptatius",
"horizon.options.lightbar": "Barra de llums",
"horizon.options.look_sensitivity": "Sensibilitat de la mirada",
"horizon.options.invert_x": "Invertir la mirada X",
"horizon.options.invert_y": "Invertir la mirada Y",
"horizon.options.sneak_button": "Botó d'ajupir-se",
"horizon.options.move_deadzone": "Zona morta del moviment",
"horizon.options.look_deadzone": "Zona morta de la vista",
"horizon.options.button_glyphs": "Glifos de botons",
"horizon.options.on_screen_keyboard": "Teclat en pantalla",
"horizon.options.chat_delay": "Retard de xat",
"horizon.options.lightbar.health": "Salut",
"horizon.options.lightbar.dimension": "Dimensió",
"horizon.options.glyphs.auto": "Automàtic",
"horizon.options.keyboard.auto": "Automàtic",
"horizon.options.unavailable": "Aquesta funció encara no està disponible a Horizon.",
"horizon.options.search": "Configuració de cerca...",
"horizon.pages.shader_packs": "Paquets Shader",
"horizon.pages.shader_options": "Opcions d'ombra",
"horizon.pages.controller": "Configuració del controlador",
"horizon.pages.controller_binds": "El controlador enllaça",
"horizon.pages.settings": "Configuració d'Horizon",
"horizon.pages.agent": "Agent d'IA",
"horizon.split.leave": "marxar",
"horizon.pages.split_settings": "Configuració de pantalla dividida",
"horizon.pages.split_profiles": "Qui juga?",
"horizon.pages.split_setup": "Configuració de pantalla dividida",
"horizon.pages.music": "Música",
"horizon.pages.music.menu": "Menú",
"horizon.pages.music.creative": "Creatiu",
"horizon.pages.music.underwater": "Sota l'aigua",
"horizon.pages.music.end": "El final",
"horizon.pages.music.boss": "Cap",
"horizon.pages.music.credits": "Crèdits",
"horizon.pages.music.overworld_biomes": "Biomes del sobremón",
"horizon.pages.music.nether_biomes": "Biomes inferiors",
"horizon.pages.music.end_biomes": "Fi dels biomes",
"horizon.pages.music.other": "Altres músiques",
"horizon.pages.music.empty": "Sense pistes de música: els sons del joc no es carreguen.",
"horizon.pages.music.tooltip": "Les pistes integrades, per situació: juga-ne una ara o desactiva-ne qualsevol perquè el joc deixi de triar-ne.",
"horizon.pages.settings.tooltip": "Configuració pròpia d'Horizon: gràfics, controladors, extres i modificacions.",
"horizon.pages.graphics.tooltip": "Gràfics, senzillament: cada paràmetre diu el que costa.",
"horizon.pages.graphics_advanced.tooltip": "Tots els paràmetres gràfics, inclòs Horizon, amb el que costa cadascun.",
"horizon.pages.controller.tooltip": "Controladors: els seus botons, pals, vibració i navegació per menús.",
"horizon.pages.extras.tooltip": "Addicions opcionals, totes desactivades de manera predeterminada: el joc es manté de vainilla fins que en activeu un.",
"horizon.pages.agent.tooltip": "Deixa que jugui el teu propi agent d'IA: clients horizon-ctl i MCP en aquest ordinador.",
"horizon.pages.split_profiles.tooltip": "Qui juga: un perfil guardat en aquesta carpeta del joc.",
"horizon.pages.split_setup.tooltip": "Les finestres i els monitors, i qui juga on.",
"horizon.pages.shader_packs.tooltip": "Paquets shader de la carpeta shaderpacks: els propis d'Horizon i els del format shaders/. Un corre a la vegada.",
"horizon.pages.mods.tooltip": "Mods fets per a Horizon, des de la carpeta horizon-mods. Cadascun funciona en una caixa de sorra i es manté apagat fins que l'enceneu.",
"horizon.pages.open.graphics": "Gràfics...",
"horizon.pages.open.graphics_advanced": "Gràfics avançats...",
"horizon.pages.open.shader_packs": "Paquets d'ombres...",
"horizon.pages.open.controller": "Configuració del controlador...",
"horizon.pages.open.split_setup": "Pantalla dividida...",
"horizon.pages.open.extras": "Extres...",
"horizon.pages.open.agent": "Agent d'IA...",
"horizon.pages.open.horizon_graphics": "Gràfics d'Horizon...",
"horizon.pages.open.controller_binds": "El controlador s'enllaça...",
"horizon.pages.open.horizon_settings": "Configuració d'Horizon...",
"horizon.pages.open.music": "Música...",
"horizon.mods.empty": "Sense modificacions: poseu fitxers .hmod a la carpeta horizon-mods",
"horizon.mods.stopped": " (aturat)",
"horizon.mods.not_built": " (No construït)",
"horizon.mods.dev": " (Desenvolupament)",
"horizon.mods.settings": "Configuració",
"horizon.mods.in_world": " (deixar el món per canviar)",
"horizon.mods.open_folder": "Obriu la carpeta de modificacions",
"horizon.shader_packs.empty": "Sense paquets d'ombres: col·loqueu-los a la carpeta shaderpacks",
"horizon.shader_options.open": "Opcions d'ombrejat...",
"horizon.shader_options.profile": "Perfil",
"horizon.shader_options.loading": "Llegint les opcions del paquet shader...",
"horizon.agent.copy_details": "Copia els detalls de la connexió",
"horizon.multiplayer.join": "Uneix-te",
"horizon.multiplayer.cancel_reconnecting": "Cancel·la la tornada a connectar",
"horizon.multiplayer.join_friend": "Uneix-te a un amic",
"horizon.multiplayer.invite_code": "Codi d'invitació",
"horizon.multiplayer.relay": "Relé (opcional)",
"Preset": "Preestablert",
"LOD Distance": "Distància LOD",
"Far Detail": "Detall llunyà",
"Cave Culling": "Culling de coves",
"Entity Culling": "Culling d'entitats",
"Chunk Updates": "Actualitzacions de blocs",
"Upload Budget": "Carregar pressupost",
"Sky": "Cel",
"Fog": "Boira",
"Weather": "El temps",
"Animated Textures": "Textures animades",
"Present Mode": "Mode present",
"Frame Pacing": "Marc de ritme",
"Worker Threads": "Fils de treball",
"Resolution Scale": "Escala de resolució",
"Anti-aliasing": "Antialiasing",
"Upscaling": "Ampliació d'escala",
"Paper White": "Paper blanc",
"Peak Brightness": "Lluminositat màxima",
"Black Level": "Nivell negre",
"UI Brightness": "Brillantor de la IU",
"Battery Mode": "Mode de bateria",
"Graphics API": "API de gràfics",
"Translucent Batching": "Lots translúcids",
"Performance Overlay": "Superposició de rendiment",
"Full Detail Distance": "Distància de detall complet",
"On": "Encès",
"Off": "Apagat",
"Unlimited": "Il·limitat",
"Auto": "Automàtic",
"Custom": "Personalitzat",
"Performance": "Rendiment",
"Balanced": "Equilibrat",
"Quality": "Qualitat",
"Ultra Performance": "Ultra rendiment",
"Native": "Nadiu",
"Full": "Complet",
"Reduced": "Reduït",
"Efficient": "Eficient",
"Precise": "Precís",
"Auto (VSync)": "Automàtic (VSync)",
"Mailbox": "Bústia de correu",
"Immediate": "Immediatament",
"FPS and Graph": "FPS i gràfic",
"No cost": "Sense cost",
"Low cost": "Baix cost",
"Medium cost": "Cost mitjà",
"High cost": "Alt cost",
"Saves time": "Estalvia temps",
"Graphics": "Gràfics",
"Done": "Fet",
"This feature is not yet available in Horizon.": "Aquesta funció encara no està disponible a Horizon.",
"Your display or system doesn't offer HDR output.": "La vostra pantalla o sistema no ofereixen sortida HDR.",
"Turn HDR on to use it.": "Activa HDR per utilitzar-lo.",
"Your GPU or graphics API doesn't support it.": "La vostra GPU o API de gràfics no ho admet.",
"%s chunks": "%s trossos",
"%s per frame": "%s per fotograma",
"Auto (%s)": "Automàtic (%s)",
"Full-detail terrain range. LOD Distance can extend local worlds farther.": "Gamma de terreny amb tots els detalls. La distància LOD pot estendre els mons locals més lluny.",
"Coarse distant terrain in local worlds. Integrated GPUs stop at 128 chunks.": "Terreny llunyà gruixut en mons locals. Les GPU integrades s'aturen a 128 fragments.",
"Reduced draws far terrain coarser: faster on this integrated GPU.": "Dibuixos reduïts molt llunyans més gruixuts: més ràpid en aquesta GPU integrada.",
"Reduced draws far terrain coarser, for GPUs it slows down.": "Els dibuixos reduïts són molt més gruixuts, per a les GPU s'alenteix.",
"Field of view. Wider shows more terrain at once.": "Camp de visió. Més ample mostra més terreny alhora.",
"Caps frames per second; a cap saves power and heat.": "Caps fotogrames per segon; una tapa estalvia energia i calor.",
"Waits for the display's refresh: no tearing, a little latency.": "Espera que la pantalla s'actualitzi: sense trencament, una mica de latència.",
"Size of menus and the HUD.": "Mida dels menús i l'HUD.",
"Sets the options below together. Changing one makes it Custom.": "Estableix les opcions següents juntes. Canviar-ne un el fa personalitzat.",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "La lava, el foc i la llum del sol brillen més que el blanc. Necessita una pantalla HDR.",
"Your display's darkest black. 0 for OLED; LCDs glow a little.": "La pantalla és el negre més fosc. 0 per a OLED; Els LCD brillen una mica.",
"How bright highlights get. Auto: the peak your system reports.": "Com es fan els reflexos brillants. Automàtic: el pic que informa el vostre sistema.",
"How bright white paper and menus look. Auto: your desktop's white.": "Quin aspecte brillant del paper blanc i dels menús. Automàtic: el teu escriptori és blanc.",
"How bright the HUD and menus are, against paper white.": "Què brillants són l'HUD i els menús, contra el paper blanc.",
"How far mobs, crops and machines keep working. CPU time.": "Fins on segueixen treballant les turbes, els conreus i les màquines. Temps de CPU.",
"How far away mobs and items are drawn, as a share of normal.": "A quina distància es dibuixen les turbes i els articles, com a part de la normalitat.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Salta el terreny amagat darrere de seccions tancades. Off costa molt.",
"Hides entities behind opaque blocks.": "Amaga les entitats darrere de blocs opacs.",
"No entity occlusion culling.": "Cap eliminació d'oclusió d'entitats.",
"CPU occlusion test runs this frame.": "La prova d'oclusió de la CPU executa aquest marc.",
"Occlusion checks run on a worker thread.": "Les comprovacions d'oclusió s'executen en un fil de treball.",
"Meshed sections uploaded per frame. Lower is smoother while loading.": "Seccions de malla penjades per fotograma. La part inferior és més suau mentre es carrega.",
"Soft shadows in corners. Costs chunk building time only.": "Ombres suaus a les cantonades. Només costa una part del temps de construcció.",
"Smaller copies of block textures for distant terrain: less shimmer.": "Còpies més petites de textures de bloc per a terrenys llunyans: menys brillantor.",
"Blends grass and water colours across biomes when building chunks.": "Combina els colors de l'herba i l'aigua entre els biomes quan es construeixen trossos.",
"Cloud layer: off, flat (fast) or 3D (fancy).": "Capa de núvol: apagada, plana (ràpida) o 3D (de luxe).",
"How far clouds reach, in chunks.": "Fins on arriben els núvols, a trossos.",
"Draws the sky, sun, moon and stars.": "Dibuixa el cel, el sol, la lluna i les estrelles.",
"Distance fog. Off shows the edge of the loaded world.": "Boira de distància. Off mostra la vora del món carregat.",
"Draws rain and snow.": "Dibuixa pluja i neu.",
"How far rain and snow fall around you.": "A quina distància cau la pluja i la neu al teu voltant.",
"How many particles effects spawn.": "Quants efectes de partícules generen.",
"Round shadows under mobs and items.": "Ombres rodones sota turbes i objectes.",
"Leaves with see-through gaps. Off draws them solid.": "Fulles amb buits transparents. Off els dibuixa sòlids.",
"Sorts water and glass more exactly behind each other.": "Classifica l'aigua i el vidre més exactament l'un darrere l'altre.",
"Which chunk rebuilds wait for the frame: none, nearby or yours.": "Quin tros reconstrueix esperar el marc: cap, a prop o teu.",
"Animates water, lava, fire and other moving textures.": "Anima l'aigua, la lava, el foc i altres textures en moviment.",
"World pixels per screen pixel. Below 100% is faster on the GPU.": "Píxels mundials per píxel de pantalla. Per sota del 100% és més ràpid a la GPU.",
"Smooths jagged edges. FXAA: softer. TAA: smoothest, moving things may trail.": "Allisa les vores dentades. FXAA: més suau. TAA: més suau, les coses en moviment poden seguir.",
"Renders the world smaller and rebuilds it with TAA. Faster, softer.": "Redueix el món i el reconstrueix amb TAA. Més ràpid, més suau.",
"HDR output on an HDR display: lava, fire and sunlight shine brighter.": "Sortida HDR en una pantalla HDR: la lava, el foc i la llum del sol brillen més.",
"How frames reach the screen while VSync is off.": "Com arriben els fotogrames a la pantalla mentre VSync està desactivat.",
"Efficient sleeps until the next frame; Precise spins, like vanilla.": "Dors eficients fins al següent fotograma; Girades precises, com la vainilla.",
"Fills the screen (borderless).": "Omple la pantalla (sense vores).",
"Brightens dark places.": "Il·lumina els llocs foscos.",
"Lowers the frame rate when minimised, or idle too.": "Redueix la velocitat de fotogrames quan es minimitza o també està inactiva.",
"Sways the view while walking.": "Oblida la vista mentre camina.",
"Fades newly loaded chunks in.": "Esvaeix els fragments que s'han carregat recentment.",
"Darkens the screen's edges.": "Enfosqueix les vores de la pantalla.",
"Shows when the world saves.": "Mostra quan el món salva.",
"Shows attack cooldown.": "Mostra el temps de reutilització de l'atac.",
"60 FPS cap (30 in menus or unfocused), half the workers: less power.": "Limit de 60 FPS (30 en menús o desenfocat), la meitat dels treballadors: menys potència.",
"Threads that generate, light and mesh chunks. Next start.": "Fils que generen trossos lleugers i de malla. Pròxim inici.",
"Vulkan, then DirectX 12 (Windows) or OpenGL (Linux) where it fails. Next start.": "Vulkan, després DirectX 12 (Windows) o OpenGL (Linux) on falla. Pròxim inici.",
"Water and glass in one buffer: fewer draw calls, a bit more VRAM. Next start.": "Aigua i vidre en un sol buffer: menys trucades de dibuix, una mica més de VRAM. Pròxim inici.",
"Mesh data sent to the GPU per frame. Lower is smoother while loading.": "Dades de malla enviades a la GPU per fotograma. La part inferior és més suau mentre es carrega.",
"Frame rate and frame times over the game, with a graph.": "Velocitat de fotogrames i temps de fotogrames al llarg del joc, amb un gràfic.",
"horizon.multiplayer.finding_friend": "Trobar el món del teu amic...",
"horizon.multiplayer.not_signed_in": "Aquest servidor està en mode en línia. Inicieu la sessió amb el vostre compte de Microsoft al llançador d'Horizon per unir-vos-hi.",
"horizon.multiplayer.invalid_address": "Adreça del servidor no vàlida: %s",
"horizon.multiplayer.connected_friend_direct": "Connectat directament amb el teu amic...",
"horizon.multiplayer.connected_friend_relay": "Connectat amb el teu amic a través del relé...",
"Advanced": "Avançat",
"Undo": "Desfer",
"Search options": "Opcions de cerca",
"Shortcut": "Drecera",
"Distance": "Distància",
"Display": "Mostra",
"Interface": "Interfície",
"Sky and weather": "Cel i temps",
"Lighting and entities": "Il·luminació i entitats",
"Image": "Imatge",
"Chunk loading": "Càrrega de trossos",
"Power": "Poder",
"Frames": "Marcs",
"Overlay": "Superposició",
"Output": "Sortida",
"Brightness": "Brillantor",
"How far you see, the window and the HUD.": "Fins on veus, la finestra i l'HUD.",
"The sky and weather, lighting, textures and the image.": "El cel i el temps, la il·luminació, les textures i la imatge.",
"Chunk loading, culling and power use.": "Càrrega de trossos, eliminació i ús d'energia.",
"How frames reach the screen, and the performance overlay.": "Com arriben els fotogrames a la pantalla i la superposició de rendiment.",
"Brighter-than-white output on an HDR display.": "Sortida més brillant que el blanc en una pantalla HDR.",
"Results for \"%s\"": "Resultats per a \"%s\"",
"No settings match.": "No coincideix cap configuració.",
"horizon.title.not_affiliated": "No està afiliat a Mojang o Microsoft",
"horizon.extras.header.lighting": "Il·luminació",
"horizon.extras.header.third_person": "Tercera persona",
"horizon.extras.header.particles": "Partícules",
"horizon.extras.header.inventory": "Inventari",
"horizon.extras.header.chat": "Xatejar",
"horizon.extras.header.multiplayer": "Multijugador",
"horizon.extras.header.sound": "So",
"horizon.extras.header.menus": "Menús",
"horizon.extras.fullbright": "Lluminós total",
"horizon.extras.dynamic_lights": "Llums dinàmics",
"horizon.extras.own_name_tag": "Etiqueta de nom pròpia",
"horizon.extras.hide_block_particles": "Amaga les partícules de trencament de blocs",
"horizon.extras.auto_reconnect": "Reconnexió automàtica",
"horizon.extras.ui_sound_reserve": "Conserva els canals per als sons de la IU",
"horizon.extras.music_page": "Pàgina de música",
"horizon.extras.mouse_tweaks": "Controls de retocs del ratolí",
"horizon.extras.mouse_tweaks_wheel": "La roda de desplaçament mou els elements",
"horizon.extras.sort": "Botons d'ordenació",
"horizon.extras.sort_key": "Clau d'ordenació",
"horizon.extras.no_ai": "Sense IA",
"horizon.extras.agent_control": "Control d'agents",
"horizon.extras.discord_presence": "Presència enriquida de Discord",
"horizon.extras.splitscreen": "Pantalla dividida",
"horizon.extras.chat_animation": "Xat suau",
"horizon.extras.chat_indicator": "Indicador de missatge",
"horizon.extras.wheel_cycles_settings": "La roda de desplaçament canvia la configuració",
"horizon.extras.pack_browser": "Navegador de paquets de recursos",
"horizon.extras.fullbright.tooltip": "Mostra blocs i entitats a plena llum, fins i tot en zones fosques.",
"horizon.extras.dynamic_lights.tooltip": "Les torxes i altres fonts de llum agafades, caigudes o al foc il·luminen els blocs que els envolten.",
"horizon.extras.own_name_tag.tooltip": "Mostra el teu nom a sobre del teu cap en tercera persona, tal com ho veuen els altres jugadors. S'amaga quan el servidor amaga els noms del teu equip o ets invisible.",
"horizon.extras.hide_block_particles.tooltip": "Amaga les partícules de deixalles que esclaten d'un bloc trencat i s'esmicolen del que mines. Altres partícules es queden.",
"horizon.extras.auto_reconnect.tooltip": "Torna a unir-se a un servidor després d'una connexió perduda o un reinici, mai després d'una expulsió o prohibició.",
"horizon.extras.ui_sound_reserve.tooltip": "Els sons del món deixen lliures 16 dels 247 canals de so, de manera que una inundació d'ells (una gran explosió) no pot silenciar els clics del menú.",
"horizon.extras.music_page.tooltip": "Music & Sounds obté una pàgina Música...: reprodueix ara qualsevol pista integrada o desactiva-ne una perquè el joc deixi de triar-la. No canvia res fins que desactives una pista.",
"horizon.extras.mouse_tweaks.tooltip": "Arrossegueu amb la dreta en col·loqueu un per ranura, l'arrossegar amb l'esquerra recull l'element retingut, Maj + arrossegueu amb l'esquerra mou cada ranura.",
"horizon.extras.mouse_tweaks_wheel.tooltip": "Avall envia un element de la ranura a l'altre costat, cap amunt en torna un.",
"horizon.extras.sort.tooltip": "Ordena l'inventari, els cofres, els barrils, els cofres ender i les caixes shulker en ordre de pestanyes creatives, fusionant les piles.",
"horizon.extras.sort_key.tooltip": "Ordena la part sota el cursor. Feu clic i, a continuació, premeu una tecla; La fugida ho deslliga.",
"horizon.extras.no_ai.tooltip": "Amaga i desactiva totes les funcions d'IA: control d'agents, horizon-ctl i servidors MCP. L'interruptor del llançador també.",
"horizon.extras.agent_control.tooltip": "El vostre propi agent d'IA pot jugar a aquest joc mitjançant horizon-ctl o un client MCP, només en aquest ordinador.",
"horizon.extras.discord_presence.tooltip": "Mostra Playing Horizon al teu perfil de Discord mentre s'executa Discord: menús, un jugador o multijugador, la dimensió i el temps jugat. Mai el nom d'un món ni l'adreça d'un servidor.",
"Calibrate...": "Calibrar...",
"Calibrate Anyway...": "Calibra de totes maneres...",
"Use Automatic Setup": "Utilitzeu la configuració automàtica",
"Shortcut: %s": "Drecera: %s",
"Next": "A continuació",
"HDR Calibration %s/%s: %s": "Calibració HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Estableix les tres imatges de sota de pantalla completa, una imatge de prova a la vegada.",
"A key that opens this screen from the game. Not bound at first.": "Una clau que obre aquesta pantalla des del joc. No lligat al principi.",
"Your display didn't say how bright it gets: calibrate to set it up.": "La pantalla no va dir la brillantor que té: calibra per configurar-la.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Ja està configurat per a la vostra pantalla. Calibreu només si l'HDR sembla incorrecte.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Torna a posar la brillantor màxima i el blanc del paper a la configuració de la pantalla.",
"Set up for your display. Leave on Auto unless it looks wrong.": "Configureu-vos per a la vostra pantalla. Deixeu-lo en Auto tret que sembli malament.",
"Lower it until the logo just disappears into the black.": "Baixeu-lo fins que el logotip només desaparegui al negre.",
"Raise it until the logo just disappears into the bright square.": "Aixequeu-lo fins que el logotip desaparegui al quadrat brillant.",
"Make the card a white that's comfortable to look at for a long time, in the light you play in.": "Feu que la targeta sigui blanca que sigui còmode de mirar durant molt de temps, a la llum on jugueu.",
"horizon.splitscreen.profiles.forget": "Oblida't",
"horizon.splitscreen.profiles.create": "Crear",
"horizon.splitscreen.profiles.you": " - tu",
"horizon.splitscreen.profiles.forget_named": "oblida %s",
"horizon.splitscreen.profiles.previous": "Anterior",
"horizon.splitscreen.profiles.next": "A continuació",
"horizon.splitscreen.profiles.new_button": "Perfil nou...",
"horizon.splitscreen.profiles.forget_title": "Heu oblidat el perfil?",
"horizon.splitscreen.profiles.forget_leaves": "Surt de la llista d'aquesta instància.",
"horizon.splitscreen.profiles.forget_kept": "El progrés mundial i la configuració es mantenen.",
"horizon.splitscreen.profiles.new_title": "Nou perfil",
"horizon.splitscreen.profiles.name_hint": "Nom (de 3 a 16 lletres, dígits, _)",
"horizon.splitscreen.profiles.switch_title": "Canvia de perfil",
"horizon.splitscreen.profiles.offline_note": "Els perfils fora de línia pertanyen a aquesta instància",
"horizon.splitscreen.profiles.move": "Mou-te on jugues",
"horizon.splitscreen.profiles.move_place": "Mou on jugues: %s",
"horizon.friends.title": "Juga amb els amics",
"horizon.friends.hint": "Els amics enganxen el codi després del botó Amics de la pantalla del títol, a la connexió directa o a la pàgina de reproducció del llançador.",
"horizon.friends.copied": "Copiat",
"horizon.friends.copy_code": "Copia el codi",
"horizon.friends.stop_sharing": "Deixa de compartir",
"horizon.friends.none_yet": "Encara no hi ha amics.",
"horizon.friends.here_now": "Aquí ara: %s",
"horizon.friends.share_intro": "Comparteix aquest món darrere d'un codi d'invitació: s'obre a LAN i els amics d'Internet s'uneixen amb el codi. Cap servidor nostre està implicat.",
"horizon.friends.start_sharing": "Comenceu a compartir",
"horizon.friends.troubleshooting": "Resolució de problemes...",
"horizon.friends.stopped": "S'ha deixat de compartir el codi d'invitació.",
"horizon.friends.short_code_changed": "Algú va seguir provant un codi curt equivocat, així que va canviar.",
"horizon.friends.wrong_code": "Algú ha provat un codi incorrecte.",
"horizon.friends.code_ready": "El teu codi d'invitació està preparat: Menú de joc, Juga amb els amics. Copia'l i envia'l als teus amics.",
"horizon.friends.share_failed": "No s'ha pogut compartir el món: %s",
"horizon.friends.finding_route": "Trobar com els amics et poden arribar...",
"horizon.osk.shift": "Canvi",
"horizon.osk.space": "Espai",
"horizon.osk.delete": "Suprimeix",
"horizon.osk.type": "Tipus",
"horizon.osk.symbols": "Símbols",
"horizon.osk.close": "Tancar",
"horizon.extras.chat_animation.tooltip": "Una nova línia de xat es desplaça des de l'esquerra mentre s'esvaeix, en lloc de semblar sencera.",
"horizon.extras.chat_indicator.tooltip": "La barra de colors a l'esquerra d'una línia de xat.",
"horizon.extras.wheel_cycles_settings.tooltip": "La roda de desplaçament sobre un botó a la configuració canvia el seu valor, com a la vainilla. Desactivada, la roda només desplaça la llista.",
"horizon.extras.pack_browser.tooltip": "Select Resource Packs obté un botó Afegeix que cerca Modrinth i instal·la un paquet a la carpeta de paquets. Desactivat, el joc mai contacta amb Modrinth.",
"horizon.extras.splitscreen.tooltip": "En un món, un altre controlador pot unir-se com a jugador nou: premeu A quan ho demani, després trieu un perfil i on jugar.",
"horizon.splitscreen.setup.monitor_unknown": "un monitor",
"horizon.splitscreen.setup.window": "Finestra %s",
"horizon.splitscreen.setup.new_window": "Finestra nova",
"horizon.splitscreen.setup.fill_monitor": "Ompliu %s",
"horizon.splitscreen.setup.done": "Fet",
"horizon.splitscreen.setup.on": "Encès",
"horizon.splitscreen.setup.off": "Apagat",
"horizon.splitscreen.setup.player": "Jugador %s",
"horizon.splitscreen.setup.revert": "Revertir",
"horizon.splitscreen.setup.apply": "Aplicar",
"horizon.splitscreen.setup.warning.empty_main": "Ningú juga a la finestra 1: s'amaga",
"horizon.splitscreen.setup.warning.empty_close": "Ningú juga a la finestra %s: es tanca en Aplicar",
"horizon.splitscreen.setup.warning.empty_new": "Ningú juga a la finestra %s: no s'obrirà",
"horizon.splitscreen.setup.warning.on_main": "La finestra %s omple el monitor de la finestra 1: els seus jugadors juguen a la finestra 1",
"horizon.splitscreen.setup.warning.unplugged": "El monitor de la finestra %s no està connectat: els seus jugadors juguen a la finestra 1",
"horizon.splitscreen.setup.status.off": "La pantalla dividida està apagada.",
"horizon.splitscreen.setup.status.enable_hint": "Enceneu-lo i, a continuació, premeu un botó d'un altre controlador d'un món.",
"horizon.splitscreen.setup.status.join_hint": "Premeu A per unir-vos i triar un perfil.",
"horizon.splitscreen.setup.status.idle": "Pantalla dividida inactiva",
"horizon.splitscreen.setup.status.alone": "No hi ha altres jugadors locals.",
"horizon.splitscreen.setup.status.solo_controls": "Els controls individuals i la retroalimentació del controlador no canvien.",
"horizon.splitscreen.setup.window_empty": "Finestra %s: buida",
"horizon.splitscreen.setup.note.select": "Feu clic a un reproductor o arrossegueu-lo a una finestra o un monitor.",
"horizon.splitscreen.setup.note.apply": "Aplica per jugar d'aquesta manera, torna a desfer.",
"horizon.splitscreen.setup.note.place": "On hauria de jugar %s?",
"horizon.splitscreen.setup.this_player": "aquest jugador",
"horizon.splitscreen.setup.switch_disabled": "La pantalla dividida s'apaga quan els altres han marxat (el seu menú de pausa).",
"horizon.splitscreen.setup.window_place": "Finestra %s (%s de %s)",
"horizon.splitscreen.setup.names": "Noms",
"horizon.splitscreen.setup.identify": "Identificar",
"horizon.splitscreen.setup.layout.two": "2 compartint: %s",
"horizon.splitscreen.setup.layout.three": "3 compartint: %s",
"horizon.splitscreen.setup.layout.two.0": "Automàtic",
"horizon.splitscreen.setup.layout.two.1": "Colze a colze",
"horizon.splitscreen.setup.layout.two.2": "A dalt i a baix",
"horizon.splitscreen.setup.layout.three.0": "Un d'ample al damunt",
"horizon.splitscreen.setup.layout.three.1": "Un alt a l'esquerra",
"horizon.splitscreen.setup.layout.three.2": "Colze a colze",
"horizon.splitscreen.setup.help.player": "Feu clic o arrossegueu %s per moure'ls.",
"horizon.splitscreen.setup.help.layout_auto": "%s (al costat de l'altre quan la finestra és ampla). Premeu per canviar.",
"horizon.splitscreen.setup.help.layout": "%s. Premeu per canviar.",
"horizon.splitscreen.setup.help.switch": "Pantalla dividida per a aquest ordinador, encès o apagat.",
"horizon.splitscreen.setup.help.identify": "La vista de cada jugador mostra en quina finestra es troba.",
"horizon.splitscreen.setup.help.names": "El nom de cada jugador a la cantonada de la seva vista.",
"horizon.splitscreen.setup.help.revert": "Desfer els canvis encara no aplicats.",
"horizon.splitscreen.setup.help.apply": "Juga com mostra el mapa.",
"horizon.splitscreen.setup.help.unchanged": "Encara no ha canviat res.",
"horizon.splitscreen.setup.give_up_pad": "Renunciar Pad",
"horizon.splitscreen.prompt.controller_connected": "Controlador connectat",
"horizon.splitscreen.prompt.controller_disconnected": "Controlador desconnectat",
"horizon.splitscreen.prompt.controller_disconnected.hint": "Torneu-lo a connectar o premeu un botó d'un altre",
"horizon.splitscreen.prompt.join.one": "%s vol unir-se",
"horizon.splitscreen.prompt.join.many": "%s els controladors volen unir-se",
"horizon.splitscreen.prompt.join.yes": "Uneix-te",
"horizon.splitscreen.prompt.join.as_p1": "Juga com a P1",
"horizon.splitscreen.prompt.lost": "%s: controlador desconnectat",
"horizon.splitscreen.prompt.lost.use_another": "Fes servir un altre",
"horizon.splitscreen.prompt.lost.leave": "marxar",
"horizon.splitscreen.setup.help.give_up_pad": "El vostre controlador deixa de conduir-vos i se li ofereix unir-se (A).",
"horizon.splitscreen.setup.note.held_at": "%s a %s: A la deixa caure aquí, la B la torna a posar.",
"horizon.splitscreen.setup.note.held": "%s agafat: esquerra/dreta escull on, A cau, B el torna a posar.",
"Hides models behind opaque blocks. Async checks off-thread; entities tick.": "Amaga models darrere de blocs opacs. Async comprova fora de fil; les entitats marquen.",
"Block Entities": "Bloquejar entitats",
"Shulker Boxes": "Caixes Shulker",
"Reuses settled boxes; lid movement stays animated.": "Reutilitza caixes assentades; el moviment de la tapa es manté animat.",
"Signs": "Signes",
"Reuses sign text until text, light, or font changes.": "Reutilitza el text del signe fins que canviï el text, la llum o la font.",
"Decorated Pots": "Olles decorades",
"Reuses still pots; break wobble and sherd changes redraw.": "Reutilitza olles encara; trencar l'oscil·lació i els canvis de fragments tornar a dibuixar.",
"Bells": "Campanes",
"Reuses still bells; ringing stays animated.": "Reutilitza campanes encara; el timbre es manté animat.",
"Chests": "Cofres",
"Reuses closed chests; moving lids stay animated.": "Reutilitza cofres tancats; les parpelles en moviment es mantenen animades.",
"Banners": "Pancartes",
"Reuses poles; flags and pattern layers still update.": "Reutilitza pals; les banderes i les capes de patrons encara s'actualitzen.",
"Copper Golem Statues": "Estàtues de Golem de coure",
"Reuses statue geometry until its block state changes.": "Reutilitza la geometria de l'estàtua fins que el seu estat de bloc canvia.",
"Leaf Culling": "Talla de fulles",
"Every leaf face. Vanilla's fancy leaves.": "Cada cara de fulla. Fulles elegants de vainilla.",
"Distant Plant Dither": "Dither de planta llunyana",
"Keeps fine plants in high-res packs from fading far away.": "Evita que les plantes fines en paquets d'alta resolució s'esvaeixin lluny.",
"Screenshot Quality": "Qualitat de la captura de pantalla",
"F2 screenshots at this many times the game's resolution. Higher takes longer.": "Captures de pantalla F2 amb moltes vegades la resolució del joc. Més alt triga més.",
"Frame Generation": "Generació de fotogrames",
"Shows a made-up frame between each two real ones: smoother, a little more lag.": "Mostra un marc inventat entre cadascun dels dos reals: més suau, una mica més de retard.",
"Reuses the pack shadow map when sun, view and world match. Off by default.": "Reutilitza el mapa d'ombres del paquet quan el sol, la vista i el món coincideixen. Desactivat per defecte.",
"Find BlueMap": "Busca BlueMap",
"On servers, finds their BlueMap web map for far terrain. Next join.": "Als servidors, troba el seu mapa web BlueMap per a terrenys llunyans. A continuació uneix-te.",
"BlueMap Radius": "Radi BlueMap",
"How far around you BlueMap tiles are downloaded. Next join.": "A quina distància al teu voltant es descarreguen les fitxes de BlueMap. A continuació uneix-te.",
"Synchronous": "Sincrònic",
"Asynchronous": "Asíncron",
"Solid": "Sòlid",
"Solid Aggressive": "Sòlid Agressiu",
"Hollow": "Buit",
"Leaves fully inside a canopy draw solid: fewer see-through layers.": "Les fulles completament dins d'un dosser dibuixen sòlids: menys capes transparents.",
"Leaves with one open side draw solid too: faster, chunkier edges.": "Les fulles amb un costat obert també dibuixen sòlids: vores més ràpides i gruixudes.",
"Drops faces between leaves: faster, but gaps look through.": "Deixa caure cares entre les fulles: més ràpid, però els buits es veuen a través.",
"Hotbar: Previous Slot": "Barra activa: ranura anterior",
"Hotbar: Next Slot": "Hotbar: següent ranura",
"horizon.agent.connected.one": "Connectat: 1 connexió d'agent",
"horizon.agent.connected.many": "Connectat: %s connexions d'agent",
"horizon.agent.connected.one_ago": "Connectat: 1 connexió d'agent, darrera ordre fa %s s",
"horizon.agent.connected.many_ago": "Connectat: %s connexions d'agent, darrera ordre fa %s s",
"horizon.agent.waiting": "Esperant un agent.",
"horizon.agent.off": "Desactivat: els agents no poden accedir a aquest joc.",
"horizon.agent.session_opened": "Obert per a aquesta sessió pel programa que va iniciar el joc.",
"horizon.agent.socket": "Socket: %s (copieu els detalls del camí complet)",
"horizon.agent.local_only": "Només local: agents en aquest ordinador, mai la xarxa.",
"Auto (%s nits)": "Automàtic (%s nits)",
"Hides models behind opaque blocks. Entities update. Off with custom packs.": "Amaga models darrere de blocs opacs. Actualització d'entitats. Apaga amb paquets personalitzats.",
"horizon.extras.repro_keys": "Dreceres de gravació",
"horizon.extras.repro_keys.tooltip": "F3+R inicia o atura un enregistrament de reproducció i F3+E escriu el seu paquet. Desactivat per defecte: una gravació copia el món.",
"horizon.agent.record_session": "Inicieu la sessió de gravació",
"horizon.agent.record_stop": "Atura la sessió de gravació",
"horizon.agent.export_repro": "Exporta el paquet Repro",
"horizon.repro.started": "La gravació va començar; F3+R l'atura i escriu el paquet.",
"horizon.repro.stopped": "Enregistrament aturat: %s",
"horizon.repro.bundle": "Paquet de reproducció: %s",
"horizon.repro.error": "Reproducció: %s",
"horizon.repro.no_recording": "Encara no hi ha cap gravació per exportar.",
"horizon.repro.replay_started": "La reproducció ha començat: %s",
"horizon.pack.addInfo": "Afegiu un paquet de recursos de Modrinth",
"horizon.pack.addWorking": "Preguntant a Modrinth...",
"horizon.pack.addNone": "No s'han trobat paquets de recursos",
"horizon.pack.addTitle": "Afegiu un paquet de recursos",
"horizon.pack.install": "Instal·lar",
"horizon.pack.installing": "Instal·lació",
"horizon.pack.installed": "Instal·lat",
"horizon.pack.search": "Cerca",
"horizon.pack.searchHint": "Cerca en Modrinth...",
"horizon.pack.filters": "Filtres",
"horizon.pack.filter.realistic": "Realista",
"horizon.pack.filter.simplistic": "Simplista",
"horizon.pack.filter.vanillaLike": "semblant a la vainilla",
"horizon.pack.addFailed": "No s'ha pogut afegir el paquet: %s",
"horizon.extras.header.cosmetics": "Cosmètics",
"horizon.extras.cosmetics_mine": "Els meus cosmètics",
"horizon.extras.cosmetics_mine.tooltip": "Ocult: tots els jugadors d'Horizon et veuen sense els teus cosmètics, tu inclòs. El mateix interruptor que l'armari del llançador.",
"horizon.extras.cosmetics_others": "Cosmètica d'altres jugadors",
"horizon.extras.cosmetics_others.tooltip": "Què porten altres jugadors d'Horizon: tot, només els seus estils d'etiquetes de nom o res. Amagat, el joc mai pregunta què porten.",
"horizon.extras.cosmetics_auras": "Les aures i els camins dels altres",
"horizon.extras.cosmetics_auras.tooltip": "Aures i rastres que porten altres jugadors. Desactivat per defecte.",
"horizon.extras.cosmetics.shown": "Es mostra",
"horizon.extras.cosmetics.hidden": "Ocult",
"horizon.extras.cosmetics.name_tags": "Només etiquetes de nom"
}
@@ -10,13 +10,10 @@
"horizon.options.move_deadzone": "Mrtvá zóna pohybu",
"horizon.options.look_deadzone": "Mrtvá zóna pohledu",
"horizon.options.button_glyphs": "Glyfy tlačítek",
"horizon.options.menu_navigation": "Navigace v menu",
"horizon.options.on_screen_keyboard": "Klávesnice na obrazovce",
"horizon.options.chat_delay": "Zpoždění chatu",
"horizon.options.lightbar.health": "Zdraví",
"horizon.options.lightbar.dimension": "Dimenze",
"horizon.options.menu_focus": "Zaměřte se",
"horizon.options.menu_pointer": "Ukazatel",
"horizon.options.keyboard.horizon": "Horizont",
"horizon.options.keyboard.steam": "Pára",
"horizon.options.unavailable": "Tato funkce zatím není v Horizon k dispozici.",
@@ -0,0 +1,492 @@
{
"horizon.options.controller": "Контроллер",
"horizon.options.three_d_skin_layers": "3D скин сийӗсем",
"horizon.options.vibration": "Вибраци",
"horizon.options.adaptive_triggers": "Адаптивлӑ триггерсем",
"horizon.options.lightbar": "Ҫутӑ йӗрӗ",
"horizon.options.look_sensitivity": "Пӑхӑвӑн сисӗмлӗхӗ",
"horizon.options.invert_x": "Пӑхӑвӑн X инверсийӗ",
"horizon.options.invert_y": "Пӑхӑвӑн Y инверсийӗ",
"horizon.options.sneak_button": "Йӑпшӑнмалли кнопка",
"horizon.options.move_deadzone": "Куҫӑмӑн вилӗ зони",
"horizon.options.look_deadzone": "Пӑхӑвӑн вилӗ зони",
"horizon.options.button_glyphs": "Кнопка глифӗсем",
"horizon.options.on_screen_keyboard": "Экран ҫинчи клавиатура",
"horizon.options.chat_delay": "Чат кая юлни",
"horizon.options.lightbar.health": "Сывлӑх",
"horizon.options.lightbar.dimension": "Виҫе",
"horizon.options.glyphs.auto": "Авто",
"horizon.options.keyboard.auto": "Авто",
"horizon.options.unavailable": "Ку функци Horizon-ра хальлӗхе ҫук-ха.",
"horizon.options.search": "Шырав настройкисем...",
"horizon.pages.mods": "Модсем",
"horizon.pages.shader_packs": "Шейдер пакетӗсем",
"horizon.pages.shader_options": "Шейдер опцийӗсем",
"horizon.pages.controller": "Контроллер настройкисем",
"horizon.pages.controller_binds": "Контроллер ҫыхӑнтарать",
"horizon.pages.extras": "Horizon Хушма",
"horizon.pages.settings": "Horizon Настройки",
"horizon.pages.agent": "ИИ агенчӗ",
"horizon.split.leave": "Тухса кай",
"horizon.pages.split_settings": "Сплитскрин настройкисем",
"horizon.pages.split_profiles": "Кам вылять?",
"horizon.pages.split_setup": "Сплит-скрин настройки",
"horizon.pages.music": "Музыка",
"horizon.pages.music.menu": "Меню",
"horizon.pages.music.overworld": "Ҫӳлти тӗнче",
"horizon.pages.music.creative": "Пултарулӑх",
"horizon.pages.music.underwater": "Шыв айӗнче",
"horizon.pages.music.end": "Вӗҫӗ",
"horizon.pages.music.boss": "Пуҫлӑх",
"horizon.pages.music.credits": "Кредитсем",
"horizon.pages.music.overworld_biomes": "Тӗнче ҫийӗнчи биомсем",
"horizon.pages.music.nether_biomes": "Ҫӗр айӗнчи биомсем",
"horizon.pages.music.end_biomes": "Вӗҫлӗ биомсем",
"horizon.pages.music.other": "Ытти музыка",
"horizon.pages.music.empty": "Музыка йӗрӗсем ҫук: вӑйӑ сассисем ҫӗкленмеҫҫӗ.",
"horizon.pages.music.tooltip": "Вӑрттӑн трексем, лару-тӑру тӑрӑх: халех пӗрне выляса пӑхӑр е хӑшне те пулин сӳнтерӗр, вара вӑйӑ ӑна суйласа илме чарӑнӗ.",
"horizon.pages.settings.tooltip": "Horizon хӑйӗн настройкисем: графика, контроллерсем, экстрасем тата модсем.",
"horizon.pages.graphics.tooltip": "Графика, ансат: кашни настройка мӗн хак тӑнине калать.",
"horizon.pages.graphics_advanced.tooltip": "Кашни графика настройки, Horizon кӗнӗ, кашни хакӗ мӗн чухлӗ.",
"horizon.pages.controller.tooltip": "Контроллерсем: вӗсен кнопкисем, патаккисем, вибраци тата меню навигацийӗ.",
"horizon.pages.extras.tooltip": "Опционаллӑ хушӑмсем, пурте стандартлӑ майпа: вӑйӑ ваниль пек юлать, эсир ӑна ҫутиччен.",
"horizon.pages.agent.tooltip": "Хӑвӑрӑн AI агентне ҫак компьютер ҫинче horizon-ctl тата MCP клиентсемпе выляма ирӗк парӑр.",
"horizon.pages.split_profiles.tooltip": "Кам вылять: ҫак вӑйӑ папкинче упранакан профиль.",
"horizon.pages.split_setup.tooltip": "Чӳречесемпе мониторсем, кам ӑҫта выляни.",
"horizon.pages.shader_packs.tooltip": "Шейдер пакетӗсем шейдер пакетӗсенчен: Horizon хӑйӗн тата шейдерсен/ форматӗнчисем. Пӗрре чупать.",
"horizon.pages.mods.tooltip": "Horizon валли тунӑ модсем, horizon-mods папкинчен. Кашниех хӑйӑр ещӗкӗнче ӗҫлет те ӑна ҫутиччен сӳнсе тӑрать.",
"horizon.pages.open.graphics": "Графика...",
"horizon.pages.open.graphics_advanced": "Анлӑ графика...",
"horizon.pages.open.shader_packs": "Шейдер пакетӗсем...",
"horizon.pages.open.controller": "Контроллер настройкисем...",
"horizon.pages.open.split_setup": "Сплитскрин...",
"horizon.pages.open.extras": "Ытлашши...",
"horizon.pages.open.mods": "Модсем...",
"horizon.pages.open.agent": "ИИ агенчӗ...",
"horizon.pages.open.horizon_graphics": "Horizon Графика...",
"horizon.pages.open.controller_binds": "Контроллер ҫыхӑнтарать...",
"horizon.pages.open.horizon_settings": "Horizon Настройки...",
"horizon.pages.open.music": "Музыка...",
"horizon.mods.empty": "Модсем ҫук: .hmod файлсене horizon-mods папкине лартӑр",
"horizon.mods.stopped": " (Чарӑннӑ)",
"horizon.mods.not_built": " (Туман)",
"horizon.mods.settings": "Настройкӑсем",
"horizon.mods.in_world": " (тӗнчене улӑштарма хӑвар)",
"horizon.mods.open_folder": "Мод папкине уҫӑр",
"horizon.shader_packs.empty": "Шейдер пакетӗсем ҫук: вӗсене шейдер пакетӗсем папкӑна лартӑр",
"horizon.shader_options.open": "Шейдер опцийӗсем...",
"horizon.shader_options.profile": "Профиль",
"horizon.shader_options.loading": "Шейдер пакетӗн опцийӗсене вулатӑп...",
"horizon.agent.copy_details": "Ҫыхӑну даннӑйӗсене копилесе илӗр",
"horizon.multiplayer.join": "Пӗрлешӗр",
"horizon.multiplayer.cancel_reconnecting": "Ҫӗнӗрен ҫыхӑнассине пӑрахӑҫла",
"horizon.multiplayer.join_friend": "Тус ҫумне хутшӑн",
"horizon.multiplayer.invite_code": "Йыхрав кодӗ",
"horizon.multiplayer.relay": "Реле (опционально)",
"Preset": "Малтанах лартнӑ",
"LOD Distance": "LOD дистанцийӗ",
"Far Detail": "Инҫетри вак-тӗвек",
"Cave Culling": "Ханкӑрсене пӗтерни",
"Entity Culling": "Субъектсене пӗтересси",
"Chunk Updates": "Ҫӗнӗлӗхсем",
"Upload Budget": "Бюджета ярса парӑр",
"Sky": "Тӳпе",
"Fog": "Тӗтре",
"Weather": "Ҫанталӑк",
"Animated Textures": "Анимациллӗ текстурӑсем",
"Present Mode": "Хальхи режим",
"Frame Pacing": "Кадр темпӗ",
"Worker Threads": "Ӗҫлекен ҫипсем",
"Resolution Scale": "Ҫирӗплӗх шкали",
"Anti-aliasing": "Анти-плюс",
"Upscaling": "Ӳстересси",
"Paper White": "Шурӑ хут",
"Peak Brightness": "Чи пысӑк ҫутӑлӑх",
"Black Level": "Хура ур.",
"UI Brightness": "UI ҫутӑлӑхӗ",
"Battery Mode": "Аккумулятор режимӗ",
"Graphics API": "Графика API",
"Translucent Batching": "Ҫурма ҫутӑ ҫыхӑну",
"Performance Overlay": "Ӗҫ тухӑҫлӑхӗн ҫиелти сийӗ",
"Full Detail Distance": "Пӗтӗмӗшле дистанци",
"On": "Ҫ",
"Off": "Сӳнтернӗ",
"Unlimited": "Чикӗсӗр",
"Auto": "Авто",
"Custom": "Ятарлӑ",
"Performance": "Спектакль",
"Balanced": "Шайлашуллӑ",
"Quality": "Пахалӑх",
"Ultra": "Ультра",
"Ultra Performance": "Ультра ӗҫ тухӑҫлӑхӗ",
"Native": "Натив",
"Full": "Тулли",
"Reduced": "Пӗчӗклетнӗ",
"Efficient": "Тухӑҫлӑ",
"Precise": "Тӗрӗс",
"Auto (VSync)": "Авто (VSync)",
"Mailbox": "Почта ещӗкӗ",
"Immediate": "Çийӗнчех",
"FPS and Graph": "FPS тата график",
"TAA": "ТАА",
"No cost": "Тӑкак ҫук",
"Low cost": "Йӳнӗ хак",
"Medium cost": "Вӑтам хак",
"High cost": "Пысӑк хак",
"Saves time": "Вӑхӑта перекетлет",
"Graphics": "Графика",
"Done": "Пӗтернӗ",
"This feature is not yet available in Horizon.": "Ку функци Horizon-ра хальлӗхе ҫук-ха.",
"Your display or system doesn't offer HDR output.": "Сирӗн дисплей е система HDR тухӑҫне сӗнмест.",
"Turn HDR on to use it.": "Ӑна усӑ курма HDR ҫутӑр.",
"Your GPU or graphics API doesn't support it.": "Сирӗн GPU е графика API ӑна пулӑшмасть.",
"%s chunks": "%s татӑкӗсем",
"%s per frame": "%s кашни кадршӑн",
"%s MiB/frame": "%s МиБ/кадр",
"%s KiB/frame": "%s КиБ/кадр",
"Auto (%s)": "Авто (%s)",
"Full-detail terrain range. LOD Distance can extend local worlds farther.": "Пӗтӗм детальлӗ ҫӗр диапазонӗ. LOD Дистанци вырӑнти тӗнчесене тата аяккарах тӑсма пултарать.",
"Coarse distant terrain in local worlds. Integrated GPUs stop at 128 chunks.": "Вырӑнти тӗнчери инҫетри ҫӗр лаптӑкӗ. Пӗрлештернӗ GPU-сем 128 татӑк ҫинче чарӑнаҫҫӗ.",
"Reduced draws far terrain coarser: faster on this integrated GPU.": "Ҫак пӗрлештернӗ GPU-ра пӗчӗклетни ҫӗре чылай хытӑрах туртать: хӑвӑртрах.",
"Reduced draws far terrain coarser, for GPUs it slows down.": "Пӗчӗклетни ҫӗре чылай хытӑрах туртать, GPU-семшӗн вӑл ерипенлетет.",
"Field of view. Wider shows more terrain at once.": "Курӑну уйӗ. Сарлакараххи пӗр харӑсах ытларах ҫӗре кӑтартать.",
"Caps frames per second; a cap saves power and heat.": "Ҫеккунтра кадрсене чакарать; ҫӗлӗк энергипе ӑшша перекетлет.",
"Waits for the display's refresh: no tearing, a little latency.": "Дисплей ҫӗнелессине кӗтет: ҫурӑлмасть, кӑштах кая юлать.",
"Size of menus and the HUD.": "Менюсен калӑпӑшӗ тата HUD.",
"Sets the options below together. Changing one makes it Custom.": "Аялта панӑ опцисене пӗрле лартать. Пӗрне улӑштарни ӑна Custom тӑвать.",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "Лава, вут-ҫулӑм тата хӗвел ҫути шурӑран ҫутӑрах ҫуталса тӑраҫҫӗ. HDR дисплей кирлӗ.",
"Your display's darkest black. 0 for OLED; LCDs glow a little.": "Сирӗн дисплей чи тӗксӗм хура. 0 OLED валли; ЖК-дисплейсем кӑштах ҫуталаҫҫӗ.",
"How bright highlights get. Auto: the peak your system reports.": "Мӗнле ҫутӑ ҫутӑсем пулаҫҫӗ. Авто: сирӗн системӑн пӗлтерекен пикӗ.",
"How bright white paper and menus look. Auto: your desktop's white.": "Мӗнле ҫутӑ шурӑ хут тата менюсем курӑнаҫҫӗ. Авто: сирӗн ӗҫ сӗтелӗ шурӑ.",
"How bright the HUD and menus are, against paper white.": "HUD тата менюсем мӗнле ҫутӑ, хут ҫинчи шурӑ тӗспе танлаштарсан.",
"How far mobs, crops and machines keep working. CPU time.": "Мобсем, культурӑсем тата машинӑсем мӗн таран ӗҫлеҫҫӗ. Процессор вӑхӑчӗ.",
"How far away mobs and items are drawn, as a share of normal.": "Мобсемпе япаласем мӗн чухлӗ аяккалла туртӑнаҫҫӗ, йӗркеллӗ пай пек.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Хупӑ участоксем хыҫӗнче пытаннӑ ҫӗре сиктерсе хӑварать. Офф нумай тӑкакланать.",
"Hides entities behind opaque blocks.": "Тӗксӗм блоксем хыҫӗнче япаласене пытарать.",
"No entity occlusion culling.": "Объект окклюзийӗ ҫук.",
"CPU occlusion test runs this frame.": "Ҫак кадра процессор окклюзи тӗрӗслевӗ ӗҫлеттерет.",
"Occlusion checks run on a worker thread.": "Окклюзи тӗрӗслевӗсем ӗҫлекен ҫип ҫинче ӗҫлеҫҫӗ.",
"Meshed sections uploaded per frame. Lower is smoother while loading.": "Кадр пуҫне ярса панӑ сетчатӑй пайсем. Аялта тиенӗ чухне тикӗсрех.",
"Soft shadows in corners. Costs chunk building time only.": "Кӗтессенче ҫемҫе мӗлкесем. Ҫурт-йӗр тунӑ вӑхӑта ҫеҫ тӑкаклать.",
"Smaller copies of block textures for distant terrain: less shimmer.": "Инҫетри ҫӗр ҫинчи блок текстурин пӗчӗкрех копийӗсем: йӑлтӑркка сахалрах.",
"Blends grass and water colours across biomes when building chunks.": "Татӑксем тунӑ чухне курӑкпа шыв тӗсӗсене биомсем тӑрӑх хутӑштарать.",
"Cloud layer: off, flat (fast) or 3D (fancy).": "Пӗлӗт сийӗ: сӳннӗ, лаптак (хӑвӑрт) е 3D (чаплӑ).",
"How far clouds reach, in chunks.": "Пӗлӗтсем мӗн таран ҫитеҫҫӗ, татӑкӑн-татӑкӑн.",
"Draws the sky, sun, moon and stars.": "Тÿпене, хӗвеле, уйӑха тата ҫӑлтӑрсене ÿкерет.",
"Distance fog. Off shows the edge of the loaded world.": "Инҫетри тӗтре. Off — тиеннӗ тӗнче хӗррине кӑтартать.",
"Draws rain and snow.": "Ҫумӑрпа юр ҫӑвать.",
"How far rain and snow fall around you.": "Сирӗн тавра ҫумӑрпа юр мӗн чухлӗ ӳкет.",
"How many particles effects spawn.": "Миҫе пайӑрка эффект ҫуратать.",
"Round shadows under mobs and items.": "Ҫаврака мӗлкесем ушкӑнсемпе япаласем айӗнче.",
"Leaves with see-through gaps. Off draws them solid.": "Ҫулҫӑсем витӗр курӑнакан хушӑксемлӗ. Офф вӗсене ҫирӗп туртать.",
"Sorts water and glass more exactly behind each other.": "Шывпа кӗленчене пӗр-пӗрин хыҫҫӑн тӗрӗсрех уйӑрать.",
"Which chunk rebuilds wait for the frame: none, nearby or yours.": "Хӑш татӑк ҫӗнӗрен йӗркеленет кадра кӗтет: ҫук, ҫывӑхра е санӑн.",
"Animates water, lava, fire and other moving textures.": "Шыв, лава, вут-ҫулӑм тата ытти куҫакан япаласене чӗртсе тӑратать.",
"World pixels per screen pixel. Below 100% is faster on the GPU.": "Экран пикселӗ пуҫне тӗнче пикселӗсем. 100% аяларах GPU ҫинче хӑвӑртрах.",
"Smooths jagged edges. FXAA: softer. TAA: smoothest, moving things may trail.": "Шӑллӑ хӗррисене тикӗслетет. FXAA: ҫемҫерех. ТАА: чи тикӗс, куҫакан япаласем йӗрлеме пултараҫҫӗ.",
"Renders the world smaller and rebuilds it with TAA. Faster, softer.": "Тӗнчене пӗчӗклетет те ӑна TAA-па ҫӗнӗрен тӑвать. Хӑвӑртрах, ҫемҫерех.",
"HDR output on an HDR display: lava, fire and sunlight shine brighter.": "HDR дисплей ҫинче HDR тухӑҫӗ: лава, вут тата хӗвел ҫути ҫутӑрах ҫутатаҫҫӗ.",
"How frames reach the screen while VSync is off.": "VSync ӗҫлемен чухне кадрсем экран ҫине мӗнле ҫитеҫҫӗ.",
"Efficient sleeps until the next frame; Precise spins, like vanilla.": "Тепӗр кадрчченех лайӑх ҫывӑрать; Тӗрӗс ҫаврӑнусем, ваниль пек.",
"Fills the screen (borderless).": "Экрана тултарать (чикӗсӗр).",
"Brightens dark places.": "Тӗттӗм вырӑнсене ҫутатать.",
"Lowers the frame rate when minimised, or idle too.": "Кадрсен хӑвӑртлӑхне пӗчӗклетнӗ чухне е ӗҫлемен чухне те чакарать.",
"Sways the view while walking.": "Утнӑ чухне курӑнӑва суллантарать.",
"Fades newly loaded chunks in.": "Ҫӗнӗрен ҫӗкленӗ татӑксене ҫухатать.",
"Darkens the screen's edges.": "Экран хӗррисене тӗксӗмлетет.",
"Shows when the world saves.": "Тӗнче хӑҫан ҫӑлӑннине кӑтартать.",
"Shows attack cooldown.": "Атакӑна сивӗтмелли вӑхӑта кӑтартать.",
"60 FPS cap (30 in menus or unfocused), half the workers: less power.": "60 FPS чикӗ (менюсенче 30 е фокуссӑр), ӗҫлекенсен ҫурри: хӑват сахалрах.",
"Threads that generate, light and mesh chunks. Next start.": "Генерацилекен ҫипсем, ҫутӑ тата сетка татӑкӗсем. Тепӗр пуҫламӑш.",
"Vulkan, then DirectX 12 (Windows) or OpenGL (Linux) where it fails. Next start.": "Vulkan, унтан DirectX 12 (Windows) е OpenGL (Linux) ӑҫта вӑл ӗҫлемест. Тепӗр пуҫламӑш.",
"Water and glass in one buffer: fewer draw calls, a bit more VRAM. Next start.": "Шывпа кантӑк пӗр буферта: сахалрах драв чӗнӗвӗсем, кӑштах ытларах VRAM. Тепӗр пуҫламӑш.",
"Mesh data sent to the GPU per frame. Lower is smoother while loading.": "Меш даннӑйӗсем кашни кадрта GPU-на ярса параҫҫӗ. Аялта тиенӗ чухне тикӗсрех.",
"Frame rate and frame times over the game, with a graph.": "Вӑйӑ тӑршшӗпе кадрсен хӑвӑртлӑхӗ тата кадрсен вӑхӑчӗ, графикпа.",
"horizon.multiplayer.finding_friend": "Юлташӑн тӗнчине тупни...",
"horizon.multiplayer.not_signed_in": "Ку сервер онлайн режимра. Унта кӗме Horizon лаунчерта хӑвӑрӑн Microsoft аккаунтпа кӗрӗр.",
"horizon.multiplayer.invalid_address": "Тӗрӗс мар сервер адресӗ: %s",
"horizon.multiplayer.connected_friend_direct": "Тӳрех тусупа ҫыхӑнтӑм...",
"horizon.multiplayer.connected_friend_relay": "Реле урлӑ тусупа ҫыхӑннӑ...",
"General": "Пӗтӗмӗшле",
"Advanced": "Малалла",
"Undo": "Каялла тавӑр",
"Search options": "Шырав опцийӗсем",
"Shortcut": "Кӗске ҫул",
"Distance": "Инҫет",
"Display": "Дисплей",
"Interface": "Интерфейс",
"Sky and weather": "Тӳпе тата ҫанталӑк",
"Lighting and entities": "Ҫутӑ тата япаласем",
"Textures": "Текстурӑсем",
"Image": "Сӑнӳкерчӗк",
"Chunk loading": "Чунк загрузки",
"Culling": "Кульлинг",
"Power": "Влаҫ",
"Frames": "Рамкӑсем",
"Overlay": "Накладка",
"Output": "Тухӑҫ",
"Brightness": "Ҫутӑлӑх",
"How far you see, the window and the HUD.": "Эсир мӗн чухлӗ куратӑр, чӳрече тата HUD.",
"The sky and weather, lighting, textures and the image.": "Тӳпе тата ҫанталӑк, ҫутӑ, текстурӑсем тата сӑн.",
"Chunk loading, culling and power use.": "Татӑксене тиесе ҫӗмӗресси, ҫӗмӗресси тата энергипе усӑ курасси.",
"How frames reach the screen, and the performance overlay.": "Кадрсем экран ҫине мӗнле ҫитни тата ӗҫ тухӑҫлӑхӗн ҫиелти сийӗ.",
"Brighter-than-white output on an HDR display.": "HDR дисплей ҫинче шурӑран ҫутӑрах тухӑҫ.",
"Results for \"%s\"": "Результаты для \"%s\"",
"No settings match.": "Настройкӑсем пӗр пек мар.",
"horizon.title.not_affiliated": "Mojang е Microsoft ҫыхӑнман",
"horizon.extras.header.lighting": "Ҫутӑ",
"horizon.extras.header.third_person": "Виҫҫӗмӗш сӑпат",
"horizon.extras.header.particles": "Частицӑсем",
"horizon.extras.header.inventory": "Инвентаризаци",
"horizon.extras.header.chat": "Чат",
"horizon.extras.header.multiplayer": "Нумай ҫынлӑ",
"horizon.extras.header.sound": "Сасӑ",
"horizon.extras.header.menus": "Менюсем",
"horizon.extras.fullbright": "Фуллбрайт",
"horizon.extras.dynamic_lights": "Динамикӑллӑ ҫутӑсем",
"horizon.extras.own_name_tag": "Хӑйӗн ячӗ",
"horizon.extras.hide_block_particles": "Блок-брейк пайӑркисене пытарӑр",
"horizon.extras.auto_reconnect": "Авто-ҫыхӑну",
"horizon.extras.ui_sound_reserve": "UI сассисем валли каналсем упрӑр",
"horizon.extras.music_page": "Музыка страници",
"horizon.extras.mouse_tweaks": "Шӑши Tweaks управленийӗсем",
"horizon.extras.mouse_tweaks_wheel": "Прокрутка урапи япаласене куҫарать",
"horizon.extras.sort": "Сортировка кнопкисем",
"horizon.extras.sort_key": "Сортировка уҫҫи",
"horizon.extras.no_ai": "ИИ ҫук",
"horizon.extras.agent_control": "Агент контролӗ",
"horizon.extras.discord_presence": "Discord Пуян пурнӑҫ",
"horizon.extras.splitscreen": "Сплитскрин",
"horizon.extras.chat_animation": "Ҫемҫе калаҫу",
"horizon.extras.chat_indicator": "Хыпар кӑтартӑвӗ",
"horizon.extras.wheel_cycles_settings": "Прокрутка урапи настройкӑсене улӑштарать",
"horizon.extras.pack_browser": "Ресурс пакетӗн браузерӗ",
"horizon.extras.fullbright.tooltip": "Блоксемпе объектсене тулли ҫутӑра, тӗттӗм вырӑнсенче те кӑтартӑр.",
"horizon.extras.dynamic_lights.tooltip": "Факелсем тата ытти ҫутӑ ҫӑлкуҫӗсем тытса тӑракан, пӑрахнӑ е ҫунакан ҫутӑ ҫӑлкуҫӗсем хӑйсен таврашӗнчи блоксене ҫутатса тӑраҫҫӗ.",
"horizon.extras.own_name_tag.tooltip": "Ытти вылякансем курнӑ пек, сирӗн ята пуҫ тӗлӗнче виҫҫӗмӗш сӑпатра кӑтартать. Сервер сирӗн командӑн ячӗсене пытарнӑ чухне е эсир курӑнман чухне пытарнӑ.",
"horizon.extras.hide_block_particles.tooltip": "Ҫӗмӗрӗлнӗ блокран ҫурӑлса тухнӑ ҫӳп-ҫап пайӗсене пытарать те эсир кӑларнӑ блокран ишӗлсе анать. Ытти пайӑркасем юлаҫҫӗ.",
"horizon.extras.auto_reconnect.tooltip": "Ҫыхӑну ҫухалнӑ е ҫӗнӗрен пуҫланӑ хыҫҫӑн сервера каялла кӗрет, нихӑҫан та тапӑннӑ е чарнӑ хыҫҫӑн мар.",
"horizon.extras.ui_sound_reserve.tooltip": "Тӗнчери сасӑсем 247 сасӑ каналӗнчен 16-шне пушӑ хӑвараҫҫӗ, ҫавӑнпа та вӗсен шывӗ (пысӑк взрыв) меню ҫине пуснине шӑплантараймасть.",
"horizon.extras.music_page.tooltip": "Music & Sounds Music... страницӑна илет: халех кирек мӗнле вӑрттӑн трека та выляса парӑр е ӑна сӳнтерӗр, вара вӑйӑ ӑна суйласа илме пӑрахӗ. Эсир ҫула сӳнтериччен нимӗн те улшӑнмасть.",
"horizon.extras.mouse_tweaks.tooltip": "Сылтӑм еннелле туртни кашни слота пӗрер вырнаҫтарать, сулахай еннелле туртни тытса тӑракан япалана пухать, Shift + сулахай еннелле туртни кашни слота куҫарать.",
"horizon.extras.mouse_tweaks_wheel.tooltip": "Аялалла слотӑн пӗр япалине тепӗр еннелле ярать, ҫӳлелле пӗрне каялла илсе килет.",
"horizon.extras.sort.tooltip": "Инвентаре, арчасене, пичкесене, вӗҫлӗ арчасене тата шулкер ещӗкӗсене творчествӑлла вкладка йӗркипе йӗркелет, стексене пӗрлештерет.",
"horizon.extras.sort_key.tooltip": "Курсор айӗнчи пайне йӗркелет. Пусӑр, унтан клавишӑна пусӑр; Тарасси ӑна салтать.",
"horizon.extras.no_ai.tooltip": "ИИ-н кашни функцине пытарать тата сӳнтерет: агент управленийӗ, horizon-ctl тата MCP серверӗсем. Пускачӑн переключателӗ те.",
"horizon.extras.agent_control.tooltip": "Сирӗн хӑвӑрӑн ИИ агенчӗ ҫак вӑййа horizon-ctl е MCP клиент урлӑ ҫак компьютер ҫинче кӑна выляма пултарать.",
"horizon.extras.discord_presence.tooltip": "Discord ӗҫленӗ вӑхӑтра сирӗн Discord профильте Horizon вылянине кӑтартать: менюсем, пӗр ҫын е нумай ҫын, виҫе тата вылянӑ вӑхӑт. Нихӑҫан та тӗнче ячӗ е сервер адресӗ.",
"Calibrate...": "Калибровка...",
"Calibrate Anyway...": "Ҫапах та калибровка...",
"Use Automatic Setup": "Автоматла настройкӑпа усӑ курӑр",
"Shortcut: %s": "Кӗске ҫул: %s",
"Next": "Малалла",
"HDR Calibration %s/%s: %s": "HDR калибровка %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Аялта виҫҫӗшне тулли экран ҫине лартать, пӗрер тӗрӗслев ӳкерчӗкӗ.",
"A key that opens this screen from the game. Not bound at first.": "Вӑйӑран ҫак экрана уҫакан уҫҫи. Малтанах ҫыхӑнман.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Сирӗн дисплей ҫинче вӑл мӗнле ҫутӑ пулнине каламан: ӑна лартма калибровка тумалла.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Сирӗн дисплей валли хатӗрленӗ ӗнтӗ. HDR тӗрӗс мар курӑнсан ҫеҫ калибровка тӑвӑр.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Чи пысӑк ҫутӑлӑха тата хут шуррине сирӗн дисплей настройкине каялла тавӑрать.",
"Set up for your display. Leave on Auto unless it looks wrong.": "Хӑвӑрӑн дисплей валли настройка тӑвӑр. Авто ҫинче хӑварӑр, енчен те вӑл тӗрӗс мар пек курӑнмасть пулсан.",
"Lower it until the logo just disappears into the black.": "Ӑна логотип хура тӗспе ҫухаличчен антарӑр.",
"Raise it until the logo just disappears into the bright square.": "Ӑна логотип ҫутӑ тӑваткал ҫине ҫухаличченех ҫӗклесе тӑрӑр.",
"Make the card a white that's comfortable to look at for a long time, in the light you play in.": "Карттӑна шурӑ тӗслӗ тӑвӑр, ӑна нумайччен пӑхма меллӗ пултӑр, эсир вылякан ҫутӑра.",
"horizon.splitscreen.profiles.forget": "Манӑр",
"horizon.splitscreen.profiles.create": "Тӑвӑр",
"horizon.splitscreen.profiles.you": " - эсир",
"horizon.splitscreen.profiles.forget_named": "Манӑр %s",
"horizon.splitscreen.profiles.previous": "Унчченхи",
"horizon.splitscreen.profiles.next": "Малалла",
"horizon.splitscreen.profiles.new_button": "Ҫӗнӗ профиль...",
"horizon.splitscreen.profiles.forget_title": "Профиль маннӑ-и?",
"horizon.splitscreen.profiles.forget_leaves": "Вӑл ҫак экземплярӑн списокӗнчен тухать.",
"horizon.splitscreen.profiles.forget_kept": "Тӗнчери прогресс тата настройкӑсем упранаҫҫӗ.",
"horizon.splitscreen.profiles.new_title": "Ҫӗнӗ профиль",
"horizon.splitscreen.profiles.name_hint": "Ят (3-16 сас палли, цифра, _)",
"horizon.splitscreen.profiles.switch_title": "Профиль улӑштармалла",
"horizon.splitscreen.profiles.offline_note": "Офлайн профильсем ҫак экземпляра кӗреҫҫӗ",
"horizon.splitscreen.profiles.move": "Ӑҫта вылятӑн, ҫавӑнта куҫ",
"horizon.splitscreen.profiles.move_place": "Ӑҫта вылятӑн, ҫавӑнта куҫ: %s",
"horizon.friends.title": "Юлташсемпе выля",
"horizon.friends.hint": "Туссем ҫак кода титул экранӗнчи Туссем кнопки хыҫҫӑн, Тӳрӗ ҫыхӑнура е пуҫлаканӑн Play страницинче ҫыпӑҫтараҫҫӗ.",
"horizon.friends.copied": "Копиленӗ",
"horizon.friends.copy_code": "Код копийӗ",
"horizon.friends.stop_sharing": "Пайлама пӑрахӑр",
"horizon.friends.none_yet": "Кунта туссем ҫук-ха.",
"horizon.friends.here_now": "Акӑ халӗ: %s",
"horizon.friends.share_intro": "Ҫак тӗнчене йыхрав кодӗ хыҫӗнче пайла: вӑл LAN-а уҫӑлать, интернетри туссем вара ҫав кодпа хутшӑнаҫҫӗ. Пирӗн пӗр сервер та ҫак ӗҫе хутшӑнмасть.",
"horizon.friends.start_sharing": "Пайлама пуҫлӑр",
"horizon.friends.troubleshooting": "Ҫитменлӗхсене пӗтересси...",
"horizon.friends.stopped": "Чӗнӳ кодне пайлама пӑрахрӑм.",
"horizon.friends.short_code_changed": "Такам кӗске кода йӑнӑш ҫырса пӑхрӗ, ҫавӑнпа вӑл улшӑнчӗ.",
"horizon.friends.wrong_code": "Такам йӑнӑш кодпа усӑ курса пӑхнӑ.",
"horizon.friends.code_ready": "Сирӗн чӗнӳ кодӗ хатӗр: Вӑйӑ меню, Туссемпе выля. Ӑна копилесе юлташсене ярса парӑр.",
"horizon.friends.share_failed": "Тӗнчене пайлаймарӑм: %s",
"horizon.friends.finding_route": "Юлташсем сирӗн пата мӗнле ҫитме пултарнине тупасси...",
"horizon.osk.shift": "Смена",
"horizon.osk.space": "Космос",
"horizon.osk.delete": "Пӗтер",
"horizon.osk.type": "Тип",
"horizon.osk.symbols": "Символсем",
"horizon.osk.close": "Хуп",
"horizon.extras.chat_animation.tooltip": "Ҫӗнӗ чат йӗрки сулахай енчен шуса кӗрет, вӑл вара пӗтӗмӗшпех курӑнмасть.",
"horizon.extras.chat_indicator.tooltip": "Чат йӗркин сулахай енчи тӗслӗ йӗр.",
"horizon.extras.wheel_cycles_settings.tooltip": "Настройкӑсенчи кнопка урлӑ ҫаврӑнакан ҫаврашка унӑн хакне улӑштарать, ваниль ҫинчи пекех. Сӳнтерсен, урапа списока ҫеҫ куҫарать.",
"horizon.extras.pack_browser.tooltip": "Ресурс пакетӗсене суйласа илсен, хушма кнопка пулать, вӑл Modrinth шырать те сирӗн пакетсен папкине пакет вырнаҫтарать. Ҫук, вӑйӑ нихҫан та Modrinth ҫыхӑнмасть.",
"horizon.extras.splitscreen.tooltip": "Тӗнчере тепӗр контроллер ҫӗнӗ вылякан пулса хутшӑнма пултарать: вӑл ыйтсан А пусмалла, унтан профиль суйласа илмелле тата ӑҫта вылямаллине суйласа илмелле.",
"horizon.splitscreen.setup.monitor": "Монитор %s",
"horizon.splitscreen.setup.monitor_unknown": "монитор",
"horizon.splitscreen.setup.window": "Чӳрече %s",
"horizon.splitscreen.setup.new_window": "Çӗнӗ чӳрече",
"horizon.splitscreen.setup.fill_monitor": "%s тултар",
"horizon.splitscreen.setup.done": "Пӗтернӗ",
"horizon.splitscreen.setup.on": "Ҫ",
"horizon.splitscreen.setup.off": "Сӳнтернӗ",
"horizon.splitscreen.setup.player": "Вылякан %s",
"horizon.splitscreen.setup.revert": "Каялла тавӑр",
"horizon.splitscreen.setup.apply": "Заявлени",
"horizon.splitscreen.setup.warning.empty_main": "1-мӗш чӳречере никам та вылямасть: вӑл пытанать",
"horizon.splitscreen.setup.warning.empty_close": "Window %s-ра никам та вылямасть: вӑл Apply ҫинче хупӑнать",
"horizon.splitscreen.setup.warning.empty_new": "Window %s-ра никам та вылямасть: вӑл уҫӑлмасть",
"horizon.splitscreen.setup.warning.on_main": "%s чӳрече 1-мӗш чӳрече мониторне тултарать: унӑн выляканӗсем 1-мӗш чӳречере выляҫҫӗ",
"horizon.splitscreen.setup.warning.unplugged": "Window %s мониторӗ ҫыхӑнман: унӑн выляканӗсем Window 1-ре выляҫҫӗ",
"horizon.splitscreen.setup.status.off": "Сплитскрин сӳннӗ.",
"horizon.splitscreen.setup.status.enable_hint": "Ӑна ҫутӑр та тӗнчери тепӗр контроллер ҫинчи кнопкӑна пусӑр.",
"horizon.splitscreen.setup.status.join_hint": "А пусса хутшӑнӑр та профиль суйласа илӗр.",
"horizon.splitscreen.setup.status.idle": "Сплитскрин ӗҫлемест",
"horizon.splitscreen.setup.status.alone": "Ытти вырӑнти вылякансем ҫук.",
"horizon.splitscreen.setup.status.solo_controls": "Пӗччен управлени тата контроллер ҫыхӑнӑвӗ улшӑнман.",
"horizon.splitscreen.setup.window_empty": "Чӳрече %s: пушӑ",
"horizon.splitscreen.setup.note.select": "Плеер ҫине пусӑр е ӑна чӳрече е монитор ҫине сӗтӗрсе кайӑр.",
"horizon.splitscreen.setup.note.apply": "Ҫакӑн пек выляма ыйтӑр, каялла тавӑрса каялла тавӑрӑр.",
"horizon.splitscreen.setup.note.place": "%s ӑҫта вылямалла?",
"horizon.splitscreen.setup.this_player": "ку вылякан",
"horizon.splitscreen.setup.switch_disabled": "Ыттисем тухса кайсанах Splitscreen сӳнет (вӗсен пауза меню).",
"horizon.splitscreen.setup.window_place": "Чӳрече %s (%s %s)",
"horizon.splitscreen.setup.names": "Ятсем",
"horizon.splitscreen.setup.identify": "Паллӑрт",
"horizon.splitscreen.setup.layout.two": "2 пайлани: %s",
"horizon.splitscreen.setup.layout.three": "3 пайлани: %s",
"horizon.splitscreen.setup.layout.two.0": "Авто",
"horizon.splitscreen.setup.layout.two.1": "Юнашар",
"horizon.splitscreen.setup.layout.two.2": "Ҫӳлти тата аялти",
"horizon.splitscreen.setup.layout.three.0": "Ҫӳлте пӗр сарлака",
"horizon.splitscreen.setup.layout.three.1": "Сулахай енче пӗр ҫӳллӗ",
"horizon.splitscreen.setup.layout.three.2": "Юнашар",
"horizon.splitscreen.setup.help.player": "Вӗсене куҫарма %s пусмалла е сӗтӗрмелле.",
"horizon.splitscreen.setup.help.layout_auto": "%s (чӳрече сарлака чухне юнашар). Улшӑнма пусӑр.",
"horizon.splitscreen.setup.help.layout": "%s. Улшӑнма пусӑр.",
"horizon.splitscreen.setup.help.switch": "Ҫак компьютер валли пайланӑ экран, ҫутӑ е сӳнтернӗ.",
"horizon.splitscreen.setup.help.identify": "Кашни выляканӑн курӑмӗ вӑл хӑш чӳречере пулнине кӑтартать.",
"horizon.splitscreen.setup.help.names": "Кашни выляканӑн ячӗ вӗсен курӑмӗн кӗтессинче.",
"horizon.splitscreen.setup.help.revert": "Хальлӗхе кӗртмен улшӑнусене каялла тавӑрӑр.",
"horizon.splitscreen.setup.help.apply": "Карттӑ ҫинче мӗнле кӑтартнӑ, ҫавӑн пек выля.",
"horizon.splitscreen.setup.help.unchanged": "Нимӗн те улшӑнман-ха.",
"horizon.splitscreen.prompt.controller_connected": "Контроллер ҫыхӑннӑ",
"horizon.splitscreen.prompt.controller_disconnected": "Контроллер татӑлнӑ",
"horizon.splitscreen.prompt.controller_disconnected.hint": "Ӑна тепӗр хут ҫыхӑнтарӑр е тепӗр кнопкӑна пусӑр",
"horizon.splitscreen.prompt.join.one": "%s хутшӑнасшӑн",
"horizon.splitscreen.prompt.join.many": "%s контроллерсем хутшӑнасшӑн",
"horizon.splitscreen.prompt.join.yes": "Пӗрлешӗр",
"horizon.splitscreen.prompt.join.as_p1": "P1 пек выля",
"horizon.splitscreen.prompt.join.no": "Ҫук",
"horizon.splitscreen.prompt.lost": "%s: контроллер татӑлнӑ",
"horizon.splitscreen.prompt.lost.use_another": "Урӑххипе усӑ курӑр",
"horizon.splitscreen.prompt.lost.leave": "Тухса кай",
"horizon.splitscreen.setup.help.give_up_pad": "Сирӗн контроллер сире илсе ҫӳреме пӑрахать те (А) ушкӑна кӗме сӗнет.",
"horizon.splitscreen.setup.note.held_at": "%s to %s: А ӑна кунта пӑрахать, В ӑна каялла хурать.",
"horizon.splitscreen.setup.note.held": "%s ҫӗкленӗ: сулахай/сылтӑм ӑҫта илсе тухать, А ӳкерет, В ӑна каялла хурать.",
"horizon.splitscreen.online_only": "Ку сервер онлайн режимра: офлайн профильсем унта кӗреймеҫҫӗ, кашни выляканӑн хӑйӗн Minecraft аккаунчӗ кирлӗ.",
"horizon.splitscreen.own_account": "Ку сервера кашни выляканӑн хӑйӗн Minecraft аккаунчӗ кирлӗ",
"horizon.splitscreen.local_only": "Ытти вылякансем ҫак сервера хӑйсен Minecraft аккаунчӗпе кӑна кӗме пултараҫҫӗ: офлайн профильсем сирӗн тӗнчере, LAN-ра е сирӗн вырӑнти сетьри серверта выляҫҫӗ.",
"Hides models behind opaque blocks. Async checks off-thread; entities tick.": "Модельсене тӗксӗм блоксем хыҫне пытарать. Асинхронлӑ ҫипрен тухнине тӗрӗслет; субъектсем тик.",
"Block Entities": "Блок субъекчӗсем",
"Shulker Boxes": "Шулькер ещӗкӗсем",
"Reuses settled boxes; lid movement stays animated.": "Ҫирӗплетнӗ ещӗксемпе тепӗр хут усӑ курать; хупӑлча хусканӑвӗ чӗрӗ юлать.",
"Signs": "Паллӑсем",
"Reuses sign text until text, light, or font changes.": "Текст, ҫутӑ е шрифт улшӑниччен паллӑ текстне тепӗр хут усӑ курать.",
"Decorated Pots": "Илемлетнӗ чӳлмексем",
"Reuses still pots; break wobble and sherd changes redraw.": "Ҫирӗп чӳлмексемпе тепӗр хут усӑ курать; ҫӗмӗрме, сулланма тата ҫӗмӗрме улшӑнусем ҫӗнӗрен ӳкерме.",
"Bells": "Шӑнкӑравсем",
"Reuses still bells; ringing stays animated.": "Ҫӗнӗрен усӑ курать шӑнкӑравсем; шӑнкӑртатни чӗрӗ юлать.",
"Chests": "Арчасем",
"Reuses closed chests; moving lids stay animated.": "Хупӑ арчасене тепӗр хут усӑ курать; куҫакан хупӑлчасем чӗрӗ юлаҫҫӗ.",
"Banners": "Баннерсем",
"Reuses poles; flags and pattern layers still update.": "Юпасене тепӗр хут усӑ курать; ялавсемпе ӳкерчӗк сийӗсем халӗ те ҫӗнелеҫҫӗ.",
"Copper Golem Statues": "Пӑхӑр голем статуйисем",
"Reuses statue geometry until its block state changes.": "Статуя геометрине унӑн блок тӑрӑмӗ улшӑниччен тепӗр хут усӑ курать.",
"Leaf Culling": "Ҫулҫӑсене касасси",
"Every leaf face. Vanilla's fancy leaves.": "Кашни ҫулҫӑ пит-куҫӗ. Ваниль ҫулҫисем.",
"Distant Plant Dither": "Инҫетри ӳсентӑран чӗтренет",
"Keeps fine plants in high-res packs from fading far away.": "Пысӑк ҫивӗчлӗхлӗ пакетсенчи вӗтӗ ӳсентӑрансене инҫете сӳнме памасть.",
"Screenshot Quality": "Скриншот пахалӑхӗ",
"F2 screenshots at this many times the game's resolution. Higher takes longer.": "F2 скриншочӗсем ҫак вӑйӑ татӑклӑхӗнчен нумай хут пысӑкрах. Ҫӳлтиреххи вӑраха тӑсӑлать.",
"Frame Generation": "Кадр генерацийӗ",
"Shows a made-up frame between each two real ones: smoother, a little more lag.": "Кашни икӗ чӑн-чӑн хушшинче тунӑ кадра кӑтартать: тикӗсрех, кӑштах кая юлса.",
"Shadow Cache": "Мӗлке кэшӗ",
"Reuses the pack shadow map when sun, view and world match. Off by default.": "Хӗвел, курӑну тата тӗнче пӗр пек чухне мӗлке карттипе тепӗр хут усӑ курать. Выключен по умолчанию.",
"Find BlueMap": "BlueMap тупӑр",
"On servers, finds their BlueMap web map for far terrain. Next join.": "Серверсем ҫинче инҫетри ҫӗр ҫинчи BlueMap веб-карттине тупать. Тепӗр хут хутшӑнӑр.",
"BlueMap Radius": "BlueMap радиусӗ",
"How far around you BlueMap tiles are downloaded. Next join.": "Сирӗн тавра BlueMap плиткисене мӗн таран скачать тӑваҫҫӗ. Тепӗр хут хутшӑнӑр.",
"Synchronous": "Синхронлӑ",
"Asynchronous": "Асинхронлӑ",
"Solid": "Хытӑ",
"Solid Aggressive": "Ҫирӗп агрессивлӑ",
"Hollow": "Пушӑ",
"Leaves fully inside a canopy draw solid: fewer see-through layers.": "Ҫулҫӑсем ҫивитти ӑшӗнче туллин ҫирӗпленсе лараҫҫӗ: витӗр курӑнакан сийсем сахалрах.",
"Leaves with one open side draw solid too: faster, chunkier edges.": "Пӗр уҫӑ енлӗ ҫулҫӑсем те ҫирӗп ӳкереҫҫӗ: хӑвӑртрах, ҫирӗпрех хӗррисем.",
"Drops faces between leaves: faster, but gaps look through.": "Ҫулҫӑсем хушшинчи пит-куҫа тумлатать: хӑвӑртрах, анчах хушӑксем витӗр курӑнаҫҫӗ.",
"Hotbar: Previous Slot": "Hotbar: Унчченхи слот",
"Hotbar: Next Slot": "Hotbar: Тепӗр слот",
"horizon.agent.connected.one": "Ҫыхӑннӑ: 1 агент ҫыхӑнӑвӗ",
"horizon.agent.connected.many": "Ҫыхӑннӑ: %s агент ҫыхӑнӑвӗсем",
"horizon.agent.connected.one_ago": "Ҫыхӑннӑ: 1 агент ҫыхӑнӑвӗ, юлашки команда %s s каялла",
"horizon.agent.connected.many_ago": "Ҫыхӑннӑ: %s агент ҫыхӑнӑвӗсем, юлашки команда %s s каялла",
"horizon.agent.waiting": "Агента кӗтетӗп.",
"horizon.agent.off": "Ҫук: агентсем ҫак вӑййа ҫитеймеҫҫӗ.",
"horizon.agent.session_opened": "Ку сеанс валли вӑййа пуҫарса янӑ программа уҫнӑ.",
"horizon.agent.socket": "Сокет: %s (унӑн тулли ҫулӗ валли детальсене копиле)",
"horizon.agent.local_only": "Вырӑнти кӑна: ку компьютер ҫинчи агентсем, нихӑҫан та сеть мар.",
"%s nits": "%s нитс",
"Auto (%s nits)": "Авто (%s нит)",
"%.2f nits": "%.2f нитс",
"Hides models behind opaque blocks. Entities update. Off with custom packs.": "Модельсене тӗксӗм блоксем хыҫне пытарать. Субъектсем ҫӗнелеҫҫӗ. Ятарлӑ пакетсемпе тухӑр.",
"horizon.extras.repro_keys": "Ҫырса илмелли кӗске ҫулсем",
"horizon.extras.repro_keys.tooltip": "F3+R репро ҫырӑва пуҫлать е чарать, F3+E вара хӑйӗн ҫыххине ҫырать. Стандартпа ӗҫлемен: ҫыру тӗнчене копилет.",
"horizon.agent.record_session": "Ҫырса илме пуҫла",
"horizon.agent.record_stop": "Ҫырса илме чарӑнӑр",
"horizon.agent.export_repro": "Репродукци пуххине экспортла",
"horizon.repro.started": "Ҫырса илме пуҫларӗҫ; F3+R ӑна чарать те ҫыхӑ ҫырать.",
"horizon.repro.stopped": "Ҫырса илме чарӑннӑ: %s",
"horizon.repro.bundle": "Репро пуххи: %s",
"horizon.repro.error": "Репро: %s",
"horizon.repro.no_recording": "Хальлӗхе экспортлама ҫыру ҫук.",
"horizon.repro.replay_started": "Реплей пуҫланчӗ: %s",
"horizon.pack.addInfo": "Modrinth-ран пуҫласа ресурс пакетне хушӑр",
"horizon.pack.addWorking": "Modrinth ыйтатӑп...",
"horizon.pack.addNone": "Ресурс пакетӗсем тупӑнман",
"horizon.pack.addTitle": "Ресурссен пакетне хушӑр",
"horizon.pack.install": "Установка",
"horizon.pack.installing": "Установка",
"horizon.pack.installed": "вырнаҫтарнӑ",
"horizon.pack.search": "Шырав",
"horizon.pack.searchHint": "Шырав Modrinth...",
"horizon.pack.filters": "Фильтрсем",
"horizon.pack.filter.realistic": "Чӑн пурнӑҫри пек",
"horizon.pack.filter.simplistic": "Ансат",
"horizon.pack.filter.vanillaLike": "Ваниль евӗрлӗ",
"horizon.pack.filter.x16": "16х",
"horizon.pack.filter.x32": "32х",
"horizon.pack.filter.x64": "64х",
"horizon.pack.addFailed": "Пакета хушма май килмерӗ: %s",
"horizon.extras.header.cosmetics": "Косметика",
"horizon.extras.cosmetics_mine": "Манӑн косметика",
"horizon.extras.cosmetics_mine.tooltip": "Пытарнӑ: кашни Horizon вылякан сире косметикӑсӑр курать, эсир те ҫав шутра. Пуҫкаҫӑн гардеробӗнчи пекех переключатель.",
"horizon.extras.cosmetics_others": "Ытти вылякансен косметики",
"horizon.extras.cosmetics_others.tooltip": "Ытти Horizon вылякансем мӗн тӑхӑнаҫҫӗ: пурте, вӗсен ячӗсен стилӗсем ҫеҫ, е нимӗн те мар. Вӑрттӑн, вӑйӑ нихҫан та вӗсем мӗн тӑхӑннине ыйтмасть.",
"horizon.extras.cosmetics_auras": "Ыттисен аурисем тата йӗрӗсем",
"horizon.extras.cosmetics_auras.tooltip": "Ытти вылякансем тӑхӑнакан аурӑсемпе йӗрсем. Выключен по умолчанию.",
"horizon.extras.cosmetics.shown": "Кӑтартнӑ",
"horizon.extras.cosmetics.hidden": "Пытарнӑ",
"horizon.extras.cosmetics.name_tags": "Ят паллисем кӑна"
}
@@ -0,0 +1,467 @@
{
"horizon.options.controller": "Rheolydd",
"horizon.options.vibration": "Dirgryniad",
"horizon.options.adaptive_triggers": "Sbardunau Addasol",
"horizon.options.lightbar": "Bar golau",
"horizon.options.look_sensitivity": "Sensitifrwydd edrych",
"horizon.options.invert_x": "Edrych Gwrthdro X",
"horizon.options.invert_y": "Edrych Gwrthdro Y",
"horizon.options.sneak_button": "Botwm sleifio",
"horizon.options.move_deadzone": "Parth marw symud",
"horizon.options.look_deadzone": "Parth marw edrych",
"horizon.options.button_glyphs": "Glyffau Botwm",
"horizon.options.on_screen_keyboard": "Bysellfwrdd Ar-Sgrin",
"horizon.options.chat_delay": "Oedi Sgwrsio",
"horizon.options.lightbar.health": "Iechyd",
"horizon.options.lightbar.dimension": "Dimensiwn",
"horizon.options.unavailable": "Nid yw'r nodwedd hon ar gael eto yn Horizon.",
"horizon.options.search": "Gosodiadau chwilio...",
"horizon.pages.shader_packs": "Pecynnau Shader",
"horizon.pages.shader_options": "Opsiynau Shader",
"horizon.pages.controller": "Gosodiadau Rheolydd",
"horizon.pages.controller_binds": "Rhwymiadau Rheolydd",
"horizon.pages.extras": "Horizon Ychwanegiadau",
"horizon.pages.settings": "Horizon Gosodiadau",
"horizon.pages.agent": "AI Asiant",
"horizon.split.leave": "Gadael",
"horizon.pages.split_settings": "Gosodiadau Sgrin Hollt",
"horizon.pages.split_profiles": "Pwy sy'n chwarae?",
"horizon.pages.split_setup": "Gosod Sgrîn Hollt",
"horizon.pages.music": "Cerddoriaeth",
"horizon.pages.music.menu": "Bwydlen",
"horizon.pages.music.overworld": "Gor-fyd",
"horizon.pages.music.creative": "Creadigol",
"horizon.pages.music.underwater": "Dan y dwr",
"horizon.pages.music.end": "Y Diwedd",
"horizon.pages.music.credits": "Credydau",
"horizon.pages.music.overworld_biomes": "Biomau gor-fyd",
"horizon.pages.music.nether_biomes": "Na biomau chwaith",
"horizon.pages.music.end_biomes": "Diwedd biomau",
"horizon.pages.music.other": "Cerddoriaeth arall",
"horizon.pages.music.empty": "Dim traciau cerddoriaeth: nid yw synau'r gêm yn cael eu llwytho.",
"horizon.pages.music.tooltip": "Y traciau adeiledig, fesul sefyllfa: chwaraewch un nawr, neu trowch i ffwrdd fel bod y gêm yn peidio â'i ddewis.",
"horizon.pages.settings.tooltip": "Gosodiadau Horizon ei hun: graffeg, rheolyddion, pethau ychwanegol a mods.",
"horizon.pages.graphics.tooltip": "Graffeg, yn syml: mae pob lleoliad yn dweud beth mae'n ei gostio.",
"horizon.pages.graphics_advanced.tooltip": "Pob gosodiad graffeg, Horizon wedi'i gynnwys, gyda'r hyn y mae pob un yn ei gostio.",
"horizon.pages.controller.tooltip": "Rheolyddion: eu botymau, ffyn, dirgryniad a llywio dewislen.",
"horizon.pages.extras.tooltip": "Ychwanegiadau dewisol, i gyd i ffwrdd yn ddiofyn: mae'r gêm yn aros yn fanila nes i chi droi un ymlaen.",
"horizon.pages.agent.tooltip": "Gadewch i'ch asiant AI eich hun chwarae: horizon-ctl a chleientiaid MCP ar y cyfrifiadur hwn.",
"horizon.pages.split_profiles.tooltip": "Pwy sy'n chwarae: proffil a gedwir yn y ffolder gêm hon.",
"horizon.pages.split_setup.tooltip": "Y ffenestri a'r monitorau, a phwy sy'n chwarae ble.",
"horizon.pages.shader_packs.tooltip": "Pecynnau cysgodi o'r ffolder shaderpacks: Horizon ei hun a'r rhai yn y shaders/fformat. Mae un yn rhedeg ar y tro.",
"horizon.pages.mods.tooltip": "Mods wedi'u gwneud ar gyfer Horizon, o'r ffolder horizon-mods. Mae pob un yn rhedeg mewn blwch tywod ac yn aros i ffwrdd nes i chi ei droi ymlaen.",
"horizon.pages.open.graphics": "Graffeg...",
"horizon.pages.open.graphics_advanced": "Graffeg Uwch...",
"horizon.pages.open.shader_packs": "Pecynnau Shader...",
"horizon.pages.open.controller": "Gosodiadau Rheolydd...",
"horizon.pages.open.split_setup": "Sgrin hollti...",
"horizon.pages.open.extras": "Ychwanegion...",
"horizon.pages.open.agent": "Asiant AI...",
"horizon.pages.open.horizon_graphics": "Horizon Graffeg...",
"horizon.pages.open.controller_binds": "Rheolydd yn rhwymo...",
"horizon.pages.open.horizon_settings": "Horizon Gosodiadau...",
"horizon.pages.open.music": "Cerddoriaeth...",
"horizon.mods.empty": "Dim mods: rhowch ffeiliau .hmod yn y ffolder horizon-mods",
"horizon.mods.stopped": " (Wedi stopio)",
"horizon.mods.not_built": " (Heb ei adeiladu)",
"horizon.mods.settings": "Gosodiadau",
"horizon.mods.in_world": " (gadewch y byd i newid)",
"horizon.mods.open_folder": "Agor Ffolder Mod",
"horizon.shader_packs.empty": "Dim pecynnau lliwiwr: rhowch nhw yn y ffolder shaderpacks",
"horizon.shader_options.open": "Opsiynau Shader...",
"horizon.shader_options.profile": "Proffil",
"horizon.shader_options.loading": "Darllen opsiynau'r pecyn lliwiwr...",
"horizon.agent.copy_details": "Copïo Manylion Cysylltiad",
"horizon.multiplayer.join": "Ymuno",
"horizon.multiplayer.cancel_reconnecting": "Canslo Ailgysylltu",
"horizon.multiplayer.join_friend": "Ymunwch â Ffrind",
"horizon.multiplayer.invite_code": "Cod Gwahodd",
"horizon.multiplayer.relay": "Cyfnewid (dewisol)",
"Preset": "Rhagosodedig",
"LOD Distance": "Pellter LOD",
"Far Detail": "Manylyn Pell",
"Cave Culling": "Difa Ogof",
"Entity Culling": "Difa Endid",
"Chunk Updates": "Diweddariadau Chunk",
"Upload Budget": "Uwchlwytho Cyllideb",
"Sky": "Awyr",
"Fog": "Niwl",
"Weather": "Tywydd",
"Animated Textures": "Gweadau Animeiddiedig",
"Present Mode": "Modd Presennol",
"Frame Pacing": "Cyflymder Ffrâm",
"Worker Threads": "Trywyddau Gweithiwr",
"Resolution Scale": "Graddfa Datrysiad",
"Anti-aliasing": "Gwrth-aliasing",
"Paper White": "Papur Gwyn",
"Peak Brightness": "Disgleirdeb Brig",
"Black Level": "Lefel Ddu",
"UI Brightness": "Disgleirdeb UI",
"Battery Mode": "Modd Batri",
"Graphics API": "Graffeg API",
"Translucent Batching": "Sypynnu Tryloyw",
"Performance Overlay": "Troshaen Perfformiad",
"Full Detail Distance": "Pellter Manylion Llawn",
"On": "Ar",
"Off": "I ffwrdd",
"Unlimited": "Diderfyn",
"Performance": "Perfformiad",
"Balanced": "Cytbwys",
"Quality": "Ansawdd",
"Ultra Performance": "Perfformiad Ultra",
"Native": "Brodorol",
"Full": "Llawn",
"Reduced": "Gostyngedig",
"Efficient": "Effeithlon",
"Precise": "Cywir",
"Mailbox": "Blwch post",
"Immediate": "Ar unwaith",
"FPS and Graph": "FPS a Graff",
"No cost": "Dim cost",
"Low cost": "Cost isel",
"Medium cost": "Cost ganolig",
"High cost": "Cost uchel",
"Saves time": "Yn arbed amser",
"Graphics": "Graffeg",
"Done": "Wedi'i wneud",
"This feature is not yet available in Horizon.": "Nid yw'r nodwedd hon ar gael eto yn Horizon.",
"Your display or system doesn't offer HDR output.": "Nid yw eich arddangosfa neu system yn cynnig allbwn HDR.",
"Turn HDR on to use it.": "Trowch HDR ymlaen i'w ddefnyddio.",
"Your GPU or graphics API doesn't support it.": "Nid yw eich GPU neu API graffeg yn ei gefnogi.",
"%s chunks": "%s talpiau",
"%s per frame": "%s fesul ffrâm",
"%s MiB/frame": "%s MiB/ffrâm",
"%s KiB/frame": "%s KiB/ffrâm",
"Full-detail terrain range. LOD Distance can extend local worlds farther.": "Amrediad tir manwl llawn. Gall pellter LOD ymestyn bydoedd lleol ymhellach.",
"Coarse distant terrain in local worlds. Integrated GPUs stop at 128 chunks.": "Tir garw pell yn y bydoedd lleol. Mae GPUs integredig yn stopio ar 128 talpiau.",
"Reduced draws far terrain coarser: faster on this integrated GPU.": "Llai yn tynnu tir llawer mwy bras: yn gyflymach ar y GPU integredig hwn.",
"Reduced draws far terrain coarser, for GPUs it slows down.": "Mae llai yn tynnu tir llawer mwy bras, ar gyfer GPUs mae'n arafu.",
"Field of view. Wider shows more terrain at once.": "Maes golygfa. Yn ehangach yn dangos mwy o dir ar unwaith.",
"Caps frames per second; a cap saves power and heat.": "Fframiau capiau yr eiliad; mae cap yn arbed pŵer a gwres.",
"Waits for the display's refresh: no tearing, a little latency.": "Aros am adnewyddu'r arddangosfa: dim rhwygo, ychydig o hwyrni.",
"Size of menus and the HUD.": "Maint y bwydlenni a'r HUD.",
"Sets the options below together. Changing one makes it Custom.": "Yn gosod yr opsiynau isod gyda'i gilydd. Mae newid un yn ei gwneud yn Custom.",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "Mae lafa, tân a golau'r haul yn disgleirio'n fwy disglair na gwyn. Angen arddangosfa HDR.",
"Your display's darkest black. 0 for OLED; LCDs glow a little.": "Du tywyllaf eich arddangosfa. 0 ar gyfer OLED; Mae LCDs yn tywynnu ychydig.",
"How bright highlights get. Auto: the peak your system reports.": "Sut mae uchafbwyntiau disglair yn ei gael. Auto: yr uchafbwynt y mae eich system yn ei adrodd.",
"How bright white paper and menus look. Auto: your desktop's white.": "Sut olwg sydd ar bapur gwyn llachar a bwydlenni. Auto: gwyn eich bwrdd gwaith.",
"How bright the HUD and menus are, against paper white.": "Pa mor llachar yw'r HUD a'r bwydlenni, yn erbyn papur gwyn.",
"How far mobs, crops and machines keep working. CPU time.": "Pa mor bell y mae mobs, cnydau a pheiriannau yn dal i weithio. Amser CPU.",
"How far away mobs and items are drawn, as a share of normal.": "Pa mor bell i ffwrdd mae mobs ac eitemau yn cael eu tynnu, fel cyfran o'r arferol.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Sgipiau tir wedi'i guddio y tu ôl i adrannau caeedig. Mae i ffwrdd yn costio llawer.",
"Hides entities behind opaque blocks.": "Yn cuddio endidau y tu ôl i flociau afloyw.",
"No entity occlusion culling.": "Dim difa endid occlusion.",
"CPU occlusion test runs this frame.": "Mae prawf occlusion CPU yn rhedeg y ffrâm hon.",
"Occlusion checks run on a worker thread.": "Mae gwiriadau Occlusion yn rhedeg ar edefyn gweithiwr.",
"Meshed sections uploaded per frame. Lower is smoother while loading.": "Adrannau rhwyllog wedi'u huwchlwytho fesul ffrâm. Mae is yn llyfnach wrth lwytho.",
"Soft shadows in corners. Costs chunk building time only.": "Cysgodion meddal mewn corneli. Mae'n costio talp o amser adeiladu yn unig.",
"Smaller copies of block textures for distant terrain: less shimmer.": "Copïau llai o weadau bloc ar gyfer tir pell: llai o sglein.",
"Blends grass and water colours across biomes when building chunks.": "Cyfuno lliwiau glaswellt a dŵr ar draws biomau wrth adeiladu talpiau.",
"Cloud layer: off, flat (fast) or 3D (fancy).": "Haen cwmwl: i ffwrdd, fflat (cyflym) neu 3D (ffansi).",
"How far clouds reach, in chunks.": "Pa mor bell y mae cymylau'n cyrraedd, mewn talpiau.",
"Draws the sky, sun, moon and stars.": "Yn darlunio'r awyr, yr haul, y lleuad a'r sêr.",
"Distance fog. Off shows the edge of the loaded world.": "Niwl pellter. Mae Off yn dangos ymyl y byd llwythog.",
"Draws rain and snow.": "Yn tynnu glaw ac eira.",
"How far rain and snow fall around you.": "Pa mor bell y mae glaw ac eira yn disgyn o'ch cwmpas.",
"How many particles effects spawn.": "Faint o effeithiau gronynnau sy'n silio.",
"Round shadows under mobs and items.": "Cysgodion crwn o dan mobs ac eitemau.",
"Leaves with see-through gaps. Off draws them solid.": "Dail gyda bylchau amlwg. I ffwrdd yn eu tynnu solet.",
"Sorts water and glass more exactly behind each other.": "Didoli dŵr a gwydr yn fwy union y tu ôl i'w gilydd.",
"Which chunk rebuilds wait for the frame: none, nearby or yours.": "Pa dalp ailadeiladu aros am y ffrâm: dim, gerllaw neu eich un chi.",
"Animates water, lava, fire and other moving textures.": "Yn animeiddio dŵr, lafa, tân a gweadau symudol eraill.",
"World pixels per screen pixel. Below 100% is faster on the GPU.": "Picsel y byd fesul picsel sgrin. Mae llai na 100% yn gyflymach ar y GPU.",
"Smooths jagged edges. FXAA: softer. TAA: smoothest, moving things may trail.": "Llyfn ymylon miniog. FXAA: meddalach. TAA: mae'n bosibl y bydd pethau llyfn, symudol yn dilyn.",
"Renders the world smaller and rebuilds it with TAA. Faster, softer.": "Yn gwneud y byd yn llai ac yn ei ailadeiladu gyda TAA. Cyflymach, meddalach.",
"HDR output on an HDR display: lava, fire and sunlight shine brighter.": "Allbwn HDR ar arddangosfa HDR: lafa, tân a golau haul yn disgleirio'n fwy disglair.",
"How frames reach the screen while VSync is off.": "Sut mae fframiau'n cyrraedd y sgrin tra bod VSync i ffwrdd.",
"Efficient sleeps until the next frame; Precise spins, like vanilla.": "Cysgu'n effeithlon tan y ffrâm nesaf; Troelli manwl gywir, fel fanila.",
"Fills the screen (borderless).": "Yn llenwi'r sgrin (heb ffiniau).",
"Brightens dark places.": "Yn goleuo lleoedd tywyll.",
"Lowers the frame rate when minimised, or idle too.": "Yn gostwng y gyfradd ffrâm pan gaiff ei lleihau, neu'n segur hefyd.",
"Sways the view while walking.": "Troi'r olygfa wrth gerdded.",
"Fades newly loaded chunks in.": "Yn pylu darnau newydd eu llwytho i mewn.",
"Darkens the screen's edges.": "Tywyllu ymylon y sgrin.",
"Shows when the world saves.": "Yn dangos pan fydd y byd yn arbed.",
"Shows attack cooldown.": "Yn dangos ymosodiad ymosodiad.",
"60 FPS cap (30 in menus or unfocused), half the workers: less power.": "Cap FPS 60 (30 mewn bwydlenni neu heb ffocws), hanner y gweithwyr: llai o bŵer.",
"Threads that generate, light and mesh chunks. Next start.": "Trywyddau sy'n cynhyrchu darnau ysgafn a rhwyll. Cychwyn nesaf.",
"Vulkan, then DirectX 12 (Windows) or OpenGL (Linux) where it fails. Next start.": "Vulkan, yna DirectX 12 (Windows) neu OpenGL (Linux) lle mae'n methu. Cychwyn nesaf.",
"Water and glass in one buffer: fewer draw calls, a bit more VRAM. Next start.": "Dŵr a gwydr mewn un byffer: llai o alwadau tynnu, ychydig mwy o VRAM. Cychwyn nesaf.",
"Mesh data sent to the GPU per frame. Lower is smoother while loading.": "Anfonir data rhwyll i'r GPU fesul ffrâm. Mae is yn llyfnach wrth lwytho.",
"Frame rate and frame times over the game, with a graph.": "Cyfradd ffrâm ac amseroedd ffrâm dros y gêm, gyda graff.",
"horizon.multiplayer.finding_friend": "Dod o hyd i fyd eich ffrind...",
"horizon.multiplayer.not_signed_in": "Mae'r gweinydd hwn yn y modd ar-lein. Mewngofnodwch gyda'ch cyfrif Microsoft yn y lansiwr Horizon i ymuno ag ef.",
"horizon.multiplayer.invalid_address": "Cyfeiriad gweinydd annilys: %s",
"horizon.multiplayer.connected_friend_direct": "Wedi'i gysylltu'n syth â'ch ffrind...",
"horizon.multiplayer.connected_friend_relay": "Wedi'i gysylltu â'ch ffrind trwy'r ras gyfnewid ...",
"General": "Cyffredinol",
"Advanced": "Uwch",
"Undo": "Dadwneud",
"Search options": "Dewisiadau chwilio",
"Shortcut": "Llwybr byr",
"Distance": "Pellter",
"Display": "Arddangos",
"Interface": "Rhyngwyneb",
"Sky and weather": "Awyr a thywydd",
"Lighting and entities": "Goleuadau ac endidau",
"Textures": "Gweadau",
"Image": "Delwedd",
"Chunk loading": "Llwytho i lawr",
"Culling": "Difa",
"Power": "Grym",
"Frames": "Fframiau",
"Overlay": "Troshaen",
"Output": "Allbwn",
"Brightness": "Disgleirdeb",
"How far you see, the window and the HUD.": "Pa mor bell y gwelwch, y ffenestr a'r HUD.",
"The sky and weather, lighting, textures and the image.": "Yr awyr a'r tywydd, goleuo, gweadau a'r ddelwedd.",
"Chunk loading, culling and power use.": "Llwytho talp, difa a defnydd pŵer.",
"How frames reach the screen, and the performance overlay.": "Sut mae fframiau'n cyrraedd y sgrin, a'r troshaen perfformiad.",
"Brighter-than-white output on an HDR display.": "Allbwn mwy disglair na gwyn ar arddangosfa HDR.",
"Results for \"%s\"": "Canlyniadau ar gyfer \"%s\"",
"No settings match.": "Dim gosodiadau yn cyfateb.",
"horizon.title.not_affiliated": "Ddim yn gysylltiedig â Mojang na Microsoft",
"horizon.extras.header.lighting": "Goleuo",
"horizon.extras.header.third_person": "Trydydd person",
"horizon.extras.header.particles": "Gronynnau",
"horizon.extras.header.inventory": "Stocrestr",
"horizon.extras.header.chat": "Sgwrsio",
"horizon.extras.header.multiplayer": "Aml-chwaraewr",
"horizon.extras.header.sound": "Sain",
"horizon.extras.header.menus": "Bwydlenni",
"horizon.extras.fullbright": "Llawnbright",
"horizon.extras.dynamic_lights": "Goleuadau deinamig",
"horizon.extras.own_name_tag": "Tag enw eich hun",
"horizon.extras.hide_block_particles": "Cuddio gronynnau bloc-toriad",
"horizon.extras.auto_reconnect": "Ailgysylltu'n awtomatig",
"horizon.extras.ui_sound_reserve": "Cadwch sianeli ar gyfer synau UI",
"horizon.extras.music_page": "Tudalen cerddoriaeth",
"horizon.extras.mouse_tweaks": "Rheolaethau Llygoden Tweaks",
"horizon.extras.mouse_tweaks_wheel": "Olwyn sgrolio yn symud eitemau",
"horizon.extras.sort": "Botymau didoli",
"horizon.extras.sort_key": "Allwedd didoli",
"horizon.extras.no_ai": "Dim AI",
"horizon.extras.agent_control": "Rheoli Asiant",
"horizon.extras.discord_presence": "Presenoldeb Cyfoethog Discord",
"horizon.extras.splitscreen": "Sgrîn hollt",
"horizon.extras.chat_animation": "Sgwrs esmwyth",
"horizon.extras.chat_indicator": "Dangosydd neges",
"horizon.extras.wheel_cycles_settings": "Gosodiadau newid olwyn sgrolio",
"horizon.extras.pack_browser": "Porwr pecyn adnoddau",
"horizon.extras.fullbright.tooltip": "Dangoswch flociau ac endidau ar olau llawn, hyd yn oed mewn ardaloedd tywyll.",
"horizon.extras.dynamic_lights.tooltip": "Mae fflachlampau a ffynonellau golau eraill sy'n cael eu dal, eu gollwng neu ar dân yn cynnau'r blociau o'u cwmpas.",
"horizon.extras.own_name_tag.tooltip": "Yn dangos eich enw uwch eich pen yn y trydydd person, fel y mae chwaraewyr eraill yn ei weld. Wedi'i guddio pan fydd y gweinydd yn cuddio enwau eich tîm neu pan fyddwch chi'n anweledig.",
"horizon.extras.hide_block_particles.tooltip": "Yn cuddio'r gronynnau malurion sy'n byrstio o floc sydd wedi torri ac yn dadfeilio'r un rydych chi'n ei gloddio. Mae gronynnau eraill yn aros.",
"horizon.extras.auto_reconnect.tooltip": "Yn ailymuno â gweinydd ar ôl colli cysylltiad neu ailgychwyn, byth ar ôl cic neu waharddiad.",
"horizon.extras.ui_sound_reserve.tooltip": "Mae synau yn y byd yn gadael 16 o'r 247 sianel sain yn rhydd, felly ni all llifogydd ohonynt (ffrwydrad mawr) dawelu cliciau dewislen.",
"horizon.extras.music_page.tooltip": "Mae Music & Sounds yn cael tudalen Cerddoriaeth...: chwaraewch unrhyw drac adeiledig nawr, neu trowch un i ffwrdd fel bod y gêm yn peidio â'i ddewis. Does dim byd yn newid nes i chi droi trac i ffwrdd.",
"horizon.extras.mouse_tweaks.tooltip": "Mae llusgo dde yn gosod un fesul slot, mae llusgiad chwith yn casglu'r eitem a ddelir, mae Shift + llusgo chwith yn symud pob slot.",
"horizon.extras.mouse_tweaks_wheel.tooltip": "Mae Down yn anfon un eitem o'r slot i'r ochr arall, i fyny yn dod ag un yn ôl.",
"horizon.extras.sort.tooltip": "Trefnu'r rhestr eiddo, cistiau, casgenni, cistiau ender a blychau shulker yn nhrefn tabiau creadigol, gan uno staciau.",
"horizon.extras.sort_key.tooltip": "Didoli'r rhan o dan y cyrchwr. Cliciwch, yna pwyswch allwedd; Mae dianc yn ei ddadrwymo.",
"horizon.extras.no_ai.tooltip": "Yn cuddio ac yn diffodd pob nodwedd AI: rheolaeth asiant, horizon-ctl a gweinyddwyr MCP. Switsh y lansiwr hefyd.",
"horizon.extras.agent_control.tooltip": "Gall eich asiant AI eich hun chwarae'r gêm hon trwy horizon-ctl neu gleient MCP, ar y cyfrifiadur hwn yn unig.",
"horizon.extras.discord_presence.tooltip": "Yn dangos Chwarae Horizon ar eich proffil Discord tra bod Discord yn rhedeg: bwydlenni, chwaraewr sengl neu aml-chwaraewr, y dimensiwn a'r amser a chwaraeir. Peidiwch byth ag enw byd na chyfeiriad gweinydd.",
"Calibrate...": "Graddnodi...",
"Calibrate Anyway...": "Calibro beth bynnag...",
"Use Automatic Setup": "Defnyddiwch Gosodiad Awtomatig",
"Shortcut: %s": "Llwybr byr: %s",
"Next": "Nesaf",
"HDR Calibration %s/%s: %s": "Graddnodi HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Yn gosod y tri isod sgrin lawn, un llun prawf ar y tro.",
"A key that opens this screen from the game. Not bound at first.": "Allwedd sy'n agor y sgrin hon o'r gêm. Heb ei rwymo ar y dechrau.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Ni ddywedodd eich arddangosfa pa mor llachar y mae'n ei gael: graddnodwch i'w osod.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Eisoes wedi'i osod ar gyfer eich arddangosfa. Calibrowch dim ond os yw HDR yn edrych yn anghywir.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Yn rhoi Disgleirdeb Peak a Phapur Gwyn yn ôl i drefniant eich arddangosfa.",
"Set up for your display. Leave on Auto unless it looks wrong.": "Gosodwch ar gyfer eich arddangosfa. Gadewch ymlaen Auto oni bai ei fod yn edrych yn anghywir.",
"Lower it until the logo just disappears into the black.": "Gostyngwch ef nes bod y logo'n diflannu i'r du.",
"Raise it until the logo just disappears into the bright square.": "Codwch ef nes bod y logo'n diflannu i'r sgwâr llachar.",
"Make the card a white that's comfortable to look at for a long time, in the light you play in.": "Gwnewch y cerdyn yn wyn sy'n gyfforddus i edrych arno am amser hir, yn y golau rydych chi'n chwarae ynddo.",
"horizon.splitscreen.profiles.forget": "Anghofiwch",
"horizon.splitscreen.profiles.create": "Creu",
"horizon.splitscreen.profiles.you": " - chi",
"horizon.splitscreen.profiles.forget_named": "Anghofiwch %s",
"horizon.splitscreen.profiles.previous": "Blaenorol",
"horizon.splitscreen.profiles.next": "Nesaf",
"horizon.splitscreen.profiles.new_button": "Proffil Newydd...",
"horizon.splitscreen.profiles.forget_title": "Anghofio proffil?",
"horizon.splitscreen.profiles.forget_leaves": "Mae'n gadael rhestr yr enghraifft hon.",
"horizon.splitscreen.profiles.forget_kept": "Cedwir cynnydd a gosodiadau'r byd.",
"horizon.splitscreen.profiles.new_title": "Proffil Newydd",
"horizon.splitscreen.profiles.name_hint": "Enw (3 i 16 llythyren, digid, _)",
"horizon.splitscreen.profiles.switch_title": "Newid proffil",
"horizon.splitscreen.profiles.offline_note": "Mae proffiliau all-lein yn perthyn i'r achos hwn",
"horizon.splitscreen.profiles.move": "Symudwch lle rydych chi'n chwarae",
"horizon.splitscreen.profiles.move_place": "Symudwch ble rydych chi'n chwarae: %s",
"horizon.friends.title": "Chwarae gyda Ffrindiau",
"horizon.friends.hint": "Mae ffrindiau'n gludo'r cod ar ôl botwm Friends y sgrin deitl, yn Direct Connection, neu ar dudalen Chwarae'r lansiwr.",
"horizon.friends.copied": "Wedi'i gopïo",
"horizon.friends.copy_code": "Copi Cod",
"horizon.friends.stop_sharing": "Stopio Rhannu",
"horizon.friends.none_yet": "Dim ffrindiau yma eto.",
"horizon.friends.here_now": "Yma nawr: %s",
"horizon.friends.share_intro": "Rhannwch y byd hwn y tu ôl i god gwahoddiad: mae'n agor i LAN, ac mae ffrindiau ar y rhyngrwyd yn ymuno â'r cod. Nid oes unrhyw weinydd ohonom yn cymryd rhan.",
"horizon.friends.start_sharing": "Dechrau Rhannu",
"horizon.friends.troubleshooting": "Datrys Problemau...",
"horizon.friends.stopped": "Wedi stopio rhannu'r cod gwahoddiad.",
"horizon.friends.short_code_changed": "Roedd rhywun yn ceisio cod byr anghywir o hyd, felly fe newidiodd.",
"horizon.friends.wrong_code": "Ceisiodd rhywun god anghywir.",
"horizon.friends.code_ready": "Mae eich cod gwahoddiad yn barod: Dewislen Gêm, Chwarae gyda Ffrindiau. Copïwch ef a'i anfon at eich ffrindiau.",
"horizon.friends.share_failed": "Methu rhannu'r byd: %s",
"horizon.friends.finding_route": "Darganfod sut y gall ffrindiau eich cyrraedd…",
"horizon.osk.shift": "Turn",
"horizon.osk.space": "Gofod",
"horizon.osk.delete": "Dileu",
"horizon.osk.type": "Math",
"horizon.osk.symbols": "Symbolau",
"horizon.osk.close": "Cau",
"horizon.extras.chat_animation.tooltip": "Mae llinell sgwrsio newydd yn llithro i mewn o'r chwith tra mae'n pylu i mewn, yn lle ymddangos yn gyfan.",
"horizon.extras.chat_indicator.tooltip": "Y bar lliw ar ochr chwith llinell sgwrsio.",
"horizon.extras.wheel_cycles_settings.tooltip": "Mae'r olwyn sgrolio dros fotwm yn y gosodiadau yn newid ei werth, fel yn y fanila. I ffwrdd, mae'r olwyn yn sgrolio'r rhestr yn unig.",
"horizon.extras.pack_browser.tooltip": "Mae Dewis Pecynnau Adnoddau yn cael botwm Ychwanegu sy'n chwilio Modrinth ac yn gosod pecyn yn eich ffolder pecynnau. I ffwrdd, nid yw'r gêm byth yn cysylltu â Modrinth.",
"horizon.extras.splitscreen.tooltip": "Mewn byd, gall rheolydd arall ymuno fel chwaraewr newydd: pwyswch A pan fydd yn gofyn, yna dewiswch broffil a ble i chwarae.",
"horizon.splitscreen.setup.monitor": "Monitro %s",
"horizon.splitscreen.setup.monitor_unknown": "monitor",
"horizon.splitscreen.setup.window": "Ffenest %s",
"horizon.splitscreen.setup.new_window": "Ffenestr newydd",
"horizon.splitscreen.setup.fill_monitor": "Llenwch %s",
"horizon.splitscreen.setup.done": "Wedi'i wneud",
"horizon.splitscreen.setup.on": "Ar",
"horizon.splitscreen.setup.off": "I ffwrdd",
"horizon.splitscreen.setup.player": "Chwaraewr %s",
"horizon.splitscreen.setup.revert": "Dychwelyd",
"horizon.splitscreen.setup.apply": "Ymgeisiwch",
"horizon.splitscreen.setup.warning.empty_main": "Nid oes neb yn chwarae yn Ffenestr 1: mae'n cuddio",
"horizon.splitscreen.setup.warning.empty_close": "Does neb yn chwarae yn Ffenestr %s: mae'n cau ar Apply",
"horizon.splitscreen.setup.warning.empty_new": "Does neb yn chwarae yn Ffenestr %s: ni fydd yn agor",
"horizon.splitscreen.setup.warning.on_main": "Mae ffenestr %s yn llenwi monitor Ffenestr 1: mae ei chwaraewyr yn chwarae yn Ffenestr 1",
"horizon.splitscreen.setup.warning.unplugged": "Nid yw monitor Window %s wedi'i gysylltu: mae ei chwaraewyr yn chwarae yn Ffenestr 1",
"horizon.splitscreen.setup.status.off": "Mae sgrin hollti i ffwrdd.",
"horizon.splitscreen.setup.status.enable_hint": "Trowch ef ymlaen, yna pwyswch botwm ar reolwr arall mewn byd.",
"horizon.splitscreen.setup.status.join_hint": "Pwyswch A i ymuno a dewis proffil.",
"horizon.splitscreen.setup.status.idle": "Sgrîn hollt yn segur",
"horizon.splitscreen.setup.status.alone": "Dim chwaraewyr lleol eraill.",
"horizon.splitscreen.setup.status.solo_controls": "Nid yw rheolyddion unigol ac adborth rheolwyr wedi newid.",
"horizon.splitscreen.setup.window_empty": "Ffenestr %s: gwag",
"horizon.splitscreen.setup.note.select": "Cliciwch ar chwaraewr, neu llusgwch ef i ffenestr neu fonitor.",
"horizon.splitscreen.setup.note.apply": "Gwnewch gais i chwarae fel hyn, Dychwelwch i ddadwneud.",
"horizon.splitscreen.setup.note.place": "Ble ddylai %s chwarae?",
"horizon.splitscreen.setup.this_player": "y chwaraewr hwn",
"horizon.splitscreen.setup.switch_disabled": "Mae sgrin Hollt yn diffodd unwaith y bydd y lleill wedi gadael (eu dewislen saib).",
"horizon.splitscreen.setup.window_place": "Ffenestr %s (%s o %s)",
"horizon.splitscreen.setup.names": "Enwau",
"horizon.splitscreen.setup.identify": "Adnabod",
"horizon.splitscreen.setup.layout.two": "2 yn rhannu: %s",
"horizon.splitscreen.setup.layout.three": "3 yn rhannu: %s",
"horizon.splitscreen.setup.layout.two.1": "Ochr yn ochr",
"horizon.splitscreen.setup.layout.two.2": "Top a gwaelod",
"horizon.splitscreen.setup.layout.three.0": "Un llydan ar ei ben",
"horizon.splitscreen.setup.layout.three.1": "Un tal ar y chwith",
"horizon.splitscreen.setup.layout.three.2": "Ochr yn ochr",
"horizon.splitscreen.setup.help.player": "Cliciwch neu llusgwch %s i'w symud.",
"horizon.splitscreen.setup.help.layout_auto": "%s (ochr yn ochr pan fydd y ffenestr yn llydan). Pwyswch i newid.",
"horizon.splitscreen.setup.help.layout": "%s. Pwyswch i newid.",
"horizon.splitscreen.setup.help.switch": "Sgrîn hollti ar gyfer y cyfrifiadur hwn, ymlaen neu i ffwrdd.",
"horizon.splitscreen.setup.help.identify": "Mae golygfa pob chwaraewr yn dangos ym mha ffenestr y mae.",
"horizon.splitscreen.setup.help.names": "Enw pob chwaraewr yng nghornel eu golygfa.",
"horizon.splitscreen.setup.help.revert": "Dad-wneud y newidiadau nas gweithredwyd eto.",
"horizon.splitscreen.setup.help.apply": "Chwaraewch y ffordd mae'r map yn ei ddangos.",
"horizon.splitscreen.setup.help.unchanged": "Dim byd wedi newid eto.",
"horizon.splitscreen.setup.give_up_pad": "Pad rhoi'r gorau iddi",
"horizon.splitscreen.prompt.controller_connected": "Rheolwr wedi'i gysylltu",
"horizon.splitscreen.prompt.controller_disconnected": "Rheolydd wedi'i ddatgysylltu",
"horizon.splitscreen.prompt.controller_disconnected.hint": "Ailgysylltu ef, neu gwasgwch botwm ar un arall",
"horizon.splitscreen.prompt.join.one": "Mae %s eisiau ymuno",
"horizon.splitscreen.prompt.join.many": "%s rheolwyr eisiau ymuno",
"horizon.splitscreen.prompt.join.yes": "Ymuno",
"horizon.splitscreen.prompt.join.as_p1": "Chwarae fel P1",
"horizon.splitscreen.prompt.join.no": "Nac ydw",
"horizon.splitscreen.prompt.lost": "%s: rheolydd wedi'i ddatgysylltu",
"horizon.splitscreen.prompt.lost.use_another": "Defnyddiwch un arall",
"horizon.splitscreen.prompt.lost.leave": "Gadael",
"horizon.splitscreen.setup.help.give_up_pad": "Mae eich rheolydd yn rhoi'r gorau i'ch gyrru a chynigir iddo ymuno (A).",
"horizon.splitscreen.setup.note.held_at": "%s i %s: Mae A yn ei ollwng yma, mae B yn ei roi yn ôl.",
"horizon.splitscreen.setup.note.held": "%s codi: chwith/dde yn codi ble, A yn disgyn, B yn ei roi yn ôl.",
"Hides models behind opaque blocks. Async checks off-thread; entities tick.": "Yn cuddio modelau y tu ôl i flociau afloyw. Mae Async yn gwirio oddi ar yr edau; endidau tic.",
"Block Entities": "Endidau Bloc",
"Shulker Boxes": "Blychau Shulker",
"Reuses settled boxes; lid movement stays animated.": "Yn ailddefnyddio blychau sefydlog; symudiad caead yn aros yn animeiddiedig.",
"Signs": "Arwyddion",
"Reuses sign text until text, light, or font changes.": "Yn ailddefnyddio testun arwyddo nes bod testun, golau neu ffont yn newid.",
"Decorated Pots": "Potiau Addurnedig",
"Reuses still pots; break wobble and sherd changes redraw.": "Yn ailddefnyddio potiau llonydd; torri siglo ac ail-lunio newidiadau sherd.",
"Bells": "Clychau",
"Reuses still bells; ringing stays animated.": "Yn ailddefnyddio clychau o hyd; mae canu yn aros yn animeiddiedig.",
"Chests": "Cistiau",
"Reuses closed chests; moving lids stay animated.": "Yn ailddefnyddio cistiau caeedig; symud caeadau aros yn animeiddiedig.",
"Banners": "Baneri",
"Reuses poles; flags and pattern layers still update.": "Yn ailddefnyddio polion; baneri a haenau patrwm yn dal i ddiweddaru.",
"Copper Golem Statues": "Cerfluniau Golem Copr",
"Reuses statue geometry until its block state changes.": "Yn ailddefnyddio geometreg cerflun nes bod ei gyflwr bloc yn newid.",
"Leaf Culling": "Difa Dail",
"Every leaf face. Vanilla's fancy leaves.": "Wyneb pob deilen. Dail ffansi fanila.",
"Distant Plant Dither": "Dither Planhigion Pell",
"Keeps fine plants in high-res packs from fading far away.": "Yn cadw planhigion mân mewn pecynnau cydraniad uchel rhag pylu ymhell i ffwrdd.",
"Screenshot Quality": "Ansawdd Sgrinlun",
"F2 screenshots at this many times the game's resolution. Higher takes longer.": "Sgrinluniau F2 ar hyn o bryd lawer o benderfyniad y gêm. Mae uwch yn cymryd mwy o amser.",
"Frame Generation": "Cynhyrchu Ffrâm",
"Shows a made-up frame between each two real ones: smoother, a little more lag.": "Yn dangos ffrâm wedi'i gwneud i fyny rhwng pob dau go iawn: llyfnach, ychydig yn fwy oedi.",
"Shadow Cache": "Cache Cysgod",
"Reuses the pack shadow map when sun, view and world match. Off by default.": "Yn ailddefnyddio'r map cysgodion pecyn pan fydd yr haul, yr olygfa a'r byd yn cyd-fynd. Wedi'i ddiffodd yn ddiofyn.",
"Find BlueMap": "Dewch o hyd i BlueMap",
"On servers, finds their BlueMap web map for far terrain. Next join.": "Ar weinyddion, yn dod o hyd i'w map gwe BlueMap ar gyfer tir pell. Ymunwch nesaf.",
"BlueMap Radius": "Radiws BlueMap",
"How far around you BlueMap tiles are downloaded. Next join.": "Pa mor bell o'ch cwmpas y mae teils BlueMap yn cael eu llwytho i lawr. Ymunwch nesaf.",
"Synchronous": "Cydamserol",
"Solid Aggressive": "Solid Ymosodol",
"Hollow": "gwag",
"Leaves fully inside a canopy draw solid: fewer see-through layers.": "Mae'r dail yn gyfan gwbl y tu mewn i ganopi yn tynnu'n solet: llai o haenau trwodd.",
"Leaves with one open side draw solid too: faster, chunkier edges.": "Mae dail gydag un ochr agored yn tynnu'n solet hefyd: ymylon cyflymach, mwy trwchus.",
"Drops faces between leaves: faster, but gaps look through.": "Wynebau diferion rhwng dail: yn gyflymach, ond bylchau yn edrych drwodd.",
"Hotbar: Previous Slot": "Hotbar: Slot Blaenorol",
"Hotbar: Next Slot": "Hotbar: Slot Nesaf",
"horizon.agent.connected.one": "Cysylltiedig: 1 cysylltiad asiant",
"horizon.agent.connected.many": "Cysylltiedig: %s cysylltiadau asiant",
"horizon.agent.connected.one_ago": "Cysylltiedig: 1 cysylltiad asiant, gorchymyn diwethaf %s s yn ôl",
"horizon.agent.connected.many_ago": "Cysylltiedig: %s cysylltiadau asiant, gorchymyn diwethaf %s s yn ôl",
"horizon.agent.waiting": "Aros am asiant.",
"horizon.agent.off": "I ffwrdd: ni all asiantau gyrraedd y gêm hon.",
"horizon.agent.session_opened": "Agorwyd ar gyfer y sesiwn hon gan y rhaglen a ddechreuodd y gêm.",
"horizon.agent.socket": "Soced: %s (copïwch y manylion ar gyfer ei lwybr llawn)",
"horizon.agent.local_only": "Lleol yn unig: asiantau ar y cyfrifiadur hwn, byth y rhwydwaith.",
"Hides models behind opaque blocks. Entities update. Off with custom packs.": "Yn cuddio modelau y tu ôl i flociau afloyw. Diweddariad endidau. I ffwrdd â phecynnau arferol.",
"horizon.extras.repro_keys": "Recordio llwybrau byr",
"horizon.extras.repro_keys.tooltip": "Mae F3+R yn cychwyn neu'n stopio recordiad repro ac mae F3+E yn ysgrifennu ei fwndel. Wedi'i ddiffodd yn ddiofyn: mae recordiad yn copïo'r byd.",
"horizon.agent.record_session": "Dechrau Sesiwn Recordio",
"horizon.agent.record_stop": "Sesiwn Stopio Recordio",
"horizon.agent.export_repro": "Bwndel Repro Allforio",
"horizon.repro.started": "Dechreuwyd recordio; Mae F3+R yn ei stopio ac yn ysgrifennu'r bwndel.",
"horizon.repro.stopped": "Stopio recordio: %s",
"horizon.repro.bundle": "Bwndel repro: %s",
"horizon.repro.error": "Atgynhyrchiad: %s",
"horizon.repro.no_recording": "Nid oes recordiad i'w allforio eto.",
"horizon.repro.replay_started": "Ailchwarae wedi dechrau: %s",
"horizon.pack.addInfo": "Ychwanegu pecyn adnoddau gan Modrinth",
"horizon.pack.addWorking": "Wrthi'n holi Modrinth...",
"horizon.pack.addNone": "Heb ganfod pecynnau adnoddau",
"horizon.pack.addTitle": "Ychwanegu Pecyn Adnoddau",
"horizon.pack.install": "Gosod",
"horizon.pack.installing": "Gosod",
"horizon.pack.installed": "Wedi'i osod",
"horizon.pack.search": "Chwiliwch",
"horizon.pack.searchHint": "Chwilio Modrinth...",
"horizon.pack.filters": "Hidlau",
"horizon.pack.filter.realistic": "Realistig",
"horizon.pack.filter.simplistic": "Syml",
"horizon.pack.filter.vanillaLike": "Fel fanila",
"horizon.pack.addFailed": "Ni ellid ychwanegu'r pecyn: %s",
"horizon.extras.cosmetics_mine": "Fy colur",
"horizon.extras.cosmetics_mine.tooltip": "Cudd: mae pob chwaraewr Horizon yn eich gweld heb eich colur, fe wnaethoch chi gynnwys. Yr un switsh â Wardrob y lansiwr.",
"horizon.extras.cosmetics_others": "Colur chwaraewyr eraill",
"horizon.extras.cosmetics_others.tooltip": "Beth mae chwaraewyr Horizon eraill yn ei wisgo: popeth, dim ond eu steil tag enw, neu ddim byd. Yn gudd, nid yw'r gêm byth yn gofyn beth maen nhw'n ei wisgo.",
"horizon.extras.cosmetics_auras": "Auras a llwybrau eraill",
"horizon.extras.cosmetics_auras.tooltip": "Auras a llwybrau mae chwaraewyr eraill yn eu gwisgo. Wedi'i ddiffodd yn ddiofyn.",
"horizon.extras.cosmetics.shown": "Dangosir",
"horizon.extras.cosmetics.hidden": "Cudd",
"horizon.extras.cosmetics.name_tags": "Tagiau enw yn unig"
}
@@ -8,11 +8,9 @@
"horizon.options.move_deadzone": "Dødzone for bevægelse",
"horizon.options.look_deadzone": "Dødzone for kig",
"horizon.options.button_glyphs": "Knap Glyfer",
"horizon.options.menu_navigation": "Menu navigation",
"horizon.options.on_screen_keyboard": "Skærmtastatur",
"horizon.options.chat_delay": "Chatforsinkelse",
"horizon.options.lightbar.health": "Sundhed",
"horizon.options.menu_focus": "Fokus",
"horizon.options.keyboard.steam": "Damp",
"horizon.options.unavailable": "Denne funktion er endnu ikke tilgængelig i Horizon.",
"horizon.options.search": "Søgeindstillinger...",
@@ -154,7 +152,7 @@
"Cloud layer: off, flat (fast) or 3D (fancy).": "Skylag: slukket, fladt (hurtigt) eller 3D (fancy).",
"How far clouds reach, in chunks.": "Hvor langt skyer når, i bidder.",
"Draws the sky, sun, moon and stars.": "Tegner himmel, sol, måne og stjerner.",
"Distance fog. Off shows the edge of the loaded world.": "Afstandståge. Off viser kanten af ​​den belastede verden.",
"Distance fog. Off shows the edge of the loaded world.": "Afstandståge. Off viser kanten af den belastede verden.",
"Draws rain and snow.": "Tegner regn og sne.",
"How far rain and snow fall around you.": "Hvor langt regn og sne falder omkring dig.",
"How many particles effects spawn.": "Hvor mange partikler effekter gyder.",
@@ -287,7 +285,7 @@
"horizon.friends.stop_sharing": "Stop deling",
"horizon.friends.none_yet": "Ingen venner her endnu.",
"horizon.friends.here_now": "Her nu: %s",
"horizon.friends.share_intro": "Del denne verden bag en invitationskode: den åbner til LAN, og venner på internettet deltager med koden. Ingen af ​​vores servere er involveret.",
"horizon.friends.share_intro": "Del denne verden bag en invitationskode: den åbner til LAN, og venner på internettet deltager med koden. Ingen af vores servere er involveret.",
"horizon.friends.start_sharing": "Begynd at dele",
"horizon.friends.troubleshooting": "Fejlfinding...",
"horizon.friends.stopped": "Stoppet med at dele invitationskoden.",
@@ -11,7 +11,6 @@
"horizon.options.move_deadzone": "Totzone der Bewegung",
"horizon.options.look_deadzone": "Totzone des Blicks",
"horizon.options.button_glyphs": "Tastensymbole",
"horizon.options.menu_navigation": "Menünavigation",
"horizon.options.on_screen_keyboard": "Bildschirmtastatur",
"horizon.options.chat_delay": "Chatverzögerung",
"horizon.options.lightbar.health": "Gesundheit",
@@ -20,8 +19,6 @@
"horizon.options.glyphs.xbox": "Xbox",
"horizon.options.glyphs.playstation": "PlayStation",
"horizon.options.glyphs.nintendo": "Nintendo",
"horizon.options.menu_focus": "Fokus",
"horizon.options.menu_pointer": "Zeiger",
"horizon.options.keyboard.auto": "Auto",
"horizon.options.keyboard.horizon": "Horizon",
"horizon.options.keyboard.steam": "Steam",
@@ -9,14 +9,11 @@
"horizon.options.move_deadzone": "Νεκρή ζώνη κίνησης",
"horizon.options.look_deadzone": "Νεκρή ζώνη κάμερας",
"horizon.options.button_glyphs": "Γλυφικά κουμπιών",
"horizon.options.menu_navigation": "Πλοήγηση μενού",
"horizon.options.on_screen_keyboard": "Πληκτρολόγιο οθόνης",
"horizon.options.chat_delay": "Καθυστέρηση συνομιλίας",
"horizon.options.lightbar.health": "Υγεία",
"horizon.options.lightbar.dimension": "Διάσταση",
"horizon.options.glyphs.auto": "Αυτόματο",
"horizon.options.menu_focus": "Εστίαση",
"horizon.options.menu_pointer": "Δείκτης",
"horizon.options.keyboard.auto": "Αυτόματο",
"horizon.options.unavailable": "Αυτή η δυνατότητα δεν είναι ακόμη διαθέσιμη στο Horizon.",
"horizon.options.search": "Ρυθμίσεις αναζήτησης...",
@@ -11,7 +11,6 @@
"horizon.options.move_deadzone": "Move Deadzone",
"horizon.options.look_deadzone": "Look Deadzone",
"horizon.options.button_glyphs": "Button Glyphs",
"horizon.options.menu_navigation": "Menu Navigation",
"horizon.options.on_screen_keyboard": "On-Screen Keyboard",
"horizon.options.chat_delay": "Chat Delay",
"horizon.options.lightbar.health": "Health",
@@ -20,8 +19,6 @@
"horizon.options.glyphs.xbox": "Xbox",
"horizon.options.glyphs.playstation": "PlayStation",
"horizon.options.glyphs.nintendo": "Nintendo",
"horizon.options.menu_focus": "Focus",
"horizon.options.menu_pointer": "Pointer",
"horizon.options.keyboard.auto": "Auto",
"horizon.options.keyboard.horizon": "Horizon",
"horizon.options.keyboard.steam": "Steam",
@@ -522,5 +519,10 @@
"horizon.extras.cosmetics_auras.tooltip": "Auras and trails other players wear. Off by default.",
"horizon.extras.cosmetics.shown": "Shown",
"horizon.extras.cosmetics.hidden": "Hidden",
"horizon.extras.cosmetics.name_tags": "Name tags only"
"horizon.extras.cosmetics.name_tags": "Name tags only",
"horizon.options.pointer_speed": "Menu Pointer Speed",
"horizon.options.scroll_speed": "Menu Scroll Speed",
"horizon.options.repeat_delay": "Navigation Repeat Delay",
"horizon.options.repeat_interval": "Navigation Repeat Interval",
"horizon.options.milliseconds": "%s ms"
}
@@ -0,0 +1,475 @@
{
"horizon.options.controller": "Regilo",
"horizon.options.three_d_skin_layers": "3D Haŭtaj Tavoloj",
"horizon.options.vibration": "Vibro",
"horizon.options.adaptive_triggers": "Adaptigaj ellasiloj",
"horizon.options.lightbar": "Lumbreto",
"horizon.options.look_sensitivity": "Rigarda sentemo",
"horizon.options.invert_x": "Inversa rigardo X",
"horizon.options.invert_y": "Inversa rigardo Y",
"horizon.options.sneak_button": "Ŝtelira Butono",
"horizon.options.move_deadzone": "Mova mortzono",
"horizon.options.look_deadzone": "Rigarda mortzono",
"horizon.options.button_glyphs": "Butonaj Glifoj",
"horizon.options.on_screen_keyboard": "Surekrana Klavaro",
"horizon.options.chat_delay": "Babilado Prokrasto",
"horizon.options.lightbar.health": "Sano",
"horizon.options.lightbar.dimension": "Dimensio",
"horizon.options.glyphs.auto": "Aŭtomate",
"horizon.options.keyboard.auto": "Aŭtomate",
"horizon.options.unavailable": "Ĉi tiu funkcio ankoraŭ ne disponeblas en Horizon.",
"horizon.options.search": "Serĉaj agordoj...",
"horizon.pages.shader_packs": "Shader-Pakoj",
"horizon.pages.shader_options": "Shader Opcioj",
"horizon.pages.controller": "Agordoj de la regilo",
"horizon.pages.controller_binds": "Regilo Ligas",
"horizon.pages.extras": "Horizon Kromaĵoj",
"horizon.pages.settings": "Horizon Agordoj",
"horizon.pages.agent": "AI-agento",
"horizon.split.leave": "Foriru",
"horizon.pages.split_settings": "Splitekranaj Agordoj",
"horizon.pages.split_profiles": "Kiu ludas?",
"horizon.pages.split_setup": "Agordo de Splitekrano",
"horizon.pages.music": "Muziko",
"horizon.pages.music.menu": "Menuo",
"horizon.pages.music.overworld": "Supermondo",
"horizon.pages.music.creative": "Kreiva",
"horizon.pages.music.underwater": "Subakve",
"horizon.pages.music.end": "La Fino",
"horizon.pages.music.boss": "Estro",
"horizon.pages.music.credits": "Kreditoj",
"horizon.pages.music.overworld_biomes": "Supermondaj biomoj",
"horizon.pages.music.nether_biomes": "Malsupraj biomoj",
"horizon.pages.music.end_biomes": "Fini biomojn",
"horizon.pages.music.other": "Alia muziko",
"horizon.pages.music.empty": "Neniuj muzikoj: la sonoj de la ludo ne estas ŝarĝitaj.",
"horizon.pages.music.tooltip": "La enkonstruitaj spuroj, laŭ situacio: ludu unu nun, aŭ malŝaltu iun ajn por ke la ludo ĉesu elekti ĝin.",
"horizon.pages.settings.tooltip": "La propraj agordoj de Horizon: grafikaĵoj, regiloj, kromaĵoj kaj modoj.",
"horizon.pages.graphics.tooltip": "Grafikoj, simple: ĉiu agordo diras, kion ĝi kostas.",
"horizon.pages.graphics_advanced.tooltip": "Ĉiu grafika agordo, inkluzivita Horizon, kun tio, kion ĉiu kostas.",
"horizon.pages.controller.tooltip": "Regiloj: iliaj butonoj, bastonoj, vibro kaj menua navigado.",
"horizon.pages.extras.tooltip": "Laŭvolaj aldonoj, ĉiuj malŝaltitaj defaŭlte: la ludo restas vanilo ĝis vi ŝaltas unu.",
"horizon.pages.agent.tooltip": "Lasu vian propran AI-agenton ludi: horizon-ctl kaj MCP-klientoj sur ĉi tiu komputilo.",
"horizon.pages.split_profiles.tooltip": "Kiu ludas: profilo konservita en ĉi tiu luddosierujo.",
"horizon.pages.split_setup.tooltip": "La fenestroj kaj monitoroj, kaj kiu ludas kie.",
"horizon.pages.shader_packs.tooltip": "Shader-pakaĵoj el la dosierujo shaderpacks: la propra Horizon kaj tiuj en la ombraj/formato. Oni kuras samtempe.",
"horizon.pages.mods.tooltip": "Modifoj faritaj por Horizon, el la dosierujo horizon-mods. Ĉiu kuras en sablokesto kaj restas malŝaltita ĝis vi ŝaltas ĝin.",
"horizon.pages.open.graphics": "Grafikoj...",
"horizon.pages.open.graphics_advanced": "Altnivelaj Grafikoj...",
"horizon.pages.open.shader_packs": "Shader-Pakoj...",
"horizon.pages.open.controller": "Agordoj de regilo...",
"horizon.pages.open.split_setup": "Splitekrano...",
"horizon.pages.open.extras": "Kromaĵoj...",
"horizon.pages.open.mods": "Modifoj...",
"horizon.pages.open.agent": "AI-agento...",
"horizon.pages.open.horizon_graphics": "Horizon Grafikoj...",
"horizon.pages.open.controller_binds": "Regilo ligas...",
"horizon.pages.open.horizon_settings": "Horizon Agordoj...",
"horizon.pages.open.music": "Muziko...",
"horizon.mods.empty": "Neniuj modoj: metu .hmod dosierojn en la dosierujon horizon-mods",
"horizon.mods.stopped": " (Haltis)",
"horizon.mods.not_built": " (Ne konstruita)",
"horizon.mods.settings": "Agordoj",
"horizon.mods.in_world": " (lasu la mondon ŝanĝi)",
"horizon.mods.open_folder": "Malfermu Mod-dosierujon",
"horizon.shader_packs.empty": "Neniuj ombraj pakoj: metu ilin en la dosierujon de ombraj pakoj",
"horizon.shader_options.open": "Ombrilaj Opcioj...",
"horizon.shader_options.profile": "Profilo",
"horizon.shader_options.loading": "Legante la opciojn de la ombra pako...",
"horizon.agent.copy_details": "Kopiu Detalojn pri Konekto",
"horizon.multiplayer.join": "Aliĝu",
"horizon.multiplayer.cancel_reconnecting": "Nuligi Rekonekti",
"horizon.multiplayer.join_friend": "Aliĝu al Amiko",
"horizon.multiplayer.invite_code": "Inviti Kodon",
"horizon.multiplayer.relay": "Relajso (laŭvola)",
"Preset": "Antaŭdifinita",
"LOD Distance": "LOD Distanco",
"Far Detail": "Malproksima Detalo",
"Cave Culling": "Kavernhakado",
"Entity Culling": "Ento Culling",
"Chunk Updates": "Chunk Ĝisdatigoj",
"Upload Budget": "Alŝutu Buĝeton",
"Sky": "Ĉielo",
"Fog": "Nebulo",
"Weather": "Vetero",
"Animated Textures": "Viglaj Teksturoj",
"Present Mode": "Nuna Reĝimo",
"Frame Pacing": "Kadra Paŝado",
"Worker Threads": "Laboristaj Fadenoj",
"Resolution Scale": "Rezolucia Skalo",
"Anti-aliasing": "Kontraŭaliasing",
"Upscaling": "Altskalo",
"Paper White": "Papero Blanka",
"Peak Brightness": "Pinta Brilo",
"Black Level": "Nigra Nivelo",
"UI Brightness": "UI Brilo",
"Battery Mode": "Bateria Reĝimo",
"Graphics API": "Grafika API",
"Translucent Batching": "Travidebla Batching",
"Performance Overlay": "Efikeco Overlay",
"Full Detail Distance": "Plena Detalo Distanco",
"Off": "Malŝaltita",
"Unlimited": "Senlima",
"Auto": "Aŭtomate",
"Custom": "Kutimo",
"Performance": "Agado",
"Balanced": "Ekvilibrata",
"Quality": "Kvalito",
"Native": "Denaska",
"Full": "Plena",
"Reduced": "Reduktita",
"Efficient": "Efika",
"Precise": "Precize",
"Auto (VSync)": "Aŭtomata (VSync)",
"Mailbox": "Leterkesto",
"Immediate": "Tuj",
"FPS and Graph": "FPS kaj Grafiko",
"No cost": "Sen kosto",
"Low cost": "Malalta kosto",
"Medium cost": "Meza kosto",
"High cost": "Alta kosto",
"Saves time": "Ŝparas tempon",
"Graphics": "Grafikoj",
"Done": "Farita",
"This feature is not yet available in Horizon.": "Ĉi tiu funkcio ankoraŭ ne disponeblas en Horizon.",
"Your display or system doesn't offer HDR output.": "Via ekrano aŭ sistemo ne ofertas HDR-eligon.",
"Turn HDR on to use it.": "Ŝaltu HDR por uzi ĝin.",
"Your GPU or graphics API doesn't support it.": "Via GPU aŭ grafika API ne subtenas ĝin.",
"%s chunks": "%s pecoj",
"%s per frame": "%s per kadro",
"%s MiB/frame": "%s MiB/kadro",
"%s KiB/frame": "%s KiB/kadro",
"Auto (%s)": "Aŭtomata (%s)",
"Full-detail terrain range. LOD Distance can extend local worlds farther.": "Plendetala terena gamo. LOD Distanco povas etendi lokajn mondojn pli for.",
"Coarse distant terrain in local worlds. Integrated GPUs stop at 128 chunks.": "Kruda malproksima tereno en lokaj mondoj. Integritaj GPUoj ĉesas ĉe 128 pecoj.",
"Reduced draws far terrain coarser: faster on this integrated GPU.": "Reduktitaj tiradoj multe pli krudaj: pli rapide sur ĉi tiu integra GPU.",
"Reduced draws far terrain coarser, for GPUs it slows down.": "Reduktita tirado multe pli kruda, por GPU-oj ĝi malrapidiĝas.",
"Field of view. Wider shows more terrain at once.": "Vidkampo. Pli larĝa montras pli da tereno samtempe.",
"Caps frames per second; a cap saves power and heat.": "Ĉapaj kadroj por sekundo; ĉapo ŝparas potencon kaj varmon.",
"Waits for the display's refresh: no tearing, a little latency.": "Atendas la refreŝigon de la ekrano: neniu ŝirado, iom da latenco.",
"Size of menus and the HUD.": "Grandeco de menuoj kaj la HUD.",
"Sets the options below together. Changing one makes it Custom.": "Kunigas la eblojn sube. Ŝanĝi unu faras ĝin Propra.",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "Lafo, fajro kaj sunlumo brilas pli hele ol blanka. Bezonas HDR-ekranon.",
"Your display's darkest black. 0 for OLED; LCDs glow a little.": "Via ekrano estas plej malhela nigra. 0 por OLED; LCD-oj brilas iomete.",
"How bright highlights get. Auto: the peak your system reports.": "Kiel brilaj brilaĵoj ricevas. Aŭtomata: la pinto kiun via sistemo raportas.",
"How bright white paper and menus look. Auto: your desktop's white.": "Kiel brile blankaj paperoj kaj menuoj aspektas. Aŭtomata: via labortablo estas blanka.",
"How bright the HUD and menus are, against paper white.": "Kiel brilaj la HUD kaj menuoj estas, kontraŭ papera blanka.",
"How far mobs, crops and machines keep working. CPU time.": "Kiom malproksime amasoj, kultivaĵoj kaj maŝinoj daŭre funkcias. CPU-tempo.",
"How far away mobs and items are drawn, as a share of normal.": "Kiom malproksime amasoj kaj eroj estas tiritaj, kiel parto de normalo.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Saltas terenon kaŝitan malantaŭ fermitaj sekcioj. For kostas multe.",
"Hides entities behind opaque blocks.": "Kaŝas estaĵojn malantaŭ maldiafanaj blokoj.",
"No entity occlusion culling.": "Neniu ento-okludo-eliminado.",
"CPU occlusion test runs this frame.": "CPU-okludtesto kuras ĉi tiun kadron.",
"Occlusion checks run on a worker thread.": "Fermokontroloj funkcias sur laborfadeno.",
"Meshed sections uploaded per frame. Lower is smoother while loading.": "Reksitaj sekcioj alŝutitaj per kadro. Malsupra estas pli glata dum ŝarĝo.",
"Soft shadows in corners. Costs chunk building time only.": "Mildaj ombroj en anguloj. Kostas nur ioman konstruan tempon.",
"Smaller copies of block textures for distant terrain: less shimmer.": "Pli malgrandaj kopioj de blokaj teksturoj por malproksima tereno: malpli brilo.",
"Blends grass and water colours across biomes when building chunks.": "Miksas herbon kaj akvokolorojn tra biomoj dum konstruado de pecoj.",
"Cloud layer: off, flat (fast) or 3D (fancy).": "Nuba tavolo: malŝaltita, plata (rapida) aŭ 3D (fantazia).",
"How far clouds reach, in chunks.": "Kiom malproksime atingas nuboj, en pecoj.",
"Draws the sky, sun, moon and stars.": "Desegnas la ĉielon, sunon, lunon kaj stelojn.",
"Distance fog. Off shows the edge of the loaded world.": "Distanca nebulo. Off montras la randon de la ŝarĝita mondo.",
"Draws rain and snow.": "Desegnas pluvon kaj neĝon.",
"How far rain and snow fall around you.": "Kiom longe pluvo kaj neĝo falas ĉirkaŭ vi.",
"How many particles effects spawn.": "Kiom da partikloj generas.",
"Round shadows under mobs and items.": "Rondaj ombroj sub homamasoj kaj aĵoj.",
"Leaves with see-through gaps. Off draws them solid.": "Folioj kun travideblaj interspacoj. Off tiras ilin solidaj.",
"Sorts water and glass more exactly behind each other.": "Ordigas akvon kaj glason pli precize unu malantaŭ la alia.",
"Which chunk rebuilds wait for the frame: none, nearby or yours.": "Kiu peco rekonstruas atendas la kadron: neniu, proksime aŭ via.",
"Animates water, lava, fire and other moving textures.": "Animas akvon, lafon, fajron kaj aliajn moviĝantajn teksturojn.",
"World pixels per screen pixel. Below 100% is faster on the GPU.": "Mondaj pikseloj per ekranpikselo. Sub 100% estas pli rapida sur la GPU.",
"Smooths jagged edges. FXAA: softer. TAA: smoothest, moving things may trail.": "Glatigas dentitajn randojn. FXAA: pli mola. TAA: plej glate, moviĝantaj aferoj povas spuri.",
"Renders the world smaller and rebuilds it with TAA. Faster, softer.": "Malgrandigas la mondon kaj rekonstruas ĝin per TAA. Faster, pli mola.",
"HDR output on an HDR display: lava, fire and sunlight shine brighter.": "HDR-eligo sur HDR-ekrano: lafo, fajro kaj sunlumo brilas pli hele.",
"How frames reach the screen while VSync is off.": "Kiel kadroj atingas la ekranon dum VSync estas malŝaltita.",
"Efficient sleeps until the next frame; Precise spins, like vanilla.": "Efika dormas ĝis la sekva kadro; Precizaj turnoj, kiel vanilo.",
"Fills the screen (borderless).": "Plenigas la ekranon (senborda).",
"Brightens dark places.": "Heligas malhelajn lokojn.",
"Lowers the frame rate when minimised, or idle too.": "Malaltigas la framfrekvencon kiam minimumigita, aŭ neaktiva ankaŭ.",
"Sways the view while walking.": "Ŝancelas la vidon dum marŝado.",
"Fades newly loaded chunks in.": "Fadas lastatempe ŝarĝitajn pecojn.",
"Darkens the screen's edges.": "Malheligas la randojn de la ekrano.",
"Shows when the world saves.": "Montras kiam la mondo savas.",
"Shows attack cooldown.": "Montras atakan malvarmiĝon.",
"60 FPS cap (30 in menus or unfocused), half the workers: less power.": "60 FPS-ĉapo (30 en menuoj aŭ nefokusitaj), duono de la laboristoj: malpli da potenco.",
"Threads that generate, light and mesh chunks. Next start.": "Fadenoj kiuj generas, malpezajn kaj maŝpecojn. Sekva komenco.",
"Vulkan, then DirectX 12 (Windows) or OpenGL (Linux) where it fails. Next start.": "Vulkan, tiam DirectX 12 (Windows) aŭ OpenGL (Linux) kie ĝi malsukcesas. Sekva komenco.",
"Water and glass in one buffer: fewer draw calls, a bit more VRAM. Next start.": "Akvo kaj glaso en unu bufro: malpli da tirvokoj, iom pli da VRAM. Sekva komenco.",
"Mesh data sent to the GPU per frame. Lower is smoother while loading.": "Maŝdatenoj senditaj al la GPU per kadro. Malsupra estas pli glata dum ŝarĝo.",
"Frame rate and frame times over the game, with a graph.": "Framfrekvenco kaj framtempoj dum la ludo, kun grafikaĵo.",
"horizon.multiplayer.finding_friend": "Trovi la mondon de via amiko...",
"horizon.multiplayer.not_signed_in": "Ĉi tiu servilo estas en reta reĝimo. Ensalutu per via Microsoft konto en la Horizon lanĉilo por aliĝi al ĝi.",
"horizon.multiplayer.invalid_address": "Nevalida servila adreso: %s",
"horizon.multiplayer.connected_friend_direct": "Konektita rekte al via amiko...",
"horizon.multiplayer.connected_friend_relay": "Konektita al via amiko per la relajso...",
"General": "Generalo",
"Advanced": "Altnivela",
"Undo": "Malfari",
"Search options": "Serĉaj opcioj",
"Shortcut": "Ŝparvojo",
"Distance": "Distanco",
"Display": "Montru",
"Interface": "Interfaco",
"Sky and weather": "Ĉielo kaj vetero",
"Lighting and entities": "Lumigo kaj entoj",
"Textures": "Teksturoj",
"Image": "Bildo",
"Chunk loading": "Peceta ŝarĝo",
"Power": "Potenco",
"Frames": "Kadroj",
"Output": "Eligo",
"Brightness": "Brilo",
"How far you see, the window and the HUD.": "Kiom malproksime vi vidas, la fenestron kaj la HUD.",
"The sky and weather, lighting, textures and the image.": "La ĉielo kaj vetero, lumigado, teksturoj kaj la bildo.",
"Chunk loading, culling and power use.": "Chunk-ŝarĝado, forigo kaj potenco-uzo.",
"How frames reach the screen, and the performance overlay.": "Kiel kadroj atingas la ekranon, kaj la rendimentan superkovron.",
"Brighter-than-white output on an HDR display.": "Pli hela ol blanka eligo sur HDR-ekrano.",
"Results for \"%s\"": "Rezultoj por \"%s\"",
"No settings match.": "Neniuj agordoj kongruas.",
"horizon.title.not_affiliated": "Ne aligita kun Mojang aŭ Microsoft",
"horizon.extras.header.lighting": "Lumigo",
"horizon.extras.header.third_person": "Tria persono",
"horizon.extras.header.particles": "Partikloj",
"horizon.extras.header.inventory": "Inventaro",
"horizon.extras.header.chat": "Babilado",
"horizon.extras.header.multiplayer": "Plurludanto",
"horizon.extras.header.sound": "Sono",
"horizon.extras.header.menus": "Menuoj",
"horizon.extras.fullbright": "Plenluma",
"horizon.extras.dynamic_lights": "Dinamikaj lumoj",
"horizon.extras.own_name_tag": "Propra nometikedo",
"horizon.extras.hide_block_particles": "Kaŝi blok-rompajn partiklojn",
"horizon.extras.auto_reconnect": "Aŭtomata rekonekto",
"horizon.extras.ui_sound_reserve": "Konservu kanalojn por UI-sonoj",
"horizon.extras.music_page": "Muzika paĝo",
"horizon.extras.mouse_tweaks": "Mouse Tweaks kontroloj",
"horizon.extras.mouse_tweaks_wheel": "Rulumrado movas erojn",
"horizon.extras.sort": "Ordigi butonojn",
"horizon.extras.sort_key": "Ordiga klavo",
"horizon.extras.no_ai": "Neniu AI",
"horizon.extras.agent_control": "Agentkontrolo",
"horizon.extras.discord_presence": "Discord Riĉa Ĉeesto",
"horizon.extras.splitscreen": "Splitekrano",
"horizon.extras.chat_animation": "Glata babilado",
"horizon.extras.chat_indicator": "Mesaĝa indikilo",
"horizon.extras.wheel_cycles_settings": "Rulumrado ŝanĝas agordojn",
"horizon.extras.pack_browser": "Retumilo pri paka rimedo",
"horizon.extras.fullbright.tooltip": "Montru blokojn kaj entojn ĉe plena lumo, eĉ en malhelaj lokoj.",
"horizon.extras.dynamic_lights.tooltip": "Torĉoj kaj aliaj lumfontoj tenis, faligis aŭ sur fajro lumigas la blokojn ĉirkaŭ ili.",
"horizon.extras.own_name_tag.tooltip": "Montras vian nomon super via kapo en tria persono, kiel aliaj ludantoj vidas ĝin. Kaŝita kiam la servilo kaŝas la nomojn de via teamo aŭ vi estas nevidebla.",
"horizon.extras.hide_block_particles.tooltip": "Kaŝas la derompaĵojn, kiuj krevas el rompita bloko kaj diseriĝas de tiu, kiun vi minas. Aliaj partikloj restas.",
"horizon.extras.auto_reconnect.tooltip": "Reeniĝas al servilo post perdita konekto aŭ rekomenco, neniam post piedbato aŭ malpermeso.",
"horizon.extras.ui_sound_reserve.tooltip": "Sonoj en la mondo lasas 16 el la 247 sonkanaloj liberaj, do inundo da ili (granda eksplodo) ne povas silentigi menuklakojn.",
"horizon.extras.music_page.tooltip": "Muziko & Sonoj ricevas paĝon Muziko...: ludu iun ajn enkonstruitan trakon nun aŭ malŝaltu unu por ke la ludo ĉesu elekti ĝin. Nenio ŝanĝiĝas ĝis vi malŝaltas trakon.",
"horizon.extras.mouse_tweaks.tooltip": "Dekstre-tiri lokas po unu por fendeto, maldekstre-treni kolektas la tenitan objekton, Shift + maldekstra-tiri movas ĉiun fendon.",
"horizon.extras.mouse_tweaks_wheel.tooltip": "Suben sendas unu objekton de la fendo al la alia flanko, supren alportas unu reen.",
"horizon.extras.sort.tooltip": "Ordigas la inventaron, kestojn, barelojn, ender-kestojn kaj ŝulker-skatolojn en krea klapeta ordo, kunfandante stakojn.",
"horizon.extras.sort_key.tooltip": "Ordigas la parton sub la kursoro. Klaku, tiam premu klavon; Eskapo malligas ĝin.",
"horizon.extras.no_ai.tooltip": "Kaŝas kaj malŝaltas ĉiun AI-funkcion: agentkontrolo, horizon-ctl kaj la MCP-serviloj. Ankaŭ la ŝaltilo de la lanĉilo.",
"horizon.extras.agent_control.tooltip": "Via propra AI-agento povas ludi ĉi tiun ludon per horizon-ctl aŭ MCP-kliento, nur en ĉi tiu komputilo.",
"horizon.extras.discord_presence.tooltip": "Montras Ludante Horizon sur via Discord profilo dum Discord funkcias: menuoj, unuludanto aŭ plurludanto, la dimensio kaj la tempo ludata. Neniam mondnomo aŭ adreso de servilo.",
"Calibrate...": "Kalibri...",
"Calibrate Anyway...": "Kalibru ĉiuokaze...",
"Use Automatic Setup": "Uzu Aŭtomatan Agordon",
"Shortcut: %s": "Ŝparvojo: %s",
"Next": "Poste",
"HDR Calibration %s/%s: %s": "HDR-Kalibrado %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Agordas la tri sub plenan ekranon, unu testan bildon samtempe.",
"A key that opens this screen from the game. Not bound at first.": "Ŝlosilo, kiu malfermas ĉi tiun ekranon de la ludo. Komence ne ligita.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Via ekrano ne diris kiom brilas ĝi fariĝas: kalibru por agordi ĝin.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Jam agordita por via ekrano. Nur kalibru se HDR aspektas malĝusta.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Remetas Pintan Brilon kaj Paperan Blankan al la agordo de via ekrano.",
"Set up for your display. Leave on Auto unless it looks wrong.": "Agordu por via ekrano. Lasu Aŭtomate krom se ĝi aspektas malĝusta.",
"Lower it until the logo just disappears into the black.": "Mallevu ĝin ĝis la emblemo simple malaperas en la nigran.",
"Raise it until the logo just disappears into the bright square.": "Levu ĝin ĝis la emblemo simple malaperos en la helan kvadraton.",
"Make the card a white that's comfortable to look at for a long time, in the light you play in.": "Faru la karton blankan, kiu estas komforta rigardi dum longa tempo, en la lumo en kiu vi ludas.",
"horizon.splitscreen.profiles.forget": "Forgesu",
"horizon.splitscreen.profiles.create": "Krei",
"horizon.splitscreen.profiles.you": " - vi",
"horizon.splitscreen.profiles.forget_named": "Forgesu %s",
"horizon.splitscreen.profiles.previous": "Antaŭa",
"horizon.splitscreen.profiles.next": "Poste",
"horizon.splitscreen.profiles.new_button": "Nova Profilo...",
"horizon.splitscreen.profiles.forget_title": "Forgesu profilon?",
"horizon.splitscreen.profiles.forget_leaves": "Ĝi forlasas la liston de ĉi tiu kazo.",
"horizon.splitscreen.profiles.forget_kept": "Monda progreso kaj agordoj estas konservitaj.",
"horizon.splitscreen.profiles.new_title": "Nova Profilo",
"horizon.splitscreen.profiles.name_hint": "Nomo (3 ĝis 16 literoj, ciferoj, _)",
"horizon.splitscreen.profiles.switch_title": "Ŝanĝu profilon",
"horizon.splitscreen.profiles.offline_note": "Senretaj profiloj apartenas al ĉi tiu kazo",
"horizon.splitscreen.profiles.move": "Movu kien vi ludas",
"horizon.splitscreen.profiles.move_place": "Movu kien vi ludas: %s",
"horizon.friends.title": "Ludu kun Amikoj",
"horizon.friends.hint": "Amikoj algluas la kodon post la butono Amikoj de la titolekrano, en Rekta Konekto aŭ sur la Luda paĝo de la lanĉilo.",
"horizon.friends.copied": "Kopiita",
"horizon.friends.copy_code": "Kopiu Kodon",
"horizon.friends.stop_sharing": "Ĉesu Kundividon",
"horizon.friends.none_yet": "Ankoraŭ neniuj amikoj ĉi tie.",
"horizon.friends.here_now": "Ĉi tie nun: %s",
"horizon.friends.share_intro": "Kunhavigu ĉi tiun mondon malantaŭ invitkodo: ĝi malfermiĝas al LAN, kaj amikoj en la interreto aliĝas kun la kodo. Neniu servilo nia estas implikita.",
"horizon.friends.start_sharing": "Komencu Kunhavigi",
"horizon.friends.troubleshooting": "Solvado de problemoj...",
"horizon.friends.stopped": "Ĉesis kunhavigi la invitkodon.",
"horizon.friends.short_code_changed": "Iu daŭre provis malĝustan mallongan kodon, do ĝi ŝanĝiĝis.",
"horizon.friends.wrong_code": "Iu provis malĝustan kodon.",
"horizon.friends.code_ready": "Via invitkodo estas preta: Luda Menuo, Ludu kun Amikoj. Kopiu ĝin kaj sendu ĝin al viaj amikoj.",
"horizon.friends.share_failed": "Ne povis dividi la mondon: %s",
"horizon.friends.finding_route": "Trovi kiel amikoj povas atingi vin...",
"horizon.osk.space": "Spaco",
"horizon.osk.delete": "Forigi",
"horizon.osk.type": "Tajpu",
"horizon.osk.symbols": "Simboloj",
"horizon.osk.close": "Fermu",
"horizon.extras.chat_animation.tooltip": "Nova babillinio glitas enen de maldekstre dum ĝi fadas, anstataŭ aperi tute.",
"horizon.extras.chat_indicator.tooltip": "La kolora stango maldekstre de babillinio.",
"horizon.extras.wheel_cycles_settings.tooltip": "La rula rado super butono en la agordoj ŝanĝas sian valoron, kiel en vanilo. Malŝaltita, la rado nur rulumas la liston.",
"horizon.extras.pack_browser.tooltip": "Elektu Rimedo-Pakojn ricevas butonon Aldoni, kiu serĉas Modrinth kaj instalas pakon en viajn pakajn dosierujojn. Malŝaltita, la ludo neniam kontaktas Modrinth.",
"horizon.extras.splitscreen.tooltip": "En mondo, alia regilo povas aliĝi kiel nova ludanto: premu A kiam ĝi petas, tiam elektu profilon kaj kie ludi.",
"horizon.splitscreen.setup.monitor": "Monitoro %s",
"horizon.splitscreen.setup.monitor_unknown": "monitoro",
"horizon.splitscreen.setup.window": "Fenestro %s",
"horizon.splitscreen.setup.new_window": "Nova fenestro",
"horizon.splitscreen.setup.fill_monitor": "Plenigu %s",
"horizon.splitscreen.setup.done": "Farita",
"horizon.splitscreen.setup.off": "Malŝaltita",
"horizon.splitscreen.setup.player": "Ludanto %s",
"horizon.splitscreen.setup.revert": "Reiri",
"horizon.splitscreen.setup.apply": "Apliki",
"horizon.splitscreen.setup.warning.empty_main": "Neniu ludas en Fenestro 1: ĝi kaŝas",
"horizon.splitscreen.setup.warning.empty_close": "Neniu ludas en Fenestro %s: ĝi fermiĝas sur Apliki",
"horizon.splitscreen.setup.warning.empty_new": "Neniu ludas en Fenestro %s: ĝi ne malfermos",
"horizon.splitscreen.setup.warning.on_main": "Fenestro %s plenigas la ekranon de Fenestro 1: ĝiaj ludantoj ludas en Fenestro 1",
"horizon.splitscreen.setup.warning.unplugged": "La ekrano de Fenestro %s ne estas konektita: ĝiaj ludantoj ludas en Fenestro 1",
"horizon.splitscreen.setup.status.off": "Splitekrano estas malŝaltita.",
"horizon.splitscreen.setup.status.enable_hint": "Ŝaltu ĝin, tiam premu butonon de alia regilo en mondo.",
"horizon.splitscreen.setup.status.join_hint": "Premu A por aliĝi kaj elekti profilon.",
"horizon.splitscreen.setup.status.idle": "Splitekrano neaktiva",
"horizon.splitscreen.setup.status.alone": "Neniuj aliaj lokaj ludantoj.",
"horizon.splitscreen.setup.status.solo_controls": "Solaj kontroloj kaj regilo-religoj estas senŝanĝaj.",
"horizon.splitscreen.setup.window_empty": "Fenestro %s: malplena",
"horizon.splitscreen.setup.note.select": "Alklaku ludanton aŭ trenu ĝin al fenestro aŭ ekrano.",
"horizon.splitscreen.setup.note.apply": "Apliki por ludi tiamaniere, Reiru por malfari.",
"horizon.splitscreen.setup.note.place": "Kie devus ludi %s?",
"horizon.splitscreen.setup.this_player": "ĉi tiu ludanto",
"horizon.splitscreen.setup.switch_disabled": "Splitekrano malŝaltas post kiam la aliaj foriris (ilia paŭza menuo).",
"horizon.splitscreen.setup.window_place": "Fenestro %s (%s de %s)",
"horizon.splitscreen.setup.names": "Nomoj",
"horizon.splitscreen.setup.identify": "Identigi",
"horizon.splitscreen.setup.layout.two": "2 kundivido: %s",
"horizon.splitscreen.setup.layout.three": "3 kundivido: %s",
"horizon.splitscreen.setup.layout.two.0": "Aŭtomate",
"horizon.splitscreen.setup.layout.two.1": "Flank-al-flanke",
"horizon.splitscreen.setup.layout.two.2": "Supre kaj malsupre",
"horizon.splitscreen.setup.layout.three.0": "Unu larĝa supre",
"horizon.splitscreen.setup.layout.three.1": "Unu alta maldekstre",
"horizon.splitscreen.setup.layout.three.2": "Flank-al-flanke",
"horizon.splitscreen.setup.help.player": "Klaku aŭ trenu %s por movi ilin.",
"horizon.splitscreen.setup.help.layout_auto": "%s (flank-al-flanke kiam la fenestro estas larĝa). Premu por ŝanĝi.",
"horizon.splitscreen.setup.help.layout": "%s. Premu por ŝanĝi.",
"horizon.splitscreen.setup.help.switch": "Splitekrano por ĉi tiu komputilo, ŝaltita aŭ malŝaltita.",
"horizon.splitscreen.setup.help.identify": "La vido de ĉiu ludanto montras en kiu fenestro ĝi estas.",
"horizon.splitscreen.setup.help.names": "La nomo de ĉiu ludanto en la angulo de sia vido.",
"horizon.splitscreen.setup.help.revert": "Malfaru la ŝanĝojn ankoraŭ ne aplikitajn.",
"horizon.splitscreen.setup.help.apply": "Ludu kiel la mapo montras.",
"horizon.splitscreen.setup.help.unchanged": "Ankoraŭ nenio ŝanĝiĝis.",
"horizon.splitscreen.setup.give_up_pad": "Rezignu Pad",
"horizon.splitscreen.prompt.controller_connected": "Regilo konektita",
"horizon.splitscreen.prompt.controller_disconnected": "Regilo malkonektita",
"horizon.splitscreen.prompt.controller_disconnected.hint": "Rekonektu ĝin, aŭ premu butonon sur alia",
"horizon.splitscreen.prompt.join.one": "%s volas aliĝi",
"horizon.splitscreen.prompt.join.many": "%s regiloj volas aliĝi",
"horizon.splitscreen.prompt.join.yes": "Aliĝu",
"horizon.splitscreen.prompt.join.as_p1": "Ludu kiel P1",
"horizon.splitscreen.prompt.join.no": "Ne",
"horizon.splitscreen.prompt.lost": "%s: regilo malkonektita",
"horizon.splitscreen.prompt.lost.use_another": "Uzu alian",
"horizon.splitscreen.prompt.lost.leave": "Foriru",
"horizon.splitscreen.setup.help.give_up_pad": "Via regilo ĉesas veturigi vin kaj estas proponita aliĝi (A).",
"horizon.splitscreen.setup.note.held_at": "%s al %s: A faligas ĝin ĉi tien, B remetas ĝin.",
"horizon.splitscreen.setup.note.held": "%s kaptis: maldekstre/dekstre elektas kie, A faligas, B remetas ĝin.",
"Hides models behind opaque blocks. Async checks off-thread; entities tick.": "Kaŝas modelojn malantaŭ maldiafanaj blokoj. Async kontrolas eksterfadenon; entoj tiktas.",
"Block Entities": "Blokaj Entoj",
"Shulker Boxes": "Ŝulkeraj Kestoj",
"Reuses settled boxes; lid movement stays animated.": "Reuzas fiksitajn skatolojn; klapomovo restas vigla.",
"Signs": "Signoj",
"Reuses sign text until text, light, or font changes.": "Reuzas signan tekston ĝis teksto, lumo aŭ tiparo ŝanĝiĝas.",
"Decorated Pots": "Ornitaj Potoj",
"Reuses still pots; break wobble and sherd changes redraw.": "Reuzas ankoraŭ potojn; rompi ŝanceliĝon kaj ŝancelŝanĝojn redesegni.",
"Bells": "Sonoriloj",
"Reuses still bells; ringing stays animated.": "Reuzas ankoraŭ sonorilojn; sonorado restas vigla.",
"Chests": "Kestoj",
"Reuses closed chests; moving lids stay animated.": "Reuzas fermitajn kestojn; movantaj kovriloj restas viglaj.",
"Banners": "Standardoj",
"Reuses poles; flags and pattern layers still update.": "Reuzas polojn; flagoj kaj ŝablono tavoloj ankoraŭ ĝisdatigas.",
"Copper Golem Statues": "Kupro-Golemaj Statuoj",
"Reuses statue geometry until its block state changes.": "Reuzas statuan geometrion ĝis ĝia bloka stato ŝanĝiĝas.",
"Leaf Culling": "Foliĉado",
"Every leaf face. Vanilla's fancy leaves.": "Ĉiu folia vizaĝo. La fantaziaj folioj de vanilo.",
"Distant Plant Dither": "Malproksima Plant Dither",
"Keeps fine plants in high-res packs from fading far away.": "Malproksimigas bonajn plantojn en alt-rezultaj pakoj.",
"Screenshot Quality": "Ekrankopio Kvalito",
"F2 screenshots at this many times the game's resolution. Higher takes longer.": "F2-ekrankopioj je tio multfoje la rezolucio de la ludo. Pli alta daŭras pli longe.",
"Frame Generation": "Frama Generacio",
"Shows a made-up frame between each two real ones: smoother, a little more lag.": "Montras elfaritan kadron inter ĉiu du realaj: pli glata, iom pli da malfruo.",
"Shadow Cache": "Ombra Deponejo",
"Reuses the pack shadow map when sun, view and world match. Off by default.": "Reuzas la pak-ombromapon kiam suno, vido kaj mondo kongruas. Malŝaltita defaŭlte.",
"Find BlueMap": "Trovu BlueMap",
"On servers, finds their BlueMap web map for far terrain. Next join.": "Sur serviloj, trovas ilian BlueMap-retmapon por malproksima tereno. Poste aliĝu.",
"BlueMap Radius": "BlueMap Radiuso",
"How far around you BlueMap tiles are downloaded. Next join.": "Kiom malproksime ĉirkaŭ vi BlueMap-kaheloj estas elŝutitaj. Poste aliĝu.",
"Synchronous": "Sinkrona",
"Asynchronous": "Nesinkrona",
"Solid": "Solida",
"Solid Aggressive": "Solida Agresema",
"Hollow": "Kava",
"Leaves fully inside a canopy draw solid: fewer see-through layers.": "Folioj plene ene de baldakeno tiras solidan: malpli da travideblaj tavoloj.",
"Leaves with one open side draw solid too: faster, chunkier edges.": "Folioj kun unu malfermita flanko ankaŭ desegnas solidan: pli rapidaj, pli dikaj randoj.",
"Drops faces between leaves: faster, but gaps look through.": "Faligas vizaĝojn inter folioj: pli rapide, sed interspacoj trarigardas.",
"Hotbar: Previous Slot": "Hotbar: Antaŭa Slot",
"Hotbar: Next Slot": "Hotbar: Sekva Slot",
"horizon.agent.connected.one": "Konektita: 1 agenta konekto",
"horizon.agent.connected.many": "Konektitaj: %s agentaj konektoj",
"horizon.agent.connected.one_ago": "Konektita: 1 agenta konekto, lasta komando antaŭ %s s",
"horizon.agent.connected.many_ago": "Konektita: %s agentaj konektoj, lasta komando antaŭ %s s",
"horizon.agent.waiting": "Atendante agenton.",
"horizon.agent.off": "Malŝaltita: agentoj ne povas atingi ĉi tiun ludon.",
"horizon.agent.session_opened": "Malfermita por ĉi tiu sesio de la programo kiu komencis la ludon.",
"horizon.agent.socket": "Ingo: %s (kopiu la detalojn por ĝia plena vojo)",
"horizon.agent.local_only": "Loka nur: agentoj sur ĉi tiu komputilo, neniam la reto.",
"Auto (%s nits)": "Aŭtomata (%s nits)",
"Hides models behind opaque blocks. Entities update. Off with custom packs.": "Kaŝas modelojn malantaŭ maldiafanaj blokoj. Entoj ĝisdatigas. For per kutimaj pakoj.",
"horizon.extras.repro_keys": "Registrado de ŝparvojoj",
"horizon.extras.repro_keys.tooltip": "F3+R komencas aŭ ĉesigas reproduktan registradon kaj F3+E skribas ĝian pakaĵon. Malŝaltita defaŭlte: registrado kopias la mondon.",
"horizon.agent.record_session": "Komencu Registradan Sesion",
"horizon.agent.record_stop": "Ĉesu Registrado-Sesion",
"horizon.agent.export_repro": "Eksportu Repro Bundle",
"horizon.repro.started": "Registrado komenciĝis; F3+R haltigas ĝin kaj skribas la pakaĵon.",
"horizon.repro.stopped": "Registrado ĉesis: %s",
"horizon.repro.bundle": "Reprodukta pakaĵo: %s",
"horizon.repro.no_recording": "Ankoraŭ ne estas registrado por eksporti.",
"horizon.repro.replay_started": "Ripeto komenciĝis: %s",
"horizon.pack.addInfo": "Aldonu rimedan pakon de Modrinth",
"horizon.pack.addWorking": "Demandante Modrinth...",
"horizon.pack.addNone": "Neniuj rimedpakaĵoj trovitaj",
"horizon.pack.addTitle": "Aldonu Rimedan Pakon",
"horizon.pack.install": "Instali",
"horizon.pack.installing": "Instalado",
"horizon.pack.installed": "Instalita",
"horizon.pack.search": "Serĉu",
"horizon.pack.searchHint": "Serĉu Modrinth...",
"horizon.pack.filters": "Filtriloj",
"horizon.pack.filter.realistic": "Realisma",
"horizon.pack.filter.simplistic": "Simplisma",
"horizon.pack.filter.vanillaLike": "Vanilsimila",
"horizon.pack.addFailed": "La pako ne povis esti aldonita: %s",
"horizon.extras.header.cosmetics": "Kosmetikaĵoj",
"horizon.extras.cosmetics_mine": "Miaj kosmetikaĵoj",
"horizon.extras.cosmetics_mine.tooltip": "Kaŝita: ĉiu Horizon-ludanto vidas vin sen viaj kosmetikaĵoj, vi inkluzivis. La sama ŝaltilo kiel la Vestoŝranko de la lanĉilo.",
"horizon.extras.cosmetics_others": "Kosmetikaĵoj de aliaj ludantoj",
"horizon.extras.cosmetics_others.tooltip": "Kion aliaj Horizon ludantoj portas: ĉion, nur siajn nometikedstilojn, aŭ nenion. Kaŝita, la ludo neniam demandas kion ili portas.",
"horizon.extras.cosmetics_auras": "Aŭroj kaj spuroj de aliaj",
"horizon.extras.cosmetics_auras.tooltip": "Aŭroj kaj spuroj kiujn aliaj ludantoj portas. Malŝaltita defaŭlte.",
"horizon.extras.cosmetics.shown": "Montrita",
"horizon.extras.cosmetics.hidden": "Kaŝita",
"horizon.extras.cosmetics.name_tags": "Nomaj etikedoj nur"
}
@@ -10,7 +10,6 @@
"horizon.options.move_deadzone": "Zona muerta de movimiento",
"horizon.options.look_deadzone": "Zona muerta de vista",
"horizon.options.button_glyphs": "Símbolos de botones",
"horizon.options.menu_navigation": "Navegación de menús",
"horizon.options.on_screen_keyboard": "Teclado en pantalla",
"horizon.options.chat_delay": "Retardo del chat",
"horizon.options.lightbar.health": "Salud",
@@ -19,8 +18,6 @@
"horizon.options.glyphs.xbox": "Xbox",
"horizon.options.glyphs.playstation": "PlayStation",
"horizon.options.glyphs.nintendo": "Nintendo",
"horizon.options.menu_focus": "Foco",
"horizon.options.menu_pointer": "Puntero",
"horizon.options.keyboard.auto": "Automático",
"horizon.options.keyboard.horizon": "Horizon",
"horizon.options.keyboard.steam": "Steam",
@@ -0,0 +1,472 @@
{
"horizon.options.controller": "Kontroller",
"horizon.options.vibration": "Vibratsioon",
"horizon.options.adaptive_triggers": "Adaptiivsed päästikud",
"horizon.options.lightbar": "Valgusriba",
"horizon.options.look_sensitivity": "Vaate tundlikkus",
"horizon.options.invert_x": "Vaate X-telje pööramine",
"horizon.options.invert_y": "Vaate Y-telje pööramine",
"horizon.options.sneak_button": "Hiilimisnupp",
"horizon.options.move_deadzone": "Liikumise surnud tsoon",
"horizon.options.look_deadzone": "Vaate surnud tsoon",
"horizon.options.button_glyphs": "Nuppude glüüfid",
"horizon.options.on_screen_keyboard": "Ekraaniklaviatuur",
"horizon.options.chat_delay": "Vestluse viivitus",
"horizon.options.lightbar.health": "Tervis",
"horizon.options.lightbar.dimension": "Mõõtmed",
"horizon.options.glyphs.auto": "Automaatne",
"horizon.options.keyboard.auto": "Automaatne",
"horizon.options.unavailable": "See funktsioon pole Horizonis veel saadaval.",
"horizon.options.search": "Otsinguseaded...",
"horizon.pages.mods": "Modifikatsioonid",
"horizon.pages.shader_options": "Varjundi valikud",
"horizon.pages.controller": "Kontrolleri sätted",
"horizon.pages.controller_binds": "Kontroller seob",
"horizon.pages.extras": "Horizoni lisad",
"horizon.pages.settings": "Horizoni sätted",
"horizon.pages.agent": "AI agent",
"horizon.split.leave": "Lahku",
"horizon.pages.split_settings": "Jagatud ekraani seaded",
"horizon.pages.split_profiles": "Kes mängib?",
"horizon.pages.split_setup": "Jagatud ekraani seadistamine",
"horizon.pages.music": "Muusika",
"horizon.pages.music.menu": "Menüü",
"horizon.pages.music.overworld": "Ülemaailm",
"horizon.pages.music.creative": "Loominguline",
"horizon.pages.music.underwater": "Vee all",
"horizon.pages.music.end": "Lõpp",
"horizon.pages.music.credits": "Krediidid",
"horizon.pages.music.overworld_biomes": "Ülemaailma elustikud",
"horizon.pages.music.nether_biomes": "Nether bioom",
"horizon.pages.music.end_biomes": "Lõpetage bioomid",
"horizon.pages.music.other": "Muu muusika",
"horizon.pages.music.empty": "Muusikalugusid pole: mängu helisid ei laadita.",
"horizon.pages.music.tooltip": "Sisseehitatud lood vastavalt olukorrale: mängige kohe ühte või lülitage mis tahes välja, et mäng lõpetaks selle valimise.",
"horizon.pages.settings.tooltip": "Horizoni enda seaded: graafika, kontrollerid, lisad ja modifikatsioonid.",
"horizon.pages.graphics.tooltip": "Graafika lihtsalt: iga seade ütleb, kui palju see maksab.",
"horizon.pages.graphics_advanced.tooltip": "Iga graafikaseade, Horizon kaasas, koos sellega, mis maksab.",
"horizon.pages.controller.tooltip": "Kontrollerid: nende nupud, pulgad, vibratsioon ja menüüs navigeerimine.",
"horizon.pages.extras.tooltip": "Valikulised täiendused, kõik vaikimisi välja lülitatud: mäng jääb vaniljeks, kuni selle sisse lülitate.",
"horizon.pages.agent.tooltip": "Laske oma AI-agendil selles arvutis mängida: horizon-ctl ja MCP-kliendid.",
"horizon.pages.split_profiles.tooltip": "Kes mängib: selles mängukaustas hoitav profiil.",
"horizon.pages.split_setup.tooltip": "Aknad ja monitorid ning kes kus mängib.",
"horizon.pages.shader_packs.tooltip": "Varjupaketid kaustast Shaderpacks: Horizoni oma ja need, mis on formaadis Shader/. Üks jookseb korraga.",
"horizon.pages.mods.tooltip": "Horizoni jaoks tehtud modifikatsioonid horizon-mods kaustast. Igaüks neist töötab liivakastis ja jääb väljalülitatuks, kuni selle sisse lülitate.",
"horizon.pages.open.graphics": "Graafika...",
"horizon.pages.open.graphics_advanced": "Täiustatud graafika...",
"horizon.pages.open.controller": "Kontrolleri seaded...",
"horizon.pages.open.split_setup": "Jagatud ekraan...",
"horizon.pages.open.extras": "Lisad...",
"horizon.pages.open.mods": "Modifikatsioonid...",
"horizon.pages.open.agent": "AI agent...",
"horizon.pages.open.horizon_graphics": "Horizoni graafika...",
"horizon.pages.open.controller_binds": "Kontroller seob...",
"horizon.pages.open.horizon_settings": "Horizoni seaded...",
"horizon.pages.open.music": "Muusika...",
"horizon.mods.empty": "Modifikatsioone pole: asetage .hmod-failid kausta horizon-mods",
"horizon.mods.stopped": " (Peatatud)",
"horizon.mods.not_built": " (Ehitamata)",
"horizon.mods.dev": " (arendaja)",
"horizon.mods.settings": "Seaded",
"horizon.mods.in_world": " (jätke maailm muutuma)",
"horizon.mods.open_folder": "Avage kaust Mod",
"horizon.shader_packs.empty": "Varjupakkideta: pange need varjupakkide kausta",
"horizon.shader_options.open": "Varjundi valikud...",
"horizon.shader_options.profile": "Profiil",
"horizon.shader_options.loading": "Varjutuspaketi valikute lugemine...",
"horizon.agent.copy_details": "Kopeerige ühenduse üksikasjad",
"horizon.multiplayer.join": "Liitu",
"horizon.multiplayer.cancel_reconnecting": "Tühista uuesti ühendamine",
"horizon.multiplayer.join_friend": "Liituge sõbraga",
"horizon.multiplayer.invite_code": "Kutsekood",
"horizon.multiplayer.relay": "Relee (valikuline)",
"Preset": "Eelseadistatud",
"LOD Distance": "LOD vahemaa",
"Far Detail": "Kauge detail",
"Entity Culling": "Üksuste tapmine",
"Chunk Updates": "Tükkide värskendused",
"Upload Budget": "Laadi eelarve üles",
"Sky": "Taevas",
"Fog": "Udu",
"Weather": "Ilm",
"Animated Textures": "Animeeritud tekstuurid",
"Present Mode": "Praegune režiim",
"Worker Threads": "Tööliste niidid",
"Resolution Scale": "Eraldusvõime skaala",
"Anti-aliasing": "Antialiasing",
"Upscaling": "Suurendamine",
"Paper White": "Paber Valge",
"Peak Brightness": "Maksimaalne heledus",
"Black Level": "Must tase",
"UI Brightness": "UI heledus",
"Battery Mode": "Akurežiim",
"Graphics API": "Graafika API",
"Translucent Batching": "Läbipaistev pakkimine",
"Performance Overlay": "Toimivuse ülekate",
"Full Detail Distance": "Täielik detailne kaugus",
"On": "Sees",
"Off": "Väljas",
"Unlimited": "Piiramatu",
"Auto": "Automaatne",
"Custom": "Kohandatud",
"Performance": "Esitus",
"Balanced": "Tasakaalustatud",
"Quality": "Kvaliteet",
"Ultra Performance": "Ultra jõudlus",
"Native": "Natiivne",
"Full": "Täis",
"Reduced": "Vähendatud",
"Efficient": "Tõhus",
"Precise": "Täpne",
"Auto (VSync)": "Automaatne (VSync)",
"Mailbox": "Postkast",
"Immediate": "Vahetu",
"FPS and Graph": "FPS ja graafik",
"No cost": "Ei mingeid kulusid",
"Low cost": "Madalad kulud",
"Medium cost": "Keskmine kulu",
"High cost": "Kõrge hind",
"Saves time": "Säästab aega",
"Graphics": "Graafika",
"Done": "Valmis",
"This feature is not yet available in Horizon.": "See funktsioon pole Horizonis veel saadaval.",
"Your display or system doesn't offer HDR output.": "Teie ekraan või süsteem ei paku HDR-väljundit.",
"Turn HDR on to use it.": "Selle kasutamiseks lülitage HDR sisse.",
"Your GPU or graphics API doesn't support it.": "Teie GPU või graafika API seda ei toeta.",
"%s chunks": "%s tükki",
"%s per frame": "%s kaadri kohta",
"%s MiB/frame": "%s MiB/kaader",
"%s KiB/frame": "%s KiB/kaader",
"Auto (%s)": "Automaatne (%s)",
"Full-detail terrain range. LOD Distance can extend local worlds farther.": "Täielik detailne maastikuvalik. LOD Distance võib laiendada kohalikke maailmu kaugemale.",
"Coarse distant terrain in local worlds. Integrated GPUs stop at 128 chunks.": "Jäme kauge maastik kohalikes maailmades. Integreeritud GPU-de arv on 128 tükki.",
"Reduced draws far terrain coarser: faster on this integrated GPU.": "Vähendatud joonistab maastikul jämedam: kiirem selle integreeritud GPU-ga.",
"Reduced draws far terrain coarser, for GPUs it slows down.": "Vähendatud tõmbab maastikku jämedamalt, GPU-de puhul aeglustab.",
"Field of view. Wider shows more terrain at once.": "Vaateväli. Laiem näitab korraga rohkem maastikku.",
"Caps frames per second; a cap saves power and heat.": "Caps kaadrit sekundis; kork säästab elektrit ja soojust.",
"Waits for the display's refresh: no tearing, a little latency.": "Ootab ekraani värskendust: ei rebene, väike latentsus.",
"Size of menus and the HUD.": "Menüüde suurus ja HUD.",
"Sets the options below together. Changing one makes it Custom.": "Määrab allolevad valikud koos. Ühe muutmine muudab selle kohandatud.",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "Laava, tuli ja päikesevalgus säravad valgest heledamalt. Vajab HDR-ekraani.",
"Your display's darkest black. 0 for OLED; LCDs glow a little.": "Teie ekraan on kõige tumedam must. 0 OLED-i jaoks; LCD-ekraanid helendavad veidi.",
"How bright highlights get. Auto: the peak your system reports.": "Kui eredad esiletõstud saavad. Automaatne: tipp, millest süsteem teatab.",
"How bright white paper and menus look. Auto: your desktop's white.": "Kuidas helevalge paber ja menüüd välja näevad. Automaatne: teie töölaud on valge.",
"How bright the HUD and menus are, against paper white.": "Kui eredad on HUD ja menüüd pabervalge taustal.",
"How far mobs, crops and machines keep working. CPU time.": "Kui kaugele mobid, põllukultuurid ja masinad töötavad. CPU aeg.",
"How far away mobs and items are drawn, as a share of normal.": "Kui kaugele mobid ja esemed tõmmatakse, kui osa normaalsest.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Jätab vahele suletud lõikude taha peidetud maastiku. Off maksab palju.",
"Hides entities behind opaque blocks.": "Peidab olemid läbipaistmatute plokkide taha.",
"No entity occlusion culling.": "Üksuse oklusiooni praakimine puudub.",
"CPU occlusion test runs this frame.": "CPU oklusiooni test käivitab selle kaadri.",
"Occlusion checks run on a worker thread.": "Oklusioonikontrollid käitatakse töötaja lõimel.",
"Meshed sections uploaded per frame. Lower is smoother while loading.": "Ühe kaadri kohta on üles laaditud võrgulõigud. Alumine on laadimise ajal sujuvam.",
"Soft shadows in corners. Costs chunk building time only.": "Nurkades pehmed varjud. Maksab ainult ehitusaja osa.",
"Smaller copies of block textures for distant terrain: less shimmer.": "Plokkide tekstuuride väiksemad koopiad kauge maastiku jaoks: vähem sära.",
"Blends grass and water colours across biomes when building chunks.": "Segab tükkide ehitamisel bioomide vahel muru ja veevärve.",
"Cloud layer: off, flat (fast) or 3D (fancy).": "Pilvekiht: väljas, tasane (kiire) või 3D (väljamõeldud).",
"How far clouds reach, in chunks.": "Kui kaugele pilved ulatuvad, tükkidena.",
"Draws the sky, sun, moon and stars.": "Joonistab taevast, päikest, kuud ja tähti.",
"Distance fog. Off shows the edge of the loaded world.": "Kaugus udu. Väljas näitab laaditud maailma serva.",
"Draws rain and snow.": "Tõmbab vihma ja lund.",
"How far rain and snow fall around you.": "Kui kaugele sajab vihma ja lund sinu ümber.",
"How many particles effects spawn.": "Kui palju osakesi mõju kudema.",
"Round shadows under mobs and items.": "Ümmargused varjud mobide ja esemete all.",
"Leaves with see-through gaps. Off draws them solid.": "Lehed läbipaistvate vahedega. Off tõmbab need tugevaks.",
"Sorts water and glass more exactly behind each other.": "Sorteerib vett ja klaasi täpsemalt üksteise taha.",
"Which chunk rebuilds wait for the frame: none, nearby or yours.": "Millised ümberehitused ootavad kaadrit: puudub, läheduses või teie.",
"Animates water, lava, fire and other moving textures.": "Animeerib vett, laavat, tuld ja muid liikuvaid tekstuure.",
"World pixels per screen pixel. Below 100% is faster on the GPU.": "Maailma piksleid ekraani piksli kohta. Alla 100% on GPU puhul kiirem.",
"Smooths jagged edges. FXAA: softer. TAA: smoothest, moving things may trail.": "Silustab sakilised servad. FXAA: pehmem. TAA: kõige sujuvamad, liikuvad asjad võivad järele jääda.",
"Renders the world smaller and rebuilds it with TAA. Faster, softer.": "Muudab maailma väiksemaks ja ehitab selle TAA abil uuesti üles. Kiirem, pehmem.",
"HDR output on an HDR display: lava, fire and sunlight shine brighter.": "HDR-väljund HDR-ekraanil: laava, tuli ja päikesevalgus säravad eredamalt.",
"How frames reach the screen while VSync is off.": "Kuidas raamid ekraanile jõuavad, kui VSync on välja lülitatud.",
"Efficient sleeps until the next frame; Precise spins, like vanilla.": "Tõhusad magamised kuni järgmise kaadrini; Täpsed keerutused, nagu vanilje.",
"Fills the screen (borderless).": "Täidab ekraani (ääristeta).",
"Brightens dark places.": "Teeb tumedad kohad heledamaks.",
"Lowers the frame rate when minimised, or idle too.": "Vähendab kaadrisagedust, kui see on minimeeritud või ka tühikäigul.",
"Sways the view while walking.": "Kõndib kõndides vaadet.",
"Fades newly loaded chunks in.": "Peidab äsja laaditud tükid sisse.",
"Darkens the screen's edges.": "Tumendab ekraani servi.",
"Shows when the world saves.": "Näitab, millal maailm päästab.",
"Shows attack cooldown.": "Näitab rünnaku jahtumist.",
"60 FPS cap (30 in menus or unfocused), half the workers: less power.": "60 kaadrit sekundis (menüüdes või fokuseerimata 30 kaadrit sekundis), pooled töötajad: vähem võimsust.",
"Threads that generate, light and mesh chunks. Next start.": "Niidid, mis tekitavad, valgustavad ja võrgustavad tükke. Järgmine algus.",
"Vulkan, then DirectX 12 (Windows) or OpenGL (Linux) where it fails. Next start.": "Vulkan, seejärel DirectX 12 (Windows) või OpenGL (Linux), kui see ebaõnnestub. Järgmine algus.",
"Water and glass in one buffer: fewer draw calls, a bit more VRAM. Next start.": "Vesi ja klaas ühes puhvris: vähem joonistuskõnesid, natuke rohkem VRAM-i. Järgmine algus.",
"Mesh data sent to the GPU per frame. Lower is smoother while loading.": "GPU-le saadetud võrguandmed kaadri kohta. Alumine on laadimise ajal sujuvam.",
"Frame rate and frame times over the game, with a graph.": "Kaadrisagedus ja kaadriajad mängu jooksul koos graafikuga.",
"horizon.multiplayer.finding_friend": "Oma sõbra maailma leidmine...",
"horizon.multiplayer.not_signed_in": "See server on võrgurežiimis. Sellega liitumiseks logige Horizoni käivitajas sisse oma Microsofti kontoga.",
"horizon.multiplayer.invalid_address": "Vale serveri aadress: %s",
"horizon.multiplayer.connected_friend_direct": "Ühendatakse otse teie sõbraga...",
"horizon.multiplayer.connected_friend_relay": "Sõbraga relee kaudu ühendatud...",
"General": "Kindral",
"Advanced": "Täpsemalt",
"Undo": "Võta tagasi",
"Search options": "Otsinguvalikud",
"Shortcut": "Otsetee",
"Distance": "Kaugus",
"Display": "Ekraan",
"Interface": "Liides",
"Sky and weather": "Taevas ja ilm",
"Lighting and entities": "Valgustus ja üksused",
"Textures": "Tekstuurid",
"Image": "Pilt",
"Chunk loading": "Tükkide laadimine",
"Culling": "Tapmine",
"Power": "Võimsus",
"Frames": "Raamid",
"Overlay": "Ülekate",
"Output": "Väljund",
"Brightness": "Heledus",
"How far you see, the window and the HUD.": "Kui kaugele näete, aken ja HUD.",
"The sky and weather, lighting, textures and the image.": "Taevas ja ilm, valgustus, tekstuurid ja pilt.",
"Chunk loading, culling and power use.": "Tükkide laadimine, praakimine ja energiakasutus.",
"How frames reach the screen, and the performance overlay.": "Kuidas kaadrid ekraanile jõuavad ja jõudluse ülekate.",
"Brighter-than-white output on an HDR display.": "Valgest heledam väljund HDR-ekraanil.",
"Results for \"%s\"": "Tulemused päringule \"%s\"",
"No settings match.": "Ükski seade ei sobi.",
"horizon.title.not_affiliated": "Ei ole seotud Mojangi ega Microsoftiga",
"horizon.extras.header.lighting": "Valgustus",
"horizon.extras.header.third_person": "Kolmas isik",
"horizon.extras.header.particles": "Osakesed",
"horizon.extras.header.inventory": "Inventuur",
"horizon.extras.header.chat": "Vestlus",
"horizon.extras.header.multiplayer": "Mitmikmäng",
"horizon.extras.header.sound": "Heli",
"horizon.extras.header.menus": "Menüüd",
"horizon.extras.dynamic_lights": "Dünaamilised tuled",
"horizon.extras.own_name_tag": "Oma nimesilt",
"horizon.extras.hide_block_particles": "Peida ploki purunevad osakesed",
"horizon.extras.auto_reconnect": "Automaatne taasühendamine",
"horizon.extras.ui_sound_reserve": "Hoidke kasutajaliidese helide jaoks kanalid alles",
"horizon.extras.music_page": "Muusika leht",
"horizon.extras.mouse_tweaks": "Mouse Tweaksi juhtnupud",
"horizon.extras.mouse_tweaks_wheel": "Kerimisratas liigutab üksusi",
"horizon.extras.sort": "Sorteerimisnupud",
"horizon.extras.sort_key": "Sorteerimisvõti",
"horizon.extras.no_ai": "Ei mingit AI-d",
"horizon.extras.agent_control": "Agendi kontroll",
"horizon.extras.discord_presence": "Discordi rikkalik kohalolek",
"horizon.extras.splitscreen": "Jagatud ekraan",
"horizon.extras.chat_animation": "Sujuv vestlus",
"horizon.extras.chat_indicator": "Sõnumi indikaator",
"horizon.extras.wheel_cycles_settings": "Kerimisratas muudab seadeid",
"horizon.extras.pack_browser": "Ressursipaketi brauser",
"horizon.extras.fullbright.tooltip": "Kuva plokke ja üksusi täisvalguses, isegi pimedates kohtades.",
"horizon.extras.dynamic_lights.tooltip": "Tõrvikud ja muud valgusallikad hoidsid, kukkusid või põlesid põlema, valgustavad neid ümbritsevaid klotse.",
"horizon.extras.own_name_tag.tooltip": "Näitab teie nime teie pea kohal kolmandas isikus, nagu teised mängijad seda näevad. Peidetud, kui server peidab teie meeskonna nimed või olete nähtamatu.",
"horizon.extras.hide_block_particles.tooltip": "Peidab prahiosakesed, mis purunenud plokist pursavad välja ja murenevad maha sellelt, mille kaevandasite. Teised osakesed jäävad.",
"horizon.extras.auto_reconnect.tooltip": "Liitub serveriga uuesti pärast ühenduse katkemist või taaskäivitamist, mitte kunagi pärast lööki või keelamist.",
"horizon.extras.ui_sound_reserve.tooltip": "Helid maailmas jätavad vabaks 16 helikanalit 247-st, nii et nende tulv (suur plahvatus) ei suuda menüüklõpse vaigistada.",
"horizon.extras.music_page.tooltip": "Muusika ja helid saavad lehe Muusika...: mängige kohe mis tahes sisseehitatud lugu või lülitage see välja, et mäng lõpetaks selle valimise. Midagi ei muutu enne, kui lülitate raja välja.",
"horizon.extras.mouse_tweaks.tooltip": "Parem-lohistamine asetab ühe pesa kohta, vasak-lohistamine kogub kinni hoitud üksuse, tõstuklahv + vasak-lohistamine liigutab iga pesa.",
"horizon.extras.mouse_tweaks_wheel.tooltip": "Alla saadab ühe pesa elemendi teisele poole, üles toob ühe tagasi.",
"horizon.extras.sort.tooltip": "Sorteerib laoseisud, kastid, tünnid, enderkastid ja shulkeri kastid loomingulises vahekaardi järjekorras, liites virnad.",
"horizon.extras.sort_key.tooltip": "Sorteerib kursori all oleva osa. Klõpsake, seejärel vajutage klahvi; Põgenemine seob selle lahti.",
"horizon.extras.no_ai.tooltip": "Peidab ja lülitab välja kõik AI funktsioonid: agendi juhtimine, horizon-ctl ja MCP-serverid. Ka kanderaketi lüliti.",
"horizon.extras.agent_control.tooltip": "Teie enda tehisintellekti agent saab seda mängu mängida horizon-ctl-i või MCP-kliendi kaudu ainult selles arvutis.",
"horizon.extras.discord_presence.tooltip": "Kuvab Discordi profiilil Play Horizon, kui Discord töötab: menüüd, üksik- või mitme mängijaga mäng, dimensioon ja mängimisaeg. Mitte kunagi maailma nime ega serveri aadressi.",
"Calibrate...": "Kalibreeri...",
"Calibrate Anyway...": "Kalibreerige ikkagi...",
"Use Automatic Setup": "Kasutage automaatset seadistust",
"Shortcut: %s": "Otsetee: %s",
"Next": "Edasi",
"HDR Calibration %s/%s: %s": "HDR-kalibreerimine %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Määrab kolm allolevat täisekraani, üks testpilt korraga.",
"A key that opens this screen from the game. Not bound at first.": "Klahv, mis avab mängust selle ekraani. Alguses ei köideta.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Teie ekraan ei öelnud, kui hele see muutub: seadistamiseks kalibreerige.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Teie kuva jaoks on juba seadistatud. Kalibreerige ainult siis, kui HDR tundub vale.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Tagab maksimaalse ereduse ja paberivalgeduse teie ekraani seadistustesse.",
"Set up for your display. Leave on Auto unless it looks wrong.": "Seadistage oma kuva jaoks. Jätke valikule Auto, kui see ei tundu vale.",
"Lower it until the logo just disappears into the black.": "Langetage seda, kuni logo kaob lihtsalt mustaks.",
"Raise it until the logo just disappears into the bright square.": "Tõstke seda seni, kuni logo kaob heledasse ruutu.",
"Make the card a white that's comfortable to look at for a long time, in the light you play in.": "Muuda kaart valgeks, mida on mugav pikka aega vaadata, selle valguses, milles mängid.",
"horizon.splitscreen.profiles.forget": "Unusta",
"horizon.splitscreen.profiles.create": "Loo",
"horizon.splitscreen.profiles.you": " - sina",
"horizon.splitscreen.profiles.forget_named": "Unusta %s",
"horizon.splitscreen.profiles.previous": "Eelmine",
"horizon.splitscreen.profiles.next": "Edasi",
"horizon.splitscreen.profiles.new_button": "Uus profiil...",
"horizon.splitscreen.profiles.forget_title": "Kas unustate profiili?",
"horizon.splitscreen.profiles.forget_leaves": "See lahkub selle eksemplari loendist.",
"horizon.splitscreen.profiles.forget_kept": "Maailma edenemist ja seadeid hoitakse alles.",
"horizon.splitscreen.profiles.new_title": "Uus profiil",
"horizon.splitscreen.profiles.name_hint": "Nimi (3 kuni 16 tähte, numbrit, _)",
"horizon.splitscreen.profiles.switch_title": "Vaheta profiili",
"horizon.splitscreen.profiles.offline_note": "Sellesse eksemplari kuuluvad võrguühenduseta profiilid",
"horizon.splitscreen.profiles.move": "Liikuge sinna, kus mängite",
"horizon.splitscreen.profiles.move_place": "Liikuge sinna, kus mängite: %s",
"horizon.friends.title": "Mängige sõpradega",
"horizon.friends.hint": "Sõbrad kleebivad koodi pealkirjaekraani nupu Sõbrad järele, otseühenduses või käivitusprogrammi Play lehel.",
"horizon.friends.copied": "Kopeeritud",
"horizon.friends.copy_code": "Kopeeri kood",
"horizon.friends.stop_sharing": "Lõpeta jagamine",
"horizon.friends.none_yet": "Siin pole veel sõpru.",
"horizon.friends.here_now": "Siin praegu: %s",
"horizon.friends.share_intro": "Jagage seda maailma kutsekoodi taga: see avaneb LAN-i ja Internetis olevad sõbrad liituvad koodiga. Ükski meie server ei ole sellega seotud.",
"horizon.friends.start_sharing": "Alusta jagamist",
"horizon.friends.troubleshooting": "Veaotsing...",
"horizon.friends.stopped": "Kutsekoodi jagamine lõpetati.",
"horizon.friends.short_code_changed": "Keegi proovis pidevalt valet lühikoodi, nii et see muutus.",
"horizon.friends.wrong_code": "Keegi proovis vale koodi.",
"horizon.friends.code_ready": "Teie kutsekood on valmis: Mängumenüü, Mängige sõpradega. Kopeerige see ja saatke see oma sõpradele.",
"horizon.friends.share_failed": "Ei saanud maailma jagada: %s",
"horizon.friends.finding_route": "Kuidas sõbrad teiega ühendust saavad…",
"horizon.osk.space": "Kosmos",
"horizon.osk.delete": "Kustuta",
"horizon.osk.type": "Tüüp",
"horizon.osk.symbols": "Sümbolid",
"horizon.osk.close": "Sule",
"horizon.extras.chat_animation.tooltip": "Uus vestlusliin libiseb vasakult sisse, kui see sumbub, selle asemel, et paista tervena.",
"horizon.extras.chat_indicator.tooltip": "Vestlusrea vasakul olev värviline riba.",
"horizon.extras.wheel_cycles_settings.tooltip": "Seadetes nupu kohal olev kerimisratas muudab selle väärtust, nagu vanilje puhul. Väljas, ratas kerib ainult loendit.",
"horizon.extras.pack_browser.tooltip": "Ressursipaketi valikul kuvatakse nupp Lisa, mis otsib Modrinthi ja installib paketi teie pakettide kausta. Väljas, mäng ei võta kunagi Modrinthiga ühendust.",
"horizon.extras.splitscreen.tooltip": "Maailmas saab uue mängijana liituda teine kontroller: vajutage A, kui see küsib, seejärel valige profiil ja kus mängida.",
"horizon.splitscreen.setup.monitor_unknown": "monitor",
"horizon.splitscreen.setup.window": "Aken %s",
"horizon.splitscreen.setup.new_window": "Uus aken",
"horizon.splitscreen.setup.fill_monitor": "Täida %s",
"horizon.splitscreen.setup.done": "Valmis",
"horizon.splitscreen.setup.on": "Sees",
"horizon.splitscreen.setup.off": "Väljas",
"horizon.splitscreen.setup.player": "Mängija %s",
"horizon.splitscreen.setup.revert": "Taasta",
"horizon.splitscreen.setup.apply": "Rakenda",
"horizon.splitscreen.setup.warning.empty_main": "Keegi ei mängi aknas 1: see peidab",
"horizon.splitscreen.setup.warning.empty_close": "Keegi ei mängi aknas %s: see suletakse rakenduses Rakenda",
"horizon.splitscreen.setup.warning.empty_new": "Keegi ei mängi aknas %s: see ei avane",
"horizon.splitscreen.setup.warning.on_main": "Aken %s täidab akna 1 monitori: selle mängijad mängivad aknas 1",
"horizon.splitscreen.setup.warning.unplugged": "Akna %s monitor pole ühendatud: selle mängijad mängivad aknas 1",
"horizon.splitscreen.setup.status.off": "Jagatud ekraan on välja lülitatud.",
"horizon.splitscreen.setup.status.enable_hint": "Lülitage see sisse ja seejärel vajutage nuppu mõnel teisel kontrolleril maailmas.",
"horizon.splitscreen.setup.status.join_hint": "Liitumiseks ja profiili valimiseks vajutage A.",
"horizon.splitscreen.setup.status.idle": "Tükeldatud ekraan jõudeolekus",
"horizon.splitscreen.setup.status.alone": "Teisi kohalikke mängijaid pole.",
"horizon.splitscreen.setup.status.solo_controls": "Üksikjuhtimisseadmed ja kontrolleri tagasiside ei muutu.",
"horizon.splitscreen.setup.window_empty": "Aken %s: tühi",
"horizon.splitscreen.setup.note.select": "Klõpsake pleieril või lohistage see aknale või monitorile.",
"horizon.splitscreen.setup.note.apply": "Sel viisil mängimiseks rakendage, tagasivõtmiseks ennista.",
"horizon.splitscreen.setup.note.place": "Kus peaks %s mängima?",
"horizon.splitscreen.setup.this_player": "see mängija",
"horizon.splitscreen.setup.switch_disabled": "Jaotatud ekraan lülitub välja, kui teised on lahkunud (nende pausimenüü).",
"horizon.splitscreen.setup.window_place": "Aken %s (%s/%s)",
"horizon.splitscreen.setup.names": "Nimed",
"horizon.splitscreen.setup.identify": "Tuvastage",
"horizon.splitscreen.setup.layout.two": "2 jagamist: %s",
"horizon.splitscreen.setup.layout.three": "3 jagamist: %s",
"horizon.splitscreen.setup.layout.two.0": "Automaatne",
"horizon.splitscreen.setup.layout.two.1": "Kõrvuti",
"horizon.splitscreen.setup.layout.two.2": "Ülemine ja alumine",
"horizon.splitscreen.setup.layout.three.0": "Pealt üks lai",
"horizon.splitscreen.setup.layout.three.1": "Üks pikk vasakul",
"horizon.splitscreen.setup.layout.three.2": "Kõrvuti",
"horizon.splitscreen.setup.help.player": "Nende teisaldamiseks klõpsake või lohistage %s.",
"horizon.splitscreen.setup.help.layout_auto": "%s (kõrvuti, kui aken on lai). Vajutage muutmiseks.",
"horizon.splitscreen.setup.help.layout": "%s. Vajutage muutmiseks.",
"horizon.splitscreen.setup.help.switch": "Selle arvuti jagatud ekraan, sisse või välja lülitatud.",
"horizon.splitscreen.setup.help.identify": "Iga mängija vaade näitab, millises aknas ta on.",
"horizon.splitscreen.setup.help.names": "Iga mängija nimi tema vaate nurgas.",
"horizon.splitscreen.setup.help.revert": "Võta tagasi muudatused, mida pole veel rakendatud.",
"horizon.splitscreen.setup.help.apply": "Mängige nii, nagu kaart näitab.",
"horizon.splitscreen.setup.help.unchanged": "Midagi pole veel muutunud.",
"horizon.splitscreen.setup.give_up_pad": "Loobu padjast",
"horizon.splitscreen.prompt.controller_connected": "Kontroller ühendatud",
"horizon.splitscreen.prompt.controller_disconnected": "Kontroller on lahti ühendatud",
"horizon.splitscreen.prompt.controller_disconnected.hint": "Ühendage see uuesti või vajutage mõnel teisel nupul",
"horizon.splitscreen.prompt.join.one": "%s soovib liituda",
"horizon.splitscreen.prompt.join.many": "%s kontrollerit soovivad liituda",
"horizon.splitscreen.prompt.join.yes": "Liitu",
"horizon.splitscreen.prompt.join.as_p1": "Mängi kui P1",
"horizon.splitscreen.prompt.join.no": "Ei",
"horizon.splitscreen.prompt.lost": "%s: kontroller on lahti ühendatud",
"horizon.splitscreen.prompt.lost.use_another": "Kasutage teist",
"horizon.splitscreen.prompt.lost.leave": "Lahku",
"horizon.splitscreen.setup.help.give_up_pad": "Teie kontroller lõpetab teie juhtimise ja talle pakutakse liitumist (A).",
"horizon.splitscreen.setup.note.held_at": "%s kuni %s: A viskab selle siia, B paneb selle tagasi.",
"horizon.splitscreen.setup.note.held": "%s korjatud: vasak/parem valib, kuhu, A kukub, B paneb tagasi.",
"Hides models behind opaque blocks. Async checks off-thread; entities tick.": "Peidab mudelid läbipaistmatute plokkide taha. Async kontrollib lõimevälist; olemid tiksuvad.",
"Block Entities": "Blokeeri üksused",
"Shulker Boxes": "Shulkeri karbid",
"Reuses settled boxes; lid movement stays animated.": "Taaskasutab settinud kaste; kaane liikumine jääb animeeritud.",
"Signs": "Märgid",
"Reuses sign text until text, light, or font changes.": "Kasutab märgiteksti uuesti, kuni tekst, valgustus või font muutub.",
"Decorated Pots": "Kaunistatud potid",
"Reuses still pots; break wobble and sherd changes redraw.": "Taaskasutab veel potte; murda võnkuma ja shed muutused uuesti joonistama.",
"Bells": "Kellad",
"Reuses still bells; ringing stays animated.": "Taaskasutab liikumatuid kellasid; helin jääb animeeritud.",
"Chests": "Kummutid",
"Reuses closed chests; moving lids stay animated.": "Taaskasutab suletud kaste; liikuvad kaaned jäävad animeeritud.",
"Banners": "Bännerid",
"Reuses poles; flags and pattern layers still update.": "Taaskasutab poste; lipud ja mustrikihid uuendatakse endiselt.",
"Copper Golem Statues": "Vasest Golemi kujud",
"Reuses statue geometry until its block state changes.": "Taaskasutab kuju geomeetriat, kuni selle ploki olek muutub.",
"Leaf Culling": "Lehtede tapmine",
"Every leaf face. Vanilla's fancy leaves.": "Iga lehe nägu. Vanilli uhked lehed.",
"Keeps fine plants in high-res packs from fading far away.": "Hoiab kõrge eraldusvõimega pakendites peeneid taimi kaugele tuhmumise eest.",
"Screenshot Quality": "Ekraanipildi kvaliteet",
"F2 screenshots at this many times the game's resolution. Higher takes longer.": "F2 ekraanipildid selle mitmekordse mängu eraldusvõimega. Kõrgem võtab kauem aega.",
"Frame Generation": "Raami genereerimine",
"Shows a made-up frame between each two real ones: smoother, a little more lag.": "Näitab väljamõeldud kaadrit iga kahe päriskaadri vahel: sujuvam, veidi rohkem viivitust.",
"Reuses the pack shadow map when sun, view and world match. Off by default.": "Taaskasutab paki varjukaarti, kui päike, vaade ja maailm ühtivad. Vaikimisi väljas.",
"Find BlueMap": "Otsige üles BlueMap",
"On servers, finds their BlueMap web map for far terrain. Next join.": "Serverites leiab nende BlueMapi veebikaardi kauge maastiku jaoks. Järgmine liitumine.",
"BlueMap Radius": "BlueMap raadius",
"How far around you BlueMap tiles are downloaded. Next join.": "Kui kaugele teie ümber BlueMapi paanid alla laaditakse. Järgmine liitumine.",
"Synchronous": "Sünkroonne",
"Asynchronous": "Asünkroonne",
"Solid": "Tahke",
"Solid Aggressive": "Tugev agressiivne",
"Hollow": "Õõnes",
"Leaves fully inside a canopy draw solid: fewer see-through layers.": "Lehed täielikult võra sees tõmbuvad tugevalt: vähem läbipaistvaid kihte.",
"Leaves with one open side draw solid too: faster, chunkier edges.": "Ühe avatud küljega lehed on samuti tugevad: kiiremad ja turskemad servad.",
"Drops faces between leaves: faster, but gaps look through.": "Langeb näod lehtede vahele: kiiremini, kuid vahed paistavad läbi.",
"Hotbar: Previous Slot": "Hotbar: eelmine pesa",
"Hotbar: Next Slot": "Hotbar: järgmine pesa",
"horizon.agent.connected.one": "Ühendatud: 1 agendiühendus",
"horizon.agent.connected.many": "Ühendatud: %s agendi ühendused",
"horizon.agent.connected.one_ago": "Ühendatud: 1 agendiühendus, viimane käsk %s s tagasi",
"horizon.agent.connected.many_ago": "Ühendatud: %s agendiühendused, viimane käsk %s s tagasi",
"horizon.agent.waiting": "Ootan agenti.",
"horizon.agent.off": "Väljas: agendid ei pääse selle mängu juurde.",
"horizon.agent.session_opened": "Selle seansi jaoks avas mängu käivitanud programm.",
"horizon.agent.socket": "Pistikupesa: %s (kopeerige selle täieliku tee üksikasjad)",
"horizon.agent.local_only": "Ainult kohalik: selles arvutis olevad agendid, mitte kunagi võrk.",
"Auto (%s nits)": "Automaatne (%s nits)",
"Hides models behind opaque blocks. Entities update. Off with custom packs.": "Peidab mudelid läbipaistmatute plokkide taha. Olemite värskendamine. Väljas kohandatud pakkidega.",
"horizon.extras.repro_keys": "Salvestamise otseteed",
"horizon.extras.repro_keys.tooltip": "F3+R alustab või peatab reprosalvestuse ja F3+E kirjutab selle paketi. Vaikimisi väljas: salvestis kopeerib maailma.",
"horizon.agent.record_session": "Alustage salvestamise seanssi",
"horizon.agent.record_stop": "Seansi salvestamise lõpetamine",
"horizon.agent.export_repro": "Ekspordi Repro komplekt",
"horizon.repro.started": "Salvestamine algas; F3+R peatab selle ja kirjutab kimbu.",
"horizon.repro.stopped": "Salvestamine peatatud: %s",
"horizon.repro.bundle": "Repro pakett: %s",
"horizon.repro.no_recording": "Eksportimiseks pole veel salvestist.",
"horizon.repro.replay_started": "Taasesitus algas: %s",
"horizon.pack.addInfo": "Lisage Modrinthist ressursipakett",
"horizon.pack.addWorking": "Modrinthilt küsides...",
"horizon.pack.addNone": "Ressursipakette ei leitud",
"horizon.pack.addTitle": "Lisage ressursipakett",
"horizon.pack.install": "Installige",
"horizon.pack.installing": "Paigaldamine",
"horizon.pack.installed": "Paigaldatud",
"horizon.pack.search": "Otsi",
"horizon.pack.searchHint": "Otsi Modrinthist...",
"horizon.pack.filters": "Filtrid",
"horizon.pack.filter.realistic": "Realistlik",
"horizon.pack.filter.simplistic": "Lihtsustatud",
"horizon.pack.filter.vanillaLike": "Vaniljelaadne",
"horizon.pack.addFailed": "Pakki ei saanud lisada: %s",
"horizon.extras.header.cosmetics": "Kosmeetika",
"horizon.extras.cosmetics_mine": "Minu kosmeetika",
"horizon.extras.cosmetics_mine.tooltip": "Varjatud: iga Horizoni mängija näeb teid ilma teie kosmeetikata, kaasa arvatud. Sama lüliti, mis käivitaja riidekapil.",
"horizon.extras.cosmetics_others": "Teiste mängijate kosmeetika",
"horizon.extras.cosmetics_others.tooltip": "Mida teised Horizoni mängijad kannavad: kõike, ainult nende nimesiltide stiile või mitte midagi. Varjatud, mäng ei küsi kunagi, mida nad kannavad.",
"horizon.extras.cosmetics_auras": "Teiste aurad ja jäljed",
"horizon.extras.cosmetics_auras.tooltip": "Aurad ja jäljed, mida teised mängijad kannavad. Vaikimisi väljas.",
"horizon.extras.cosmetics.shown": "Näidatud",
"horizon.extras.cosmetics.hidden": "Peidetud",
"horizon.extras.cosmetics.name_tags": "Ainult nimesildid"
}
@@ -0,0 +1,476 @@
{
"horizon.options.controller": "Kontrolatzailea",
"horizon.options.vibration": "Dardara",
"horizon.options.adaptive_triggers": "Abiarazle moldagarriak",
"horizon.options.lightbar": "Argi-barra",
"horizon.options.look_sensitivity": "Begiradaren sentikortasuna",
"horizon.options.invert_x": "Begirada X alderantzikatua",
"horizon.options.invert_y": "Begirada Y alderantzikatua",
"horizon.options.sneak_button": "Makurtzeko botoia",
"horizon.options.move_deadzone": "Mugimenduaren zona hila",
"horizon.options.look_deadzone": "Begiradaren zona hila",
"horizon.options.button_glyphs": "Botoien glifoak",
"horizon.options.on_screen_keyboard": "Pantailako teklatua",
"horizon.options.chat_delay": "Txat-atzerapena",
"horizon.options.lightbar.health": "Osasuna",
"horizon.options.lightbar.dimension": "Dimentsioa",
"horizon.options.glyphs.auto": "Autoa",
"horizon.options.keyboard.auto": "Autoa",
"horizon.options.unavailable": "Eginbide hau oraindik ez dago erabilgarri Horizon-n.",
"horizon.options.search": "Bilaketa-ezarpenak...",
"horizon.pages.mods": "Moduak",
"horizon.pages.shader_packs": "Shader paketeak",
"horizon.pages.shader_options": "Itzalaren aukerak",
"horizon.pages.controller": "Kontrolagailuaren ezarpenak",
"horizon.pages.controller_binds": "Kontrolatzaileak lotzen ditu",
"horizon.pages.extras": "Horizon Gehigarriak",
"horizon.pages.settings": "Horizon Ezarpenak",
"horizon.pages.agent": "AI agentea",
"horizon.split.leave": "Utzi",
"horizon.pages.split_settings": "Pantaila zatituko ezarpenak",
"horizon.pages.split_profiles": "Nor ari da jolasten?",
"horizon.pages.split_setup": "Pantaila zatituaren konfigurazioa",
"horizon.pages.music": "Musika",
"horizon.pages.music.menu": "Menua",
"horizon.pages.music.creative": "Sortzailea",
"horizon.pages.music.underwater": "Urpean",
"horizon.pages.music.end": "Amaiera",
"horizon.pages.music.boss": "Nagusia",
"horizon.pages.music.credits": "Kredituak",
"horizon.pages.music.overworld_biomes": "Overworld biomak",
"horizon.pages.music.nether_biomes": "Beheko biomak",
"horizon.pages.music.end_biomes": "Amaiera biomak",
"horizon.pages.music.other": "Beste musika batzuk",
"horizon.pages.music.empty": "Ez dago musika pistarik: jokoaren soinuak ez dira kargatzen.",
"horizon.pages.music.tooltip": "Inkorporatutako pistak, egoeraren arabera: jokatu bat orain edo desaktibatu beste bat, jokoak hautatzeari uzteko.",
"horizon.pages.settings.tooltip": "Horizonren ezarpenak: grafikoak, kontrolagailuak, gehigarriak eta modak.",
"horizon.pages.graphics.tooltip": "Grafikoak, besterik gabe: ezarpen bakoitzak zer balio duen esaten du.",
"horizon.pages.graphics_advanced.tooltip": "Ezarpen grafiko bakoitza, Horizon barne, bakoitzak balio duenarekin.",
"horizon.pages.controller.tooltip": "Kontrolagailuak: haien botoiak, makilak, bibrazioa eta menuen nabigazioa.",
"horizon.pages.extras.tooltip": "Aukerako gehigarriak, guztiak lehenespenez desaktibatuta: jokoak bainila izaten jarraitzen du bat aktibatu arte.",
"horizon.pages.agent.tooltip": "Utzi zure AI agentea jolasten: horizon-ctl eta MCP bezeroak ordenagailu honetan.",
"horizon.pages.split_profiles.tooltip": "Nork jokatzen du: jokoaren karpeta honetan gordetako profila.",
"horizon.pages.split_setup.tooltip": "Leihoak eta monitoreak, eta nork jokatzen duen non.",
"horizon.pages.shader_packs.tooltip": "Shader-paketeak shaderpacks karpetatik: Horizon bereak eta shaders/ formatuan daudenak. Bat aldi berean korrika.",
"horizon.pages.mods.tooltip": "Horizon-rako egindako modak, horizon-mods karpetatik. Bakoitza sandbox batean exekutatzen da eta itzalita geratzen da pizten duzun arte.",
"horizon.pages.open.graphics": "Grafikoak...",
"horizon.pages.open.graphics_advanced": "Grafiko aurreratuak...",
"horizon.pages.open.shader_packs": "Shader paketeak...",
"horizon.pages.open.controller": "Kontrolagailuaren ezarpenak...",
"horizon.pages.open.split_setup": "Pantaila zatitua...",
"horizon.pages.open.extras": "Gehigarriak...",
"horizon.pages.open.mods": "Moduak...",
"horizon.pages.open.agent": "AI agentea...",
"horizon.pages.open.horizon_graphics": "Horizon Grafikoak...",
"horizon.pages.open.controller_binds": "Kontrolagailua lotzen da...",
"horizon.pages.open.horizon_settings": "Horizon Ezarpenak...",
"horizon.pages.open.music": "Musika...",
"horizon.mods.empty": "Modrik ez: jarri .hmod fitxategiak horizon-mods karpetan",
"horizon.mods.stopped": " (Gelditu)",
"horizon.mods.not_built": " (Ez eraikia)",
"horizon.mods.dev": " (Garapena)",
"horizon.mods.settings": "Ezarpenak",
"horizon.mods.in_world": " (utzi mundua aldatzen)",
"horizon.mods.open_folder": "Ireki Mod karpeta",
"horizon.shader_packs.empty": "Ez dago itzal-paketerik: jarri itzazu itzal-paketeen karpetan",
"horizon.shader_options.open": "Itzalaren aukerak...",
"horizon.shader_options.profile": "Profila",
"horizon.shader_options.loading": "Shader paketearen aukerak irakurtzen...",
"horizon.agent.copy_details": "Kopiatu konexioaren xehetasunak",
"horizon.multiplayer.join": "Sartu",
"horizon.multiplayer.cancel_reconnecting": "Utzi berriro konektatzea",
"horizon.multiplayer.join_friend": "Sartu Lagun batekin",
"horizon.multiplayer.invite_code": "Gonbidatu kodea",
"horizon.multiplayer.relay": "Erreleboa (aukerakoa)",
"Preset": "Aurrez ezarritakoa",
"LOD Distance": "LOD Distantzia",
"Far Detail": "Urruneko xehetasuna",
"Cave Culling": "Haitzuloen Mozketa",
"Entity Culling": "Entitatea Kulling",
"Chunk Updates": "Chunk eguneraketak",
"Upload Budget": "Kargatu Aurrekontua",
"Sky": "Zerua",
"Fog": "Lainoa",
"Weather": "Eguraldia",
"Animated Textures": "Ehundura animatuak",
"Present Mode": "Oraingo Modua",
"Frame Pacing": "Markoaren erritmoa",
"Worker Threads": "Langileen hariak",
"Resolution Scale": "Ebazpen Eskala",
"Anti-aliasing": "Aliasaren aurkakoa",
"Upscaling": "Handitzea",
"Paper White": "Paper Zuria",
"Peak Brightness": "Distira gailurra",
"Black Level": "Beltza Maila",
"UI Brightness": "Interfazearen distira",
"Battery Mode": "Bateria modua",
"Graphics API": "Grafikoen APIa",
"Translucent Batching": "Lote zeharrargitsua",
"Full Detail Distance": "Xehetasun osoa Distantzia",
"Off": "Desaktibatuta",
"Unlimited": "Mugagabea",
"Auto": "Autoa",
"Custom": "Pertsonalizatua",
"Performance": "Errendimendua",
"Balanced": "Orekatua",
"Quality": "Kalitatea",
"Native": "Jatorrizkoa",
"Full": "Osoa",
"Reduced": "Murriztua",
"Efficient": "Eraginkorra",
"Precise": "Zehatza",
"Auto (VSync)": "Automatikoa (VSync)",
"Mailbox": "Postontzia",
"Immediate": "Berehala",
"FPS and Graph": "FPS eta grafikoa",
"No cost": "Ez dago kosturik",
"Low cost": "Kostu baxua",
"Medium cost": "Kostu ertaina",
"High cost": "Kostu handia",
"Saves time": "Denbora aurrezten du",
"Graphics": "Grafikoak",
"Done": "Eginda",
"This feature is not yet available in Horizon.": "Eginbide hau oraindik ez dago erabilgarri Horizon-n.",
"Your display or system doesn't offer HDR output.": "Zure pantailak edo sistemak ez du HDR irteerarik eskaintzen.",
"Turn HDR on to use it.": "Aktibatu HDR hura erabiltzeko.",
"Your GPU or graphics API doesn't support it.": "Zure GPU edo grafikoen APIak ez du onartzen.",
"%s chunks": "%s puskak",
"%s per frame": "%s fotograma bakoitzeko",
"%s MiB/frame": "%s MiB/markoa",
"%s KiB/frame": "%s KiB/markoa",
"Full-detail terrain range. LOD Distance can extend local worlds farther.": "Xehetasun osoko lur eremua. LOD Distantziak tokiko munduak urrunago heda ditzake.",
"Coarse distant terrain in local worlds. Integrated GPUs stop at 128 chunks.": "Urruneko lur lodia tokiko munduetan. GPU integratuak 128 zatitan gelditzen dira.",
"Reduced draws far terrain coarser: faster on this integrated GPU.": "Marrazki murrizketak lur urrunago lodiagoak: azkarrago GPU integratu honetan.",
"Reduced draws far terrain coarser, for GPUs it slows down.": "Marrazki murriztuak lur urrunago lodiagoak dira, GPUentzat moteldu egiten da.",
"Field of view. Wider shows more terrain at once.": "Ikus-eremua. Zabalerak lur gehiago erakusten du aldi berean.",
"Caps frames per second; a cap saves power and heat.": "Maiuskulak segundoko fotogramak; txano batek energia eta beroa aurrezten ditu.",
"Waits for the display's refresh: no tearing, a little latency.": "Pantaila freskatzeko zain dago: urratu gabe, latentzia pixka bat.",
"Size of menus and the HUD.": "Menuen tamaina eta HUD-a.",
"Sets the options below together. Changing one makes it Custom.": "Beheko aukerak batera ezartzen ditu. Bat aldatzeak pertsonalizatu egiten du.",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "Labak, suak eta eguzki-argiak zuriak baino distira handiagoa dute. HDR pantaila bat behar du.",
"Your display's darkest black. 0 for OLED; LCDs glow a little.": "Zure pantailako beltz ilunena da. 0 OLEDrako; LCDek apur bat distira egiten dute.",
"How bright highlights get. Auto: the peak your system reports.": "Nola distiratsuak diren argiak. Auto: zure sistemak ematen duen gailurra.",
"How bright white paper and menus look. Auto: your desktop's white.": "Zein itxura duten paper zuriak eta menuak. Auto: zure mahaigaina zuria da.",
"How bright the HUD and menus are, against paper white.": "Zein distiratsuak diren HUD eta menuak, paper zuriaren aurka.",
"How far mobs, crops and machines keep working. CPU time.": "Noraino jarraitzen duten mafiak, laboreak eta makinak lanean. CPU denbora.",
"How far away mobs and items are drawn, as a share of normal.": "Noraino ateratzen diren mobs eta elementuak, normalaren zati gisa.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Sekzio itxien atzean ezkutatuta dagoen lurra saltatzen du. Off asko kostatzen da.",
"Hides entities behind opaque blocks.": "Bloke opakuen atzean dauden entitateak ezkutatzen ditu.",
"No entity occlusion culling.": "Ez dago entitateen oklusioa kulling.",
"CPU occlusion test runs this frame.": "PUZaren oklusioaren probak marko hau exekutatzen du.",
"Occlusion checks run on a worker thread.": "Oklusio-egiaztapenak langile-hari batean exekutatzen dira.",
"Meshed sections uploaded per frame. Lower is smoother while loading.": "Sareko atalak fotograma bakoitzeko kargatu dira. Behea leunagoa da kargatzean.",
"Soft shadows in corners. Costs chunk building time only.": "Itzal leunak bazterretan. Eraikitzeko denbora zati bat bakarrik kostatzen du.",
"Smaller copies of block textures for distant terrain: less shimmer.": "Bloke-ehunduraren kopia txikiagoak lur urrunetarako: distira gutxiago.",
"Blends grass and water colours across biomes when building chunks.": "Belarra eta ur koloreak nahasten ditu biometan zatiak eraikitzean.",
"Cloud layer: off, flat (fast) or 3D (fancy).": "Hodei geruza: desaktibatuta, laua (bizkorra) edo 3D (fantastikoa).",
"How far clouds reach, in chunks.": "Noraino iristen diren hodeiak, zatika.",
"Draws the sky, sun, moon and stars.": "Zerua, eguzkia, ilargia eta izarrak marrazten ditu.",
"Distance fog. Off shows the edge of the loaded world.": "Distantzia lainoa. Off mundu kargatuaren ertza erakusten du.",
"Draws rain and snow.": "Euria eta elurra marrazten ditu.",
"How far rain and snow fall around you.": "Noraino erortzen diren euria eta elurra zure inguruan.",
"How many particles effects spawn.": "Zenbat partikulen eraginak sortzen diren.",
"Round shadows under mobs and items.": "Itzal biribilak mob eta elementuen azpian.",
"Leaves with see-through gaps. Off draws them solid.": "Hutsune ikusgarriak dituzten hostoak. Off-ek sendo marrazten ditu.",
"Sorts water and glass more exactly behind each other.": "Ura eta beira bata bestearen atzetik zehatzago sailkatzen ditu.",
"Which chunk rebuilds wait for the frame: none, nearby or yours.": "Zein zati berreraikitzen den markoaren zain: bat ere ez, gertukoa edo zurea.",
"Animates water, lava, fire and other moving textures.": "Ura, laba, sua eta beste ehundura mugikor batzuk animatzen ditu.",
"World pixels per screen pixel. Below 100% is faster on the GPU.": "Munduko pixelak pantailako pixel bakoitzeko. % 100etik behera azkarragoa da GPUan.",
"Smooths jagged edges. FXAA: softer. TAA: smoothest, moving things may trail.": "Ertz bitxiak leuntzen ditu. FXAA: bigunagoa. TAA: leunena, mugitzen diren gauzak atzetik joan daitezke.",
"Renders the world smaller and rebuilds it with TAA. Faster, softer.": "Mundua txikiagotzen du eta TAArekin berreraikitzen du. Azkarrago, leunagoa.",
"HDR output on an HDR display: lava, fire and sunlight shine brighter.": "HDR irteera HDR pantaila batean: labak, sua eta eguzki-argiak distiratsuagoak dira.",
"How frames reach the screen while VSync is off.": "Nola iristen diren fotogramak pantailara VSync itzalita dagoen bitartean.",
"Efficient sleeps until the next frame; Precise spins, like vanilla.": "Lo eraginkorra hurrengo fotograma arte; Bira zehatzak, bainila bezala.",
"Fills the screen (borderless).": "Pantaila betetzen du (ertzak gabe).",
"Brightens dark places.": "Leku ilunak argitzen ditu.",
"Lowers the frame rate when minimised, or idle too.": "Fotograma-abiadura murrizten du minimizatzen denean, edo inaktibo ere.",
"Sways the view while walking.": "Ikuspegia aldarazi egiten du oinez ibiltzean.",
"Fades newly loaded chunks in.": "Kargatutako zati berriak lausotzen ditu.",
"Darkens the screen's edges.": "Pantailaren ertzak iluntzen ditu.",
"Shows when the world saves.": "Munduak salbatzen duenean erakusten du.",
"Shows attack cooldown.": "Erasoaren hoztea erakusten du.",
"60 FPS cap (30 in menus or unfocused), half the workers: less power.": "60 FPS muga (30 menuetan edo fokatu gabe), langileen erdia: potentzia gutxiago.",
"Threads that generate, light and mesh chunks. Next start.": "Sortzen dituzten hariak, zati argiak eta sareak. Hurrengo hasiera.",
"Vulkan, then DirectX 12 (Windows) or OpenGL (Linux) where it fails. Next start.": "Vulkan, gero DirectX 12 (Windows) edo OpenGL (Linux) non huts egiten duen. Hurrengo hasiera.",
"Water and glass in one buffer: fewer draw calls, a bit more VRAM. Next start.": "Ura eta beira buffer batean: zozketa-dei gutxiago, VRAM pixka bat gehiago. Hurrengo hasiera.",
"Mesh data sent to the GPU per frame. Lower is smoother while loading.": "GPUra bidalitako sareko datuak fotograma bakoitzeko. Behea leunagoa da kargatzean.",
"Frame rate and frame times over the game, with a graph.": "Fotograma-tasa eta fotograma-denborak jokoan zehar, grafiko batekin.",
"horizon.multiplayer.finding_friend": "Zure lagunaren mundua aurkitzen...",
"horizon.multiplayer.not_signed_in": "Zerbitzari hau lineako moduan dago. Hasi saioa zure Microsoft kontuarekin Horizon abiarazlean bertan sartzeko.",
"horizon.multiplayer.invalid_address": "Zerbitzariaren helbide baliogabea: %s",
"horizon.multiplayer.connected_friend_direct": "Zure lagunarekin zuzenean konektatuta...",
"horizon.multiplayer.connected_friend_relay": "Zure lagunarekin erreleboaren bidez konektatuta...",
"General": "Orokorra",
"Advanced": "Aurreratua",
"Undo": "Desegin",
"Search options": "Bilaketa aukerak",
"Shortcut": "Lasterbidea",
"Distance": "Distantzia",
"Display": "Bistaratzea",
"Interface": "Interfazea",
"Sky and weather": "Zerua eta eguraldia",
"Lighting and entities": "Argiztapena eta entitateak",
"Textures": "Ehundurak",
"Image": "Irudia",
"Chunk loading": "Zati zatiak kargatzea",
"Culling": "Mozketa",
"Power": "Boterea",
"Frames": "Markoak",
"Overlay": "Gainjartzea",
"Output": "Irteera",
"Brightness": "Distira",
"How far you see, the window and the HUD.": "Noraino ikusten duzun, leihoa eta HUD-a.",
"The sky and weather, lighting, textures and the image.": "Zerua eta eguraldia, argia, testurak eta irudia.",
"Chunk loading, culling and power use.": "Zama zatiak, mozketa eta energia erabiltzea.",
"How frames reach the screen, and the performance overlay.": "Fotogramak pantailara nola iristen diren eta errendimenduaren gainjartzea.",
"Brighter-than-white output on an HDR display.": "Irteera zuria baino distiratsuagoa HDR pantaila batean.",
"Results for \"%s\"": "\"%s\" bilaketaren emaitzak",
"No settings match.": "Ez dago ezarpenik bat.",
"horizon.title.not_affiliated": "Ez dago Mojang edo Microsoft-ekin afiliatuta",
"horizon.extras.header.lighting": "Argiztapena",
"horizon.extras.header.third_person": "Hirugarren pertsona",
"horizon.extras.header.particles": "Partikulak",
"horizon.extras.header.inventory": "Inbentarioa",
"horizon.extras.header.chat": "Txateatu",
"horizon.extras.header.multiplayer": "Jokalari anitzekoa",
"horizon.extras.header.sound": "Soinua",
"horizon.extras.header.menus": "Menuak",
"horizon.extras.fullbright": "Argi osoa",
"horizon.extras.dynamic_lights": "Argi dinamikoak",
"horizon.extras.own_name_tag": "Izen-etiketa propioa",
"horizon.extras.hide_block_particles": "Ezkutatu bloke-hausturako partikulak",
"horizon.extras.auto_reconnect": "Berriro konektatu automatikoki",
"horizon.extras.ui_sound_reserve": "Mantendu kanalak UI soinuetarako",
"horizon.extras.music_page": "Musika orria",
"horizon.extras.mouse_tweaks": "Mouse Tweaks kontrolak",
"horizon.extras.mouse_tweaks_wheel": "Korritzeko gurpilak elementuak mugitzen ditu",
"horizon.extras.sort": "Ordenatzeko botoiak",
"horizon.extras.sort_key": "Ordenatzeko tekla",
"horizon.extras.no_ai": "AIrik ez",
"horizon.extras.agent_control": "Agenteen Kontrola",
"horizon.extras.discord_presence": "Discord Presentzia aberatsa",
"horizon.extras.splitscreen": "Pantaila zatitua",
"horizon.extras.chat_animation": "Txat leuna",
"horizon.extras.chat_indicator": "Mezuen adierazlea",
"horizon.extras.wheel_cycles_settings": "Korritzeko gurpilak ezarpenak aldatzen ditu",
"horizon.extras.pack_browser": "Baliabide paketeen arakatzailea",
"horizon.extras.fullbright.tooltip": "Erakutsi blokeak eta entitateak argi betean, baita eremu ilunetan ere.",
"horizon.extras.dynamic_lights.tooltip": "Zuziak eta beste argi-iturri batzuk eusten, erorita edo sutan pizten dituzte inguruko blokeak.",
"horizon.extras.own_name_tag.tooltip": "Zure izena zure buruaren gainean erakusten du hirugarren pertsonan, beste jokalariek ikusten duten moduan. Ezkutatuta dago zerbitzariak zure taldearen izenak ezkutatzen dituenean edo ikusezina zarenean.",
"horizon.extras.hide_block_particles.tooltip": "Hautsitako bloke batetik lehertu eta mina duzuna erortzen diren hondakin-partikulak ezkutatzen ditu. Beste partikula batzuk geratzen dira.",
"horizon.extras.auto_reconnect.tooltip": "Konexioa galdu edo berrabiarazi ondoren zerbitzari batean sartzen da berriro, inoiz ez ostiko bat edo debeku baten ondoren.",
"horizon.extras.ui_sound_reserve.tooltip": "Munduko soinuek 247 soinu-kanaletatik 16 libre uzten dituzte, beraz, uholde batek (leherketa handi bat) ezin ditu menuko klikak isilarazi.",
"horizon.extras.music_page.tooltip": "Music & Sounds-ek Musika... orria lortzen du: erreproduzitu integratutako edozein pista orain, edo desaktibatu bat jokoak hautatzeari uzteko. Ez da ezer aldatzen pista bat itzali arte.",
"horizon.extras.mouse_tweaks.tooltip": "Eskuinean arrastatu sakatuta bat jartzen du zirrikitu bakoitzeko, ezkerrean arrastatu edukitako elementua biltzen du, Maius + ezkerrera arrastatu zirrikitu guztiak mugitzen ditu.",
"horizon.extras.mouse_tweaks_wheel.tooltip": "Beherak zirrikituaren elementu bat beste aldera bidaltzen du, gorak bat itzultzen du.",
"horizon.extras.sort.tooltip": "Inbentarioa, kutxak, upelak, ender kutxak eta shulker-kutxak sortze-ordenan ordenatzen ditu, pilak batuz.",
"horizon.extras.sort_key.tooltip": "Kurtsorea azpian dagoen zatia ordenatzen du. Egin klik, eta sakatu tekla bat; Ihesak askatzen du.",
"horizon.extras.no_ai.tooltip": "AI funtzio guztiak ezkutatu eta desaktibatu egiten ditu: agenteen kontrola, horizon-ctl eta MCP zerbitzariak. Abiarazlearen etengailua ere.",
"horizon.extras.agent_control.tooltip": "Zure AI agenteak horizon-ctl edo MCP bezero baten bidez jolastu dezake joko honetara, ordenagailu honetan soilik.",
"horizon.extras.discord_presence.tooltip": "Discord exekutatzen den bitartean Horizon Jolasean erakusten du: menuak, jokalari bakarreko edo jokalari anitzeko, dimentsioa eta jokatutako denbora. Inoiz ez mundu baten izenik edo zerbitzari baten helbidea.",
"Calibrate...": "Kalibratu...",
"Calibrate Anyway...": "Kalibratu Dena den...",
"Use Automatic Setup": "Erabili konfigurazio automatikoa",
"Shortcut: %s": "Lasterbidea: %s",
"Next": "Hurrengoa",
"HDR Calibration %s/%s: %s": "HDR Kalibrazioa %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Pantaila osoko azpian dauden hiru ezartzen ditu, probako argazki bat aldi berean.",
"A key that opens this screen from the game. Not bound at first.": "Pantaila hau jokotik irekitzen duen giltza. Hasieran lotu gabe.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Pantailak ez du esan zenbat argi dagoen: kalibratu konfiguratzeko.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Dagoeneko konfiguratuta zaude zure pantailarako. Kalibratu HDR okerra bada bakarrik.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Distira gailurra eta Paper Zuria berriro jartzen ditu pantailaren konfiguraziora.",
"Set up for your display. Leave on Auto unless it looks wrong.": "Konfiguratu zure pantailarako. Utzi automatikoa aktibatuta, gaizki ikusten ez bada.",
"Lower it until the logo just disappears into the black.": "Jaitsi logoa beltzean desagertu arte.",
"Raise it until the logo just disappears into the bright square.": "Altxa ezazu logotipoa karratu distiratsuan desagertu arte.",
"Make the card a white that's comfortable to look at for a long time, in the light you play in.": "Egin ezazu txartela denbora luzez begiratzeko erosoa den zuria, jolasten duzun argitan.",
"horizon.splitscreen.profiles.forget": "Ahaztu",
"horizon.splitscreen.profiles.create": "Sortu",
"horizon.splitscreen.profiles.you": " - zu",
"horizon.splitscreen.profiles.forget_named": "Ahaztu %s",
"horizon.splitscreen.profiles.previous": "Aurrekoa",
"horizon.splitscreen.profiles.next": "Hurrengoa",
"horizon.splitscreen.profiles.new_button": "Profil berria...",
"horizon.splitscreen.profiles.forget_title": "Profila ahaztu?",
"horizon.splitscreen.profiles.forget_leaves": "Instantzia honen zerrenda uzten du.",
"horizon.splitscreen.profiles.forget_kept": "Munduko aurrerapena eta ezarpenak mantentzen dira.",
"horizon.splitscreen.profiles.new_title": "Profil berria",
"horizon.splitscreen.profiles.name_hint": "Izena (3 eta 16 letra, zifra, _)",
"horizon.splitscreen.profiles.switch_title": "Aldatu profila",
"horizon.splitscreen.profiles.offline_note": "Lineaz kanpoko profilak instantzia honetakoak dira",
"horizon.splitscreen.profiles.move": "Mugitu jolasten duzun tokira",
"horizon.splitscreen.profiles.move_place": "Mugitu jolasten duzun tokira: %s",
"horizon.friends.title": "Jolastu Lagunekin",
"horizon.friends.hint": "Lagunek itsatsi dute kodea izenburu-pantailaren Lagunak botoian, Zuzeneko konexioan edo abiarazlearen Play orrian.",
"horizon.friends.copied": "Kopiatua",
"horizon.friends.copy_code": "Kopiatu kodea",
"horizon.friends.stop_sharing": "Utzi partekatzeari",
"horizon.friends.none_yet": "Hemen oraindik ez dago lagunik.",
"horizon.friends.here_now": "Hemen orain: %s",
"horizon.friends.share_intro": "Partekatu mundu hau gonbidapen-kode baten atzean: LANera irekitzen da eta Interneteko lagunak bat egiten dute kodearekin. Ez dago gure zerbitzaririk parte hartzen.",
"horizon.friends.start_sharing": "Hasi Partekatzen",
"horizon.friends.troubleshooting": "Arazoak konpontzen...",
"horizon.friends.stopped": "Gonbidapen kodea partekatzeari utzi dio.",
"horizon.friends.short_code_changed": "Norbaitek kode labur oker bat probatzen jarraitu zuen, beraz, aldatu egin zen.",
"horizon.friends.wrong_code": "Norbaitek kode oker batekin saiatu da.",
"horizon.friends.code_ready": "Zure gonbidapen-kodea prest dago: Jokoen menua, Jolastu lagunekin. Kopiatu eta bidali zure lagunei.",
"horizon.friends.share_failed": "Ezin izan da mundua partekatu: %s",
"horizon.friends.finding_route": "Lagunak zurekin nola irits daitezkeen aurkitzen...",
"horizon.osk.shift": "Txanda",
"horizon.osk.space": "Espazioa",
"horizon.osk.delete": "Ezabatu",
"horizon.osk.type": "Mota",
"horizon.osk.symbols": "Sinboloak",
"horizon.osk.close": "Itxi",
"horizon.extras.chat_animation.tooltip": "Txat-lerro berri bat ezkerretik sartzen da desagertzen den bitartean, osorik agertu beharrean.",
"horizon.extras.chat_indicator.tooltip": "Txat-lerro baten ezkerreko koloretako barra.",
"horizon.extras.wheel_cycles_settings.tooltip": "Ezarpenetako botoi baten gainean korritzeko gurpilak bere balioa aldatzen du, bainilan bezala. Desaktibatuta, gurpilak zerrendan bakarrik mugitzen du.",
"horizon.extras.pack_browser.tooltip": "Aukeratu Baliabide Paketeak Gehitu botoi bat lortzen du, Modrinth bilatzen duena eta pakete bat instalatzen du zure paketeen karpetan. Desaktibatuta, jokoa ez da inoiz Modrinth-ekin harremanetan jartzen.",
"horizon.extras.splitscreen.tooltip": "Mundu batean, beste kontrolagailu bat sar daiteke jokalari berri gisa: sakatu A eskatzen dionean, eta hautatu profil bat eta non jokatu.",
"horizon.splitscreen.setup.monitor": "Monitorea %s",
"horizon.splitscreen.setup.monitor_unknown": "monitore bat",
"horizon.splitscreen.setup.window": "%s leihoa",
"horizon.splitscreen.setup.new_window": "Leiho berria",
"horizon.splitscreen.setup.fill_monitor": "Bete %s",
"horizon.splitscreen.setup.done": "Eginda",
"horizon.splitscreen.setup.off": "Desaktibatuta",
"horizon.splitscreen.setup.player": "Jokalari %s",
"horizon.splitscreen.setup.revert": "Atzera",
"horizon.splitscreen.setup.apply": "Aplikatu",
"horizon.splitscreen.setup.warning.empty_main": "Inork ez du jolasten Window 1-n: ezkutatzen da",
"horizon.splitscreen.setup.warning.empty_close": "Inork ez du jolasten %s leihoan: Aplikatuan ixten da",
"horizon.splitscreen.setup.warning.empty_new": "Inork ez du jolasten %s leihoan: ez da irekiko",
"horizon.splitscreen.setup.warning.on_main": "%s leihoak 1. leihoaren monitorea betetzen du: bere jokalariek 1. leihoan jokatzen dute",
"horizon.splitscreen.setup.warning.unplugged": "%s leihoaren monitorea ez dago konektatuta: bere jokalariek 1. leihoan jokatzen dute",
"horizon.splitscreen.setup.status.off": "Pantaila zatitua itzalita dago.",
"horizon.splitscreen.setup.status.enable_hint": "Piztu eta sakatu mundu bateko beste kontrolagailu bateko botoi bat.",
"horizon.splitscreen.setup.status.join_hint": "Sakatu A sartzeko eta profil bat hautatzeko.",
"horizon.splitscreen.setup.status.idle": "Pantaila zatitua inaktibo",
"horizon.splitscreen.setup.status.alone": "Bertako beste jokalaririk ez.",
"horizon.splitscreen.setup.status.solo_controls": "Bakarkako kontrolak eta kontroladorearen feedbacka ez dira aldatu.",
"horizon.splitscreen.setup.window_empty": "%s leihoa: hutsik",
"horizon.splitscreen.setup.note.select": "Egin klik erreproduzitzaile batean, edo arrastatu leiho batera edo monitore batera.",
"horizon.splitscreen.setup.note.apply": "Aplikatu modu honetan erreproduzitzeko, itzuli desegin egiteko.",
"horizon.splitscreen.setup.note.place": "Non jokatu behar du %s?",
"horizon.splitscreen.setup.this_player": "jokalari hau",
"horizon.splitscreen.setup.switch_disabled": "Pantaila zatitua itzaltzen da besteek irten direnean (haien eten menua).",
"horizon.splitscreen.setup.window_place": "%s leihoa (%s/%s)",
"horizon.splitscreen.setup.names": "Izenak",
"horizon.splitscreen.setup.identify": "Identifikatu",
"horizon.splitscreen.setup.layout.two": "2 partekatzea: %s",
"horizon.splitscreen.setup.layout.three": "3 partekatzea: %s",
"horizon.splitscreen.setup.layout.two.0": "Autoa",
"horizon.splitscreen.setup.layout.two.1": "Elkarren ondoan",
"horizon.splitscreen.setup.layout.two.2": "Goian eta behean",
"horizon.splitscreen.setup.layout.three.0": "Bat zabal gainean",
"horizon.splitscreen.setup.layout.three.1": "Altuera bat ezkerraldean",
"horizon.splitscreen.setup.layout.three.2": "Elkarren ondoan",
"horizon.splitscreen.setup.help.player": "Egin klik edo arrastatu %s horiek mugitzeko.",
"horizon.splitscreen.setup.help.layout_auto": "%s (leihoa zabala denean elkarren ondoan). Sakatu aldatzeko.",
"horizon.splitscreen.setup.help.layout": "%s. Sakatu aldatzeko.",
"horizon.splitscreen.setup.help.switch": "Ordenagailu honen pantaila zatitua, piztuta edo itzalita.",
"horizon.splitscreen.setup.help.identify": "Jokalari bakoitzaren ikuspegiak zein leihotan dagoen erakusten du.",
"horizon.splitscreen.setup.help.names": "Jokalari bakoitzaren izena bere ikuspegiaren izkinan.",
"horizon.splitscreen.setup.help.revert": "Desegin oraindik aplikatu gabeko aldaketak.",
"horizon.splitscreen.setup.help.apply": "Erreproduzitu mapak erakusten duen moduan.",
"horizon.splitscreen.setup.help.unchanged": "Oraindik ez da ezer aldatu.",
"horizon.splitscreen.setup.give_up_pad": "Eman Pad",
"horizon.splitscreen.prompt.controller_connected": "Kontroladorea konektatuta",
"horizon.splitscreen.prompt.controller_disconnected": "Kontrolagailua deskonektatuta dago",
"horizon.splitscreen.prompt.controller_disconnected.hint": "Konektatu berriro edo sakatu beste botoi bat",
"horizon.splitscreen.prompt.join.one": "%s sartu nahi du",
"horizon.splitscreen.prompt.join.many": "%s kontrolatzaileek sartu nahi dute",
"horizon.splitscreen.prompt.join.yes": "Sartu",
"horizon.splitscreen.prompt.join.as_p1": "Jokatu P1 bezala",
"horizon.splitscreen.prompt.join.no": "Ez",
"horizon.splitscreen.prompt.lost": "%s: kontrolagailua deskonektatuta dago",
"horizon.splitscreen.prompt.lost.use_another": "Erabili beste bat",
"horizon.splitscreen.prompt.lost.leave": "Utzi",
"horizon.splitscreen.setup.help.give_up_pad": "Kontrolagailuak gidatzeari uzten dio eta sartzea eskaintzen zaio (A).",
"horizon.splitscreen.setup.note.held_at": "%sri %sri: A-k hemen erortzen du, B-k berriro jartzen du.",
"horizon.splitscreen.setup.note.held": "%s jasoa: ezkerrera/eskuinera hautatzen du non, A jaisten da, B-k berriro jartzen du.",
"Hides models behind opaque blocks. Async checks off-thread; entities tick.": "Bloke opakuen atzean ereduak ezkutatzen ditu. Async egiaztapenak off-thread; entitateak markatuta.",
"Block Entities": "Blokeatu Entitateak",
"Shulker Boxes": "Shulker Kutxak",
"Reuses settled boxes; lid movement stays animated.": "Kutxa finkatuak berrerabili; estalkien mugimendua animatuta geratzen da.",
"Signs": "Seinaleak",
"Reuses sign text until text, light, or font changes.": "Berrerabili zeinu-testua testua, argia edo letra-tipoa aldatu arte.",
"Decorated Pots": "Pote apainduak",
"Reuses still pots; break wobble and sherd changes redraw.": "Berrerabili oraindik lapikoak; hautsi wobble eta sherd aldaketak birmarraztu.",
"Bells": "Kanpaiak",
"Reuses still bells; ringing stays animated.": "Kanpai geldiak berrerabili; eraztunak animatuta geratzen dira.",
"Chests": "Kutxak",
"Reuses closed chests; moving lids stay animated.": "Itxitako kutxak berrerabili; mugitzen diren estalkiak animatuta geratzen dira.",
"Banners": "Pankartak",
"Reuses poles; flags and pattern layers still update.": "Zutoinak berrerabili; banderak eta eredu-geruzak oraindik eguneratzen dira.",
"Copper Golem Statues": "Kobrezko Golem estatuak",
"Reuses statue geometry until its block state changes.": "Estatuaren geometria berrerabili bere bloke-egoera aldatu arte.",
"Leaf Culling": "Hosto-mozketa",
"Every leaf face. Vanilla's fancy leaves.": "Hosto aurpegi bakoitza. Banilaren hosto dotoreak.",
"Distant Plant Dither": "Urrutiko Landare Dither",
"Keeps fine plants in high-res packs from fading far away.": "Erresoluzio handiko paketeetan landare finak urruti desager ez daitezen mantentzen du.",
"Screenshot Quality": "Pantaila-argazkiaren kalitatea",
"F2 screenshots at this many times the game's resolution. Higher takes longer.": "F2 pantaila-argazkiak jokoaren bereizmen askotan. Handiagoa denbora gehiago behar da.",
"Frame Generation": "Frame Sorkuntza",
"Shows a made-up frame between each two real ones: smoother, a little more lag.": "Bi erreal bakoitzaren artean osaturiko fotograma erakusten du: leunagoa, lag apur bat gehiago.",
"Shadow Cache": "Itzalen cachea",
"Reuses the pack shadow map when sun, view and world match. Off by default.": "Berrerabili paketearen itzal-mapa eguzkia, ikuspegia eta mundua bat datozenean. Desaktibatuta lehenespenez.",
"Find BlueMap": "Bilatu BlueMap",
"On servers, finds their BlueMap web map for far terrain. Next join.": "Zerbitzarietan, BlueMap web-mapa aurkitzen du urruneko lursailetarako. Hurrengoa sartu.",
"BlueMap Radius": "BlueMap Erradioa",
"How far around you BlueMap tiles are downloaded. Next join.": "Zure inguruan BlueMap fitxak deskargatzen diren noraino. Hurrengoa sartu.",
"Synchronous": "Sinkronikoa",
"Asynchronous": "Asinkronoak",
"Solid": "Solidoa",
"Solid Aggressive": "Solido Agresiboa",
"Hollow": "Hutsak",
"Leaves fully inside a canopy draw solid: fewer see-through layers.": "Hostoek erabat barnean marrazten dute koipea: geruza ikusgarri gutxiago.",
"Leaves with one open side draw solid too: faster, chunkier edges.": "Alde bat irekita duten hostoek ere sendoak marrazten dituzte: ertz azkarragoak eta potoloagoak.",
"Drops faces between leaves: faster, but gaps look through.": "Aurpegiak hostoen artean erortzen ditu: azkarrago, baina hutsuneak zeharkatzen ditu.",
"Hotbar: Previous Slot": "Hotbar: Aurreko Slot",
"Hotbar: Next Slot": "Hotbar: hurrengo zirrikitua",
"horizon.agent.connected.one": "Konektatuta: agente konexio 1",
"horizon.agent.connected.many": "Konektatuta: %s agente konexioak",
"horizon.agent.connected.one_ago": "Konektatuta: 1 agente konexio, azken komandoa duela %s s",
"horizon.agent.connected.many_ago": "Konektatuta: %s agente konexioak, duela %s azken komandoa",
"horizon.agent.waiting": "Agente baten zain.",
"horizon.agent.off": "Desaktibatuta: agenteak ezin dira joko honetara iritsi.",
"horizon.agent.session_opened": "Saio honetarako irekia jokoa hasi zuen programak.",
"horizon.agent.socket": "Socketa: %s (kopiatu bere bide osoaren xehetasunak)",
"horizon.agent.local_only": "Tokiko soilik: ordenagailu honetako agenteak, inoiz ez sarekoak.",
"Hides models behind opaque blocks. Entities update. Off with custom packs.": "Bloke opakuen atzean ereduak ezkutatzen ditu. Entitateak eguneratzea. Off pakete pertsonalizatuekin.",
"horizon.extras.repro_keys": "Lasterbideak grabatzea",
"horizon.extras.repro_keys.tooltip": "F3+R erreprodukzio-grabaketa bat hasten edo gelditzen da eta F3+E-k bere sorta idazten du. Desaktibatuta lehenespenez: grabazio batek mundua kopiatzen du.",
"horizon.agent.record_session": "Hasi Grabaketa Saioa",
"horizon.agent.record_stop": "Gelditu grabaketa saioa",
"horizon.agent.export_repro": "Esportatu Repro sorta",
"horizon.repro.started": "Grabaketa hasi zen; F3+R gelditu egiten da eta sorta idazten du.",
"horizon.repro.stopped": "Grabaketa gelditu da: %s",
"horizon.repro.bundle": "Erreprodukzio sorta: %s",
"horizon.repro.error": "Erreprodukzioa: %s",
"horizon.repro.no_recording": "Oraindik ez dago esportatzeko grabaziorik.",
"horizon.repro.replay_started": "Erreprodukzioa hasi da: %s",
"horizon.pack.addInfo": "Gehitu Modrinth-en baliabide-paketea",
"horizon.pack.addWorking": "Modrinth galdetzen...",
"horizon.pack.addNone": "Ez da aurkitu baliabide paketerik",
"horizon.pack.addTitle": "Gehitu baliabide pakete bat",
"horizon.pack.install": "Instalatu",
"horizon.pack.installing": "Instalatzen",
"horizon.pack.installed": "Instalatua",
"horizon.pack.search": "Bilatu",
"horizon.pack.searchHint": "Bilatu Modrinth...",
"horizon.pack.filters": "Iragazkiak",
"horizon.pack.filter.realistic": "Errealista",
"horizon.pack.filter.simplistic": "Sinplista",
"horizon.pack.filter.vanillaLike": "Banila itxurakoa",
"horizon.pack.addFailed": "Ezin izan da paketea gehitu: %s",
"horizon.extras.header.cosmetics": "Kosmetikoak",
"horizon.extras.cosmetics_mine": "Nire kosmetikoak",
"horizon.extras.cosmetics_mine.tooltip": "Ezkutuan: Horizon jokalari bakoitzak zure kosmetikarik gabe ikusten zaitu, zuk barne. Abiarazlearen armairuaren etengailu bera.",
"horizon.extras.cosmetics_others": "Beste jokalarien kosmetika",
"horizon.extras.cosmetics_others.tooltip": "Beste Horizon jokalariek zer janzten duten: denetarik, izenaren etiketa estiloak bakarrik, edo ezer ez. Ezkutuan, jokoak ez du inoiz galdetzen zer janzten duten.",
"horizon.extras.cosmetics_auras": "Besteen aura eta ibilbideak",
"horizon.extras.cosmetics_auras.tooltip": "Beste jokalariek janzten dituzten aura eta arrastoak. Desaktibatuta lehenespenez.",
"horizon.extras.cosmetics.shown": "Erakutsita",
"horizon.extras.cosmetics.hidden": "Ezkutuan",
"horizon.extras.cosmetics.name_tags": "Izena etiketak soilik"
}
@@ -10,14 +10,11 @@
"horizon.options.move_deadzone": "ناحیه مرده حرکت",
"horizon.options.look_deadzone": "ناحیه مرده نگاه",
"horizon.options.button_glyphs": "گلیف دکمه",
"horizon.options.menu_navigation": "پیمایش منو",
"horizon.options.on_screen_keyboard": "صفحه کلید روی صفحه",
"horizon.options.chat_delay": "تاخیر چت",
"horizon.options.lightbar.health": "سلامتی",
"horizon.options.lightbar.dimension": "بعد",
"horizon.options.glyphs.auto": "خودکار",
"horizon.options.menu_focus": "تمرکز کنید",
"horizon.options.menu_pointer": "اشاره گر",
"horizon.options.keyboard.auto": "خودکار",
"horizon.options.keyboard.steam": "بخار",
"horizon.options.unavailable": "این ویژگی هنوز در Horizon موجود نیست.",
@@ -9,13 +9,10 @@
"horizon.options.move_deadzone": "Liikkeen kuollut alue",
"horizon.options.look_deadzone": "Katseen kuollut alue",
"horizon.options.button_glyphs": "Painikkeiden kuviot",
"horizon.options.menu_navigation": "Valikkonavigointi",
"horizon.options.on_screen_keyboard": "Näyttönäppäimistö",
"horizon.options.chat_delay": "Chatin viive",
"horizon.options.lightbar.health": "Terveys",
"horizon.options.lightbar.dimension": "Mitat",
"horizon.options.menu_focus": "Keskity",
"horizon.options.menu_pointer": "Osoitin",
"horizon.options.unavailable": "Tämä ominaisuus ei ole vielä saatavilla Horizonissa.",
"horizon.options.search": "Hakuasetukset...",
"horizon.pages.mods": "Modit",
@@ -0,0 +1,429 @@
{
"horizon.options.vibration": "Panginginig ng boses",
"horizon.options.adaptive_triggers": "Mga Adaptive Trigger",
"horizon.options.look_sensitivity": "Sensitivity ng Tingin",
"horizon.options.invert_x": "Baliktad na Tingin X",
"horizon.options.invert_y": "Baliktad na Tingin Y",
"horizon.options.sneak_button": "Pindutan ng Sneak",
"horizon.options.move_deadzone": "Deadzone ng Paggalaw",
"horizon.options.look_deadzone": "Deadzone ng Tingin",
"horizon.options.button_glyphs": "Mga Glyph ng Button",
"horizon.options.on_screen_keyboard": "On-Screen na Keyboard",
"horizon.options.chat_delay": "Pagkaantala sa Chat",
"horizon.options.lightbar.health": "Kalusugan",
"horizon.options.lightbar.dimension": "Dimensyon",
"horizon.options.unavailable": "Hindi pa available ang feature na ito sa Horizon.",
"horizon.options.search": "Mga setting ng paghahanap...",
"horizon.pages.mods": "Mga mod",
"horizon.pages.shader_packs": "Mga Shader Pack",
"horizon.pages.shader_options": "Mga Pagpipilian sa Shader",
"horizon.pages.controller": "Mga Setting ng Controller",
"horizon.pages.controller_binds": "Nagbubuklod ang Controller",
"horizon.pages.extras": "Horizon Mga extra",
"horizon.pages.settings": "Horizon Mga Setting",
"horizon.pages.agent": "Ahente ng AI",
"horizon.split.leave": "umalis",
"horizon.pages.split_settings": "Mga Setting ng Splitscreen",
"horizon.pages.split_profiles": "Sino naglalaro?",
"horizon.pages.split_setup": "Pag-setup ng Splitscreen",
"horizon.pages.music": "Musika",
"horizon.pages.music.creative": "Malikhain",
"horizon.pages.music.underwater": "Sa ilalim ng tubig",
"horizon.pages.music.end": "Ang Katapusan",
"horizon.pages.music.credits": "Mga kredito",
"horizon.pages.music.end_biomes": "Tapusin ang mga biome",
"horizon.pages.music.other": "Iba pang musika",
"horizon.pages.music.empty": "Walang mga track ng musika: ang mga tunog ng laro ay hindi na-load.",
"horizon.pages.music.tooltip": "Ang mga built-in na track, ayon sa sitwasyon: maglaro ng isa ngayon, o i-off ang anuman para ihinto ng laro ang pagpili nito.",
"horizon.pages.settings.tooltip": "Sariling setting ni Horizon: graphics, controllers, extras at mods.",
"horizon.pages.graphics.tooltip": "Mga graphic, simple: sinasabi ng bawat setting kung ano ang halaga nito.",
"horizon.pages.graphics_advanced.tooltip": "Bawat graphics setting, kasama si Horizon, kung magkano ang halaga ng bawat isa.",
"horizon.pages.controller.tooltip": "Mga Controller: ang kanilang mga button, stick, vibration at menu navigation.",
"horizon.pages.extras.tooltip": "Mga opsyonal na karagdagan, lahat ay naka-off bilang default: ang laro ay mananatiling vanilla hanggang sa i-on mo ang isa.",
"horizon.pages.agent.tooltip": "Hayaang maglaro ang sarili mong ahente ng AI: horizon-ctl at MCP client sa computer na ito.",
"horizon.pages.split_profiles.tooltip": "Sino ang gumaganap: isang profile na nakatago sa folder ng larong ito.",
"horizon.pages.split_setup.tooltip": "Ang mga bintana at monitor, at kung saan naglalaro.",
"horizon.pages.shader_packs.tooltip": "Shader pack mula sa folder ng shaderpacks: sa sarili ni Horizon at sa mga shader/ format. Ang isa ay tumatakbo sa isang pagkakataon.",
"horizon.pages.mods.tooltip": "Mga mod na ginawa para sa Horizon, mula sa folder ng horizon-mods. Ang bawat isa ay tumatakbo sa isang sandbox at nananatiling naka-off hanggang sa i-on mo ito.",
"horizon.pages.open.graphics": "Mga graphic...",
"horizon.pages.open.graphics_advanced": "Advanced na Graphics...",
"horizon.pages.open.shader_packs": "Mga Shader Pack...",
"horizon.pages.open.controller": "Mga Setting ng Controller...",
"horizon.pages.open.extras": "Mga extra...",
"horizon.pages.open.mods": "Mga mod...",
"horizon.pages.open.agent": "Ahente ng AI...",
"horizon.pages.open.horizon_graphics": "Horizon Mga graphic...",
"horizon.pages.open.controller_binds": "Nagbubuklod ang Controller...",
"horizon.pages.open.horizon_settings": "Horizon Mga Setting...",
"horizon.pages.open.music": "Musika...",
"horizon.mods.empty": "Walang mods: ilagay ang mga .hmod file sa horizon-mods folder",
"horizon.mods.stopped": " (Tumigil)",
"horizon.mods.not_built": " (Hindi binuo)",
"horizon.mods.settings": "Mga setting",
"horizon.mods.in_world": " (iwanan ang mundo upang magbago)",
"horizon.mods.open_folder": "Buksan ang Mod Folder",
"horizon.shader_packs.empty": "Walang shader pack: ilagay ang mga ito sa shaderpacks folder",
"horizon.shader_options.open": "Mga Pagpipilian sa Shader...",
"horizon.shader_options.loading": "Binabasa ang mga opsyon ng shader pack...",
"horizon.agent.copy_details": "Kopyahin ang Mga Detalye ng Koneksyon",
"horizon.multiplayer.join": "Sumali",
"horizon.multiplayer.cancel_reconnecting": "Kanselahin ang Muling Pagkonekta",
"horizon.multiplayer.join_friend": "Sumali sa isang Kaibigan",
"horizon.multiplayer.invite_code": "Code ng Imbitasyon",
"horizon.multiplayer.relay": "Relay (opsyonal)",
"LOD Distance": "LOD Distansya",
"Far Detail": "Malayong Detalye",
"Chunk Updates": "Mga Update ng Tipak",
"Upload Budget": "Mag-upload ng Badyet",
"Fog": "Ulap",
"Weather": "Panahon",
"Animated Textures": "Mga Animated na Texture",
"Present Mode": "Kasalukuyang Mode",
"Worker Threads": "Mga Thread ng Manggagawa",
"Resolution Scale": "Iskala ng Resolusyon",
"Paper White": "Papel na Puti",
"Peak Brightness": "Tuktok na Liwanag",
"Black Level": "Itim na Antas",
"UI Brightness": "Liwanag ng UI",
"Battery Mode": "Mode ng Baterya",
"Performance Overlay": "Overlay ng Pagganap",
"Full Detail Distance": "Buong Detalye ng Distansya",
"On": "Naka-on",
"Off": "Naka-off",
"Unlimited": "Walang limitasyon",
"Performance": "Pagganap",
"Balanced": "Balanseng",
"Quality": "Kalidad",
"Native": "Orihinal",
"Full": "Puno",
"Reduced": "Nabawasan",
"Efficient": "Mahusay",
"Precise": "tumpak",
"Immediate": "Agad-agad",
"FPS and Graph": "FPS at Graph",
"No cost": "Walang gastos",
"Low cost": "Mababang gastos",
"Medium cost": "Katamtamang halaga",
"High cost": "Mataas na gastos",
"Saves time": "Nakakatipid ng oras",
"Graphics": "Mga graphic",
"Done": "Tapos na",
"This feature is not yet available in Horizon.": "Hindi pa available ang feature na ito sa Horizon.",
"Your display or system doesn't offer HDR output.": "Ang iyong display o system ay hindi nag-aalok ng HDR output.",
"Turn HDR on to use it.": "I-on ang HDR para magamit ito.",
"Your GPU or graphics API doesn't support it.": "Hindi ito sinusuportahan ng iyong GPU o graphics API.",
"%s chunks": "%s tipak",
"%s per frame": "%s bawat frame",
"Full-detail terrain range. LOD Distance can extend local worlds farther.": "Saklaw ng buong detalye ng lupain. Maaaring pahabain ng LOD Distance ang mga lokal na mundo nang mas malayo.",
"Coarse distant terrain in local worlds. Integrated GPUs stop at 128 chunks.": "Magaspang na malayong lupain sa mga lokal na mundo. Humihinto ang mga pinagsamang GPU sa 128 chunks.",
"Reduced draws far terrain coarser: faster on this integrated GPU.": "Ang pinababang kumukuha ng malayong lupain ay mas magaspang: mas mabilis sa pinagsamang GPU na ito.",
"Reduced draws far terrain coarser, for GPUs it slows down.": "Ang pinababang kumukuha ng malayong lupain ay mas magaspang, para sa mga GPU ay bumagal ito.",
"Field of view. Wider shows more terrain at once.": "Larangan ng pananaw. Ang mas malawak ay nagpapakita ng mas maraming lupain nang sabay-sabay.",
"Caps frames per second; a cap saves power and heat.": "I-cap ang mga frame sa bawat segundo; ang takip ay nakakatipid ng kuryente at init.",
"Waits for the display's refresh: no tearing, a little latency.": "Naghihintay para sa pag-refresh ng display: walang punit, kaunting latency.",
"Size of menus and the HUD.": "Sukat ng mga menu at ang HUD.",
"Sets the options below together. Changing one makes it Custom.": "Pinagsasama-sama ang mga opsyon sa ibaba. Ang pagpapalit ng isa ay ginagawa itong Custom.",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "Lava, apoy at sikat ng araw ay mas maliwanag kaysa puti. Kailangan ng HDR display.",
"Your display's darkest black. 0 for OLED; LCDs glow a little.": "Pinakamadilim na itim ang iyong display. 0 para sa OLED; Medyo kumikinang ang mga LCD.",
"How bright highlights get. Auto: the peak your system reports.": "Kung gaano kaliwanag ang mga highlight. Auto: ang pinakamataas na ulat ng iyong system.",
"How bright white paper and menus look. Auto: your desktop's white.": "Gaano kaliwanag ang puting papel at mga menu. Auto: puti ng iyong desktop.",
"How bright the HUD and menus are, against paper white.": "Gaano kaliwanag ang HUD at mga menu, laban sa puting papel.",
"How far mobs, crops and machines keep working. CPU time.": "Hanggang saan patuloy na gumagana ang mga mandurumog, pananim at makina. oras ng CPU.",
"How far away mobs and items are drawn, as a share of normal.": "Kung gaano kalayo ang mga mandurumog at mga item ay iginuhit, bilang bahagi ng normal.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Nilalaktawan ang lupain na nakatago sa likod ng mga saradong seksyon. Malaki ang gastos sa off.",
"Hides entities behind opaque blocks.": "Itinatago ang mga entity sa likod ng mga opaque na bloke.",
"No entity occlusion culling.": "Walang entity occlusion culling.",
"CPU occlusion test runs this frame.": "Pinapatakbo ng CPU occlusion test ang frame na ito.",
"Occlusion checks run on a worker thread.": "Ang mga pagsusuri sa occlusion ay tumatakbo sa isang thread ng manggagawa.",
"Meshed sections uploaded per frame. Lower is smoother while loading.": "Mga naka-meshed na seksyon na na-upload bawat frame. Ang ibaba ay mas makinis habang naglo-load.",
"Soft shadows in corners. Costs chunk building time only.": "Malambot na mga anino sa mga sulok. Gastos ng chunk building time lang.",
"Smaller copies of block textures for distant terrain: less shimmer.": "Mas maliliit na kopya ng mga block texture para sa malayong lupain: mas kaunting shimmer.",
"Blends grass and water colours across biomes when building chunks.": "Pinagsasama ang mga kulay ng damo at tubig sa mga biome kapag gumagawa ng mga tipak.",
"Cloud layer: off, flat (fast) or 3D (fancy).": "Cloud layer: naka-off, flat (mabilis) o 3D (fancy).",
"How far clouds reach, in chunks.": "Gaano kalayo ang naabot ng mga ulap, sa mga tipak.",
"Draws the sky, sun, moon and stars.": "Iginuhit ang langit, araw, buwan at mga bituin.",
"Distance fog. Off shows the edge of the loaded world.": "Distance fog. Ipinapakita ng Off ang gilid ng na-load na mundo.",
"Draws rain and snow.": "Gumuguhit ng ulan at niyebe.",
"How far rain and snow fall around you.": "Gaano kalayo ang ulan at niyebe sa paligid mo.",
"How many particles effects spawn.": "Gaano karaming mga particle ang mga epekto ng mga itlog.",
"Round shadows under mobs and items.": "Mga bilog na anino sa ilalim ng mga mob at item.",
"Leaves with see-through gaps. Off draws them solid.": "Mga dahon na may nakikitang mga puwang. Off draws ang mga ito solid.",
"Sorts water and glass more exactly behind each other.": "Pinag-uuri-uriin ang tubig at baso nang mas eksakto sa likod ng bawat isa.",
"Which chunk rebuilds wait for the frame: none, nearby or yours.": "Aling chunk ang muling itatayo ang naghihintay para sa frame: wala, malapit o sa iyo.",
"Animates water, lava, fire and other moving textures.": "Binibigyang-buhay ang tubig, lava, apoy at iba pang gumagalaw na texture.",
"World pixels per screen pixel. Below 100% is faster on the GPU.": "World pixels bawat screen pixel. Ang mas mababa sa 100% ay mas mabilis sa GPU.",
"Smooths jagged edges. FXAA: softer. TAA: smoothest, moving things may trail.": "Makinis ang tulis-tulis na mga gilid. FXAA: mas malambot. TAA: Ang pinakamakinis, gumagalaw na mga bagay ay maaaring masunod.",
"Renders the world smaller and rebuilds it with TAA. Faster, softer.": "Ginagawang mas maliit ang mundo at muling itinatayo ito gamit ang TAA. Mas mabilis, mas malambot.",
"HDR output on an HDR display: lava, fire and sunlight shine brighter.": "HDR na output sa isang HDR display: lava, apoy at sikat ng araw ay kumikinang nang mas maliwanag.",
"How frames reach the screen while VSync is off.": "Paano naaabot ng mga frame ang screen habang naka-off ang VSync.",
"Efficient sleeps until the next frame; Precise spins, like vanilla.": "Mahusay na pagtulog hanggang sa susunod na frame; Mga tumpak na pag-ikot, tulad ng vanilla.",
"Fills the screen (borderless).": "Pinuno ang screen (walang hangganan).",
"Brightens dark places.": "Nagpapaliwanag ng mga madilim na lugar.",
"Lowers the frame rate when minimised, or idle too.": "Ibinababa ang frame rate kapag pinaliit, o idle din.",
"Sways the view while walking.": "Iniindayog ang tanawin habang naglalakad.",
"Fades newly loaded chunks in.": "Nagfades ang mga bagong na-load na chunks.",
"Darkens the screen's edges.": "Pinadidilim ang mga gilid ng screen.",
"Shows when the world saves.": "Ipinapakita kung kailan nagliligtas ang mundo.",
"Shows attack cooldown.": "Nagpapakita ng cooldown ng pag-atake.",
"60 FPS cap (30 in menus or unfocused), half the workers: less power.": "60 FPS cap (30 sa mga menu o hindi nakatutok), kalahati ng mga manggagawa: mas kaunting lakas.",
"Threads that generate, light and mesh chunks. Next start.": "Mga thread na bumubuo, magaan, at mga tipak ng mata. Susunod na simula.",
"Vulkan, then DirectX 12 (Windows) or OpenGL (Linux) where it fails. Next start.": "Vulkan, pagkatapos ay DirectX 12 (Windows) o OpenGL (Linux) kung saan ito nabigo. Susunod na simula.",
"Water and glass in one buffer: fewer draw calls, a bit more VRAM. Next start.": "Tubig at baso sa isang buffer: mas kaunting mga draw call, medyo mas VRAM. Susunod na simula.",
"Mesh data sent to the GPU per frame. Lower is smoother while loading.": "Mesh data na ipinadala sa GPU bawat frame. Ang ibaba ay mas makinis habang naglo-load.",
"Frame rate and frame times over the game, with a graph.": "Frame rate at frame times sa laro, na may graph.",
"horizon.multiplayer.finding_friend": "Hinahanap ang mundo ng iyong kaibigan...",
"horizon.multiplayer.not_signed_in": "Nasa online mode ang server na ito. Mag-sign in gamit ang iyong Microsoft account sa Horizon launcher para sumali dito.",
"horizon.multiplayer.invalid_address": "Di-wastong address ng server: %s",
"horizon.multiplayer.connected_friend_direct": "Direktang konektado sa iyong kaibigan...",
"horizon.multiplayer.connected_friend_relay": "Nakakonekta sa iyong kaibigan sa pamamagitan ng relay...",
"General": "Heneral",
"Undo": "I-undo",
"Search options": "Mga opsyon sa paghahanap",
"Distance": "Distansya",
"Display": "Pagpapakita",
"Sky and weather": "Langit at panahon",
"Lighting and entities": "Pag-iilaw at mga nilalang",
"Textures": "Mga texture",
"Image": "Imahe",
"Chunk loading": "Naglo-load ng tipak",
"Power": "kapangyarihan",
"Frames": "Mga frame",
"Brightness": "Liwanag",
"How far you see, the window and the HUD.": "Kung gaano kalayo ang nakikita mo, ang bintana at ang HUD.",
"The sky and weather, lighting, textures and the image.": "Ang langit at panahon, pag-iilaw, mga texture at ang imahe.",
"Chunk loading, culling and power use.": "Chunk loading, culling at paggamit ng kuryente.",
"How frames reach the screen, and the performance overlay.": "Paano naaabot ng mga frame ang screen, at ang overlay ng performance.",
"Brighter-than-white output on an HDR display.": "Mas maliwanag kaysa sa puting output sa isang HDR display.",
"Results for \"%s\"": "Mga resulta para sa \"%s\"",
"No settings match.": "Walang tumutugma sa mga setting.",
"horizon.title.not_affiliated": "Hindi kaakibat sa Mojang o Microsoft",
"horizon.extras.header.lighting": "Pag-iilaw",
"horizon.extras.header.third_person": "Pangatlong tao",
"horizon.extras.header.particles": "Mga particle",
"horizon.extras.header.inventory": "Imbentaryo",
"horizon.extras.header.sound": "Tunog",
"horizon.extras.header.menus": "Mga menu",
"horizon.extras.dynamic_lights": "Mga dynamic na ilaw",
"horizon.extras.own_name_tag": "Sariling name tag",
"horizon.extras.hide_block_particles": "Itago ang mga block-break na particle",
"horizon.extras.auto_reconnect": "Awtomatikong kumonekta muli",
"horizon.extras.ui_sound_reserve": "Panatilihin ang mga channel para sa mga tunog ng UI",
"horizon.extras.music_page": "Pahina ng musika",
"horizon.extras.mouse_tweaks": "Mga kontrol ng Mouse Tweaks",
"horizon.extras.mouse_tweaks_wheel": "Gumagalaw ang scroll wheel ng mga item",
"horizon.extras.sort": "Mga pindutan ng pag-uri-uriin",
"horizon.extras.no_ai": "Walang AI",
"horizon.extras.agent_control": "Kontrol ng Ahente",
"horizon.extras.chat_animation": "Makinis na chat",
"horizon.extras.chat_indicator": "Tagapagpahiwatig ng mensahe",
"horizon.extras.wheel_cycles_settings": "Nagbabago ang mga setting ng scroll wheel",
"horizon.extras.fullbright.tooltip": "Ipakita ang mga bloke at entity sa ganap na liwanag, kahit na sa madilim na lugar.",
"horizon.extras.dynamic_lights.tooltip": "Ang mga sulo at iba pang pinagmumulan ng liwanag na hawak, nalaglag o nasusunog ay nagpapasindi sa mga bloke sa paligid nila.",
"horizon.extras.own_name_tag.tooltip": "Ipinapakita ang iyong pangalan sa itaas ng iyong ulo sa pangatlong tao, tulad ng nakikita ng ibang mga manlalaro. Nakatago kapag itinago ng server ang mga pangalan ng iyong team o hindi ka nakikita.",
"horizon.extras.hide_block_particles.tooltip": "Itinatago ang mga debris na particle na sumabog mula sa isang sirang bloke at gumuho sa iyong minahan. Ang iba pang mga particle ay nananatili.",
"horizon.extras.auto_reconnect.tooltip": "Sumali muli sa isang server pagkatapos ng isang nawalang koneksyon o isang pag-restart, hindi kailanman pagkatapos ng isang sipa o pagbabawal.",
"horizon.extras.ui_sound_reserve.tooltip": "Ang mga tunog sa mundo ay nag-iiwan ng 16 sa 247 na sound channel na libre, kaya ang baha sa kanila (isang malaking pagsabog) ay hindi maaaring patahimikin ang mga pag-click sa menu.",
"horizon.extras.music_page.tooltip": "Ang Music & Sounds ay nakakakuha ng Music... page: i-play ang anumang built-in na track ngayon, o i-off ang isa para tumigil ang laro sa pagpili nito. Walang magbabago hanggang sa i-off mo ang isang track.",
"horizon.extras.mouse_tweaks.tooltip": "Ang right-drag ay naglalagay ng isa sa bawat slot, ang left-drag ay kumukuha ng hawak na item, Shift + left-drag ang gumagalaw sa bawat slot.",
"horizon.extras.mouse_tweaks_wheel.tooltip": "Ang Down ay nagpapadala ng isang item ng slot sa kabilang panig, ang pataas ay nagbabalik ng isa.",
"horizon.extras.sort.tooltip": "Pinag-uuri-uri ang imbentaryo, mga chest, barrel, ender chest at shulker box sa malikhaing pagkakasunud-sunod ng tab, pinagsasama-sama ang mga stack.",
"horizon.extras.sort_key.tooltip": "Pinagbukud-bukod ang bahagi sa ilalim ng cursor. Mag-click, pagkatapos ay pindutin ang isang key; Tinatanggal ito ng pagtakas.",
"horizon.extras.no_ai.tooltip": "Itinatago at ino-off ang bawat feature ng AI: control ng ahente, horizon-ctl at ang mga MCP server. Ang switch din ng launcher.",
"horizon.extras.agent_control.tooltip": "Ang iyong sariling ahente ng AI ay maaaring laruin ang larong ito sa pamamagitan ng horizon-ctl o isang MCP client, sa computer na ito lamang.",
"horizon.extras.discord_presence.tooltip": "Ipinapakita ang Paglalaro ng Horizon sa iyong Discord profile habang tumatakbo ang Discord: mga menu, single player o multiplayer, ang dimensyon at ang oras ng paglalaro. Hindi kailanman pangalan ng mundo o address ng server.",
"Calibrate...": "I-calibrate...",
"Calibrate Anyway...": "Mag-calibrate pa rin...",
"Use Automatic Setup": "Gamitin ang Awtomatikong Pag-setup",
"Next": "Susunod",
"HDR Calibration %s/%s: %s": "Pag-calibrate ng HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Itinatakda ang tatlo sa ibaba ng full screen, isang pagsubok na larawan sa isang pagkakataon.",
"A key that opens this screen from the game. Not bound at first.": "Isang susi na nagbubukas ng screen na ito mula sa laro. Hindi nakatali sa una.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Hindi sinabi ng iyong display kung gaano ito kaliwanag: i-calibrate para i-set up ito.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Naka-set up na para sa iyong display. I-calibrate lang kung mukhang mali ang HDR.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Ibinabalik ang Peak Brightness at Paper White sa setup ng iyong display.",
"Set up for your display. Leave on Auto unless it looks wrong.": "I-set up para sa iyong display. Umalis sa Auto maliban kung mukhang mali.",
"Lower it until the logo just disappears into the black.": "Ibaba ito hanggang sa mawala na lang sa itim ang logo.",
"Raise it until the logo just disappears into the bright square.": "Itaas ito hanggang sa mawala na lang ang logo sa maliwanag na parisukat.",
"Make the card a white that's comfortable to look at for a long time, in the light you play in.": "Gawing puti ang card na kumportableng tingnan sa mahabang panahon, sa liwanag na iyong nilalaro.",
"horizon.splitscreen.profiles.forget": "Kalimutan",
"horizon.splitscreen.profiles.create": "Lumikha",
"horizon.splitscreen.profiles.you": " - ikaw",
"horizon.splitscreen.profiles.forget_named": "Kalimutan %s",
"horizon.splitscreen.profiles.previous": "Nakaraang",
"horizon.splitscreen.profiles.next": "Susunod",
"horizon.splitscreen.profiles.new_button": "Bagong Profile...",
"horizon.splitscreen.profiles.forget_title": "Kalimutan ang profile?",
"horizon.splitscreen.profiles.forget_leaves": "Umalis ito sa listahan ng pagkakataong ito.",
"horizon.splitscreen.profiles.forget_kept": "Ang pag-unlad ng mundo at mga setting ay pinapanatili.",
"horizon.splitscreen.profiles.new_title": "Bagong Profile",
"horizon.splitscreen.profiles.name_hint": "Pangalan (3 hanggang 16 na letra, digit, _)",
"horizon.splitscreen.profiles.switch_title": "Lumipat ng profile",
"horizon.splitscreen.profiles.offline_note": "Ang mga offline na profile ay kabilang sa pagkakataong ito",
"horizon.splitscreen.profiles.move": "Lumipat kung saan ka naglalaro",
"horizon.splitscreen.profiles.move_place": "Ilipat kung saan ka naglalaro: %s",
"horizon.friends.title": "Makipaglaro sa Kaibigan",
"horizon.friends.hint": "I-paste ng mga kaibigan ang code pagkatapos ng button na Friends ng screen ng pamagat, sa Direktang Koneksyon, o sa Play page ng launcher.",
"horizon.friends.copied": "Kinopya",
"horizon.friends.copy_code": "Kopyahin ang Code",
"horizon.friends.stop_sharing": "Itigil ang Pagbabahagi",
"horizon.friends.none_yet": "Wala pang kaibigan dito.",
"horizon.friends.here_now": "Narito ngayon: %s",
"horizon.friends.share_intro": "Ibahagi ang mundong ito sa likod ng code ng imbitasyon: bumubukas ito sa LAN, at sumali ang mga kaibigan sa internet gamit ang code. Walang kasamang server namin.",
"horizon.friends.start_sharing": "Simulan ang Pagbabahagi",
"horizon.friends.troubleshooting": "Pag-troubleshoot...",
"horizon.friends.stopped": "Huminto sa pagbabahagi ng code ng imbitasyon.",
"horizon.friends.short_code_changed": "May sumubok ng maling maikling code, kaya nagbago ito.",
"horizon.friends.wrong_code": "May sumubok ng maling code.",
"horizon.friends.code_ready": "Handa na ang iyong code ng imbitasyon: Game Menu, Play with Friends. Kopyahin ito at ipadala sa iyong mga kaibigan.",
"horizon.friends.share_failed": "Hindi maibahagi ang mundo: %s",
"horizon.friends.finding_route": "Hinahanap kung paano ka maaabot ng mga kaibigan...",
"horizon.osk.shift": "Paglipat",
"horizon.osk.space": "kalawakan",
"horizon.osk.delete": "Tanggalin",
"horizon.osk.type": "Uri",
"horizon.osk.symbols": "Mga simbolo",
"horizon.osk.close": "Isara",
"horizon.extras.chat_animation.tooltip": "Ang isang bagong linya ng chat ay dumudulas mula sa kaliwa habang ito ay nawawala, sa halip na lumabas nang buo.",
"horizon.extras.chat_indicator.tooltip": "Ang may kulay na bar sa kaliwa ng isang chat line.",
"horizon.extras.wheel_cycles_settings.tooltip": "Ang scroll wheel sa ibabaw ng isang button sa mga setting ay nagbabago ng halaga nito, tulad ng sa vanilla. Naka-off, ini-scroll lang ng gulong ang listahan.",
"horizon.extras.pack_browser.tooltip": "Ang Select Resource Packs ay nakakakuha ng Add button na naghahanap sa Modrinth at nag-i-install ng pack sa iyong pack folder. Wala, ang laro ay hindi kailanman nakikipag-ugnayan sa Modrinth.",
"horizon.extras.splitscreen.tooltip": "Sa isang mundo, maaaring sumali ang isa pang controller bilang bagong player: pindutin ang A kapag nagtanong ito, pagkatapos ay pumili ng profile at kung saan maglaro.",
"horizon.splitscreen.setup.monitor": "Subaybayan %s",
"horizon.splitscreen.setup.monitor_unknown": "isang monitor",
"horizon.splitscreen.setup.window": "Bintana %s",
"horizon.splitscreen.setup.new_window": "Bagong window",
"horizon.splitscreen.setup.fill_monitor": "Punan ang %s",
"horizon.splitscreen.setup.done": "Tapos na",
"horizon.splitscreen.setup.on": "Naka-on",
"horizon.splitscreen.setup.off": "Naka-off",
"horizon.splitscreen.setup.player": "Manlalaro %s",
"horizon.splitscreen.setup.revert": "Ibalik",
"horizon.splitscreen.setup.apply": "Mag-apply",
"horizon.splitscreen.setup.warning.empty_main": "Walang naglalaro sa Window 1: nagtatago ito",
"horizon.splitscreen.setup.warning.empty_close": "Walang naglalaro sa Window %s: nagsasara ito sa Ilapat",
"horizon.splitscreen.setup.warning.empty_new": "Walang naglalaro sa Window %s: hindi ito magbubukas",
"horizon.splitscreen.setup.warning.on_main": "Pinuno ng Window %s ang monitor ng Window 1: naglalaro ang mga manlalaro nito sa Window 1",
"horizon.splitscreen.setup.warning.unplugged": "Ang monitor ng Window %s ay hindi konektado: ang mga manlalaro nito ay naglalaro sa Window 1",
"horizon.splitscreen.setup.status.off": "Naka-off ang splitscreen.",
"horizon.splitscreen.setup.status.enable_hint": "I-on ito, pagkatapos ay pindutin ang isang button sa isa pang controller sa isang mundo.",
"horizon.splitscreen.setup.status.join_hint": "Pindutin ang A para sumali at pumili ng profile.",
"horizon.splitscreen.setup.status.alone": "Walang ibang mga lokal na manlalaro.",
"horizon.splitscreen.setup.status.solo_controls": "Ang mga solong kontrol at feedback ng controller ay hindi nagbabago.",
"horizon.splitscreen.setup.window_empty": "Window %s: walang laman",
"horizon.splitscreen.setup.note.select": "Mag-click sa isang player, o i-drag ito sa isang window o isang monitor.",
"horizon.splitscreen.setup.note.apply": "Mag-apply upang maglaro sa ganitong paraan, Bumalik sa pag-undo.",
"horizon.splitscreen.setup.note.place": "Saan dapat maglaro si %s?",
"horizon.splitscreen.setup.this_player": "ang manlalarong ito",
"horizon.splitscreen.setup.switch_disabled": "Nag-o-off ang Splitscreen kapag umalis na ang iba (ang kanilang menu sa pag-pause).",
"horizon.splitscreen.setup.window_place": "Window %s (%s ng %s)",
"horizon.splitscreen.setup.names": "Mga pangalan",
"horizon.splitscreen.setup.identify": "Kilalanin",
"horizon.splitscreen.setup.layout.two": "2 pagbabahagi: %s",
"horizon.splitscreen.setup.layout.three": "3 pagbabahagi: %s",
"horizon.splitscreen.setup.layout.two.1": "Magkatabi",
"horizon.splitscreen.setup.layout.two.2": "Itaas at ibaba",
"horizon.splitscreen.setup.layout.three.0": "Isang malawak sa itaas",
"horizon.splitscreen.setup.layout.three.1": "Isang matangkad sa kaliwa",
"horizon.splitscreen.setup.layout.three.2": "Magkatabi",
"horizon.splitscreen.setup.help.player": "I-click o i-drag ang %s upang ilipat ang mga ito.",
"horizon.splitscreen.setup.help.layout_auto": "%s (magkatabi kapag malapad ang bintana). Pindutin upang baguhin.",
"horizon.splitscreen.setup.help.layout": "%s. Pindutin upang baguhin.",
"horizon.splitscreen.setup.help.switch": "Splitscreen para sa computer na ito, naka-on o naka-off.",
"horizon.splitscreen.setup.help.identify": "Ipinapakita ng view ng bawat manlalaro kung saang window ito naroroon.",
"horizon.splitscreen.setup.help.names": "Ang pangalan ng bawat manlalaro sa sulok ng kanilang view.",
"horizon.splitscreen.setup.help.revert": "I-undo ang mga pagbabagong hindi pa nalalapat.",
"horizon.splitscreen.setup.help.apply": "I-play ang paraang ipinapakita ng mapa.",
"horizon.splitscreen.setup.help.unchanged": "Wala pa ring pinagbago.",
"horizon.splitscreen.setup.give_up_pad": "Isuko Pad",
"horizon.splitscreen.prompt.controller_connected": "Nakakonekta ang controller",
"horizon.splitscreen.prompt.controller_disconnected": "Nadiskonekta ang controller",
"horizon.splitscreen.prompt.controller_disconnected.hint": "Ikonekta itong muli, o pindutin ang isang pindutan sa isa pa",
"horizon.splitscreen.prompt.join.one": "Gustong sumali ni %s",
"horizon.splitscreen.prompt.join.many": "Gustong sumali ng %s controllers",
"horizon.splitscreen.prompt.join.yes": "Sumali",
"horizon.splitscreen.prompt.join.as_p1": "Maglaro bilang P1",
"horizon.splitscreen.prompt.join.no": "Hindi",
"horizon.splitscreen.prompt.lost": "%s: nadiskonekta ang controller",
"horizon.splitscreen.prompt.lost.use_another": "Gumamit ng iba",
"horizon.splitscreen.prompt.lost.leave": "umalis",
"horizon.splitscreen.setup.help.give_up_pad": "Ang iyong controller ay huminto sa pagmamaneho sa iyo at inaalok na sumali (A).",
"horizon.splitscreen.setup.note.held_at": "%s hanggang %s: Ibinaba ni A dito, ibinalik ni B.",
"horizon.splitscreen.setup.note.held": "%s pinulot: kaliwa/kanan pumili kung saan, ibinabagsak ni A, ibinabalik ito ni B.",
"Hides models behind opaque blocks. Async checks off-thread; entities tick.": "Itinatago ang mga modelo sa likod ng mga opaque na bloke. Sinusuri ng Async ang off-thread; lagyan ng tsek ang mga entidad.",
"Block Entities": "I-block ang mga Entity",
"Shulker Boxes": "Mga Kahon ng Shulker",
"Reuses settled boxes; lid movement stays animated.": "Muling ginagamit ang mga nakaayos na kahon; nananatiling animated ang paggalaw ng talukap ng mata.",
"Signs": "Mga palatandaan",
"Reuses sign text until text, light, or font changes.": "Muling ginagamit ang sign text hanggang sa magbago ang text, ilaw, o font.",
"Decorated Pots": "Pinalamutian na mga kaldero",
"Reuses still pots; break wobble and sherd changes redraw.": "Gumamit muli ng mga kaldero; break wobble at sherd pagbabago redraw.",
"Bells": "Mga kampana",
"Reuses still bells; ringing stays animated.": "Gumamit muli ng mga kampana; nananatiling animated ang tugtog.",
"Chests": "Mga dibdib",
"Reuses closed chests; moving lids stay animated.": "Muling ginagamit ang mga saradong dibdib; nananatiling animated ang mga gumagalaw na takip.",
"Banners": "Mga banner",
"Reuses poles; flags and pattern layers still update.": "Gumamit muli ng mga poste; nag-a-update pa rin ang mga flag at pattern layer.",
"Copper Golem Statues": "Mga Estatwa ng Copper Golem",
"Reuses statue geometry until its block state changes.": "Gumamit muli ng geometry ng estatwa hanggang sa magbago ang block state nito.",
"Leaf Culling": "Paghukay ng dahon",
"Every leaf face. Vanilla's fancy leaves.": "Bawat dahon mukha. Magarbong dahon ng vanilla.",
"Distant Plant Dither": "Malayong Plant Dither",
"Keeps fine plants in high-res packs from fading far away.": "Pinapanatili ang magagandang halaman sa mga high-res na pack na hindi kumukupas sa malayo.",
"Screenshot Quality": "Kalidad ng Screenshot",
"F2 screenshots at this many times the game's resolution. Higher takes longer.": "Ang mga screenshot ng F2 sa ito ay maraming beses sa resolution ng laro. Mas tumatagal ang mas mataas.",
"Frame Generation": "Pagbuo ng Frame",
"Shows a made-up frame between each two real ones: smoother, a little more lag.": "Nagpapakita ng ginawang frame sa pagitan ng bawat dalawang tunay: mas makinis, mas lag.",
"Reuses the pack shadow map when sun, view and world match. Off by default.": "Muling ginagamit ang pack shadow map kapag nagtugma ang araw, view at mundo. Naka-off bilang default.",
"Find BlueMap": "Hanapin ang BlueMap",
"On servers, finds their BlueMap web map for far terrain. Next join.": "Sa mga server, hinahanap ang kanilang BlueMap web map para sa malayong lupain. Susunod na sumali.",
"How far around you BlueMap tiles are downloaded. Next join.": "Gaano kalayo sa paligid mo ang mga tile ng BlueMap ay na-download. Susunod na sumali.",
"Synchronous": "Kasabay",
"Hollow": "guwang",
"Leaves fully inside a canopy draw solid: fewer see-through layers.": "Ang mga dahon na ganap sa loob ng canopy ay gumuhit ng solid: mas kaunting mga see-through na layer.",
"Leaves with one open side draw solid too: faster, chunkier edges.": "Ang mga dahon na may isang bukas na gilid ay gumuhit din ng solid: mas mabilis, mas makapal na mga gilid.",
"Drops faces between leaves: faster, but gaps look through.": "Ibinababa ang mga mukha sa pagitan ng mga dahon: mas mabilis, ngunit ang mga puwang ay lumilipas.",
"Hotbar: Previous Slot": "Hotbar: Nakaraang Slot",
"Hotbar: Next Slot": "Hotbar: Susunod na Slot",
"horizon.agent.connected.one": "Konektado: 1 koneksyon ng ahente",
"horizon.agent.connected.many": "Konektado: %s na koneksyon ng ahente",
"horizon.agent.connected.one_ago": "Konektado: 1 koneksyon ng ahente, huling utos %s s ang nakalipas",
"horizon.agent.connected.many_ago": "Konektado: %s na koneksyon ng ahente, huling utos %s s ang nakalipas",
"horizon.agent.waiting": "Naghihintay ng ahente.",
"horizon.agent.off": "Naka-off: hindi maabot ng mga ahente ang larong ito.",
"horizon.agent.session_opened": "Binuksan para sa session na ito ng program na nagsimula ng laro.",
"horizon.agent.socket": "Socket: %s (kopyahin ang mga detalye para sa buong path nito)",
"horizon.agent.local_only": "Lokal lamang: mga ahente sa computer na ito, hindi sa network.",
"Hides models behind opaque blocks. Entities update. Off with custom packs.": "Itinatago ang mga modelo sa likod ng mga opaque na bloke. Pag-update ng mga entity. I-off gamit ang mga custom na pack.",
"horizon.extras.repro_keys": "Pagre-record ng mga shortcut",
"horizon.extras.repro_keys.tooltip": "Ang F3+R ay nagsisimula o huminto sa isang repro recording at ang F3+E ay nagsusulat ng bundle nito. Naka-off bilang default: kinokopya ng isang recording ang mundo.",
"horizon.agent.record_session": "Simulan ang Pagre-record ng Session",
"horizon.agent.record_stop": "Ihinto ang Pagre-record ng Session",
"horizon.agent.export_repro": "I-export ang Repro Bundle",
"horizon.repro.started": "Nagsimula ang pagre-record; Itinigil ito ng F3+R at isusulat ang bundle.",
"horizon.repro.stopped": "Nahinto ang pagre-record: %s",
"horizon.repro.no_recording": "Wala pang recording na ie-export.",
"horizon.repro.replay_started": "Nagsimula ang replay: %s",
"horizon.pack.addInfo": "Magdagdag ng resource pack mula sa Modrinth",
"horizon.pack.addWorking": "Tanong ni Modrinth...",
"horizon.pack.addNone": "Walang nakitang mga resource pack",
"horizon.pack.addTitle": "Magdagdag ng Resource Pack",
"horizon.pack.install": "I-install",
"horizon.pack.installing": "Pag-install",
"horizon.pack.installed": "Naka-install",
"horizon.pack.search": "Maghanap",
"horizon.pack.searchHint": "Maghanap sa Modrinth...",
"horizon.pack.filters": "Mga filter",
"horizon.pack.filter.realistic": "Makatotohanan",
"horizon.pack.filter.vanillaLike": "Parang vanilla",
"horizon.pack.addFailed": "Hindi maidagdag ang pack: %s",
"horizon.extras.header.cosmetics": "Mga pampaganda",
"horizon.extras.cosmetics_mine": "Aking mga pampaganda",
"horizon.extras.cosmetics_mine.tooltip": "Nakatago: makikita ka ng bawat Horizon na manlalaro nang wala ang iyong mga pampaganda, kasama mo. Ang parehong switch bilang Wardrobe ng launcher.",
"horizon.extras.cosmetics_others": "Mga pampaganda ng ibang manlalaro",
"horizon.extras.cosmetics_others.tooltip": "Ano ang isinusuot ng iba pang Horizon na manlalaro: lahat, mga istilo lang ng name tag nila, o wala. Nakatago, ang laro ay hindi nagtatanong kung ano ang kanilang isinusuot.",
"horizon.extras.cosmetics_auras": "Mga aura at landas ng iba",
"horizon.extras.cosmetics_auras.tooltip": "Mga aura at trail na isinusuot ng ibang mga manlalaro. Naka-off bilang default.",
"horizon.extras.cosmetics.shown": "Ipinakita",
"horizon.extras.cosmetics.hidden": "Nakatago",
"horizon.extras.cosmetics.name_tags": "Mga name tag lang"
}
@@ -0,0 +1,478 @@
{
"horizon.options.controller": "Stýrari",
"horizon.options.three_d_skin_layers": "3D húðløg",
"horizon.options.vibration": "Ristingar",
"horizon.options.adaptive_triggers": "Tillagandi útloysarar",
"horizon.options.lightbar": "Ljósstrika",
"horizon.options.look_sensitivity": "Hyggjuviðkvæmi",
"horizon.options.invert_x": "Umvending av hyggjan X",
"horizon.options.invert_y": "Umvending av hyggjan Y",
"horizon.options.sneak_button": "Sníkiknøttur",
"horizon.options.move_deadzone": "Deyðaøki fyri flyting",
"horizon.options.look_deadzone": "Deyðaøki fyri hyggjan",
"horizon.options.button_glyphs": "Knappaglyfur",
"horizon.options.on_screen_keyboard": "Lyklaborð á skjánum",
"horizon.options.chat_delay": "Kjakseinking",
"horizon.options.lightbar.health": "Heilsa",
"horizon.options.lightbar.dimension": "Mát",
"horizon.options.unavailable": "Hesin hentleikin er ikki tøkur enn í Horizon.",
"horizon.options.search": "Leiti innstillingar...",
"horizon.pages.shader_packs": "Skuggapakkar",
"horizon.pages.shader_options": "Skuggamøguleikar",
"horizon.pages.controller": "Innstillingar av stýriskipanini",
"horizon.pages.controller_binds": "Stýriskipanin bindur",
"horizon.pages.extras": "Horizon Eyka",
"horizon.pages.settings": "Horizon Innstillingar",
"horizon.pages.agent": "AI umboðsmaður",
"horizon.split.leave": "Far",
"horizon.pages.split_settings": "Innstillingar fyri býtisskjálvta",
"horizon.pages.split_profiles": "Hvør spælir?",
"horizon.pages.split_setup": "Uppseting av býttum skermi",
"horizon.pages.music": "Tónleikur",
"horizon.pages.music.menu": "Matskrá",
"horizon.pages.music.overworld": "Yvirheimur",
"horizon.pages.music.creative": "Skapandi",
"horizon.pages.music.underwater": "Undir vatni",
"horizon.pages.music.end": "Endin",
"horizon.pages.music.boss": "Stjóri",
"horizon.pages.music.credits": "Eindir",
"horizon.pages.music.overworld_biomes": "Yvirheimslívfrøði",
"horizon.pages.music.nether_biomes": "Neðra lívfrøði",
"horizon.pages.music.end_biomes": "Endalívfrøði",
"horizon.pages.music.other": "Annar tónleikur",
"horizon.pages.music.empty": "Eingi tónleikaløg: ljóðini í spælinum eru ikki heintað.",
"horizon.pages.music.tooltip": "Innbygdu løgini, eftir støðu: spæl eitt nú, ella sløkk nøkur so spælið steðgar við at tína tað.",
"horizon.pages.settings.tooltip": "Horizon's egnu innstillingar: grafikkur, stýringar, eyka og mods.",
"horizon.pages.graphics.tooltip": "Grafikkur, einfalt: hvør innstilling sigur hvat hon kostar.",
"horizon.pages.graphics_advanced.tooltip": "Hvør grafikk innstilling, Horizon's íroknað, við hvat hvør kostar.",
"horizon.pages.controller.tooltip": "Stýringar: teirra knappar, prikkar, vibratiónir og valmyndanavigatión.",
"horizon.pages.extras.tooltip": "Valfrí viðbót, alt sløkt sum standard: spælið heldur seg vanilju til tú tendrar eitt.",
"horizon.pages.agent.tooltip": "Lat tín egna AI umboðsmann spæla: horizon-ctl og MCP klientar á hesi telduni.",
"horizon.pages.split_profiles.tooltip": "Hvør spælir: ein profilur goymdur í hesi spælimappuni.",
"horizon.pages.split_setup.tooltip": "Vindeyguni og skjálvtar, og hvør spælir hvar.",
"horizon.pages.shader_packs.tooltip": "Skuggapakkar úr skuggapakka mappuni: Horizon's egnu og teir í skugganum/ sniðinum. Eitt rennur í senn.",
"horizon.pages.mods.tooltip": "Mods gjørd til Horizon, úr horizon-mods mappuni. Hvør koyrir í einum sandkassa og heldur seg sløkt, til tú tendrar hann.",
"horizon.pages.open.graphics": "Grafikkur...",
"horizon.pages.open.graphics_advanced": "Framkomin grafikkur...",
"horizon.pages.open.shader_packs": "Skuggapakkar...",
"horizon.pages.open.controller": "Innstillingar av stýrinum...",
"horizon.pages.open.split_setup": "Býttur skermur...",
"horizon.pages.open.extras": "Eyka...",
"horizon.pages.open.agent": "AI umboðsmaður...",
"horizon.pages.open.horizon_graphics": "Horizon Grafikkur...",
"horizon.pages.open.controller_binds": "Stýri bindur...",
"horizon.pages.open.horizon_settings": "Horizon Innstillingar...",
"horizon.pages.open.music": "Tónleikur...",
"horizon.mods.empty": "Ongar mods: set .hmod fílur í horizon-mods mappuna",
"horizon.mods.stopped": " (Steðgað)",
"horizon.mods.not_built": " (Ikki bygt)",
"horizon.mods.dev": " (Menning)",
"horizon.mods.settings": "Innstillingar",
"horizon.mods.in_world": " (lat heimin broytast)",
"horizon.mods.open_folder": "Opna Mod mappuna",
"horizon.shader_packs.empty": "Ongar skuggapakkar: set teir í mappuna skuggapakkar",
"horizon.shader_options.open": "Skuggamøguleikar...",
"horizon.shader_options.profile": "Vangamynd",
"horizon.shader_options.loading": "Lesa møguleikarnar í skuggapakkanum...",
"horizon.agent.copy_details": "Avrita sambandsupplýsingar",
"horizon.multiplayer.join": "Ver við",
"horizon.multiplayer.cancel_reconnecting": "Avlýs aftursamband",
"horizon.multiplayer.join_friend": "Melda teg til ein vin",
"horizon.multiplayer.invite_code": "Boðkoda",
"horizon.multiplayer.relay": "Stafett (valfrítt)",
"Preset": "Forsett",
"LOD Distance": "LOD Fjarstøða",
"Far Detail": "Fjar Detalja",
"Cave Culling": "Helladráp",
"Entity Culling": "Eindarskurður",
"Chunk Updates": "Klumpardagføringar",
"Upload Budget": "Send fíggjarætlan upp",
"Fog": "Toka",
"Weather": "Veður",
"Animated Textures": "Livandi tekstur",
"Present Mode": "Núverandi háttur",
"Frame Pacing": "Rammu tempo",
"Worker Threads": "Arbeiðsmannatráðir",
"Resolution Scale": "Upploysnisstiga",
"Anti-aliasing": "Anti-alias",
"Upscaling": "Uppstilling",
"Paper White": "Pappír Hvítt",
"Peak Brightness": "Toppljósstyrki",
"Black Level": "Svart stig",
"UI Brightness": "UI Ljósstyrki",
"Battery Mode": "Battarístilling",
"Graphics API": "Grafikk API",
"Translucent Batching": "Gjøgnumskygd bólking",
"Full Detail Distance": "Full detaljufjarstøða",
"On": "Á",
"Off": "Sløkt",
"Custom": "Sersniðgivið",
"Performance": "Avrikið",
"Quality": "Góðska",
"Ultra Performance": "Ultra avrikið",
"Native": "Upprunaligt",
"Reduced": "Minkað",
"Efficient": "Effektivur",
"Precise": "Nágreinilig",
"Auto (VSync)": "Sjálvvirkandi (Vsamskipan)",
"Mailbox": "Postkassi",
"Immediate": "Beinanvegin",
"FPS and Graph": "FPS og Graf",
"No cost": "Eingin kostnaður",
"Low cost": "Lágur kostnaður",
"Medium cost": "Miðalkostnaður",
"High cost": "Høgur kostnaður",
"Saves time": "Sparir tíð",
"Graphics": "Grafikkur",
"Done": "Liðugt",
"This feature is not yet available in Horizon.": "Hesin hentleikin er ikki tøkur enn í Horizon.",
"Your display or system doesn't offer HDR output.": "Tín skjár ella skipan bjóðar ikki HDR útgang.",
"Turn HDR on to use it.": "Tendra HDR fyri at brúka tað.",
"Your GPU or graphics API doesn't support it.": "Tín GPU ella grafikk API stuðlar ikki tí.",
"%s chunks": "%s bitar",
"%s per frame": "%s fyri hvørja rammu",
"%s MiB/frame": "%s MiB/ramma",
"%s KiB/frame": "%s KiB/ramma",
"Auto (%s)": "Sjálvvirkandi (%s)",
"Full-detail terrain range. LOD Distance can extend local worlds farther.": "Full-detalja lendisøki. LOD Fjarstøða kann víðka lokalar heimar longur.",
"Coarse distant terrain in local worlds. Integrated GPUs stop at 128 chunks.": "Grovt fjarskotið lendi í lokalum heimum. Integreraðir GPU'ar steðga á 128 klumpum.",
"Reduced draws far terrain coarser: faster on this integrated GPU.": "Minkað dregur langt lendi grovari: skjótari á hesum integreraða GPU.",
"Reduced draws far terrain coarser, for GPUs it slows down.": "Minkað dregur langt lendið grovari, fyri GPU'ar tað bremsar.",
"Field of view. Wider shows more terrain at once.": "Sjónarringur. Breiðari vísir meira lendi í senn.",
"Caps frames per second; a cap saves power and heat.": "Hettar rammur um sekundið; hetta sparir streym og hita.",
"Waits for the display's refresh: no tearing, a little latency.": "Bíðar eftir at skjárin verður uppdateraður: einki rív, eitt sindur av latency.",
"Size of menus and the HUD.": "Stødd á valmyndum og HUD.",
"Sets the options below together. Changing one makes it Custom.": "Setur valmøguleikarnar niðanfyri saman. At broyta eitt ger tað Custom.",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "Lava, eldur og sólarljós skína bjartari enn hvítt. Tørvar HDR skjálvta.",
"Your display's darkest black. 0 for OLED; LCDs glow a little.": "Skjárin hjá tær er myrkast svartur. 0 fyri OLED; LCD-skjáir glógva eitt sindur.",
"How bright highlights get. Auto: the peak your system reports.": "Hvussu bjart hæddarpunkt fáa. Sjálvvirkandi: toppurin, sum tín skipan greiðir frá.",
"How bright white paper and menus look. Auto: your desktop's white.": "Hvussu ljóst hvítt pappír og matskráir síggja út. Sjálvvirkandi: skriviborðið er hvítt.",
"How bright the HUD and menus are, against paper white.": "Hvussu ljós HUD og valmyndir eru, móti pappírshvítum.",
"How far mobs, crops and machines keep working. CPU time.": "Hvussu langt mobbar, grøðir og maskinur halda fram at virka. CPU tíð.",
"How far away mobs and items are drawn, as a share of normal.": "Hvussu langt burtur mobbar og lutir eru drignir, sum partur av vanligum.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Sleppur lendi goymt aftan fyri stongdar partar. Av kostar nógv.",
"Hides entities behind opaque blocks.": "Goymir eindir aftan fyri ógjøgnumskygdar blokkar.",
"No entity occlusion culling.": "Eingin eind okklusión avlíving.",
"CPU occlusion test runs this frame.": "CPU okklusiónsroynd koyrir hesa rammuna.",
"Occlusion checks run on a worker thread.": "Okklusiónskanningar koyra á einum arbeiðstráði.",
"Meshed sections uploaded per frame. Lower is smoother while loading.": "Meskaðir partar lagdir upp fyri hvørja rammu. Lægri er slættari meðan tú lastar.",
"Soft shadows in corners. Costs chunk building time only.": "Mjúkir skuggar í hornum. Kostar klump byggitíð einans.",
"Smaller copies of block textures for distant terrain: less shimmer.": "Smærri eintøk av blokktekstur til fjarskotið lendi: minni glitrandi.",
"Blends grass and water colours across biomes when building chunks.": "Blanda gras- og vatnlitir tvørtur um biomur, tá ið bygt verður klumpar.",
"Cloud layer: off, flat (fast) or 3D (fancy).": "Skýlag: av, flatt (skjótt) ella 3D (fancy).",
"How far clouds reach, in chunks.": "Hvussu langt skýggj røkka, í bitum.",
"Draws the sky, sun, moon and stars.": "Teknar himmal, sól, mána og stjørnur.",
"Distance fog. Off shows the edge of the loaded world.": "Fjarstøðumjørki. Off vísir kantin á lastaða heiminum.",
"Draws rain and snow.": "Dregur regn og hálku.",
"How far rain and snow fall around you.": "Hvussu langt regn og hálku fellur rundan um teg.",
"How many particles effects spawn.": "Hvussu nógvar partiklar ávirkanir gýta.",
"Round shadows under mobs and items.": "Rundir skuggar undir mobbum og lutum.",
"Leaves with see-through gaps. Off draws them solid.": "Bløð við gjøgnumskygdum holum. Off dregur teir solid.",
"Sorts water and glass more exactly behind each other.": "Sorterar vatn og glas meira nágreiniliga aftanfyri hvørt annað.",
"Which chunk rebuilds wait for the frame: none, nearby or yours.": "Hvør klumpi umbyggir bíða eftir rammuni: eingin, nærhendis ella tín.",
"Animates water, lava, fire and other moving textures.": "Lívgar vatn, lava, eld og aðrar rørandi tekstur.",
"World pixels per screen pixel. Below 100% is faster on the GPU.": "Heimspixel fyri hvønn skermpixel. Undir 100% er skjótari á GPU.",
"Smooths jagged edges. FXAA: softer. TAA: smoothest, moving things may trail.": "Slættar snøggaðar kantar. FXAA: mjúkari. TAA: slættast, rørandi ting kunnu slóða.",
"Renders the world smaller and rebuilds it with TAA. Faster, softer.": "Ger heimin minni og umbyggir hann við TAA. Skjótari, mjúkari.",
"HDR output on an HDR display: lava, fire and sunlight shine brighter.": "HDR útgangur á HDR skjái: lava, eldur og sólarljós skína ljósari.",
"How frames reach the screen while VSync is off.": "Hvussu rammur røkka skerminum meðan VSync er sløkt.",
"Efficient sleeps until the next frame; Precise spins, like vanilla.": "Effektivur svevur til næstu rammu; Nágreinilig snúningar, sum vanilja.",
"Fills the screen (borderless).": "Fyllir skermin (markleysur).",
"Brightens dark places.": "Lýsir myrk støð.",
"Lowers the frame rate when minimised, or idle too.": "Lækkar rammufrekvensin tá hon er minkað, ella idle eisini.",
"Sways the view while walking.": "Sveiggjar útsýnið, meðan tú gongur.",
"Fades newly loaded chunks in.": "Doyvir nýlastaðar klumpar í.",
"Darkens the screen's edges.": "Myrkir kantarnar á skerminum.",
"Shows when the world saves.": "Vísir tá heimurin bjargar.",
"Shows attack cooldown.": "Vísir álopskøling.",
"60 FPS cap (30 in menus or unfocused), half the workers: less power.": "60 FPS hetta (30 í valmyndum ella ófokuserað), helvtin av arbeiðsfólkunum: minni kraft.",
"Threads that generate, light and mesh chunks. Next start.": "Tráðir sum framleiða, ljós og mesh klumpar. Næsta byrjan.",
"Vulkan, then DirectX 12 (Windows) or OpenGL (Linux) where it fails. Next start.": "Vulkan, síðani DirectX 12 (Windows) ella OpenGL (Linux) har tað miseydnast. Næsta byrjan.",
"Water and glass in one buffer: fewer draw calls, a bit more VRAM. Next start.": "Vatn og glas í einum bufferi: færri draw call, eitt sindur meira VRAM. Næsta byrjan.",
"Mesh data sent to the GPU per frame. Lower is smoother while loading.": "Mesh dátur sendar til GPU pr rammu. Lægri er slættari meðan tú lastar.",
"Frame rate and frame times over the game, with a graph.": "Rammufrekvens og rammutíðir yvir spælið, við grafi.",
"horizon.multiplayer.finding_friend": "At finna heimin hjá tínum vini...",
"horizon.multiplayer.not_signed_in": "Hesin ambætarin er í online-stilling. Rita inn við tínari Microsoft kontu í Horizon launcher fyri at melda teg til.",
"horizon.multiplayer.invalid_address": "Ógyldug ambætaraadressa: %s",
"horizon.multiplayer.connected_friend_direct": "Samband beinleiðis við tín vin...",
"horizon.multiplayer.connected_friend_relay": "Samband við tín vin gjøgnum stafettina...",
"General": "Alment",
"Advanced": "Framkomin",
"Undo": "Angra",
"Search options": "Leitimøguleikar",
"Shortcut": "Snarvegur",
"Distance": "Fjarstøða",
"Display": "Sýn",
"Interface": "Grunnflata",
"Sky and weather": "Himmal og veður",
"Lighting and entities": "Ljós og eindir",
"Textures": "Tekstur",
"Image": "Mynd",
"Chunk loading": "Innlesing av klumpi",
"Culling": "Avlíving",
"Power": "Vald",
"Frames": "Rammur",
"Overlay": "Yvirlag",
"Output": "Útflutningur",
"Brightness": "Ljósstyrki",
"How far you see, the window and the HUD.": "Hvussu langt tú sært, vindeygað og HUD.",
"The sky and weather, lighting, textures and the image.": "Himmalin og veðrið, ljós, tekstur og ímyndin.",
"Chunk loading, culling and power use.": "Klumplasting, avlíving og orkunýtsla.",
"How frames reach the screen, and the performance overlay.": "Hvussu rammur røkka skerminum, og framførslan yvirleggur.",
"Brighter-than-white output on an HDR display.": "Ljósari-enn-hvítur útgangur á einum HDR-skjálvta.",
"Results for \"%s\"": "Úrslit fyri \"%s\"",
"No settings match.": "Ongar innstillingar passa.",
"horizon.title.not_affiliated": "Ikki tilknýtt Mojang ella Microsoft",
"horizon.extras.header.lighting": "Ljós",
"horizon.extras.header.third_person": "Triði persónur",
"horizon.extras.header.particles": "Partiklar",
"horizon.extras.header.inventory": "Goymsla",
"horizon.extras.header.chat": "Prát",
"horizon.extras.header.multiplayer": "Fleirspælari",
"horizon.extras.header.sound": "Ljóð",
"horizon.extras.header.menus": "Valmyndir",
"horizon.extras.fullbright": "Fullljós",
"horizon.extras.dynamic_lights": "Dynamisk ljós",
"horizon.extras.own_name_tag": "Egið navnamerki",
"horizon.extras.hide_block_particles": "Goym blokkbrotspartiklar",
"horizon.extras.auto_reconnect": "Sjálvvirkandi endursamband",
"horizon.extras.ui_sound_reserve": "Halt rásir til brúkaragjøgnumførsljóð",
"horizon.extras.music_page": "Tónleikasíða",
"horizon.extras.mouse_tweaks": "Mús Tweaks stýringar",
"horizon.extras.mouse_tweaks_wheel": "Rulluhjólið flytur lutir",
"horizon.extras.sort": "Sortera knappar",
"horizon.extras.sort_key": "Sortera lykil",
"horizon.extras.no_ai": "Eingin AI",
"horizon.extras.agent_control": "Umboðsmannsstýring",
"horizon.extras.discord_presence": "Discord Rík nærvera",
"horizon.extras.splitscreen": "Býtt skermur",
"horizon.extras.chat_animation": "Slætt prát",
"horizon.extras.chat_indicator": "Boðvísari",
"horizon.extras.wheel_cycles_settings": "Rulluhjólið broytir innstillingar",
"horizon.extras.pack_browser": "Tilfeingispakka kaga",
"horizon.extras.fullbright.tooltip": "Vís blokkar og eindir við fullum ljósi, eisini í myrkum økjum.",
"horizon.extras.dynamic_lights.tooltip": "Kyndlar og aðrar ljóskeldur, sum verða hildnar, sleptar ella í eldi, tendra blokkarnar rundan um seg.",
"horizon.extras.own_name_tag.tooltip": "Vísir títt navn omanfyri høvdið í triðja persóni, sum aðrir spælarar síggja tað. Dult tá ambætarin goymir nøvnini á tínum liði ella tú ert ósjónligur.",
"horizon.extras.hide_block_particles.tooltip": "Goymir ruskpartiklarnar, sum spreingjast úr einum brotnum blokki og sláa seg saman av tí, tú námar. Aðrar partiklar halda seg.",
"horizon.extras.auto_reconnect.tooltip": "Meldar seg aftur til ein ambætara eftir mist samband ella endurbyrjan, ongantíð eftir spark ella bann.",
"horizon.extras.ui_sound_reserve.tooltip": "Ljóð í heiminum lata 16 av teimum 247 ljóðrásunum vera fríar, so ein flóðaldur av teimum (ein stór spreinging) kann ikki tøgna valmyndaklikk.",
"horizon.extras.music_page.tooltip": "Music & Sounds fær eina Music... síðu: spæl eitthvørt innbygt løg nú, ella sløkk eitt, so spælið steðgar við at velja tað. Einki broytist, fyrr enn tú sløkkir eitt lag.",
"horizon.extras.mouse_tweaks.tooltip": "Høgra-drag setur eitt per slot, vinstra-drag savnar tann hilda lutin, Shift + vinstra-drag flytur hvørt slot.",
"horizon.extras.mouse_tweaks_wheel.tooltip": "Niður sendir ein lutur av slitinum til hina síðuna, upp fær ein aftur.",
"horizon.extras.sort.tooltip": "Sorterar goymsluna, kistur, tunnur, ender kistur og shulker kassar í kreativari flipa raðfylgju, letur saman stakkar.",
"horizon.extras.sort_key.tooltip": "Sorterar partin undir markørinum. Trýst, trýst síðani á ein knøtt; Flýggjan loysir tað.",
"horizon.extras.no_ai.tooltip": "Goymir og sløkkir hvønn AI-funktión: agentstýring, horizon-ctl og MCP-ambætarar. Rotarin á sjósetingini eisini.",
"horizon.extras.agent_control.tooltip": "Tín egni AI umboðsmaður kann spæla hetta spælið gjøgnum horizon-ctl ella ein MCP klient, á hesi telduni bert.",
"horizon.extras.discord_presence.tooltip": "Vísir Spæla Horizon á tínum Discord profili meðan Discord koyrir: valmyndir, einspælari ella fleirspælari, víddina og tíðina, sum er spæld. Ongantíð eitt heimsnavn ella adressa á einum ambætara.",
"Calibrate...": "Kalibrera...",
"Calibrate Anyway...": "Kalibrera allíkavæl...",
"Use Automatic Setup": "Brúka sjálvvirkandi uppseting",
"Shortcut: %s": "Snarvegur: %s",
"Next": "Næsta",
"HDR Calibration %s/%s: %s": "HDR-kalibrering %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Setur tey trý niðanfyri fullan skerm, eina royndarmynd í senn.",
"A key that opens this screen from the game. Not bound at first.": "Ein lykil, sum letur upp hendan skermin frá spælinum. Ikki bundin í fyrstuni.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Títt skjálvti segði ikki, hvussu ljóst tað blívur: kalibrera fyri at seta tað upp.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Longu sett upp til tín skjálvta. Kalibrera bert um HDR sær skeivt út.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Setur Toppljósstyrki og Pappírshvítt aftur til uppsetingina á skjánum.",
"Set up for your display. Leave on Auto unless it looks wrong.": "Set upp til tína skjálvta. Lat vera við Auto uttan so at tað sær skeivt út.",
"Lower it until the logo just disappears into the black.": "Lækka tað til búmerkið bara hvørvur í svarta.",
"Raise it until the logo just disappears into the bright square.": "Lyft tað upp, til búmerkið bara hvørvur inn í ljósa torgið.",
"Make the card a white that's comfortable to look at for a long time, in the light you play in.": "Ger kortið til eitt hvítt, sum er behagligt at hyggja at leingi, í ljósinum tú spælir í.",
"horizon.splitscreen.profiles.forget": "Gloym",
"horizon.splitscreen.profiles.create": "Stovna",
"horizon.splitscreen.profiles.you": " - tú",
"horizon.splitscreen.profiles.forget_named": "Gloym %s .",
"horizon.splitscreen.profiles.previous": "Fyrra",
"horizon.splitscreen.profiles.next": "Næsta",
"horizon.splitscreen.profiles.new_button": "Nýggj vangamynd...",
"horizon.splitscreen.profiles.forget_title": "Gloym vangamyndina?",
"horizon.splitscreen.profiles.forget_leaves": "Tað fer úr listanum hjá hesum instansi.",
"horizon.splitscreen.profiles.forget_kept": "Heimsframgongd og karmar eru hildnir.",
"horizon.splitscreen.profiles.new_title": "Nýggj vangamynd",
"horizon.splitscreen.profiles.name_hint": "Navn (3 til 16 bókstavir, siffur, _)",
"horizon.splitscreen.profiles.switch_title": "Skift vangamynd",
"horizon.splitscreen.profiles.offline_note": "Offline profilar hoyra til hesa instans",
"horizon.splitscreen.profiles.move": "Flyt har tú spælir",
"horizon.splitscreen.profiles.move_place": "Flyt har tú spælir: %s .",
"horizon.friends.title": "Spæl við vinum",
"horizon.friends.hint": "Vinir líma kotuna eftir Vinir knøttin á tittulskjálvtanum, í Beinleiðis sambandi ella á Spæla síðuni á sjósetaranum.",
"horizon.friends.copied": "Avritað",
"horizon.friends.copy_code": "Avritskotu",
"horizon.friends.stop_sharing": "Steðga við at deila",
"horizon.friends.none_yet": "Eingir vinir her enn.",
"horizon.friends.here_now": "Her nú: %s",
"horizon.friends.share_intro": "Deil hendan heimin aftanfyri eina boðkotu: hon letur upp fyri LAN, og vinir á internetinum melda seg til við koduni. Eingin servari hjá okkum er við.",
"horizon.friends.start_sharing": "Byrja at deila",
"horizon.friends.troubleshooting": "Trupulleikaloysn...",
"horizon.friends.stopped": "Steðgaði við at deila boðkotuna.",
"horizon.friends.short_code_changed": "Onkur bleiv við at royna eina skeiva stutta kodu, so hon broyttist.",
"horizon.friends.wrong_code": "Onkur royndi eina skeiva kodu.",
"horizon.friends.code_ready": "Tín boðkoda er klár: Spælvalmynd, Spæl við vinum. Kopiera tað og send tað til tínar vinir.",
"horizon.friends.share_failed": "Kundi ikki deila heimin: %s",
"horizon.friends.finding_route": "At finna útav, hvussu vinir kunnu røkka tær...",
"horizon.osk.shift": "Vakt",
"horizon.osk.space": "Rúm",
"horizon.osk.delete": "Strika",
"horizon.osk.type": "Slag",
"horizon.osk.symbols": "Tákn",
"horizon.osk.close": "Lat aftur",
"horizon.extras.chat_animation.tooltip": "Ein nýggj kjaklinja rennur inn frá vinstru, meðan hon doyvir inn, í staðin fyri at síggja heil út.",
"horizon.extras.chat_indicator.tooltip": "Litaða strikan til vinstru á kjaklinju.",
"horizon.extras.wheel_cycles_settings.tooltip": "Rulluhjólið yvir einum knøtti í innstillingunum broytir virðið, eins og í vanilju. Av, hjólið rullar bara listan.",
"horizon.extras.pack_browser.tooltip": "Vel Tilfeingispakkar fær ein Legg til knøtt, sum leitar Modrinth og setur ein pakka upp í tína pakka mappu. Sløkt, spælið ongantíð kontaktar Modrinth.",
"horizon.extras.splitscreen.tooltip": "Í einum heimi kann ein annar stýrari melda seg til sum nýggjur spælari: trýst á A tá tað spyr, vel síðani ein profil og hvar tú skalt spæla.",
"horizon.splitscreen.setup.monitor": "Skjár %s",
"horizon.splitscreen.setup.monitor_unknown": "ein skjár",
"horizon.splitscreen.setup.window": "Vindeyga %s",
"horizon.splitscreen.setup.new_window": "Nýtt vindeyga",
"horizon.splitscreen.setup.fill_monitor": "Fyll út %s",
"horizon.splitscreen.setup.done": "Liðugt",
"horizon.splitscreen.setup.on": "Á",
"horizon.splitscreen.setup.off": "Sløkt",
"horizon.splitscreen.setup.player": "Spælari %s",
"horizon.splitscreen.setup.revert": "Aftur",
"horizon.splitscreen.setup.apply": "Søk",
"horizon.splitscreen.setup.warning.empty_main": "Eingin spælir í Vindeyga 1: tað goymir seg",
"horizon.splitscreen.setup.warning.empty_close": "Eingin spælir í Vindeyga %s: tað letur aftur á Søk",
"horizon.splitscreen.setup.warning.empty_new": "Eingin spælir í Vindeyga %s: tað letur ikki upp",
"horizon.splitscreen.setup.warning.on_main": "Vindeyga %s fyllir skjálvtan hjá Vindeyga 1: tess spælarar spæla í Vindeyga 1",
"horizon.splitscreen.setup.warning.unplugged": "Skjárin hjá Vindeyga %s er ikki tengdur: spælarar hansara spæla í Vindeyga 1",
"horizon.splitscreen.setup.status.off": "Splitskermur er sløktur.",
"horizon.splitscreen.setup.status.enable_hint": "Tendra tað, og trýst síðani á ein knøtt á einum øðrum stýriskipanum í einum heimi.",
"horizon.splitscreen.setup.status.join_hint": "Trýst á A fyri at melda teg til og vel ein vangamynd.",
"horizon.splitscreen.setup.status.idle": "Býtt skermur í tómgongd",
"horizon.splitscreen.setup.status.alone": "Ongin annar lokalur leikari.",
"horizon.splitscreen.setup.status.solo_controls": "Solo stýringar og afturmeldingar hjá stýraranum eru óbroyttar.",
"horizon.splitscreen.setup.window_empty": "Vindeyga %s: tómt",
"horizon.splitscreen.setup.note.select": "Trýst á ein spælara, ella drag hann til eitt vindeyga ella ein skjálvta.",
"horizon.splitscreen.setup.note.apply": "Søk um at spæla soleiðis, Vend aftur fyri at angra.",
"horizon.splitscreen.setup.note.place": "Hvar skal %s spæla?",
"horizon.splitscreen.setup.this_player": "hesin spælarin",
"horizon.splitscreen.setup.switch_disabled": "Splitscreen sløkkir, tá hini eru farin (teirra steðgvalmynd).",
"horizon.splitscreen.setup.window_place": "Vindeyga %s (%s av %s)",
"horizon.splitscreen.setup.names": "Nøvn",
"horizon.splitscreen.setup.identify": "Eyðmerkja",
"horizon.splitscreen.setup.layout.two": "2 deiling: %s",
"horizon.splitscreen.setup.layout.three": "3 deiling: %s",
"horizon.splitscreen.setup.layout.two.1": "Lið um lið",
"horizon.splitscreen.setup.layout.two.2": "Ovast og niðast",
"horizon.splitscreen.setup.layout.three.0": "Eitt breitt omaná",
"horizon.splitscreen.setup.layout.three.1": "Ein høgur til vinstru",
"horizon.splitscreen.setup.layout.three.2": "Lið um lið",
"horizon.splitscreen.setup.help.player": "Trýst ella drag %s fyri at flyta tey.",
"horizon.splitscreen.setup.help.layout_auto": "%s (lið um lið, tá vindeygað er breitt). Trýst fyri at broyta.",
"horizon.splitscreen.setup.help.layout": "%s. Trýst fyri at broyta.",
"horizon.splitscreen.setup.help.switch": "Splitscreen til hesa telduna, tendrað ella sløkt.",
"horizon.splitscreen.setup.help.identify": "Sýningin hjá hvørjum spælara vísir hvørjum vindeyga hann er í.",
"horizon.splitscreen.setup.help.names": "Navnið á hvørjum leikara í horninum av teirra útsýni.",
"horizon.splitscreen.setup.help.revert": "Angra broytingarnar, sum ikki eru galdandi enn.",
"horizon.splitscreen.setup.help.apply": "Spæl soleiðis, sum kortið vísir.",
"horizon.splitscreen.setup.help.unchanged": "Enn er einki broytt.",
"horizon.splitscreen.setup.give_up_pad": "Gev upp Pad",
"horizon.splitscreen.prompt.controller_connected": "Stýriskipan er knýtt at",
"horizon.splitscreen.prompt.controller_disconnected": "Stýriskipanin er slitin",
"horizon.splitscreen.prompt.controller_disconnected.hint": "Set tað í samband aftur, ella trýst á ein knøtt á einum øðrum",
"horizon.splitscreen.prompt.join.one": "%s vil vera við",
"horizon.splitscreen.prompt.join.many": "%s stýrarar vilja melda seg til",
"horizon.splitscreen.prompt.join.yes": "Ver við",
"horizon.splitscreen.prompt.join.as_p1": "Spæl sum P1.",
"horizon.splitscreen.prompt.join.no": "Nei",
"horizon.splitscreen.prompt.lost": "%s: stýriskipanin er slitin",
"horizon.splitscreen.prompt.lost.use_another": "Brúka eitt annað",
"horizon.splitscreen.prompt.lost.leave": "Far",
"horizon.splitscreen.setup.help.give_up_pad": "Tín stýrimaður steðgar við at koyra teg og fær bjóðað at melda seg til (A).",
"horizon.splitscreen.setup.note.held_at": "%s til %s: A sleppur tí her, B leggur tað aftur.",
"horizon.splitscreen.setup.note.held": "%s tikið upp: vinstri/høgri tekur har, A sleppur, B leggur tað aftur.",
"horizon.splitscreen.online_only": "Hesin ambætarin er í online-stilling: offline-profilar kunnu ikki melda seg til hann, hvør spælari hevur brúk fyri síni egnu Minecraft kontu.",
"horizon.splitscreen.own_account": "Hesin ambætarin hevur brúk fyri hvørjum spælara egna Minecraft kontu",
"horizon.splitscreen.local_only": "Aðrir spælarar kunnu bert melda seg til hendan ambætaran við egnari Minecraft konto: offline profilar spæla í tínum heimi, á LAN ella á einum ambætara á tínum lokala neti.",
"Hides models behind opaque blocks. Async checks off-thread; entities tick.": "Goymir modellir aftan fyri ógjøgnumskygdar blokkar. Async kannar uttan fyri tráðin; eindir merkja.",
"Block Entities": "Blokk eindir",
"Shulker Boxes": "Shulker kassar",
"Reuses settled boxes; lid movement stays animated.": "Endurnýtir avgjørdar kassar; lok rørslan heldur seg lívgaða.",
"Signs": "Tekin",
"Reuses sign text until text, light, or font changes.": "Endurnýtir skeltitekst, til tekstur, ljós ella skriftslag broytist.",
"Decorated Pots": "Prýddir grýtur",
"Reuses still pots; break wobble and sherd changes redraw.": "Endurnýtir enn pottar; bróta vagga og skerv broytingar umtekna.",
"Bells": "Klokkur",
"Reuses still bells; ringing stays animated.": "Endurnýtir enn klokkur; ringja heldur seg lívgandi.",
"Chests": "Kistur",
"Reuses closed chests; moving lids stay animated.": "Endurnýtir lukkaðar kistur; rørandi lok halda seg lívgað.",
"Banners": "Merki",
"Reuses poles; flags and pattern layers still update.": "Endurnýtir stong; flagg og mynsturløg dagførast enn.",
"Copper Golem Statues": "Kopar Golem standmyndir",
"Reuses statue geometry until its block state changes.": "Endurnýtir standmyndageometri til blokktilstandurin broytist.",
"Leaf Culling": "Blaðsláttur",
"Every leaf face. Vanilla's fancy leaves.": "Hvørt blað andlit. Snøggu bløðini hjá vanilju.",
"Distant Plant Dither": "Fjarplantu Dither",
"Keeps fine plants in high-res packs from fading far away.": "Heldur fínum plantum í háupploysn pakkum frá at doyggja langt burtur.",
"Screenshot Quality": "Skjálvtagóðska",
"F2 screenshots at this many times the game's resolution. Higher takes longer.": "F2 skermmyndir við hesi nógvar ferðir spælsins upploysn. Hægri tekur longri tíð.",
"Frame Generation": "Rammuættarlið",
"Shows a made-up frame between each two real ones: smoother, a little more lag.": "Vísir eina gjørda rammu millum hvønn tveir veruligar: slættari, eitt sindur meira lag.",
"Shadow Cache": "Skuggagoymsla",
"Reuses the pack shadow map when sun, view and world match. Off by default.": "Endurnýtir pakka skuggakortið, tá sól, útsýni og heimur passa saman. Sløkt sum standard.",
"Find BlueMap": "Finn Blákort",
"On servers, finds their BlueMap web map for far terrain. Next join.": "Á ambætarum, finnur teirra BlueMap vevkort fyri fjart lendi. Næsta melda teg til.",
"BlueMap Radius": "Blákortradius",
"How far around you BlueMap tiles are downloaded. Next join.": "Hvussu langt rundan um teg eru BlueMap flísar heintaðar. Næsta melda teg til.",
"Synchronous": "Samsvarandi",
"Asynchronous": "Asynkront",
"Solid": "Fast",
"Solid Aggressive": "Solid ágangandi",
"Hollow": "Holur",
"Leaves fully inside a canopy draw solid: fewer see-through layers.": "Bløðini fult inni í einum høli draga fast: færri gjøgnumskygd løg.",
"Leaves with one open side draw solid too: faster, chunkier edges.": "Bløð við einari opnari síðu draga eisini fast: skjótari, klumpigari kantar.",
"Drops faces between leaves: faster, but gaps look through.": "Dropar andlit millum bløð: skjótari, men hol hyggja ígjøgnum.",
"Hotbar: Previous Slot": "Hotbar: Fyrri pláss",
"Hotbar: Next Slot": "Heitbarr: Næsta pláss",
"horizon.agent.connected.one": "Samband: 1 umboðssamband",
"horizon.agent.connected.many": "Samband: %s umboðssamband",
"horizon.agent.connected.one_ago": "Samband: 1 agentsamband, seinasta skipan %s s síðani",
"horizon.agent.connected.many_ago": "Samband: %s agentsamband, seinasta skipan fyri %s s síðani",
"horizon.agent.waiting": "Bíða eftir umboði.",
"horizon.agent.off": "Off: umboðsmenn kunnu ikki røkka hesum dystinum.",
"horizon.agent.session_opened": "Opnað fyri hesa setuna av skránni sum byrjaði dystin.",
"horizon.agent.socket": "Socket: %s (kopiera smálutirnar fyri alla leiðina)",
"horizon.agent.local_only": "Einans lokalt: umboðsmenn á hesi telduni, ongantíð netið.",
"%s nits": "%s nitur",
"Auto (%s nits)": "Sjálvvirkandi (%s nits)",
"%.2f nits": "%.2f nitur",
"Hides models behind opaque blocks. Entities update. Off with custom packs.": "Goymir modellir aftan fyri ógjøgnumskygdar blokkar. Eindir dagførast. Av við sersniðgivnum pakkum.",
"horizon.extras.repro_keys": "Upptøka av snarvegum",
"horizon.extras.repro_keys.tooltip": "F3+R byrjar ella steðgar eina repro upptøku og F3+E skrivar sína bingju. Sløkt sum standard: ein upptøka avritar heimin.",
"horizon.agent.record_session": "Byrja upptøkuløtuna",
"horizon.agent.record_stop": "Steðga upptøku",
"horizon.agent.export_repro": "Útflyt Repro bingju",
"horizon.repro.started": "Upptøkan byrjað; F3+R steðgar tí og skrivar bingjuna.",
"horizon.repro.stopped": "Upptøkan steðgað: %s",
"horizon.repro.bundle": "Repro bingja: %s",
"horizon.repro.error": "Endurtøka: %s",
"horizon.repro.no_recording": "Eingin upptøka er at útflyta enn.",
"horizon.repro.replay_started": "Endurtøka byrjað: %s",
"horizon.pack.addInfo": "Legg tilfeingispakka frá Modrinth",
"horizon.pack.addWorking": "Spyrja Modrinth...",
"horizon.pack.addNone": "Eingin tilfeingispakki funnin",
"horizon.pack.addTitle": "Legg ein tilfeingispakka til",
"horizon.pack.install": "Set upp",
"horizon.pack.installing": "Seta upp",
"horizon.pack.installed": "Sett upp",
"horizon.pack.search": "Leita",
"horizon.pack.searchHint": "Leita Modrinth...",
"horizon.pack.filters": "Filtur",
"horizon.pack.filter.realistic": "Realistiskt",
"horizon.pack.filter.simplistic": "Einfalt",
"horizon.pack.filter.vanillaLike": "Vaniljulíknandi",
"horizon.pack.addFailed": "Pakkin kundi ikki leggjast afturat: %s .",
"horizon.extras.header.cosmetics": "Snyrtivøra",
"horizon.extras.cosmetics_mine": "Mítt kosmetikk",
"horizon.extras.cosmetics_mine.tooltip": "Dult: hvør Horizon spælari sær teg uttan títt kosmetikk, tú eisini. Sama rotari sum Klædnaskápið hjá sjósetaranum.",
"horizon.extras.cosmetics_others": "Kosmetikkur hjá øðrum spælarum",
"horizon.extras.cosmetics_others.tooltip": "Hvat aðrir Horizon spælarar eru í: alt, bara teirra navnamerkjastílar, ella einki. Dult spyr spælið ongantíð, hvat tey eru í.",
"horizon.extras.cosmetics_auras": "Aura og slóðir hjá øðrum",
"horizon.extras.cosmetics_auras.tooltip": "Auras og slóðir aðrir spælarar bera. Sløkt sum standard.",
"horizon.extras.cosmetics.shown": "Víst",
"horizon.extras.cosmetics.hidden": "Dult",
"horizon.extras.cosmetics.name_tags": "Einans navnamerki"
}
Loaded 100 of 619 files, more files were not shown because too many files have changed in this diff. Show more