mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 02:56:17 -10:00
Merge remote-tracking branch 'origin/pvp-safe/wire-stream-harness' into pvp/wire-stream-legacy
This commit is contained in:
commit
a35ea8b72a
359 files changed
+21579
-1793
No files matched your search
@@ -35,6 +35,7 @@ Feature → files → harness for the most-changed areas: [docs/architecture.md]
|
||||
- **No hardcoded user-facing strings.** Keep UI translation-ready from the start. In the launcher (`launcher/`), wrap visible text with `tr("...")` or `trf("... {arg} ...", &[("arg", &val)])` from `crate::i18n` instead of raw string literals or `format!`. In the game, use translation keys (`Text::tr`, `lang::get`), never hardcoded English strings.
|
||||
- **Claim before you start**: comment `/claim` on the issue (no issue yet: open one, then claim it). An assigned
|
||||
issue is taken: pick another. `/unclaim` when you stop; a claim with no open PR linking it is released after 5 days.
|
||||
- **Controllers reach what registers.** Draw controls with a registering helper (`ui::button`, `settings_shell::foot_button`); `graphics_style::button` and `screen_draw::button` only paint, and registering one part of a screen ends its crosshair, so register all of it.
|
||||
|
||||
## Crates
|
||||
|
||||
|
||||
Generated
+1
@@ -1413,6 +1413,7 @@ dependencies = [
|
||||
"horizon-client-render",
|
||||
"horizon-command",
|
||||
"horizon-core",
|
||||
"horizon-cosmetics",
|
||||
"horizon-dualsense",
|
||||
"horizon-friends",
|
||||
"horizon-hdr",
|
||||
|
||||
@@ -314,6 +314,8 @@ pub const ACTIONS: &[(&str, Option<Button>)] = &[
|
||||
("key.screenshot", None),
|
||||
("key.toggleGui", None),
|
||||
("key.smoothCamera", None),
|
||||
// Horizon's emote wheel: no button is free by default; the stick's cursor picks.
|
||||
("key.emote", None),
|
||||
];
|
||||
|
||||
/// How a bindable action reaches the game: one of its own handlers, shared
|
||||
|
||||
@@ -23,6 +23,8 @@ pub const MAPPINGS: &[(&str, &str, &str)] = &[
|
||||
("key.pickItem", "key.mouse.middle", "gameplay"),
|
||||
("key.command", "key.keyboard.slash", "multiplayer"),
|
||||
("key.friends", "key.keyboard.o", "multiplayer"),
|
||||
// Horizon's emote wheel (docs/cosmetics.md), on a key vanilla leaves free.
|
||||
("key.emote", "key.keyboard.b", "multiplayer"),
|
||||
("key.socialInteractions", "key.keyboard.p", "multiplayer"),
|
||||
("key.toggleGui", "key.keyboard.f1", "misc"),
|
||||
(
|
||||
|
||||
@@ -18,6 +18,8 @@ horizon-client-core = { path = "../horizon-client-core" }
|
||||
horizon-client-input = { path = "../horizon-client-input" }
|
||||
horizon-client-render = { path = "../horizon-client-render" }
|
||||
horizon-command = { path = "../horizon-command" }
|
||||
# Emotes pose the player model (`Mob::emote`, `mobs::anim::setup`).
|
||||
horizon-cosmetics = { path = "../horizon-cosmetics" }
|
||||
horizon-items = { path = "../horizon-items" }
|
||||
horizon-core = { path = "../horizon-core" }
|
||||
# `horizon-ctl mcp`: the Model Context Protocol, shared with the launcher and the server.
|
||||
|
||||
@@ -164,10 +164,10 @@ pub struct Extras {
|
||||
/// Cosmetics (`horizon_cosmetics`, off unless enabled), the lines `horizon_cosmetics::prefs`
|
||||
/// shares with the launcher's Wardrobe: Show my cosmetics (the account's, sent to the service;
|
||||
/// on by default), how much of other players' to show (no line until chosen:
|
||||
/// `effective_others`), and their auras and trails (off by default).
|
||||
/// `effective_others`), and whether their trails and auras are hidden (off by default).
|
||||
pub cosmetics_mine: bool,
|
||||
pub cosmetics_others: Option<horizon_cosmetics::prefs::Others>,
|
||||
pub cosmetics_auras: bool,
|
||||
pub cosmetics_hide_fx: bool,
|
||||
/// My cosmetics in single player too (`cosmeticsSolo`, off by default): the window's own server
|
||||
/// names the player by their offline profile, which the service doesn't know, so their own
|
||||
/// cosmetics and name tag style show only with this on.
|
||||
@@ -235,7 +235,7 @@ impl Default for Extras {
|
||||
music_page: false,
|
||||
cosmetics_mine: true,
|
||||
cosmetics_others: None,
|
||||
cosmetics_auras: false,
|
||||
cosmetics_hide_fx: false,
|
||||
cosmetics_solo: false,
|
||||
cosmetics_owner: false,
|
||||
horizon_marks: true,
|
||||
@@ -349,7 +349,7 @@ impl Extras {
|
||||
e.cosmetics_others =
|
||||
horizon_cosmetics::prefs::Others::parse(value).or(e.cosmetics_others)
|
||||
}
|
||||
"cosmeticsAuras" => e.cosmetics_auras = flag(e.cosmetics_auras),
|
||||
"cosmeticsHideFx" => e.cosmetics_hide_fx = flag(e.cosmetics_hide_fx),
|
||||
"cosmeticsSolo" => e.cosmetics_solo = flag(e.cosmetics_solo),
|
||||
horizon_cosmetics::prefs::MARKS => e.horizon_marks = flag(e.horizon_marks),
|
||||
horizon_cosmetics::prefs::LISTED => e.horizon_listed = flag(e.horizon_listed),
|
||||
@@ -363,7 +363,7 @@ impl Extras {
|
||||
}
|
||||
pub fn serialize(&self) -> String {
|
||||
let mut text = format!(
|
||||
"packBrowser:{}\npregen:{}\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:{}\n{}cosmeticsAuras:{}\ncosmeticsSolo:{}\n{}:{}\n{}:{}\n{}:{}\n",
|
||||
"packBrowser:{}\npregen:{}\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:{}\n{}cosmeticsHideFx:{}\ncosmeticsSolo:{}\n{}:{}\n{}:{}\n{}:{}\n",
|
||||
bool_text(self.pack_browser),
|
||||
bool_text(self.pregen),
|
||||
bool_text(self.fullbright),
|
||||
@@ -417,7 +417,7 @@ impl Extras {
|
||||
// Written once the player chose.
|
||||
self.cosmetics_others
|
||||
.map_or(String::new(), |o| format!("cosmeticsOthers:{}\n", o.name())),
|
||||
bool_text(self.cosmetics_auras),
|
||||
bool_text(self.cosmetics_hide_fx),
|
||||
bool_text(self.cosmetics_solo),
|
||||
horizon_cosmetics::prefs::MARKS,
|
||||
bool_text(self.horizon_marks),
|
||||
@@ -598,7 +598,7 @@ pub enum Item {
|
||||
/// Shown only while cosmetics are enabled (`horizon_cosmetics::enabled`).
|
||||
CosmeticsMine,
|
||||
CosmeticsOthers,
|
||||
CosmeticsAuras,
|
||||
CosmeticsHideFx,
|
||||
CosmeticsSolo,
|
||||
HorizonMarks,
|
||||
HorizonMe,
|
||||
@@ -638,7 +638,7 @@ pub fn rows() -> Vec<Result<Item, &'static str>> {
|
||||
Ok(CosmeticsMine),
|
||||
Ok(CosmeticsSolo),
|
||||
Ok(CosmeticsOthers),
|
||||
Ok(CosmeticsAuras),
|
||||
Ok(CosmeticsHideFx),
|
||||
Ok(HorizonMarks),
|
||||
Ok(HorizonMe),
|
||||
]);
|
||||
@@ -672,7 +672,7 @@ impl Item {
|
||||
Item::Pregen => "horizon.extras.pregen",
|
||||
Item::CosmeticsMine => "horizon.extras.cosmetics_mine",
|
||||
Item::CosmeticsOthers => "horizon.extras.cosmetics_others",
|
||||
Item::CosmeticsAuras => "horizon.extras.cosmetics_auras",
|
||||
Item::CosmeticsHideFx => "horizon.extras.cosmetics_hide_fx",
|
||||
Item::CosmeticsSolo => "horizon.extras.cosmetics_solo",
|
||||
Item::HorizonMarks => "horizon.extras.horizon_marks",
|
||||
Item::HorizonMe => "horizon.extras.horizon_me",
|
||||
@@ -705,7 +705,7 @@ impl Item {
|
||||
Item::Pregen => "horizon.extras.pregen.tooltip",
|
||||
Item::CosmeticsMine => "horizon.extras.cosmetics_mine.tooltip",
|
||||
Item::CosmeticsOthers => "horizon.extras.cosmetics_others.tooltip",
|
||||
Item::CosmeticsAuras => "horizon.extras.cosmetics_auras.tooltip",
|
||||
Item::CosmeticsHideFx => "horizon.extras.cosmetics_hide_fx.tooltip",
|
||||
Item::CosmeticsSolo => "horizon.extras.cosmetics_solo.tooltip",
|
||||
Item::HorizonMarks => "horizon.extras.horizon_marks.tooltip",
|
||||
Item::HorizonMe => "horizon.extras.horizon_me.tooltip",
|
||||
@@ -747,7 +747,7 @@ impl Item {
|
||||
Item::Pregen => "Chunk pregeneration",
|
||||
Item::CosmeticsMine => "My cosmetics",
|
||||
Item::CosmeticsOthers => "Other players' cosmetics",
|
||||
Item::CosmeticsAuras => "Others' auras and trails",
|
||||
Item::CosmeticsHideFx => "Hide other players' trails and auras",
|
||||
Item::CosmeticsSolo => "In single player",
|
||||
Item::HorizonMarks => "Horizon logos",
|
||||
Item::HorizonMe => "Show me as Horizon",
|
||||
@@ -766,7 +766,7 @@ impl Item {
|
||||
Fullbright | DynamicLights | OwnNameTag | HideBlockParticles | MouseTweaks | Sort
|
||||
| AutoReconnect | NoAi | AgentControl | ReproKeys | DiscordPresence | Splitscreen
|
||||
| ChatAnimation | WheelCyclesSettings | MusicPage | PackBrowser | CosmeticsMine
|
||||
| CosmeticsOthers | CosmeticsAuras | CosmeticsSolo | HorizonMarks | HorizonMe
|
||||
| CosmeticsOthers | CosmeticsHideFx | CosmeticsSolo | HorizonMarks | HorizonMe
|
||||
| Pregen => {
|
||||
return None;
|
||||
}
|
||||
@@ -834,7 +834,9 @@ impl Item {
|
||||
Item::CosmeticsOthers => {
|
||||
"What other Horizon players wear: everything, only their name tag styles, or nothing. Hidden unless you own cosmetics, until you choose. Hidden, the game never asks what they wear."
|
||||
}
|
||||
Item::CosmeticsAuras => "Auras and trails other players wear. Off by default.",
|
||||
Item::CosmeticsHideFx => {
|
||||
"Other players' trails and auras stay hidden; their other cosmetics still show. Off by default."
|
||||
}
|
||||
Item::CosmeticsSolo => {
|
||||
"Show your cosmetics and your name tag's badge in your single-player worlds too (your name tag with Own name tag). Off: only on servers."
|
||||
}
|
||||
@@ -872,7 +874,7 @@ impl Item {
|
||||
| Item::PackBrowser => Some(Cost::None),
|
||||
Item::CosmeticsMine
|
||||
| Item::CosmeticsOthers
|
||||
| Item::CosmeticsAuras
|
||||
| Item::CosmeticsHideFx
|
||||
| Item::CosmeticsSolo
|
||||
| Item::HorizonMarks
|
||||
| Item::HorizonMe => Some(Cost::Low),
|
||||
@@ -902,7 +904,7 @@ impl Item {
|
||||
Item::WheelCyclesSettings => e.wheel_cycles_settings,
|
||||
Item::PackBrowser => e.pack_browser,
|
||||
Item::Pregen => e.pregen,
|
||||
Item::CosmeticsAuras => e.cosmetics_auras,
|
||||
Item::CosmeticsHideFx => e.cosmetics_hide_fx,
|
||||
Item::CosmeticsSolo => e.cosmetics_solo,
|
||||
Item::HorizonMarks => e.horizon_marks,
|
||||
Item::HorizonMe => e.horizon_listed,
|
||||
@@ -967,7 +969,7 @@ impl Item {
|
||||
let o = e.effective_others();
|
||||
e.cosmetics_others = Some(if back { o.next().next() } else { o.next() });
|
||||
}
|
||||
Item::CosmeticsAuras => e.cosmetics_auras = !e.cosmetics_auras,
|
||||
Item::CosmeticsHideFx => e.cosmetics_hide_fx = !e.cosmetics_hide_fx,
|
||||
Item::CosmeticsSolo => e.cosmetics_solo = !e.cosmetics_solo,
|
||||
Item::HorizonMarks => e.horizon_marks = !e.horizon_marks,
|
||||
Item::HorizonMe => e.horizon_listed = !e.horizon_listed,
|
||||
@@ -996,7 +998,7 @@ mod tests {
|
||||
..Extras::default()
|
||||
};
|
||||
assert_eq!(
|
||||
(e.effective_others(), e.cosmetics_auras, e.cosmetics_mine),
|
||||
(e.effective_others(), e.cosmetics_hide_fx, e.cosmetics_mine),
|
||||
(Others::Shown, false, true)
|
||||
);
|
||||
Item::CosmeticsOthers.cycle(&mut e, false);
|
||||
|
||||
@@ -12227,7 +12227,8 @@ mod tests {
|
||||
}
|
||||
}
|
||||
/// Every WGSL shader file translates to HLSL as wgpu's DirectX 12 backend
|
||||
/// writes it for FXC (shader model 5.1), entry point by entry point.
|
||||
/// writes it for FXC (shader model 5.1), entry point by entry point, and
|
||||
/// every shader the OpenGL backend runs also translates to GLSL.
|
||||
#[test]
|
||||
fn shaders_translate_to_hlsl_for_directx_12_and_glsl_for_opengl() {
|
||||
fn wgsl_files(dir: &std::path::Path, out: &mut Vec<std::path::PathBuf>) {
|
||||
@@ -12277,6 +12278,14 @@ mod tests {
|
||||
}
|
||||
}
|
||||
let mut glsl_checked = 0;
|
||||
// Shaders the OpenGL backend runs must translate: skipping the ones
|
||||
// that fail hides exactly the bug where a shader GL needs cannot
|
||||
// compile (#9448).
|
||||
let mut glsl_failed = Vec::new();
|
||||
let mut translated = std::collections::HashSet::new();
|
||||
// naga's GLSL writer gives an image one sampler, and the HUD's glint
|
||||
// samples one image through two; one failed entry does not hide the rest.
|
||||
let known_unsupported = [("hud.wgsl", "fs_cit_glint")];
|
||||
for (path, text) in sources {
|
||||
// entity.wgsl comes after the overlay's constants (entity_shader).
|
||||
let module = naga::front::wgsl::parse_str(&text)
|
||||
@@ -12377,11 +12386,18 @@ mod tests {
|
||||
Default::default(),
|
||||
)
|
||||
.and_then(|mut w| w.write());
|
||||
// Some shaders use features GLSL cannot express (depth textureLoad): GL does
|
||||
// not run them. The ones it does run must translate, checked below.
|
||||
if glsl_result.is_err() {
|
||||
let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
|
||||
if let Err(e) = glsl_result {
|
||||
if !known_unsupported.contains(&(name, entry.name.as_str())) {
|
||||
glsl_failed.push(format!(
|
||||
"{} {} (GLSL {version}): {e}",
|
||||
path.display(),
|
||||
entry.name
|
||||
));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
translated.insert((name.to_owned(), entry.name.clone()));
|
||||
glsl_checked += 1;
|
||||
assert!(
|
||||
!glsl.contains("ARB_arrays_of_arrays"),
|
||||
@@ -12392,6 +12408,19 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
assert!(
|
||||
glsl_failed.is_empty(),
|
||||
"shaders the OpenGL backend runs must translate:\n{}",
|
||||
glsl_failed.join("\n")
|
||||
);
|
||||
// These read the world's depth through `textureLoad`; before #9448 their
|
||||
// depth binding could not translate, so TAA and the blit never came up.
|
||||
for (file, entry) in [("world_post.wgsl", "fs_taa"), ("pack_post.wgsl", "fs_blit")] {
|
||||
assert!(
|
||||
translated.contains(&(file.to_owned(), entry.to_owned())),
|
||||
"{file} {entry} must translate to GLSL"
|
||||
);
|
||||
}
|
||||
assert!(glsl_checked >= 10, "{glsl_checked} GLSL translations");
|
||||
}
|
||||
#[test]
|
||||
|
||||
@@ -287,7 +287,7 @@ impl Cull {
|
||||
0,
|
||||
fragment,
|
||||
wgpu::BindingType::Texture {
|
||||
sample_type: wgpu::TextureSampleType::Depth,
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: false },
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
multisampled: false,
|
||||
},
|
||||
|
||||
@@ -114,8 +114,10 @@ fn see(t: u32) {
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 1's depth, where the test runs in the shader (gfx/cull.rs).
|
||||
@group(1) @binding(0) var depth: texture_depth_2d;
|
||||
// Phase 1's depth, where the test runs in the shader (gfx/cull.rs). A depth
|
||||
// view bound as an unfilterable float: naga's GLSL backend cannot `textureLoad`
|
||||
// a depth texture, and the texel is the same value.
|
||||
@group(1) @binding(0) var depth: texture_2d<f32>;
|
||||
|
||||
// The world pass's reversed depth test, greater or equal passing, made here:
|
||||
// where it cannot be forced before a shader that writes storage, a fragment
|
||||
@@ -123,7 +125,7 @@ fn see(t: u32) {
|
||||
// runs only for the fragments that pass it.
|
||||
@fragment
|
||||
fn fs_box(in: Box) {
|
||||
if in.position.z >= textureLoad(depth, vec2<u32>(in.position.xy), 0) {
|
||||
if in.position.z >= textureLoad(depth, vec2<u32>(in.position.xy), 0).r {
|
||||
see(in.test);
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,9 @@ struct Params {
|
||||
|
||||
@group(0) @binding(0) var<uniform> params: Params;
|
||||
@group(0) @binding(1) var linear_clamp: sampler;
|
||||
@group(0) @binding(2) var input_depth: texture_depth_2d;
|
||||
// A depth view bound as unfilterable float: naga's GLSL backend cannot
|
||||
// `textureLoad` a depth texture, and the texel is the same value.
|
||||
@group(0) @binding(2) var input_depth: texture_2d<f32>;
|
||||
@group(0) @binding(3) var current_color: texture_2d<f32>;
|
||||
@group(0) @binding(4) var previous_color: texture_2d<f32>;
|
||||
@group(0) @binding(5) var dilated_depth_out: texture_storage_2d<r32float, write>;
|
||||
@@ -333,7 +335,7 @@ fn load_input_depth(p: vec2<i32>) -> f32 {
|
||||
if !is_on_screen(p, params.render_size) {
|
||||
return 0.0;
|
||||
}
|
||||
return textureLoad(input_depth, p, 0);
|
||||
return textureLoad(input_depth, p, 0).r;
|
||||
}
|
||||
|
||||
@compute @workgroup_size(8, 8)
|
||||
|
||||
@@ -12,7 +12,9 @@ struct Params {
|
||||
};
|
||||
|
||||
@group(0) @binding(0) var<uniform> params: Params;
|
||||
@group(0) @binding(1) var depth: texture_depth_2d;
|
||||
// A depth view bound as unfilterable float: naga's GLSL backend cannot
|
||||
// `textureLoad` a depth texture, and the texel is the same value.
|
||||
@group(0) @binding(1) var depth: texture_2d<f32>;
|
||||
@group(0) @binding(2) var atlas: texture_2d<f32>;
|
||||
@group(0) @binding(3) var atlas_sampler: sampler;
|
||||
|
||||
@@ -38,7 +40,7 @@ struct Out {
|
||||
|
||||
// xy: the motion vector in UV (to where the pixel was last frame, FSR's convention), z: 1.
|
||||
@fragment fn fs_motion(in: Out) -> @location(0) vec4<f32> {
|
||||
let stored = textureLoad(depth, vec2<i32>(in.clip.xy), 0);
|
||||
let stored = textureLoad(depth, vec2<i32>(in.clip.xy), 0).r;
|
||||
let tolerance = stored * 1e-3 + 1e-7;
|
||||
// Not what the world shows here: a transparent texel, or behind something.
|
||||
if in.translucent != 0u {
|
||||
|
||||
@@ -1172,6 +1172,14 @@ pub fn frame_inputs(
|
||||
let k = super::native_shadow::distortion(s.distance, s.resolution);
|
||||
2.0 * s.distance / s.resolution.max(1) as f32 * (1.0 - k)
|
||||
}),
|
||||
// Shader API 0.2's display fields, which 0.1 packs don't declare: SDR's values, also
|
||||
// with HDR on, until HDR output is served for 0.2 packs.
|
||||
hdr: 0,
|
||||
headroom: 1.0,
|
||||
paper_white: 80.0,
|
||||
peak: 80.0,
|
||||
black_level: 0.0,
|
||||
padding: [0; 3],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,14 +16,16 @@ const MAPPED: u8 = 3;
|
||||
const ROW: u32 = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
|
||||
|
||||
const SHADER: &str = "
|
||||
@group(0) @binding(0) var depth: texture_depth_2d;
|
||||
// A depth view bound as unfilterable float: naga's GLSL backend cannot
|
||||
// `textureLoad` a depth texture, and the texel is the same value.
|
||||
@group(0) @binding(0) var depth: texture_2d<f32>;
|
||||
@vertex fn vs(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> {
|
||||
let p = vec2<f32>(f32((i << 1u) & 2u), f32(i & 2u));
|
||||
return vec4<f32>(p * 2.0 - 1.0, 0.0, 1.0);
|
||||
}
|
||||
@fragment fn fs() -> @location(0) vec4<f32> {
|
||||
let centre = vec2<i32>(textureDimensions(depth) / 2u);
|
||||
return vec4<f32>(textureLoad(depth, centre, 0), 0.0, 0.0, 1.0);
|
||||
return vec4<f32>(textureLoad(depth, centre, 0).r, 0.0, 0.0, 1.0);
|
||||
}
|
||||
";
|
||||
|
||||
@@ -64,7 +66,7 @@ impl CenterDepth {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
sample_type: wgpu::TextureSampleType::Depth,
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: false },
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
multisampled: false,
|
||||
},
|
||||
|
||||
@@ -433,6 +433,14 @@ struct Mips {
|
||||
levels: Vec<(wgpu::TextureView, wgpu::BindGroup)>,
|
||||
}
|
||||
|
||||
/// The picture a pack's `final` draws at the world's size while the window is another: it is
|
||||
/// copied onto the window once, after the pack's passes, as the game's own post pass does
|
||||
/// without a pack.
|
||||
struct FinalTarget {
|
||||
size: [u32; 2],
|
||||
view: wgpu::TextureView,
|
||||
}
|
||||
|
||||
struct Targets {
|
||||
size: [u32; 2],
|
||||
/// A and B of each buffer used.
|
||||
@@ -664,6 +672,12 @@ pub struct PackPost {
|
||||
blit_group: Option<([wgpu::TextureView; 2], wgpu::BindGroup)>,
|
||||
/// Shows colortex0 when the pack has no `final` (OQ-5).
|
||||
copy: (wgpu::RenderPipeline, wgpu::BindGroupLayout),
|
||||
/// Copies the picture `final` drew at the world's size onto the window: no flip, it is
|
||||
/// already in the screen's rows.
|
||||
upscale: (wgpu::RenderPipeline, wgpu::BindGroupLayout),
|
||||
/// The picture `final` draws while the world's size is not the window's.
|
||||
final_target: Option<FinalTarget>,
|
||||
screen: wgpu::TextureFormat,
|
||||
uniforms: FrameUniforms,
|
||||
}
|
||||
|
||||
@@ -2724,7 +2738,7 @@ impl PackPost {
|
||||
},
|
||||
&[
|
||||
texture_entry(0, float),
|
||||
texture_entry(1, wgpu::TextureSampleType::Depth),
|
||||
texture_entry(1, wgpu::TextureSampleType::Float { filterable: false }),
|
||||
],
|
||||
&blit_targets,
|
||||
// GL's window depth, written for depthtex0 and the terrain's depth test.
|
||||
@@ -2751,6 +2765,20 @@ impl PackPost {
|
||||
&[target(screen)],
|
||||
None,
|
||||
);
|
||||
let upscale = helper(
|
||||
device,
|
||||
"vs_screen",
|
||||
"fs_copy",
|
||||
&[
|
||||
texture_entry(0, float),
|
||||
entry(
|
||||
2,
|
||||
wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||
),
|
||||
],
|
||||
&[target(screen)],
|
||||
None,
|
||||
);
|
||||
let filterable = |b: usize| {
|
||||
formats[b].sample_type(None, Some(device.features()))
|
||||
== Some(wgpu::TextureSampleType::Float { filterable: true })
|
||||
@@ -2952,6 +2980,9 @@ impl PackPost {
|
||||
}),
|
||||
blit,
|
||||
copy,
|
||||
upscale,
|
||||
final_target: None,
|
||||
screen,
|
||||
uniforms: {
|
||||
let mut u = FrameUniforms::default();
|
||||
u.atlas = world.atlas.map_or([0; 2], |a| a.1);
|
||||
@@ -4484,6 +4515,35 @@ impl PackPost {
|
||||
terrain: &mut TerrainDraw<'_>,
|
||||
) {
|
||||
self.resize(device, size);
|
||||
// A resolution scale leaves the picture smaller (or larger) than the window: the pack's
|
||||
// `final` draws it at the picture's size, and one copy takes it to the window (#9291).
|
||||
let final_view = if size != window && self.passes.iter().any(|p| p.group == Group::Final) {
|
||||
if self.final_target.as_ref().is_none_or(|t| t.size != size) {
|
||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("Shader pack final"),
|
||||
size: wgpu::Extent3d {
|
||||
width: size[0],
|
||||
height: size[1],
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: self.screen,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
|
||||
| wgpu::TextureUsages::TEXTURE_BINDING,
|
||||
view_formats: &[],
|
||||
});
|
||||
self.final_target = Some(FinalTarget {
|
||||
size,
|
||||
view: texture.create_view(&Default::default()),
|
||||
});
|
||||
}
|
||||
self.final_target.as_ref().map(|t| t.view.clone())
|
||||
} else {
|
||||
self.final_target = None;
|
||||
None
|
||||
};
|
||||
if let Some((layout, ids, rects)) = &self.block_ids {
|
||||
// A page that grew has a new buffer.
|
||||
self.page_groups
|
||||
@@ -4788,8 +4848,8 @@ impl PackPost {
|
||||
self.mips_valid |= stale;
|
||||
let final_pass = pass.group == Group::Final;
|
||||
// A pass writing buffers of a size of their own draws at that size (they share it).
|
||||
// `final` draws the picture at the world's size; a copy takes it to the window.
|
||||
let out = match pass.writes.first() {
|
||||
_ if final_pass => window,
|
||||
Some(&(_, b)) => self.targets.as_ref().map_or(size, |t| t.sizes[b]),
|
||||
None => size,
|
||||
};
|
||||
@@ -4812,7 +4872,7 @@ impl PackPost {
|
||||
overwrite
|
||||
};
|
||||
if final_pass {
|
||||
attachments.push(attach(screen, overwrite));
|
||||
attachments.push(attach(final_view.as_ref().unwrap_or(screen), overwrite));
|
||||
shown = true;
|
||||
} else if pass.sink {
|
||||
attachments.push(attach(&targets.sink, ops));
|
||||
@@ -4895,6 +4955,31 @@ impl PackPost {
|
||||
rp.set_pipeline(&self.copy.0);
|
||||
rp.set_bind_group(0, &group, &[]);
|
||||
rp.draw(0..3, 0..1);
|
||||
} else if let Some(picture) = &final_view {
|
||||
// The picture at the world's size onto the window: one upscale, smoothly.
|
||||
let group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("Shader pack final"),
|
||||
layout: &self.upscale.1,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(picture),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: wgpu::BindingResource::Sampler(&self.linear),
|
||||
},
|
||||
],
|
||||
});
|
||||
let mut rp = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("Shader pack final"),
|
||||
color_attachments: &[attach(screen, overwrite)],
|
||||
timestamp_writes: self.timing.as_mut().and_then(|t| t.writes("final copy")),
|
||||
..Default::default()
|
||||
});
|
||||
rp.set_pipeline(&self.upscale.0);
|
||||
rp.set_bind_group(0, &group, &[]);
|
||||
rp.draw(0..3, 0..1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ fn vs_full(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> {
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var source: texture_2d<f32>;
|
||||
@group(0) @binding(1) var world_depth: texture_depth_2d;
|
||||
// A depth view read as an unfilterable float: naga's GLSL backend cannot
|
||||
// `textureLoad` a depth texture, and the texel is the same either way.
|
||||
@group(0) @binding(1) var world_depth: texture_2d<f32>;
|
||||
|
||||
struct World {
|
||||
@location(0) color: vec4<f32>,
|
||||
@@ -24,7 +26,7 @@ fn fs_blit(@builtin(position) p: vec4<f32>) -> World {
|
||||
let at = vec2<i32>(i32(p.x), size.y - 1 - i32(p.y));
|
||||
var out: World;
|
||||
out.color = textureLoad(source, at, 0);
|
||||
out.depth = 1.0 - textureLoad(world_depth, at, 0);
|
||||
out.depth = 1.0 - textureLoad(world_depth, at, 0).r;
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -49,6 +51,13 @@ fn vs_uv(@builtin(vertex_index) i: u32) -> Full {
|
||||
return Full(vec4<f32>(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0, 0.0, 1.0), vec2<f32>(uv.x, 1.0 - uv.y));
|
||||
}
|
||||
|
||||
// The screen's own rows: a picture a shader pack's `final` drew (not a GL-rows buffer).
|
||||
@vertex
|
||||
fn vs_screen(@builtin(vertex_index) i: u32) -> Full {
|
||||
let uv = vec2<f32>(f32((i << 1u) & 2u), f32(i & 2u));
|
||||
return Full(vec4<f32>(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0, 0.0, 1.0), uv);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_copy(v: Full) -> @location(0) vec4<f32> {
|
||||
return textureSample(source, linear, v.uv);
|
||||
|
||||
@@ -43,6 +43,64 @@ fn a_pack_without_final_reads_the_replaced_world_colour() {
|
||||
replacement(Change::Colour, false);
|
||||
}
|
||||
|
||||
/// A Resolution Scale leaves the pack's picture smaller than the window. Its `final` draws at
|
||||
/// the picture's size and one copy upsizes it to the window (#9291), so a final that fetches
|
||||
/// its pixel by `gl_FragCoord` (as Photon's does) covers every pixel, not a quarter of them.
|
||||
#[test]
|
||||
fn a_scaled_picture_fills_the_whole_window() {
|
||||
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(&wgpu::DeviceDescriptor {
|
||||
required_limits: adapter.limits(),
|
||||
..Default::default()
|
||||
})) else {
|
||||
return;
|
||||
};
|
||||
eprintln!("scaled picture adapter: {:?}", adapter.get_info());
|
||||
let picture = [4, 2];
|
||||
let window = [8, 4];
|
||||
let colour = texture(&device, picture, T::Rgba8Unorm);
|
||||
let depth = texture(&device, picture, T::Depth32Float);
|
||||
clear(&device, &queue, &colour, &depth, [32, 64, 16, 255], 0.25);
|
||||
let mut post = pack_with(
|
||||
&device,
|
||||
&queue,
|
||||
&adapter,
|
||||
&colour,
|
||||
&[
|
||||
(
|
||||
"final.vsh",
|
||||
"#version 120\nvoid main(){gl_Position=ftransform();}",
|
||||
),
|
||||
(
|
||||
"final.fsh",
|
||||
"#version 330 compatibility\nuniform sampler2D colortex0;\nvoid main(){gl_FragColor=texelFetch(colortex0,ivec2(gl_FragCoord.xy),0);}",
|
||||
),
|
||||
],
|
||||
);
|
||||
let hud = HudBatch::new(window);
|
||||
let pixels = render(
|
||||
&device,
|
||||
&queue,
|
||||
&mut post,
|
||||
&colour,
|
||||
&depth,
|
||||
&frame(&hud),
|
||||
picture,
|
||||
window,
|
||||
);
|
||||
// Independently specified: the world's colour at every window pixel.
|
||||
let wrong = pixels.iter().filter(|p| **p != [32, 64, 16, 255]).count();
|
||||
assert_eq!(
|
||||
wrong,
|
||||
0,
|
||||
"{wrong} of {} pixels are not the picture",
|
||||
pixels.len()
|
||||
);
|
||||
}
|
||||
|
||||
fn replacement(change: Change, final_shader: bool) {
|
||||
let instance = crate::gfx::test_gpu::instance();
|
||||
let Ok(adapter) = pollster::block_on(instance.request_adapter(&Default::default())) else {
|
||||
@@ -62,7 +120,9 @@ fn replacement(change: Change, final_shader: bool) {
|
||||
let mut post = pack(&device, &queue, &adapter, &colour, final_shader);
|
||||
let hud = HudBatch::new(size);
|
||||
let frame = frame(&hud);
|
||||
let initial = render(&device, &queue, &mut post, &colour, &depth, &frame, size);
|
||||
let initial = render(
|
||||
&device, &queue, &mut post, &colour, &depth, &frame, size, size,
|
||||
);
|
||||
let expected = if final_shader {
|
||||
[32, 64, 191, 255]
|
||||
} else {
|
||||
@@ -93,9 +153,13 @@ fn replacement(change: Change, final_shader: bool) {
|
||||
0.75
|
||||
};
|
||||
clear(&device, &queue, &colour, &depth, pixel, reverse_depth);
|
||||
let actual = render(&device, &queue, &mut post, &colour, &depth, &frame, size);
|
||||
let actual = render(
|
||||
&device, &queue, &mut post, &colour, &depth, &frame, size, size,
|
||||
);
|
||||
let mut fresh = pack(&device, &queue, &adapter, &colour, final_shader);
|
||||
let reference = render(&device, &queue, &mut fresh, &colour, &depth, &frame, size);
|
||||
let reference = render(
|
||||
&device, &queue, &mut fresh, &colour, &depth, &frame, size, size,
|
||||
);
|
||||
// Independently specified RG channels and GL depth (1 - reverse-Z), in UNORM8.
|
||||
let expected = if !final_shader {
|
||||
[160, 96, 48, 255]
|
||||
@@ -178,13 +242,8 @@ fn pack(
|
||||
lightmap: &wgpu::TextureView,
|
||||
final_shader: bool,
|
||||
) -> PackPost {
|
||||
let mut zip = zip::ZipWriter::new(Cursor::new(Vec::new()));
|
||||
let options = zip::write::SimpleFileOptions::default();
|
||||
zip.start_file("shaders/shaders.properties", options)
|
||||
.unwrap();
|
||||
zip.write_all(b"oldLighting=false\n").unwrap();
|
||||
if final_shader {
|
||||
for (name, source) in [
|
||||
let programs: &[(&str, &str)] = if final_shader {
|
||||
&[
|
||||
(
|
||||
"final.vsh",
|
||||
"#version 120\nvarying vec2 uv;\nvoid main(){gl_Position=ftransform();uv=gl_MultiTexCoord0.xy;}",
|
||||
@@ -193,10 +252,29 @@ fn pack(
|
||||
"final.fsh",
|
||||
"#version 120\nvarying vec2 uv;uniform sampler2D colortex0;uniform sampler2D depthtex0;\nvoid main(){gl_FragColor=vec4(texture2D(colortex0,uv).rg,texture2D(depthtex0,uv).r,1.0);}",
|
||||
),
|
||||
] {
|
||||
zip.start_file(format!("shaders/{name}"), options).unwrap();
|
||||
zip.write_all(source.as_bytes()).unwrap();
|
||||
}
|
||||
]
|
||||
} else {
|
||||
&[]
|
||||
};
|
||||
pack_with(device, queue, adapter, lightmap, programs)
|
||||
}
|
||||
|
||||
/// A pack of `programs` (`name`, GLSL source) over `lightmap`.
|
||||
fn pack_with(
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
adapter: &wgpu::Adapter,
|
||||
lightmap: &wgpu::TextureView,
|
||||
programs: &[(&str, &str)],
|
||||
) -> PackPost {
|
||||
let mut zip = zip::ZipWriter::new(Cursor::new(Vec::new()));
|
||||
let options = zip::write::SimpleFileOptions::default();
|
||||
zip.start_file("shaders/shaders.properties", options)
|
||||
.unwrap();
|
||||
zip.write_all(b"oldLighting=false\n").unwrap();
|
||||
for (name, source) in programs {
|
||||
zip.start_file(format!("shaders/{name}"), options).unwrap();
|
||||
zip.write_all(source.as_bytes()).unwrap();
|
||||
}
|
||||
let pack = horizon_shaders::Pack::from_zip_bytes(
|
||||
"original-world-inputs",
|
||||
@@ -241,12 +319,13 @@ fn render(
|
||||
depth: &wgpu::TextureView,
|
||||
frame: &Frame,
|
||||
size: [u32; 2],
|
||||
window: [u32; 2],
|
||||
) -> Vec<[u8; 4]> {
|
||||
let screen = texture(device, size, T::Rgba8Unorm);
|
||||
let screen = texture(device, window, T::Rgba8Unorm);
|
||||
let stride = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
|
||||
let read = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: None,
|
||||
size: u64::from(stride * size[1]),
|
||||
size: u64::from(stride * window[1]),
|
||||
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
@@ -261,7 +340,7 @@ fn render(
|
||||
frame,
|
||||
&Default::default(),
|
||||
size,
|
||||
size,
|
||||
window,
|
||||
&[],
|
||||
&mut |_, _, _, _| {},
|
||||
);
|
||||
@@ -285,7 +364,7 @@ fn render(
|
||||
.unwrap()
|
||||
.chunks_exact(stride as usize)
|
||||
.flat_map(|row| {
|
||||
row[..size[0] as usize * 4]
|
||||
row[..window[0] as usize * 4]
|
||||
.as_chunks::<4>()
|
||||
.0
|
||||
.iter()
|
||||
|
||||
@@ -319,7 +319,7 @@ mod tests {
|
||||
#[test]
|
||||
fn a_native_weather_stage_validates() {
|
||||
use horizon_shader_api::stages::StageKind;
|
||||
let source = StageKind::Weather.prelude()
|
||||
let source = StageKind::Weather.prelude(horizon_shader_api::Api::V0_1)
|
||||
+ "fn weather(input: WeatherInput) -> vec4<f32> {\n \
|
||||
return input.color * vec4(input.light, input.position.x, f32(input.snow));\n}\n";
|
||||
let text = native_source(&source);
|
||||
|
||||
@@ -43,9 +43,7 @@ struct TaaUniform {
|
||||
jitter: [f32; 4],
|
||||
}
|
||||
|
||||
/// TAA's GPU resources, absent when the device cannot compile the shader
|
||||
/// (the OpenGL backend's naga GLSL emitter does not support `textureLoad`
|
||||
/// from depth textures).
|
||||
/// TAA's GPU resources, absent when the device cannot compile the shader.
|
||||
struct TaaResources {
|
||||
layout: wgpu::BindGroupLayout,
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
@@ -146,9 +144,6 @@ impl Post {
|
||||
cache: None,
|
||||
})
|
||||
};
|
||||
// The OpenGL backend's GLSL translator does not support textureLoad
|
||||
// from depth textures: on devices running through OpenGL (such as older
|
||||
// Intel GPUs on Linux), TAA cannot compile its pipeline.
|
||||
let taa_resources = {
|
||||
let taa_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
label: Some("Horizon TAA bindings"),
|
||||
@@ -161,7 +156,9 @@ impl Post {
|
||||
count: None,
|
||||
},
|
||||
texture(2, float),
|
||||
texture(3, wgpu::TextureSampleType::Depth),
|
||||
// A depth view through an unfilterable float binding: the
|
||||
// texel is the same, and the GLSL backend can read it.
|
||||
texture(3, wgpu::TextureSampleType::Float { filterable: false }),
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 4,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
|
||||
@@ -1232,7 +1232,8 @@ impl HudAtlas {
|
||||
.ok();
|
||||
let animation = animation_of(mcmeta.as_deref());
|
||||
// An animated strip keeps its first frame.
|
||||
let frames = name.starts_with("block/") || name.starts_with("item/");
|
||||
let frames =
|
||||
animation.is_some() || name.starts_with("block/") || name.starts_with("item/");
|
||||
let (w, h, pixels) = if frames {
|
||||
first_frame(image, animation.as_ref())
|
||||
} else {
|
||||
@@ -3495,6 +3496,49 @@ mod tests {
|
||||
let banner = atlas.sprites["custom/banner"];
|
||||
assert_eq!(banner.texels((atlas.width, atlas.height)), (16, 32));
|
||||
}
|
||||
#[test]
|
||||
fn an_animated_gui_sprite_packs_its_first_frame_and_animates() {
|
||||
let root =
|
||||
std::env::temp_dir().join(format!("horizon-atlas-gui-anim-{}", std::process::id()));
|
||||
let sprites = root.join("assets/minecraft/textures/gui/sprites/icon");
|
||||
fs::create_dir_all(&sprites).unwrap();
|
||||
// A GUI sprite like icon/music_notes: 4 frames stacked vertically (16x64),
|
||||
// with animation metadata, and a still 16x32 texture beside it.
|
||||
write_strip(
|
||||
&sprites.join("music_notes.png"),
|
||||
16,
|
||||
64,
|
||||
&[
|
||||
[10, 20, 30, 255],
|
||||
[40, 50, 60, 255],
|
||||
[70, 80, 90, 255],
|
||||
[100, 110, 120, 255],
|
||||
],
|
||||
Some(r#"{"animation": {"frametime": 2}}"#),
|
||||
);
|
||||
write_strip(
|
||||
&sprites.join("static_badge.png"),
|
||||
16,
|
||||
32,
|
||||
&[[9, 9, 9, 255]],
|
||||
None,
|
||||
);
|
||||
let atlas = HudAtlas::build(&root).unwrap();
|
||||
let _ = fs::remove_dir_all(&root);
|
||||
let rect = atlas.sprites["gui/sprites/icon/music_notes"];
|
||||
// One frame, not the whole 16x64 strip squeezed into a slot.
|
||||
assert_eq!(rect.texels((atlas.width, atlas.height)), (16, 16));
|
||||
assert_eq!(texel_at(&atlas, rect, 0, 15), [10, 20, 30, 255]);
|
||||
let (_, animation) = atlas
|
||||
.sprite_animations
|
||||
.iter()
|
||||
.find(|(r, _)| r.u0 == rect.u0 && r.v0 == rect.v0)
|
||||
.expect("the gui sprite animates");
|
||||
assert_eq!(animation.frame_count(), 4);
|
||||
// A still texture there stays whole.
|
||||
let static_badge = atlas.sprites["gui/sprites/icon/static_badge"];
|
||||
assert_eq!(static_badge.texels((atlas.width, atlas.height)), (16, 32));
|
||||
}
|
||||
/// A resource root with one plain item texture and these CIT files (relative path, contents).
|
||||
fn cit_root(name: &str, files: &[(&str, &str)]) -> PathBuf {
|
||||
let root =
|
||||
|
||||
@@ -63,6 +63,7 @@ pub mod menu_blur;
|
||||
pub mod mesher;
|
||||
pub mod minimap;
|
||||
pub mod mipmap;
|
||||
pub mod mod_browser;
|
||||
pub mod mod_list;
|
||||
pub mod model_library;
|
||||
pub mod models;
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
//! What Options > Mods > Browse mods shows (docs/design/mod-hotload.md): the
|
||||
//! registry's content mods this game installs, a search over them, and the one
|
||||
//! whose page is open. The game fills it from a worker
|
||||
//! (`horizon_client::mod_registry`, `game/mod_browser.rs`); the options screen
|
||||
//! only reads it and asks for installs through its events.
|
||||
|
||||
/// One project of the registry, as the list and its page show it.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct Listing {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub summary: String,
|
||||
/// Markdown, shown as plain text.
|
||||
pub description: String,
|
||||
pub authors: Vec<String>,
|
||||
pub license: String,
|
||||
pub categories: Vec<String>,
|
||||
pub tags: Vec<String>,
|
||||
/// The version this game would install, its date and changelog.
|
||||
pub version: String,
|
||||
pub published: String,
|
||||
pub changelog: String,
|
||||
pub downloads: u64,
|
||||
/// The project's page on the website.
|
||||
pub page: String,
|
||||
/// The version installed in this game folder, if any.
|
||||
pub installed: Option<String>,
|
||||
}
|
||||
|
||||
impl Listing {
|
||||
/// Whether a search for `query` (already lower case) finds it: its name,
|
||||
/// id, summary, authors, categories or tags.
|
||||
pub fn matches(&self, query: &str) -> bool {
|
||||
let query = query.trim();
|
||||
query.is_empty()
|
||||
|| [&self.name, &self.id, &self.summary]
|
||||
.into_iter()
|
||||
.chain(&self.authors)
|
||||
.chain(&self.categories)
|
||||
.chain(&self.tags)
|
||||
.any(|field| field.to_lowercase().contains(query))
|
||||
}
|
||||
|
||||
/// Installed, but not the version the registry offers now.
|
||||
pub fn has_update(&self) -> bool {
|
||||
self.installed
|
||||
.as_ref()
|
||||
.is_some_and(|installed| *installed != self.version)
|
||||
}
|
||||
}
|
||||
|
||||
/// Where the browser is.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub enum Status {
|
||||
/// Not asked yet.
|
||||
#[default]
|
||||
Idle,
|
||||
/// The index is being read.
|
||||
Loading,
|
||||
/// The projects are listed.
|
||||
Ready,
|
||||
/// The index couldn't be read: why, in English (shown inside a translated line).
|
||||
Failed(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ModBrowser {
|
||||
pub status: Status,
|
||||
pub listings: Vec<Listing>,
|
||||
/// The project an install runs for.
|
||||
pub installing: Option<String>,
|
||||
/// The last install's outcome, already translated, for the page it ran on.
|
||||
pub message: Option<(String, bool)>,
|
||||
}
|
||||
|
||||
impl ModBrowser {
|
||||
/// The listings a search for `query` finds, by index into `listings`.
|
||||
pub fn found(&self, query: &str) -> Vec<usize> {
|
||||
let query = query.to_lowercase();
|
||||
(0..self.listings.len())
|
||||
.filter(|&i| self.listings[i].matches(&query))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn listing(&self, id: &str) -> Option<&Listing> {
|
||||
self.listings.iter().find(|l| l.id == id)
|
||||
}
|
||||
}
|
||||
|
||||
/// A project's markdown description as lines of plain text for the page:
|
||||
/// heading marks, emphasis and list bullets dropped, links shown as their text,
|
||||
/// blank lines and fences left out.
|
||||
pub fn plain_lines(markdown: &str) -> Vec<String> {
|
||||
let mut out = Vec::new();
|
||||
for line in markdown.lines() {
|
||||
let line = line.trim();
|
||||
if line.is_empty() || line.starts_with("```") || line.starts_with("<!--") {
|
||||
continue;
|
||||
}
|
||||
let line = line.trim_start_matches('#').trim_start();
|
||||
let line = match line.strip_prefix("- ").or_else(|| line.strip_prefix("* ")) {
|
||||
Some(rest) => format!("• {rest}"),
|
||||
None => line.to_owned(),
|
||||
};
|
||||
let line = links_as_text(&line).replace("**", "").replace('`', "");
|
||||
if !line.is_empty() {
|
||||
out.push(line);
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// `[text](url)` → `text`, `` → nothing.
|
||||
fn links_as_text(line: &str) -> String {
|
||||
let mut out = String::with_capacity(line.len());
|
||||
let mut rest = line;
|
||||
while let Some(open) = rest.find('[') {
|
||||
let Some(close) = rest[open..].find("](").map(|c| open + c) else {
|
||||
break;
|
||||
};
|
||||
let Some(end) = rest[close..].find(')').map(|e| close + e) else {
|
||||
break;
|
||||
};
|
||||
let image = open > 0 && rest[..open].ends_with('!');
|
||||
out.push_str(&rest[..if image { open - 1 } else { open }]);
|
||||
if !image {
|
||||
out.push_str(&rest[open + 1..close]);
|
||||
}
|
||||
rest = &rest[end + 1..];
|
||||
}
|
||||
out.push_str(rest);
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn listing(id: &str, tags: &[&str]) -> Listing {
|
||||
Listing {
|
||||
id: id.into(),
|
||||
name: id.to_uppercase(),
|
||||
summary: format!("All about {id}"),
|
||||
tags: tags.iter().map(|t| (*t).to_owned()).collect(),
|
||||
version: "1.0".into(),
|
||||
..Listing::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_search_finds_names_tags_and_categories_in_any_case() {
|
||||
let mut pipes = listing("pipes", &["tech"]);
|
||||
pipes.categories = vec!["technology".into()];
|
||||
let browser = ModBrowser {
|
||||
listings: vec![listing("ruby", &["gems"]), pipes],
|
||||
..ModBrowser::default()
|
||||
};
|
||||
assert_eq!(browser.found(""), [0, 1]);
|
||||
assert_eq!(browser.found("RUBY"), [0]);
|
||||
assert_eq!(browser.found("gems"), [0]);
|
||||
assert_eq!(browser.found("techno"), [1]);
|
||||
assert!(browser.found("nothing like it").is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_update_is_another_version_than_the_installed_one() {
|
||||
let mut l = listing("ruby", &[]);
|
||||
assert!(!l.has_update());
|
||||
l.installed = Some("1.0".into());
|
||||
assert!(!l.has_update());
|
||||
l.installed = Some("0.9".into());
|
||||
assert!(l.has_update());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn markdown_reads_as_plain_lines() {
|
||||
let text = "# Ruby\n\nAdds **rubies**. See [the wiki](https://x.y).\n\n\n- one\n* two\n```\ncode\n```\n";
|
||||
assert_eq!(
|
||||
plain_lines(text),
|
||||
[
|
||||
"Ruby",
|
||||
"Adds rubies. See the wiki.",
|
||||
"• one",
|
||||
"• two",
|
||||
"code"
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,30 @@ fn percent(caption: &str, value: f32) -> Text {
|
||||
],
|
||||
)
|
||||
}
|
||||
/// `text` cut into lines of at most `width` characters, at spaces where it has them.
|
||||
fn wrap(text: &str, width: usize) -> Vec<String> {
|
||||
let mut lines = Vec::new();
|
||||
let mut line = String::new();
|
||||
for word in text.split_whitespace() {
|
||||
let len = line.chars().count();
|
||||
if len > 0 && len + 1 + word.chars().count() > width {
|
||||
lines.push(std::mem::take(&mut line));
|
||||
}
|
||||
if !line.is_empty() {
|
||||
line.push(' ');
|
||||
}
|
||||
line.push_str(word);
|
||||
while line.chars().count() > width {
|
||||
let rest: String = line.chars().skip(width).collect();
|
||||
line = line.chars().take(width).collect();
|
||||
lines.push(std::mem::replace(&mut line, rest));
|
||||
}
|
||||
}
|
||||
if !line.is_empty() {
|
||||
lines.push(line);
|
||||
}
|
||||
lines
|
||||
}
|
||||
fn on_off(v: bool) -> Text {
|
||||
tr(if v { "options.on" } else { "options.off" })
|
||||
}
|
||||
@@ -1254,6 +1278,10 @@ pub enum Page {
|
||||
Font,
|
||||
Packs,
|
||||
Mods,
|
||||
/// Browse mods: the registry's content mods, searched (`mod_browser`).
|
||||
ModBrowser,
|
||||
/// One registry project's page: `OptionsUi::mod_project`.
|
||||
ModProject,
|
||||
/// A native mod's settings page: its index in `mods.installed`.
|
||||
NativeMod(usize),
|
||||
/// Horizon's shader packs, either kind (`shader_pack_list`).
|
||||
@@ -1332,6 +1360,12 @@ enum Act {
|
||||
NativeKey(usize, usize),
|
||||
NativeButton(usize, usize),
|
||||
OpenModFolder,
|
||||
/// Browse mods: the page of listing `.0` of `mod_browser`.
|
||||
ModProject(usize),
|
||||
/// The open project page's Install or Update.
|
||||
InstallMod,
|
||||
/// The open project page's page on the website.
|
||||
OpenModPage,
|
||||
/// Accessibility's "Accessibility Help" link
|
||||
/// (`AccessibilityOptionsScreen.addFooter`'s openLinkButton).
|
||||
AccessibilityHelp,
|
||||
@@ -1468,6 +1502,12 @@ pub enum Event {
|
||||
/// Data Packs' Open Pack Folder: create the folder if needed and open it.
|
||||
OpenDataPackFolder(std::path::PathBuf),
|
||||
OpenModFolder,
|
||||
/// Browse mods opened: read the registry's index into `mod_browser`.
|
||||
BrowseMods,
|
||||
/// Install (or update) the registry's content mod with this id.
|
||||
InstallMod(String),
|
||||
/// Open a registry project's page on the website.
|
||||
OpenModPage(String),
|
||||
/// Accessibility's Help link: confirm and open
|
||||
/// `CommonLinks.ACCESSIBILITY_HELP` over the options screen.
|
||||
AccessibilityHelp,
|
||||
@@ -1578,6 +1618,10 @@ pub struct OptionsUi {
|
||||
language_choice: Option<String>,
|
||||
language_search: ui::EditBox,
|
||||
pub(crate) settings_search: ui::EditBox,
|
||||
/// Browse mods: the registry's projects, filled by the game.
|
||||
pub mod_browser: crate::mod_browser::ModBrowser,
|
||||
/// The project whose page is open (`Page::ModProject`).
|
||||
pub mod_project: String,
|
||||
/// Horizon's `packBrowser` extra: the pack screen's Add button.
|
||||
pub pack_browser: bool,
|
||||
/// Packs: the packs found and the selection the screen opens with.
|
||||
@@ -1666,6 +1710,8 @@ impl Default for OptionsUi {
|
||||
language_choice: None,
|
||||
language_search: search,
|
||||
settings_search,
|
||||
mod_browser: Default::default(),
|
||||
mod_project: String::new(),
|
||||
pack_browser: false,
|
||||
pack_infos: Vec::new(),
|
||||
pack_selection: Vec::new(),
|
||||
@@ -2156,7 +2202,10 @@ impl OptionsUi {
|
||||
}
|
||||
fn settings_search_active(&self) -> bool {
|
||||
self.settings_search.focused
|
||||
&& matches!(self.page(), Page::Video | Page::Controls | Page::KeyBinds)
|
||||
&& matches!(
|
||||
self.page(),
|
||||
Page::Video | Page::Controls | Page::KeyBinds | Page::ModBrowser
|
||||
)
|
||||
}
|
||||
/// Whether the resource pack screen is open (not Create World's data packs on the same
|
||||
/// page).
|
||||
@@ -2297,7 +2346,10 @@ impl OptionsUi {
|
||||
self.focus = None;
|
||||
self.focus_from_keyboard = false;
|
||||
self.can_change_value = false;
|
||||
if matches!(page, Page::Video | Page::Controls | Page::KeyBinds) {
|
||||
if matches!(
|
||||
page,
|
||||
Page::Video | Page::Controls | Page::KeyBinds | Page::ModBrowser
|
||||
) {
|
||||
self.settings_search.set_value("");
|
||||
self.settings_search.focused = false;
|
||||
}
|
||||
@@ -2423,6 +2475,8 @@ impl OptionsUi {
|
||||
Page::Font => "options.font.title",
|
||||
Page::Packs => "resourcePack.title",
|
||||
Page::Mods => "horizon.pages.mods",
|
||||
Page::ModBrowser => "horizon.mods.browse.title",
|
||||
Page::ModProject => "horizon.mods.browse.project",
|
||||
Page::NativeMod(_) => "Mod Settings",
|
||||
Page::ShaderPacks => "horizon.pages.shader_packs",
|
||||
Page::ShaderOptions | Page::NativePackOptions(..) => "horizon.pages.shader_options",
|
||||
@@ -2489,6 +2543,144 @@ impl OptionsUi {
|
||||
filtered
|
||||
}
|
||||
|
||||
/// Browse mods: what the registry offers this game, by the search box.
|
||||
fn mod_browser_rows(&self) -> Vec<Row> {
|
||||
use crate::mod_browser::Status;
|
||||
let b = &self.mod_browser;
|
||||
match &b.status {
|
||||
Status::Idle | Status::Loading => {
|
||||
return vec![Row::Note(lang::get("horizon.mods.browse.loading"))];
|
||||
}
|
||||
Status::Failed(why) => {
|
||||
return vec![Row::Note(lang::tr("horizon.mods.browse.failed", &[why]))];
|
||||
}
|
||||
Status::Ready => {}
|
||||
}
|
||||
let found = b.found(&self.settings_search.value);
|
||||
if found.is_empty() {
|
||||
return vec![Row::Note(lang::get("horizon.mods.browse.none"))];
|
||||
}
|
||||
let mut rows = Vec::new();
|
||||
for i in found {
|
||||
let l = &b.listings[i];
|
||||
let mut label = Text::literal(format!("{} {}", l.name, l.version));
|
||||
if l.has_update() {
|
||||
label = label.append(tr("horizon.mods.browse.has_update"));
|
||||
} else if l.installed.is_some() {
|
||||
label = label.append(tr("horizon.mods.browse.is_installed"));
|
||||
}
|
||||
rows.push(Row::Buttons(
|
||||
button(0.0, 0.0, 310.0, label, Act::ModProject(i)),
|
||||
None,
|
||||
));
|
||||
rows.extend(wrap(&l.summary, 64).into_iter().take(1).map(Row::Note));
|
||||
}
|
||||
rows
|
||||
}
|
||||
/// A registry project's page: what it is, Install or Update, its switch
|
||||
/// once installed, its description and changelog.
|
||||
fn mod_project_rows(&self, s: &Settings) -> Vec<Row> {
|
||||
let b = &self.mod_browser;
|
||||
let Some(l) = b.listing(&self.mod_project) else {
|
||||
return vec![Row::Note(lang::get("horizon.mods.browse.none"))];
|
||||
};
|
||||
let mut rows = vec![Row::Note(format!("{} {}", l.name, l.version))];
|
||||
let by = [
|
||||
lang::tr("horizon.mods.browse.by", &[&l.authors.join(", ")]),
|
||||
l.license.clone(),
|
||||
l.published.clone(),
|
||||
];
|
||||
let by: Vec<&str> = by
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.filter(|t| !t.is_empty())
|
||||
.collect();
|
||||
rows.push(Row::Note(by.join(" · ")));
|
||||
let labels: Vec<&str> = l
|
||||
.categories
|
||||
.iter()
|
||||
.chain(&l.tags)
|
||||
.map(String::as_str)
|
||||
.collect();
|
||||
if !labels.is_empty() {
|
||||
rows.push(Row::Note(labels.join(", ")));
|
||||
}
|
||||
let installing = b.installing.as_deref() == Some(l.id.as_str());
|
||||
let install = match (&l.installed, installing) {
|
||||
(_, true) => inactive(button(
|
||||
0.0,
|
||||
0.0,
|
||||
150.0,
|
||||
tr("horizon.mods.browse.installing"),
|
||||
Act::None,
|
||||
)),
|
||||
(None, false) => button(
|
||||
0.0,
|
||||
0.0,
|
||||
150.0,
|
||||
tr("horizon.mods.browse.install"),
|
||||
Act::InstallMod,
|
||||
),
|
||||
(Some(_), false) if l.has_update() => button(
|
||||
0.0,
|
||||
0.0,
|
||||
150.0,
|
||||
tr("horizon.mods.browse.update"),
|
||||
Act::InstallMod,
|
||||
),
|
||||
(Some(_), false) => inactive(button(
|
||||
0.0,
|
||||
0.0,
|
||||
150.0,
|
||||
tr("horizon.mods.browse.installed"),
|
||||
Act::None,
|
||||
)),
|
||||
};
|
||||
let page = button(
|
||||
0.0,
|
||||
0.0,
|
||||
150.0,
|
||||
tr("horizon.mods.browse.open_page"),
|
||||
Act::OpenModPage,
|
||||
);
|
||||
let row = s.mods.installed.iter().position(|m| m.id == l.id && !m.dev);
|
||||
match row.filter(|_| l.installed.is_some()) {
|
||||
Some(i) => {
|
||||
let label =
|
||||
Text::literal(format!("{}: ", lang::get("horizon.mods.browse.enabled")))
|
||||
.append(on_off(s.mods.is_on(i)));
|
||||
let toggle = button(0.0, 0.0, 150.0, label, Act::ToggleMod(i));
|
||||
rows.push(Row::Buttons(install, Some(toggle)));
|
||||
rows.push(Row::Buttons(page, None));
|
||||
}
|
||||
None => rows.push(Row::Buttons(install, Some(page))),
|
||||
}
|
||||
if let Some((message, _)) = &b.message {
|
||||
rows.extend(wrap(message, 64).into_iter().map(Row::Note));
|
||||
}
|
||||
let description = crate::mod_browser::plain_lines(if l.description.is_empty() {
|
||||
&l.summary
|
||||
} else {
|
||||
&l.description
|
||||
});
|
||||
rows.extend(
|
||||
description
|
||||
.iter()
|
||||
.flat_map(|line| wrap(line, 64))
|
||||
.map(Row::Note),
|
||||
);
|
||||
let changelog = crate::mod_browser::plain_lines(&l.changelog);
|
||||
if !changelog.is_empty() {
|
||||
rows.push(Row::Heading("horizon.mods.browse.changelog".into()));
|
||||
rows.extend(
|
||||
changelog
|
||||
.iter()
|
||||
.flat_map(|line| wrap(line, 64))
|
||||
.map(Row::Note),
|
||||
);
|
||||
}
|
||||
rows
|
||||
}
|
||||
/// A native mod's page: its name, its keys as Key Binds rows (the
|
||||
/// player's choice, else the default), then its buttons.
|
||||
fn native_rows(&self, s: &Settings, i: usize) -> Vec<Row> {
|
||||
@@ -2857,61 +3049,64 @@ impl OptionsUi {
|
||||
}
|
||||
Page::Font => pairs(&[Opt::ForceUnicode, Opt::JapaneseGlyphs]),
|
||||
Page::Mods => {
|
||||
let browse = button(
|
||||
0.0,
|
||||
0.0,
|
||||
310.0,
|
||||
tr("horizon.mods.browse"),
|
||||
Act::Push(Page::ModBrowser),
|
||||
);
|
||||
let mut rows = vec![Row::Buttons(browse, None)];
|
||||
if s.mods.installed.is_empty() {
|
||||
return vec![Row::Header("horizon.mods.empty")];
|
||||
rows.push(Row::Header("horizon.mods.empty"));
|
||||
return rows;
|
||||
}
|
||||
s.mods
|
||||
.installed
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, installed)| {
|
||||
let mut label = Text::option_name_value(
|
||||
Text::literal(installed.title.clone()),
|
||||
on_off(s.mods.is_on(i)),
|
||||
rows.extend(s.mods.installed.iter().enumerate().map(|(i, installed)| {
|
||||
let mut label = Text::option_name_value(
|
||||
Text::literal(installed.title.clone()),
|
||||
on_off(s.mods.is_on(i)),
|
||||
);
|
||||
if installed.problem.as_deref() == Some(crate::mod_list::NOT_BUILT) {
|
||||
label = label.append(tr("horizon.mods.not_built"));
|
||||
} else if installed.problem.is_some() {
|
||||
label = label.append(tr("horizon.mods.stopped"));
|
||||
} else if installed.dev {
|
||||
label = label.append(tr("horizon.mods.dev"));
|
||||
}
|
||||
// A content mod's set feeds the block-state tables the
|
||||
// open world's chunks were decoded with: changing it
|
||||
// mid-world remaps their ids (Forge/Fabric freeze the
|
||||
// registry before a world opens). A native mod toggles
|
||||
// live through `native_on`, so it stays available.
|
||||
let content = installed.native.is_none();
|
||||
if self.in_world && content {
|
||||
label = label.append(tr("horizon.mods.in_world"));
|
||||
}
|
||||
let dead = installed.dev || installed.id.is_empty() || self.in_world && content;
|
||||
// Only native mods with actual settings need a second button.
|
||||
if installed
|
||||
.native
|
||||
.as_ref()
|
||||
.is_some_and(|page| !page.keys.is_empty() || !page.buttons.is_empty())
|
||||
{
|
||||
let toggle = button(0.0, 0.0, 150.0, label, Act::ToggleMod(i));
|
||||
let settings = button(
|
||||
0.0,
|
||||
0.0,
|
||||
150.0,
|
||||
tr("horizon.mods.settings"),
|
||||
Act::ModSettings(i),
|
||||
);
|
||||
if installed.problem.as_deref() == Some(crate::mod_list::NOT_BUILT) {
|
||||
label = label.append(tr("horizon.mods.not_built"));
|
||||
} else if installed.problem.is_some() {
|
||||
label = label.append(tr("horizon.mods.stopped"));
|
||||
} else if installed.dev {
|
||||
label = label.append(tr("horizon.mods.dev"));
|
||||
}
|
||||
// A content mod's set feeds the block-state tables the
|
||||
// open world's chunks were decoded with: changing it
|
||||
// mid-world remaps their ids (Forge/Fabric freeze the
|
||||
// registry before a world opens). A native mod toggles
|
||||
// live through `native_on`, so it stays available.
|
||||
let content = installed.native.is_none();
|
||||
if self.in_world && content {
|
||||
label = label.append(tr("horizon.mods.in_world"));
|
||||
}
|
||||
let dead =
|
||||
installed.dev || installed.id.is_empty() || self.in_world && content;
|
||||
// Only native mods with actual settings need a second button.
|
||||
if installed
|
||||
.native
|
||||
.as_ref()
|
||||
.is_some_and(|page| !page.keys.is_empty() || !page.buttons.is_empty())
|
||||
{
|
||||
let toggle = button(0.0, 0.0, 150.0, label, Act::ToggleMod(i));
|
||||
let settings = button(
|
||||
0.0,
|
||||
0.0,
|
||||
150.0,
|
||||
tr("horizon.mods.settings"),
|
||||
Act::ModSettings(i),
|
||||
);
|
||||
Row::Buttons(
|
||||
if dead { inactive(toggle) } else { toggle },
|
||||
Some(settings),
|
||||
)
|
||||
} else {
|
||||
let widget = button(0.0, 0.0, 310.0, label, Act::ToggleMod(i));
|
||||
Row::Buttons(if dead { inactive(widget) } else { widget }, None)
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
Row::Buttons(if dead { inactive(toggle) } else { toggle }, Some(settings))
|
||||
} else {
|
||||
let widget = button(0.0, 0.0, 310.0, label, Act::ToggleMod(i));
|
||||
Row::Buttons(if dead { inactive(widget) } else { widget }, None)
|
||||
}
|
||||
}));
|
||||
rows
|
||||
}
|
||||
Page::ModBrowser => self.mod_browser_rows(),
|
||||
Page::ModProject => self.mod_project_rows(s),
|
||||
Page::NativeMod(i) => self.native_rows(s, i),
|
||||
Page::ShaderPacks => {
|
||||
if s.shader_packs.is_empty() {
|
||||
@@ -2989,7 +3184,10 @@ impl OptionsUi {
|
||||
let mut rows = ListRows::default();
|
||||
let page = self.page();
|
||||
let cx = (w / 2.0).floor();
|
||||
if matches!(page, Page::Video | Page::Controls | Page::KeyBinds) {
|
||||
if matches!(
|
||||
page,
|
||||
Page::Video | Page::Controls | Page::KeyBinds | Page::ModBrowser
|
||||
) {
|
||||
self.settings_search.x = cx - 100.0;
|
||||
self.settings_search.y = 17.0;
|
||||
}
|
||||
@@ -3331,12 +3529,12 @@ impl OptionsUi {
|
||||
// Drawn by `pack_screen`.
|
||||
Page::Packs => {}
|
||||
_ => {
|
||||
if matches!(page, Page::Video | Page::Controls) {
|
||||
if matches!(page, Page::Video | Page::Controls | Page::ModBrowser) {
|
||||
top = 38.0;
|
||||
}
|
||||
out.push(Widget {
|
||||
x: 0.0,
|
||||
y: if matches!(page, Page::Video | Page::Controls) {
|
||||
y: if matches!(page, Page::Video | Page::Controls | Page::ModBrowser) {
|
||||
4.0
|
||||
} else {
|
||||
12.0
|
||||
@@ -3915,23 +4113,30 @@ impl OptionsUi {
|
||||
}
|
||||
self.language_search = search;
|
||||
}
|
||||
if matches!(self.page(), Page::Video | Page::Controls | Page::KeyBinds) {
|
||||
if matches!(
|
||||
self.page(),
|
||||
Page::Video | Page::Controls | Page::KeyBinds | Page::ModBrowser
|
||||
) {
|
||||
let mut search = self.settings_search.clone();
|
||||
if self.focus == Some(FocusId::Search) {
|
||||
search.focused = true;
|
||||
}
|
||||
search.draw(batch, atlas, None);
|
||||
let hint = match self.page() {
|
||||
Page::ModBrowser => "horizon.mods.browse.search",
|
||||
_ => "horizon.options.search",
|
||||
};
|
||||
// Horizon's search box sits in the header, before the list.
|
||||
crate::widgets::last_narratable(|n| {
|
||||
n.order = -1;
|
||||
if let crate::widgets::What::EditBox { message, .. } = &mut n.what {
|
||||
*message = tr("horizon.options.search").plain();
|
||||
*message = tr(hint).plain();
|
||||
}
|
||||
});
|
||||
if search.value.is_empty() && !search.focused {
|
||||
batch.text_styled(
|
||||
atlas,
|
||||
&tr("horizon.options.search").with_color(text::GRAY).italic(),
|
||||
&tr(hint).with_color(text::GRAY).italic(),
|
||||
search.text_x(),
|
||||
search.text_y(),
|
||||
u32::MAX,
|
||||
@@ -4091,7 +4296,12 @@ impl OptionsUi {
|
||||
self.size = size;
|
||||
self.framebuffer = framebuffer;
|
||||
let mut events = Vec::new();
|
||||
if !drag && matches!(self.page(), Page::Video | Page::Controls | Page::KeyBinds) {
|
||||
if !drag
|
||||
&& matches!(
|
||||
self.page(),
|
||||
Page::Video | Page::Controls | Page::KeyBinds | Page::ModBrowser
|
||||
)
|
||||
{
|
||||
let search = &mut self.settings_search;
|
||||
search.focused = ui::hovered((x, y), search.x, search.y, search.width, search.height);
|
||||
if search.focused {
|
||||
@@ -4247,6 +4457,9 @@ impl OptionsUi {
|
||||
if p == Page::Music {
|
||||
events.push(Event::ListMusicTracks);
|
||||
}
|
||||
if p == Page::ModBrowser {
|
||||
events.push(Event::BrowseMods);
|
||||
}
|
||||
self.push(p)
|
||||
}
|
||||
Act::Done => events.extend(self.back(s)),
|
||||
@@ -4297,6 +4510,21 @@ impl OptionsUi {
|
||||
}
|
||||
Act::NativeButton(i, b) => events.push(Event::NativeButton(i, b)),
|
||||
Act::OpenModFolder => events.push(Event::OpenModFolder),
|
||||
Act::ModProject(i) => {
|
||||
if let Some(listing) = self.mod_browser.listings.get(i) {
|
||||
self.mod_project = listing.id.clone();
|
||||
self.push(Page::ModProject);
|
||||
}
|
||||
}
|
||||
Act::InstallMod => {
|
||||
self.mod_browser.message = None;
|
||||
events.push(Event::InstallMod(self.mod_project.clone()));
|
||||
}
|
||||
Act::OpenModPage => {
|
||||
if let Some(l) = self.mod_browser.listing(&self.mod_project) {
|
||||
events.push(Event::OpenModPage(l.page.clone()));
|
||||
}
|
||||
}
|
||||
Act::PvpPreset => {
|
||||
crate::pvp::apply_preset(s);
|
||||
events.push(Event::Changed);
|
||||
@@ -5202,6 +5430,9 @@ impl OptionsUi {
|
||||
if p == Page::Music {
|
||||
events.push(Event::ListMusicTracks);
|
||||
}
|
||||
if p == Page::ModBrowser {
|
||||
events.push(Event::BrowseMods);
|
||||
}
|
||||
self.push(p);
|
||||
}
|
||||
Act::Done => {
|
||||
@@ -5259,6 +5490,21 @@ impl OptionsUi {
|
||||
}
|
||||
Act::NativeButton(i, b) => events.push(Event::NativeButton(i, b)),
|
||||
Act::OpenModFolder => events.push(Event::OpenModFolder),
|
||||
Act::ModProject(i) => {
|
||||
if let Some(listing) = self.mod_browser.listings.get(i) {
|
||||
self.mod_project = listing.id.clone();
|
||||
self.push(Page::ModProject);
|
||||
}
|
||||
}
|
||||
Act::InstallMod => {
|
||||
self.mod_browser.message = None;
|
||||
events.push(Event::InstallMod(self.mod_project.clone()));
|
||||
}
|
||||
Act::OpenModPage => {
|
||||
if let Some(l) = self.mod_browser.listing(&self.mod_project) {
|
||||
events.push(Event::OpenModPage(l.page.clone()));
|
||||
}
|
||||
}
|
||||
Act::PvpPreset => {
|
||||
crate::pvp::apply_preset(s);
|
||||
events.push(Event::Changed);
|
||||
|
||||
@@ -17,6 +17,7 @@ use horizon_core::{
|
||||
geometry::{Aabb, Axis, Vec3, trig},
|
||||
random::{LegacyRandom, RandomSource},
|
||||
};
|
||||
use horizon_protocol::entity_data::PositionSource;
|
||||
use horizon_world::block_collision::BlockCollisionRegistry;
|
||||
use std::{
|
||||
array,
|
||||
@@ -50,9 +51,10 @@ pub struct ParticleOptions {
|
||||
/// `GeyserParticleOptions.waterBlocks` and `GeyserBaseParticleOptions`'
|
||||
/// `waterBlocks` and `burstImpulseBase` (0 where the type has none).
|
||||
pub geyser: Option<(i32, f32)>,
|
||||
/// `VibrationParticleOption`: the listener the signal flies to and its
|
||||
/// arrival time in ticks (`ParticleEngine::vibration` is the same particle).
|
||||
pub vibration: Option<([f64; 3], i32)>,
|
||||
/// `VibrationParticleOption`: the listener's `PositionSource`, the point
|
||||
/// the signal flies to and its arrival time in ticks
|
||||
/// (`ParticleEngine::vibration` is the same particle).
|
||||
pub vibration: Option<(PositionSource, [f64; 3], i32)>,
|
||||
/// A native mod's own particle type, by its namespaced name (`mymod:spark`):
|
||||
/// `SimpleParticleType` plus the provider `particles/<name>.json` names.
|
||||
pub mod_particle: Option<String>,
|
||||
@@ -94,10 +96,11 @@ impl ParticleOptions {
|
||||
..Self::simple(ty)
|
||||
}
|
||||
}
|
||||
/// `VibrationParticleOption(destination, arrivalInTicks)`.
|
||||
pub fn vibration(dest: [f64; 3], ticks: i32) -> Self {
|
||||
/// `VibrationParticleOption(destination, arrivalInTicks)`: the listener's
|
||||
/// position source and the point this engine flies the signal to.
|
||||
pub fn vibration(source: PositionSource, dest: [f64; 3], ticks: i32) -> Self {
|
||||
Self {
|
||||
vibration: Some((dest, ticks)),
|
||||
vibration: Some((source, dest, ticks)),
|
||||
..Self::simple(ParticleType::Vibration)
|
||||
}
|
||||
}
|
||||
@@ -2988,8 +2991,8 @@ impl ParticleEngine {
|
||||
// the listener the signal flies to and its arrival time; the sprite
|
||||
// is the set's own draw from the engine's random.
|
||||
T::Vibration => {
|
||||
let (dest, ticks) = o.vibration?;
|
||||
let mut q = Self::vibration_particle(p, dest, ticks);
|
||||
let (_, dest, ticks) = o.vibration.as_ref()?;
|
||||
let mut q = Self::vibration_particle(p, *dest, *ticks);
|
||||
q.set = set;
|
||||
q.sprite = self.sprite_from_set(ty);
|
||||
q
|
||||
@@ -5644,7 +5647,8 @@ mod tests {
|
||||
#[test]
|
||||
fn a_vibration_option_makes_the_signal_its_provider_draws() {
|
||||
let mut engine = ParticleEngine::new(Path::new(""), &HashMap::new(), &HashSet::new());
|
||||
let options = ParticleOptions::vibration([10.5, 70.5, -3.5], 20);
|
||||
let options =
|
||||
ParticleOptions::vibration(PositionSource::Block([10, 70, -4]), [10.5, 70.5, -3.5], 20);
|
||||
// Within the camera's 32 blocks, so only the provider decides.
|
||||
engine.add_with(&options, [0.5, 1.5, 0.5], [0.0; 3], false, false);
|
||||
assert_eq!(engine.made().get("vibration"), Some(&1));
|
||||
|
||||
@@ -575,8 +575,53 @@ pub struct Hud {
|
||||
/// Hits in a row the server confirmed, until this player is hurt or 3 seconds pass.
|
||||
pub combo: bool,
|
||||
pub combo_corner: Corner,
|
||||
/// Frames drawn in the last second.
|
||||
pub fps: bool,
|
||||
pub fps_corner: Corner,
|
||||
/// Left and right clicks in the last second.
|
||||
pub cps: bool,
|
||||
pub cps_corner: Corner,
|
||||
/// The movement keys, mouse buttons and jump, lit while held, with clicks per second.
|
||||
pub keys: bool,
|
||||
pub keys_corner: Corner,
|
||||
/// The computer's local time.
|
||||
pub clock: bool,
|
||||
pub clock_corner: Corner,
|
||||
/// How far the last hit landed from the eyes, for 3 seconds; it only measures.
|
||||
pub reach: bool,
|
||||
pub reach_corner: Corner,
|
||||
/// Horizontal speed in blocks per second.
|
||||
pub speed: bool,
|
||||
pub speed_corner: Corner,
|
||||
/// A dark box behind each widget.
|
||||
pub background: bool,
|
||||
/// The boxes' opacity, in percent.
|
||||
pub opacity: u8,
|
||||
/// The widgets' text colour (durability keeps its own).
|
||||
pub text: Colour,
|
||||
/// A shadow under the widgets' text, as vanilla's HUD text has.
|
||||
pub shadow: bool,
|
||||
}
|
||||
impl Hud {
|
||||
/// Whether any widget is on: with none, the HUD draws nothing and reads nothing.
|
||||
pub fn any(&self) -> bool {
|
||||
[
|
||||
self.armour,
|
||||
self.effects,
|
||||
self.held,
|
||||
self.ping,
|
||||
self.coords,
|
||||
self.server,
|
||||
self.combo,
|
||||
self.fps,
|
||||
self.cps,
|
||||
self.keys,
|
||||
self.clock,
|
||||
self.reach,
|
||||
self.speed,
|
||||
]
|
||||
.contains(&true)
|
||||
}
|
||||
}
|
||||
impl Default for Hud {
|
||||
fn default() -> Self {
|
||||
@@ -596,11 +641,35 @@ impl Default for Hud {
|
||||
server_corner: Corner::TopLeft,
|
||||
combo: false,
|
||||
combo_corner: Corner::TopLeft,
|
||||
fps: false,
|
||||
fps_corner: Corner::TopLeft,
|
||||
cps: false,
|
||||
cps_corner: Corner::TopLeft,
|
||||
keys: false,
|
||||
keys_corner: Corner::BottomLeft,
|
||||
clock: false,
|
||||
clock_corner: Corner::TopRight,
|
||||
reach: false,
|
||||
reach_corner: Corner::TopLeft,
|
||||
speed: false,
|
||||
speed_corner: Corner::TopLeft,
|
||||
background: true,
|
||||
opacity: HUD_OPACITY,
|
||||
text: Colour::White,
|
||||
shadow: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use crate::extras::Corner;
|
||||
/// The widgets' boxes at first: the extras' 0x78 alpha, 47%.
|
||||
pub const HUD_OPACITY: u8 = 47;
|
||||
pub const HUD_OPACITIES: (u8, u8) = (0, 100);
|
||||
impl Hud {
|
||||
/// The boxes' colour, ARGB.
|
||||
pub fn background_argb(&self) -> u32 {
|
||||
alpha(self.opacity) << 24
|
||||
}
|
||||
}
|
||||
/// The value of the view options while they follow the world.
|
||||
fn world_label() -> String {
|
||||
crate::lang::get_or("horizon.pvp.view.world", "World's")
|
||||
@@ -690,6 +759,8 @@ pub struct Pvp {
|
||||
pub hud: Hud,
|
||||
pub tweaks: Tweaks,
|
||||
pub chat: ChatOptions,
|
||||
/// Where each HUD widget sits and its size.
|
||||
pub layout: crate::pvp_hud::Layout,
|
||||
}
|
||||
fn flag(value: &str, d: bool) -> bool {
|
||||
match value {
|
||||
@@ -743,6 +814,14 @@ impl Pvp {
|
||||
"hudHeld" => u.held = flag(value, u.held),
|
||||
"hudHeldCorner" => u.held_corner = Corner::parse(value, u.held_corner),
|
||||
"hudBackground" => u.background = flag(value, u.background),
|
||||
"hudOpacity" => u.opacity = num(value, HUD_OPACITIES, u.opacity),
|
||||
"hudTextColour" => {
|
||||
u.text = Colour::parse(value)
|
||||
.filter(|c| *c != Colour::Inverted)
|
||||
.unwrap_or(u.text)
|
||||
}
|
||||
"hudShadow" => u.shadow = flag(value, u.shadow),
|
||||
"hudPlace" => self.layout.parse(value),
|
||||
"hudPing" => u.ping = flag(value, u.ping),
|
||||
"hudPingCorner" => u.ping_corner = Corner::parse(value, u.ping_corner),
|
||||
"hudCoords" => u.coords = flag(value, u.coords),
|
||||
@@ -751,6 +830,18 @@ impl Pvp {
|
||||
"hudServerCorner" => u.server_corner = Corner::parse(value, u.server_corner),
|
||||
"hudCombo" => u.combo = flag(value, u.combo),
|
||||
"hudComboCorner" => u.combo_corner = Corner::parse(value, u.combo_corner),
|
||||
"hudFps" => u.fps = flag(value, u.fps),
|
||||
"hudFpsCorner" => u.fps_corner = Corner::parse(value, u.fps_corner),
|
||||
"hudCps" => u.cps = flag(value, u.cps),
|
||||
"hudCpsCorner" => u.cps_corner = Corner::parse(value, u.cps_corner),
|
||||
"hudKeys" => u.keys = flag(value, u.keys),
|
||||
"hudKeysCorner" => u.keys_corner = Corner::parse(value, u.keys_corner),
|
||||
"hudClock" => u.clock = flag(value, u.clock),
|
||||
"hudClockCorner" => u.clock_corner = Corner::parse(value, u.clock_corner),
|
||||
"hudReach" => u.reach = flag(value, u.reach),
|
||||
"hudReachCorner" => u.reach_corner = Corner::parse(value, u.reach_corner),
|
||||
"hudSpeed" => u.speed = flag(value, u.speed),
|
||||
"hudSpeedCorner" => u.speed_corner = Corner::parse(value, u.speed_corner),
|
||||
"numericEnchants" => t.numeric_enchants = flag(value, t.numeric_enchants),
|
||||
"lowFire" => t.fire_lower = num(value, FIRE_LOWER, t.fire_lower),
|
||||
"shieldSize" => t.shield_size = num(value, SHIELD_SIZES, t.shield_size),
|
||||
@@ -838,6 +929,34 @@ impl Pvp {
|
||||
u.combo,
|
||||
u.combo_corner.name(),
|
||||
);
|
||||
let _ = write!(
|
||||
out,
|
||||
"hudFps:{}\nhudFpsCorner:{}\nhudCps:{}\nhudCpsCorner:{}\nhudKeys:{}\nhudKeysCorner:{}\n",
|
||||
u.fps,
|
||||
u.fps_corner.name(),
|
||||
u.cps,
|
||||
u.cps_corner.name(),
|
||||
u.keys,
|
||||
u.keys_corner.name(),
|
||||
);
|
||||
let _ = write!(
|
||||
out,
|
||||
"hudClock:{}\nhudClockCorner:{}\nhudReach:{}\nhudReachCorner:{}\nhudSpeed:{}\nhudSpeedCorner:{}\n",
|
||||
u.clock,
|
||||
u.clock_corner.name(),
|
||||
u.reach,
|
||||
u.reach_corner.name(),
|
||||
u.speed,
|
||||
u.speed_corner.name(),
|
||||
);
|
||||
let _ = write!(
|
||||
out,
|
||||
"hudOpacity:{}\nhudTextColour:{}\nhudShadow:{}\n",
|
||||
u.opacity,
|
||||
u.text.name(),
|
||||
u.shadow,
|
||||
);
|
||||
self.layout.serialize(out);
|
||||
let t = &self.tweaks;
|
||||
let _ = write!(
|
||||
out,
|
||||
@@ -955,7 +1074,25 @@ opts! {
|
||||
HudCombo, "hud_combo", "Combo counter",
|
||||
"Hits in a row the server confirmed, until you are hurt or 3 seconds pass without one.";
|
||||
HudComboCorner, "hud_combo_corner", "Combo corner", "Where the combo shows.";
|
||||
HudFps, "hud_fps", "FPS", "Frames drawn in the last second.";
|
||||
HudFpsCorner, "hud_fps_corner", "FPS corner", "Where the FPS shows.";
|
||||
HudCps, "hud_cps", "Clicks per second", "Your left and right clicks in the last second.";
|
||||
HudCpsCorner, "hud_cps_corner", "CPS corner", "Where the clicks per second show.";
|
||||
HudKeys, "hud_keys", "Keystrokes",
|
||||
"Your movement keys, mouse buttons and jump, lit while you hold them, with clicks per second.";
|
||||
HudKeysCorner, "hud_keys_corner", "Keystrokes corner", "Where the keystrokes show.";
|
||||
HudClock, "hud_clock", "Clock", "Your computer's time.";
|
||||
HudClockCorner, "hud_clock_corner", "Clock corner", "Where the clock shows.";
|
||||
HudReach, "hud_reach", "Reach",
|
||||
"How far from your eyes your last hit landed, for 3 seconds. It only measures: your reach stays vanilla's.";
|
||||
HudReachCorner, "hud_reach_corner", "Reach corner", "Where the reach shows.";
|
||||
HudSpeed, "hud_speed", "Speed", "How fast you move across the ground, in blocks per second.";
|
||||
HudSpeedCorner, "hud_speed_corner", "Speed corner", "Where the speed shows.";
|
||||
HudBackground, "hud_background", "Widget backgrounds", "A dark box behind each widget.";
|
||||
HudOpacity, "hud_opacity", "Background opacity", "How dark the boxes behind the widgets are.";
|
||||
HudTextColour, "hud_text_colour", "Text colour",
|
||||
"The widgets' text colour. Durability keeps its green to red.";
|
||||
HudShadow, "hud_shadow", "Text shadow", "A shadow under the widgets' text, as vanilla's HUD text has.";
|
||||
NumericEnchants, "numeric_enchants", "Enchantment levels as numbers",
|
||||
"Tooltips and the enchanting table show Sharpness 5 instead of Sharpness V.";
|
||||
FireLower, "fire_lower", "Lower fire",
|
||||
@@ -1030,7 +1167,22 @@ pub fn rows() -> Vec<Result<Opt, &'static str>> {
|
||||
HudServerCorner,
|
||||
HudCombo,
|
||||
HudComboCorner,
|
||||
HudFps,
|
||||
HudFpsCorner,
|
||||
HudCps,
|
||||
HudCpsCorner,
|
||||
HudKeys,
|
||||
HudKeysCorner,
|
||||
HudClock,
|
||||
HudClockCorner,
|
||||
HudReach,
|
||||
HudReachCorner,
|
||||
HudSpeed,
|
||||
HudSpeedCorner,
|
||||
HudBackground,
|
||||
HudOpacity,
|
||||
HudTextColour,
|
||||
HudShadow,
|
||||
]
|
||||
.map(Ok),
|
||||
);
|
||||
@@ -1054,7 +1206,15 @@ impl Opt {
|
||||
OutlineColour | OutlineOpacity | OutlineThickness | OutlineFill => Some(Outline),
|
||||
HitColour | HitOpacity => Some(Hit),
|
||||
HudArmour | HudEffects | HudHeld | HudPing | HudCoords | HudServer | HudCombo
|
||||
| HudBackground | NumericEnchants => None,
|
||||
| HudFps | HudCps | HudKeys | HudClock | HudReach | HudSpeed | HudBackground
|
||||
| HudTextColour | HudShadow | NumericEnchants => None,
|
||||
HudOpacity => Some(HudBackground),
|
||||
HudFpsCorner => Some(HudFps),
|
||||
HudCpsCorner => Some(HudCps),
|
||||
HudKeysCorner => Some(HudKeys),
|
||||
HudClockCorner => Some(HudClock),
|
||||
HudReachCorner => Some(HudReach),
|
||||
HudSpeedCorner => Some(HudSpeed),
|
||||
FireLower | ShieldSize | TotemSize | GlintColour | ClearWater | ClearLava => None,
|
||||
ViewTime | ViewWeather => None,
|
||||
ChatTimestamps | ChatCompact | ChatCopy | ChatSearch => None,
|
||||
@@ -1081,10 +1241,17 @@ impl Opt {
|
||||
Opt::HudEffects => p.hud.effects,
|
||||
Opt::HudHeld => p.hud.held,
|
||||
Opt::HudBackground => p.hud.background,
|
||||
Opt::HudShadow => p.hud.shadow,
|
||||
Opt::HudPing => p.hud.ping,
|
||||
Opt::HudCoords => p.hud.coords,
|
||||
Opt::HudServer => p.hud.server,
|
||||
Opt::HudCombo => p.hud.combo,
|
||||
Opt::HudFps => p.hud.fps,
|
||||
Opt::HudCps => p.hud.cps,
|
||||
Opt::HudKeys => p.hud.keys,
|
||||
Opt::HudClock => p.hud.clock,
|
||||
Opt::HudReach => p.hud.reach,
|
||||
Opt::HudSpeed => p.hud.speed,
|
||||
Opt::NumericEnchants => p.tweaks.numeric_enchants,
|
||||
Opt::ClearWater => p.tweaks.clear_water,
|
||||
Opt::ClearLava => p.tweaks.clear_lava,
|
||||
@@ -1124,6 +1291,14 @@ impl Opt {
|
||||
Opt::HudCoordsCorner => corner_label(p.hud.coords_corner),
|
||||
Opt::HudServerCorner => corner_label(p.hud.server_corner),
|
||||
Opt::HudComboCorner => corner_label(p.hud.combo_corner),
|
||||
Opt::HudFpsCorner => corner_label(p.hud.fps_corner),
|
||||
Opt::HudCpsCorner => corner_label(p.hud.cps_corner),
|
||||
Opt::HudKeysCorner => corner_label(p.hud.keys_corner),
|
||||
Opt::HudClockCorner => corner_label(p.hud.clock_corner),
|
||||
Opt::HudReachCorner => corner_label(p.hud.reach_corner),
|
||||
Opt::HudSpeedCorner => corner_label(p.hud.speed_corner),
|
||||
Opt::HudOpacity => format!("{}%", p.hud.opacity),
|
||||
Opt::HudTextColour => p.hud.text.label(),
|
||||
Opt::FireLower => match p.tweaks.fire_lower {
|
||||
0 => crate::lang::get_or("Off", "Off"),
|
||||
v => format!("{v}%"),
|
||||
@@ -1191,6 +1366,9 @@ impl Opt {
|
||||
Opt::HudHeld => u.held = !u.held,
|
||||
Opt::HudHeldCorner => u.held_corner = corner(u.held_corner),
|
||||
Opt::HudBackground => u.background = !u.background,
|
||||
Opt::HudOpacity => u.opacity = step_by(u.opacity, HUD_OPACITIES, 10, back),
|
||||
Opt::HudTextColour => u.text = u.text.step_solid(back),
|
||||
Opt::HudShadow => u.shadow = !u.shadow,
|
||||
Opt::HudPing => u.ping = !u.ping,
|
||||
Opt::HudPingCorner => u.ping_corner = corner(u.ping_corner),
|
||||
Opt::HudCoords => u.coords = !u.coords,
|
||||
@@ -1199,6 +1377,18 @@ impl Opt {
|
||||
Opt::HudServerCorner => u.server_corner = corner(u.server_corner),
|
||||
Opt::HudCombo => u.combo = !u.combo,
|
||||
Opt::HudComboCorner => u.combo_corner = corner(u.combo_corner),
|
||||
Opt::HudFps => u.fps = !u.fps,
|
||||
Opt::HudFpsCorner => u.fps_corner = corner(u.fps_corner),
|
||||
Opt::HudCps => u.cps = !u.cps,
|
||||
Opt::HudCpsCorner => u.cps_corner = corner(u.cps_corner),
|
||||
Opt::HudKeys => u.keys = !u.keys,
|
||||
Opt::HudKeysCorner => u.keys_corner = corner(u.keys_corner),
|
||||
Opt::HudClock => u.clock = !u.clock,
|
||||
Opt::HudClockCorner => u.clock_corner = corner(u.clock_corner),
|
||||
Opt::HudReach => u.reach = !u.reach,
|
||||
Opt::HudReachCorner => u.reach_corner = corner(u.reach_corner),
|
||||
Opt::HudSpeed => u.speed = !u.speed,
|
||||
Opt::HudSpeedCorner => u.speed_corner = corner(u.speed_corner),
|
||||
Opt::NumericEnchants => p.tweaks.numeric_enchants = !p.tweaks.numeric_enchants,
|
||||
Opt::FireLower => t.fire_lower = step_by(t.fire_lower, FIRE_LOWER, 10, back),
|
||||
Opt::ShieldSize => t.shield_size = step_by(t.shield_size, SHIELD_SIZES, 10, back),
|
||||
|
||||
@@ -214,6 +214,320 @@ impl Combo {
|
||||
}
|
||||
}
|
||||
|
||||
/// Clicks per second: the presses of the last second, kept for the widgets that are on.
|
||||
#[derive(Default)]
|
||||
pub struct Clicks {
|
||||
/// Press times in seconds, oldest first; at most [`Clicks::KEEP`].
|
||||
times: std::collections::VecDeque<f64>,
|
||||
}
|
||||
impl Clicks {
|
||||
/// More than any hand clicks in a second: the ring never grows past it.
|
||||
const KEEP: usize = 64;
|
||||
pub fn press(&mut self, now: f64) {
|
||||
if self.times.len() == Self::KEEP {
|
||||
self.times.pop_front();
|
||||
}
|
||||
self.times.push_back(now);
|
||||
}
|
||||
/// The presses in the second up to `now`.
|
||||
pub fn per_second(&mut self, now: f64) -> u32 {
|
||||
while self.times.front().is_some_and(|&t| now - t >= 1.0) {
|
||||
self.times.pop_front();
|
||||
}
|
||||
self.times.len() as u32
|
||||
}
|
||||
}
|
||||
|
||||
/// Horizontal speed in blocks per second, from the player's own position sampled at most
|
||||
/// every [`Speed::SAMPLE`] seconds (nothing is asked of the server).
|
||||
#[derive(Default)]
|
||||
pub struct Speed {
|
||||
last: Option<([f64; 2], f64)>,
|
||||
/// Tenths of a block per second.
|
||||
tenths: u32,
|
||||
}
|
||||
impl Speed {
|
||||
pub const SAMPLE: f64 = 0.25;
|
||||
pub fn sample(&mut self, x: f64, z: f64, now: f64) -> u32 {
|
||||
match self.last {
|
||||
Some((_, at)) if now - at < Self::SAMPLE => {}
|
||||
Some(([lx, lz], at)) => {
|
||||
let d = ((x - lx).powi(2) + (z - lz).powi(2)).sqrt();
|
||||
// A teleport or respawn isn't speed.
|
||||
let v = d / (now - at);
|
||||
self.tenths = if v > 200.0 {
|
||||
0
|
||||
} else {
|
||||
(v * 10.0).round() as u32
|
||||
};
|
||||
self.last = Some(([x, z], now));
|
||||
}
|
||||
None => self.last = Some(([x, z], now)),
|
||||
}
|
||||
self.tenths
|
||||
}
|
||||
}
|
||||
|
||||
/// The reach display: how far the last hit this player made was, measured from the eyes to
|
||||
/// the point the crosshair's ray met the target's box. It measures what the client already
|
||||
/// computed for the attack and changes nothing about it.
|
||||
#[derive(Default)]
|
||||
pub struct Reach {
|
||||
last: Option<(f64, f64)>,
|
||||
}
|
||||
impl Reach {
|
||||
/// How long the last hit's distance stays shown.
|
||||
pub const SHOWN: f64 = 3.0;
|
||||
pub fn hit(&mut self, distance: f64, now: f64) {
|
||||
self.last = Some((distance, now));
|
||||
}
|
||||
/// Hundredths of a block, while the last hit is recent.
|
||||
pub fn hundredths(&self, now: f64) -> Option<u32> {
|
||||
let (d, at) = self.last?;
|
||||
(now - at <= Self::SHOWN).then(|| (d * 100.0).round() as u32)
|
||||
}
|
||||
}
|
||||
/// `n` hundredths as "3.05".
|
||||
pub fn hundredths(n: u32) -> String {
|
||||
format!("{}.{:02}", n / 100, n % 100)
|
||||
}
|
||||
/// `n` tenths as "4.3".
|
||||
pub fn tenths(n: u32) -> String {
|
||||
format!("{}.{}", n / 10, n % 10)
|
||||
}
|
||||
/// The local time as "14:05".
|
||||
pub fn clock(hour: u8, minute: u8) -> String {
|
||||
format!("{hour:02}:{minute:02}")
|
||||
}
|
||||
|
||||
/// The keystrokes widget's keys, in drawing order.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Key {
|
||||
Forward,
|
||||
Left,
|
||||
Back,
|
||||
Right,
|
||||
Attack,
|
||||
Use,
|
||||
Jump,
|
||||
}
|
||||
impl Key {
|
||||
pub const ALL: [Key; 7] = [
|
||||
Key::Forward,
|
||||
Key::Left,
|
||||
Key::Back,
|
||||
Key::Right,
|
||||
Key::Attack,
|
||||
Key::Use,
|
||||
Key::Jump,
|
||||
];
|
||||
/// The key mapping it shows.
|
||||
pub fn mapping(self) -> &'static str {
|
||||
match self {
|
||||
Key::Forward => "key.forward",
|
||||
Key::Left => "key.left",
|
||||
Key::Back => "key.back",
|
||||
Key::Right => "key.right",
|
||||
Key::Attack => "key.attack",
|
||||
Key::Use => "key.use",
|
||||
Key::Jump => "key.jump",
|
||||
}
|
||||
}
|
||||
}
|
||||
/// The keystrokes' key size and the gap between keys, in GUI pixels.
|
||||
pub const KEY: f32 = 22.0;
|
||||
pub const KEY_GAP: f32 = 2.0;
|
||||
/// The space bar's height.
|
||||
pub const SPACE: f32 = 12.0;
|
||||
/// Where each key of the keystrokes widget sits in its box: W above A S D, the two mouse
|
||||
/// buttons under them, the space bar at the bottom, as `(x, y, w, h)`.
|
||||
pub fn key_rect(key: Key) -> (f32, f32, f32, f32) {
|
||||
let step = KEY + KEY_GAP;
|
||||
let full = 3.0 * KEY + 2.0 * KEY_GAP;
|
||||
let half = (full - KEY_GAP) / 2.0;
|
||||
match key {
|
||||
Key::Forward => (step, 0.0, KEY, KEY),
|
||||
Key::Left => (0.0, step, KEY, KEY),
|
||||
Key::Back => (step, step, KEY, KEY),
|
||||
Key::Right => (2.0 * step, step, KEY, KEY),
|
||||
Key::Attack => (0.0, 2.0 * step, half, KEY),
|
||||
Key::Use => (half + KEY_GAP, 2.0 * step, half, KEY),
|
||||
Key::Jump => (0.0, 3.0 * step, full, SPACE),
|
||||
}
|
||||
}
|
||||
/// The keystrokes widget's size.
|
||||
pub fn keys_size() -> (f32, f32) {
|
||||
let (_, y, w, h) = key_rect(Key::Jump);
|
||||
(w, y + h)
|
||||
}
|
||||
|
||||
/// The HUD widgets, for their places on screen ([`Layout`]).
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum WidgetId {
|
||||
Armour,
|
||||
Effects,
|
||||
Held,
|
||||
Ping,
|
||||
Coords,
|
||||
Server,
|
||||
Combo,
|
||||
Fps,
|
||||
Cps,
|
||||
Keys,
|
||||
Clock,
|
||||
Reach,
|
||||
Speed,
|
||||
}
|
||||
impl WidgetId {
|
||||
pub const ALL: [WidgetId; 13] = [
|
||||
WidgetId::Armour,
|
||||
WidgetId::Effects,
|
||||
WidgetId::Held,
|
||||
WidgetId::Ping,
|
||||
WidgetId::Coords,
|
||||
WidgetId::Server,
|
||||
WidgetId::Combo,
|
||||
WidgetId::Fps,
|
||||
WidgetId::Cps,
|
||||
WidgetId::Keys,
|
||||
WidgetId::Clock,
|
||||
WidgetId::Reach,
|
||||
WidgetId::Speed,
|
||||
];
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
WidgetId::Armour => "armour",
|
||||
WidgetId::Effects => "effects",
|
||||
WidgetId::Held => "held",
|
||||
WidgetId::Ping => "ping",
|
||||
WidgetId::Coords => "coords",
|
||||
WidgetId::Server => "server",
|
||||
WidgetId::Combo => "combo",
|
||||
WidgetId::Fps => "fps",
|
||||
WidgetId::Cps => "cps",
|
||||
WidgetId::Keys => "keys",
|
||||
WidgetId::Clock => "clock",
|
||||
WidgetId::Reach => "reach",
|
||||
WidgetId::Speed => "speed",
|
||||
}
|
||||
}
|
||||
pub fn index(self) -> usize {
|
||||
self as usize
|
||||
}
|
||||
}
|
||||
|
||||
/// Where a widget sits: in its corner, stacked with the others there (`at` is `None`), or at a
|
||||
/// place the player dragged it to, as fractions of the room the screen leaves around the box
|
||||
/// (0 at the left or top edge, 1 at the right or bottom), so it stays on screen at any size.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Place {
|
||||
pub at: Option<(f32, f32)>,
|
||||
/// Its size in percent.
|
||||
pub scale: u8,
|
||||
}
|
||||
impl Default for Place {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
at: None,
|
||||
scale: 100,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub const SCALES: (u8, u8) = (50, 200);
|
||||
/// Every widget's place.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub struct Layout(pub [Place; 13]);
|
||||
impl Layout {
|
||||
pub fn get(&self, id: WidgetId) -> Place {
|
||||
self.0[id.index()]
|
||||
}
|
||||
pub fn get_mut(&mut self, id: WidgetId) -> &mut Place {
|
||||
&mut self.0[id.index()]
|
||||
}
|
||||
/// One `hudPlace` value: `armour,0.25,0.5,150`, or `armour,corner,,150`.
|
||||
pub fn parse(&mut self, value: &str) {
|
||||
let mut parts = value.split(',');
|
||||
let Some(id) = parts
|
||||
.next()
|
||||
.and_then(|n| WidgetId::ALL.into_iter().find(|w| w.name() == n))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let (x, y, scale) = (parts.next(), parts.next(), parts.next());
|
||||
let frac = |v: Option<&str>| {
|
||||
v.and_then(|v| v.parse::<f32>().ok())
|
||||
.filter(|v| v.is_finite())
|
||||
.map(|v| v.clamp(0.0, 1.0))
|
||||
};
|
||||
let place = self.get_mut(id);
|
||||
place.at = frac(x).zip(frac(y));
|
||||
if let Some(s) = scale.and_then(|s| s.parse::<u8>().ok()) {
|
||||
place.scale = s.clamp(SCALES.0, SCALES.1);
|
||||
}
|
||||
}
|
||||
/// The `hudPlace` lines of the widgets that left their corner or their size.
|
||||
pub fn serialize(&self, out: &mut String) {
|
||||
use std::fmt::Write;
|
||||
for id in WidgetId::ALL {
|
||||
let p = self.get(id);
|
||||
if p == Place::default() {
|
||||
continue;
|
||||
}
|
||||
let _ = match p.at {
|
||||
Some((x, y)) => writeln!(out, "hudPlace:{},{x:.4},{y:.4},{}", id.name(), p.scale),
|
||||
None => writeln!(out, "hudPlace:{},corner,,{}", id.name(), p.scale),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
/// A box `w` x `h` placed at fractions `(fx, fy)` of the room left on a `screen`.
|
||||
pub fn from_fractions((fx, fy): (f32, f32), (w, h): (f32, f32), screen: (f32, f32)) -> (f32, f32) {
|
||||
(
|
||||
(fx * (screen.0 - w).max(0.0)).round(),
|
||||
(fy * (screen.1 - h).max(0.0)).round(),
|
||||
)
|
||||
}
|
||||
/// The fractions that put a `w` x `h` box at `(x, y)` on a `screen`, kept on screen.
|
||||
pub fn to_fractions((x, y): (f32, f32), (w, h): (f32, f32), screen: (f32, f32)) -> (f32, f32) {
|
||||
let frac = |v: f32, room: f32| {
|
||||
if room > 0.0 {
|
||||
(v / room).clamp(0.0, 1.0)
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
};
|
||||
(frac(x, screen.0 - w), frac(y, screen.1 - h))
|
||||
}
|
||||
/// How near, in GUI pixels, an edge must come to another to snap to it.
|
||||
pub const SNAP: f32 = 4.0;
|
||||
/// Snaps a box's start `x` of length `w` along one axis of length `screen`: to the screen's
|
||||
/// edges and middle, and to the edges of the `others` boxes (`(start, length)`), where one
|
||||
/// is within [`SNAP`]. Returns the new start and the line it snapped to, if any.
|
||||
pub fn snap(x: f32, w: f32, screen: f32, others: &[(f32, f32)]) -> (f32, Option<f32>) {
|
||||
let mut best: Option<(f32, f32, f32)> = None;
|
||||
let mut consider = |start: f32, line: f32| {
|
||||
let d = (start - x).abs();
|
||||
if d <= SNAP && best.is_none_or(|(b, _, _)| d < b) {
|
||||
best = Some((d, start, line));
|
||||
}
|
||||
};
|
||||
// The screen's edges, with the corners' margin, and its middle.
|
||||
consider(2.0, 0.0);
|
||||
consider(screen - 2.0 - w, screen);
|
||||
consider((screen - w) / 2.0, screen / 2.0);
|
||||
for &(o, ow) in others {
|
||||
// Side by side, or lined up on the same edge.
|
||||
consider(o + ow, o + ow);
|
||||
consider(o - w, o);
|
||||
consider(o, o);
|
||||
consider(o + ow - w, o + ow);
|
||||
}
|
||||
match best {
|
||||
Some((_, start, line)) => (start.clamp(0.0, (screen - w).max(0.0)), Some(line)),
|
||||
None => (x.clamp(0.0, (screen - w).max(0.0)), None),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -338,4 +652,141 @@ mod tests {
|
||||
assert_eq!(h, 18.0 + 10.0 + 20.0 + 2.0 * PAD - 2.0);
|
||||
assert!(size(&[]) == (0.0, 0.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clicks_count_the_presses_of_the_last_second() {
|
||||
let mut c = Clicks::default();
|
||||
for t in [0.0, 0.1, 0.5, 0.9] {
|
||||
c.press(t);
|
||||
}
|
||||
assert_eq!(c.per_second(0.95), 4);
|
||||
// 1.0 s after the first press it has left the window.
|
||||
assert_eq!(c.per_second(1.0), 3);
|
||||
assert_eq!(c.per_second(1.6), 1);
|
||||
assert_eq!(c.per_second(5.0), 0);
|
||||
// The ring never keeps more than 64 presses.
|
||||
for i in 0..200 {
|
||||
c.press(10.0 + f64::from(i) * 0.001);
|
||||
}
|
||||
assert_eq!(c.per_second(10.2), 64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn speed_is_horizontal_blocks_per_second_between_samples() {
|
||||
let mut s = Speed::default();
|
||||
assert_eq!(s.sample(0.0, 0.0, 0.0), 0);
|
||||
// Sprinting: 5.612 blocks per second along x, with a jump (y is ignored).
|
||||
assert_eq!(s.sample(0.5, 0.0, 0.1), 0, "too soon for a new sample");
|
||||
assert_eq!(s.sample(1.403, 0.0, 0.25), 56);
|
||||
// 3 east and 4 south in a second: 5 blocks per second.
|
||||
assert_eq!(s.sample(1.403 + 3.0, 4.0, 1.25), 50);
|
||||
// A teleport reads as standing still.
|
||||
assert_eq!(s.sample(1000.0, 4.0, 1.5), 0);
|
||||
assert_eq!(tenths(56), "5.6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reach_shows_the_last_hit_for_three_seconds() {
|
||||
let mut r = Reach::default();
|
||||
assert_eq!(r.hundredths(0.0), None);
|
||||
r.hit(2.954, 1.0);
|
||||
assert_eq!(r.hundredths(1.5), Some(295));
|
||||
assert_eq!(hundredths(295), "2.95");
|
||||
assert_eq!(hundredths(300), "3.00");
|
||||
assert_eq!(r.hundredths(4.1), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_clock_reads_hours_and_minutes() {
|
||||
assert_eq!(clock(14, 5), "14:05");
|
||||
assert_eq!(clock(0, 0), "00:00");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keystrokes_keys_tile_their_box_without_overlap() {
|
||||
let (w, h) = keys_size();
|
||||
assert_eq!((w, h), (70.0, 3.0 * 24.0 + 12.0));
|
||||
for (i, a) in Key::ALL.iter().enumerate() {
|
||||
let (x, y, kw, kh) = key_rect(*a);
|
||||
assert!(
|
||||
x >= 0.0 && y >= 0.0 && x + kw <= w && y + kh <= h,
|
||||
"{a:?} inside"
|
||||
);
|
||||
for b in &Key::ALL[i + 1..] {
|
||||
let (bx, by, bw, bh) = key_rect(*b);
|
||||
let apart = x + kw <= bx || bx + bw <= x || y + kh <= by || by + bh <= y;
|
||||
assert!(apart, "{a:?} and {b:?} overlap");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn places_round_trip_and_only_moved_widgets_are_written() {
|
||||
let mut l = Layout::default();
|
||||
let mut text = String::new();
|
||||
l.serialize(&mut text);
|
||||
assert_eq!(text, "", "a vanilla layout writes nothing");
|
||||
*l.get_mut(WidgetId::Keys) = Place {
|
||||
at: Some((0.25, 1.0)),
|
||||
scale: 150,
|
||||
};
|
||||
l.get_mut(WidgetId::Fps).scale = 75;
|
||||
l.serialize(&mut text);
|
||||
assert_eq!(
|
||||
text,
|
||||
"hudPlace:fps,corner,,75\nhudPlace:keys,0.2500,1.0000,150\n"
|
||||
);
|
||||
let mut back = Layout::default();
|
||||
for line in text.lines() {
|
||||
back.parse(line.split_once(':').unwrap().1);
|
||||
}
|
||||
assert_eq!(back, l);
|
||||
// Out of range values are clamped, unknown widgets and bad numbers ignored.
|
||||
back.parse("speed,-3,7,250");
|
||||
assert_eq!(back.get(WidgetId::Speed).at, Some((0.0, 1.0)));
|
||||
assert_eq!(back.get(WidgetId::Speed).scale, 200);
|
||||
back.parse("radar,0.5,0.5,100");
|
||||
back.parse("clock,NaN,0.5,100");
|
||||
assert_eq!(back.get(WidgetId::Clock).at, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fractions_keep_a_box_on_screen_at_any_size() {
|
||||
let screen = (480.0, 270.0);
|
||||
let at = to_fractions((100.0, 50.0), (80.0, 20.0), screen);
|
||||
assert_eq!(from_fractions(at, (80.0, 20.0), screen), (100.0, 50.0));
|
||||
// A box at the right edge stays at the right edge on a wider screen.
|
||||
let right = to_fractions((400.0, 0.0), (80.0, 20.0), screen);
|
||||
assert_eq!(right, (1.0, 0.0));
|
||||
assert_eq!(
|
||||
from_fractions(right, (80.0, 20.0), (960.0, 540.0)),
|
||||
(880.0, 0.0)
|
||||
);
|
||||
// Dragged past the edge: kept inside.
|
||||
assert_eq!(
|
||||
to_fractions((-30.0, 900.0), (80.0, 20.0), screen),
|
||||
(0.0, 1.0)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boxes_snap_to_screen_edges_middle_and_each_other() {
|
||||
// Near the left margin.
|
||||
assert_eq!(snap(4.5, 50.0, 480.0, &[]), (2.0, Some(0.0)));
|
||||
// Centred: the middle of 480 for a 50-wide box starts at 215.
|
||||
assert_eq!(snap(213.0, 50.0, 480.0, &[]), (215.0, Some(240.0)));
|
||||
// Beside another box (100..160): its start goes to 160.
|
||||
assert_eq!(
|
||||
snap(163.0, 50.0, 480.0, &[(100.0, 60.0)]),
|
||||
(160.0, Some(160.0))
|
||||
);
|
||||
// Lined up with its left edge.
|
||||
assert_eq!(
|
||||
snap(98.0, 50.0, 480.0, &[(100.0, 60.0)]),
|
||||
(100.0, Some(100.0))
|
||||
);
|
||||
// Too far from anything: stays, but never off screen.
|
||||
assert_eq!(snap(300.0, 50.0, 480.0, &[(100.0, 60.0)]), (300.0, None));
|
||||
assert_eq!(snap(470.0, 50.0, 480.0, &[]), (430.0, None));
|
||||
}
|
||||
}
|
||||
@@ -836,7 +836,14 @@ impl<G: ChunkGenerator> Source for Jobs<G> {
|
||||
Job::Mesh(..) => 5,
|
||||
_ => 6,
|
||||
};
|
||||
(kind, crate::spans::thread_cpu().unwrap_or_default())
|
||||
// The other jobs (saves, light views, freed meshes) are many and short: counted, not
|
||||
// timed, so the clock reads don't cost more than they do.
|
||||
let cpu = if kind == 6 {
|
||||
std::time::Duration::ZERO
|
||||
} else {
|
||||
crate::spans::thread_cpu().unwrap_or_default()
|
||||
};
|
||||
(kind, cpu)
|
||||
});
|
||||
let _tag = horizon_threads::heap_tag(match &job.job {
|
||||
Job::Generate(..) => "stream.generate",
|
||||
@@ -865,8 +872,13 @@ impl<G: ChunkGenerator> Source for Jobs<G> {
|
||||
} else {
|
||||
kind
|
||||
};
|
||||
let cpu = crate::spans::thread_cpu().unwrap_or_default();
|
||||
job_stats::add(kind, cpu.saturating_sub(started).as_nanos() as u64);
|
||||
let nanos = if kind == 6 {
|
||||
0
|
||||
} else {
|
||||
let cpu = crate::spans::thread_cpu().unwrap_or_default();
|
||||
cpu.saturating_sub(started).as_nanos() as u64
|
||||
};
|
||||
job_stats::add(kind, nanos);
|
||||
}
|
||||
if let Some(done) = done {
|
||||
self.shared.done.send(done);
|
||||
|
||||
@@ -614,7 +614,12 @@ fn button_with_room(
|
||||
room: f32,
|
||||
narration: impl FnOnce([f32; 4]) -> crate::widgets::Narratable,
|
||||
) {
|
||||
crate::widgets::record(&label.plain(), [x, y, w, h], active);
|
||||
let plain = label.plain();
|
||||
crate::widgets::record(&plain, [x, y, w, h], active);
|
||||
// An icon button has no label to record, and A presses it all the same.
|
||||
if active && plain.trim().is_empty() {
|
||||
crate::widgets::target([x, y, w, h]);
|
||||
}
|
||||
crate::widgets::narratable(|| narration([x, y, w, h]).hovered(hover));
|
||||
crate::widgets::widget_cursor(hover, active);
|
||||
let sprite = if !active {
|
||||
@@ -1846,6 +1851,43 @@ mod tests {
|
||||
assert_eq!(scrolled_left(10.0, 40.0, 1_000), 10.0 - offset);
|
||||
}
|
||||
|
||||
/// A controller reaches an icon button only through its target, and can't press a disabled one.
|
||||
#[test]
|
||||
fn an_active_icon_button_is_a_focus_target_and_a_disabled_one_is_not() {
|
||||
let atlas = atlas();
|
||||
let mut batch = HudBatch::new([640, 480]);
|
||||
// Whatever this thread drew before is the last frame's, then gone.
|
||||
crate::widgets::end_frame(u64::MAX - 1);
|
||||
button(
|
||||
&mut batch,
|
||||
&atlas,
|
||||
10.0,
|
||||
20.0,
|
||||
20.0,
|
||||
20.0,
|
||||
&Text::empty(),
|
||||
true,
|
||||
false,
|
||||
);
|
||||
button(
|
||||
&mut batch,
|
||||
&atlas,
|
||||
40.0,
|
||||
20.0,
|
||||
20.0,
|
||||
20.0,
|
||||
&Text::empty(),
|
||||
false,
|
||||
false,
|
||||
);
|
||||
crate::widgets::end_frame(u64::MAX);
|
||||
let targets: Vec<[f32; 4]> = crate::widgets::last_targets()
|
||||
.iter()
|
||||
.map(|t| t.rect)
|
||||
.collect();
|
||||
assert_eq!(targets, [[10.0, 20.0, 20.0, 20.0]]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_glyph_leaves_a_label_that_fits_centred() {
|
||||
let atlas = atlas();
|
||||
|
||||
@@ -65,7 +65,11 @@ struct Taa {
|
||||
jitter: vec4<f32>,
|
||||
};
|
||||
@group(0) @binding(2) var history: texture_2d<f32>;
|
||||
@group(0) @binding(3) var depth: texture_depth_2d;
|
||||
// The world's depth as an unfilterable float texture: naga's GLSL backend
|
||||
// cannot emit `textureLoad` from a depth texture, and a depth view bound as
|
||||
// `TextureSampleType::Float { filterable: false }` gives the same texel on
|
||||
// every backend.
|
||||
@group(0) @binding(3) var depth: texture_2d<f32>;
|
||||
@group(0) @binding(4) var<uniform> taa: Taa;
|
||||
|
||||
// Catmull-Rom over the history in 5 bilinear taps: sharper than one under motion.
|
||||
@@ -113,7 +117,7 @@ fn sample_history(uv: vec2<f32>) -> vec3<f32> {
|
||||
let c = textureLoad(world, q, 0).rgb;
|
||||
lo = min(lo, c);
|
||||
hi = max(hi, c);
|
||||
near = max(near, textureLoad(depth, q, 0));
|
||||
near = max(near, textureLoad(depth, q, 0).r);
|
||||
}
|
||||
}
|
||||
let ndc = vec2(in.uv.x * 2.0 - 1.0, 1.0 - in.uv.y * 2.0);
|
||||
|
||||
@@ -472,8 +472,8 @@
|
||||
"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_hide_fx": "Versteek ander spelers se spore en auras",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Ander spelers se spore en auras bly versteek; hul ander kosmetiese items wys steeds. By verstek af.",
|
||||
"horizon.extras.cosmetics.shown": "Getoon",
|
||||
"horizon.extras.cosmetics.hidden": "Versteek",
|
||||
"horizon.extras.cosmetics.name_tags": "Slegs naamplaatjies"
|
||||
|
||||
@@ -491,8 +491,8 @@
|
||||
"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_hide_fx": "إخفاء آثار وهالات اللاعبين الآخرين",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "تبقى آثار وهالات اللاعبين الآخرين مخفية؛ وتظل مستحضراتهم الأخرى ظاهرة. معطّل افتراضيًا.",
|
||||
"horizon.extras.cosmetics.shown": "معروض",
|
||||
"horizon.extras.cosmetics.hidden": "مخفي",
|
||||
"horizon.extras.cosmetics.name_tags": "علامات الاسم فقط"
|
||||
|
||||
@@ -463,8 +463,8 @@
|
||||
"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_hide_fx": "Digər oyunçuların izlərini və auralarını gizlət",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Digər oyunçuların izləri və auraları gizli qalır; digər kosmetikaları görünməyə davam edir. Defolt olaraq söndürülüb.",
|
||||
"horizon.extras.cosmetics.shown": "Göstərildi",
|
||||
"horizon.extras.cosmetics.hidden": "Gizli",
|
||||
"horizon.extras.cosmetics.name_tags": "Yalnız ad etiketləri"
|
||||
|
||||
@@ -489,8 +489,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Йәшерен: һәр Horizon уйынсы һеҙҙең косметикаһыҙ күрә, һеҙ ҙә индерелгән. Шул уҡ переключатель стартер’s Гардероб.",
|
||||
"horizon.extras.cosmetics_others": "Башҡа уйынсыларҙың косметикаһы",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Башҡа ниндәй Horizon уйынсылар кейә: барыһы ла, тик уларҙы исем биркаһы стилдәре, йәки бер нәмә лә. Йәшерен, уйын бер ҡасан да һорамай, улар нимә кейә.",
|
||||
"horizon.extras.cosmetics_auras": "Башҡаларҙың ауралары һәм эҙҙәре",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Аура һәм эҙҙәр башҡа уйынсылар кейә. Һүндерелгән по умолчанию.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Башҡа уйынсыларҙың эҙҙәрен һәм ауралары йәшереү",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Башҡа уйынсыларҙың эҙҙәре һәм ауралары йәшерен ҡала; уларҙың башҡа косметикаһы күренә. Ғәҙәттә һүндерелгән.",
|
||||
"horizon.extras.cosmetics.shown": "Күрһәтелгән",
|
||||
"horizon.extras.cosmetics.hidden": "Йәшерен",
|
||||
"horizon.extras.cosmetics.name_tags": "Исем билдәләре генә"
|
||||
|
||||
@@ -475,8 +475,8 @@
|
||||
"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_hide_fx": "Схаваць сляды і аўры іншых гульцоў",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Сляды і аўры іншых гульцоў схаваныя; іх астатняя касметыка бачная. Па змаўчанні выкл.",
|
||||
"horizon.extras.cosmetics.shown": "Паказана",
|
||||
"horizon.extras.cosmetics.hidden": "Схаваны",
|
||||
"horizon.extras.cosmetics.name_tags": "Толькі біркі з імёнамі"
|
||||
|
||||
@@ -470,8 +470,8 @@
|
||||
"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_hide_fx": "Скриване на следите и аурите на другите играчи",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Следите и аурите на другите играчи остават скрити; другите им козметики се виждат. Изключено по подразбиране.",
|
||||
"horizon.extras.cosmetics.shown": "Показано",
|
||||
"horizon.extras.cosmetics.hidden": "Скрити",
|
||||
"horizon.extras.cosmetics.name_tags": "Само етикети с имена",
|
||||
|
||||
@@ -474,8 +474,8 @@
|
||||
"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_hide_fx": "Kuzhat roudoù hag aouraoù ar c'hoarierien all",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Kuzhet e chom roudoù hag aouraoù ar c'hoarierien all; diskouezet e vez o c'hinkladurioù all. Diweredekaet dre ziouer.",
|
||||
"horizon.extras.cosmetics.shown": "Diskouezet",
|
||||
"horizon.extras.cosmetics.hidden": "Kuzhet",
|
||||
"horizon.extras.cosmetics.name_tags": "Tikedennoù anvioù hepken"
|
||||
|
||||
@@ -415,8 +415,8 @@
|
||||
"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_hide_fx": "Sakrij tragove i aure drugih igrača",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Tragovi i aure drugih igrača ostaju skriveni; njihova ostala kozmetika se i dalje vidi. Isključeno po zadanom.",
|
||||
"horizon.extras.cosmetics.shown": "Prikazano",
|
||||
"horizon.extras.cosmetics.hidden": "Skriveno",
|
||||
"horizon.extras.cosmetics.name_tags": "Samo oznake sa imenom",
|
||||
|
||||
@@ -463,8 +463,8 @@
|
||||
"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_hide_fx": "Amaga les esteles i les aures dels altres jugadors",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Les esteles i les aures dels altres jugadors queden amagades; els seus altres cosmètics es continuen veient. Desactivat per defecte.",
|
||||
"horizon.extras.cosmetics.shown": "Es mostra",
|
||||
"horizon.extras.cosmetics.hidden": "Ocult",
|
||||
"horizon.extras.cosmetics.name_tags": "Només etiquetes de nom",
|
||||
|
||||
@@ -453,8 +453,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Skryté: každý hráč Horizon vás vidí bez vaší kosmetiky, včetně vás. Stejný vypínač jako skříň odpalovacího zařízení.",
|
||||
"horizon.extras.cosmetics_others": "Kosmetika ostatních hráčů",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Co nosí ostatní hráči Horizon: všechno, jen styly jmenovek nebo nic. Skryto, hra se nikdy neptá, co nosí.",
|
||||
"horizon.extras.cosmetics_auras": "Aury a stezky ostatních",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Aury a stezky, které nosí ostatní hráči. Ve výchozím nastavení vypnuto.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Skrýt stopy a aury ostatních hráčů",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Stopy a aury ostatních hráčů zůstanou skryté; jejich ostatní kosmetika je dál vidět. Ve výchozím stavu vypnuto.",
|
||||
"horizon.extras.cosmetics.shown": "Zobrazeno",
|
||||
"horizon.extras.cosmetics.hidden": "Skrytá",
|
||||
"horizon.extras.cosmetics.name_tags": "Pouze jmenovky"
|
||||
|
||||
@@ -485,8 +485,8 @@
|
||||
"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_hide_fx": "Ытти вӑййисен йӗрӗсене тата аурисене пытар",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Ытти вӑййисен йӗрӗсем тата аурисем пытарӑнса юлаҫҫӗ; вӗсен ытти косметики курӑнать. Йӑлана кура сӳнтернӗ.",
|
||||
"horizon.extras.cosmetics.shown": "Кӑтартнӑ",
|
||||
"horizon.extras.cosmetics.hidden": "Пытарнӑ",
|
||||
"horizon.extras.cosmetics.name_tags": "Ят паллисем кӑна"
|
||||
|
||||
@@ -460,8 +460,8 @@
|
||||
"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_hide_fx": "Cuddio llwybrau ac awrâu chwaraewyr eraill",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Mae llwybrau ac awrâu chwaraewyr eraill yn aros yn gudd; mae eu colur arall yn dal i ddangos. I ffwrdd yn ddiofyn.",
|
||||
"horizon.extras.cosmetics.shown": "Dangosir",
|
||||
"horizon.extras.cosmetics.hidden": "Cudd",
|
||||
"horizon.extras.cosmetics.name_tags": "Tagiau enw yn unig"
|
||||
|
||||
@@ -440,8 +440,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Skjult: alle Horizon-spillere ser dig uden din kosmetik, inklusive dig. Den samme kontakt som løfterakettens garderobe.",
|
||||
"horizon.extras.cosmetics_others": "Andre spilleres kosmetik",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Hvad andre Horizon-spillere bærer: alt, kun deres navnemærke eller intet. Skjult spørger spillet aldrig, hvad de har på.",
|
||||
"horizon.extras.cosmetics_auras": "Andres auraer og spor",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auraer og spor, som andre spillere bærer. Fra som standard.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Skjul andre spilleres spor og auraer",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Andre spilleres spor og auraer forbliver skjulte; deres andre kosmetikker vises stadig. Slået fra som standard.",
|
||||
"horizon.extras.cosmetics.shown": "Vist",
|
||||
"horizon.extras.cosmetics.hidden": "Skjult",
|
||||
"horizon.extras.cosmetics.name_tags": "Kun navneskilte",
|
||||
|
||||
@@ -357,10 +357,14 @@
|
||||
"horizon.extras.header.cosmetics": "Kosmetik",
|
||||
"horizon.extras.cosmetics_mine": "Meine Kosmetik",
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Ausgeblendet: Alle Horizon-Spieler sehen dich ohne deine Kosmetik, du selbst auch. Derselbe Schalter wie in der Garderobe des Launchers.",
|
||||
"key.emote": "Emote-Rad",
|
||||
"horizon.emote_wheel.title": "Emotes",
|
||||
"horizon.emote_wheel.empty": "Noch keine Emotes",
|
||||
"horizon.emote_wheel.help": "Loslassen oder klicken zum Abspielen · 1–8 · Esc schließt",
|
||||
"horizon.extras.cosmetics_others": "Kosmetik anderer Spieler",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Was andere Horizon-Spieler tragen: alles, nur ihre Namensschild-Stile oder nichts. Ausgeblendet fragt das Spiel nie, was sie tragen.",
|
||||
"horizon.extras.cosmetics_auras": "Auren und Spuren anderer",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auren und Spuren, die andere Spieler tragen. Standardmäßig aus.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Spuren und Auren anderer Spieler ausblenden",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Spuren und Auren anderer Spieler bleiben ausgeblendet; ihre anderen Kosmetika sind weiter sichtbar. Standardmäßig aus.",
|
||||
"horizon.extras.cosmetics.shown": "Angezeigt",
|
||||
"horizon.extras.cosmetics.hidden": "Ausgeblendet",
|
||||
"horizon.extras.cosmetics.name_tags": "Nur Namensschilder",
|
||||
|
||||
@@ -462,8 +462,8 @@
|
||||
"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_hide_fx": "Απόκρυψη ιχνών και αυρών άλλων παικτών",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Τα ίχνη και οι αύρες των άλλων παικτών μένουν κρυφά· τα άλλα διακοσμητικά τους εξακολουθούν να φαίνονται. Ανενεργό από προεπιλογή.",
|
||||
"horizon.extras.cosmetics.shown": "Εμφανίζεται",
|
||||
"horizon.extras.cosmetics.hidden": "Κρυφό",
|
||||
"horizon.extras.cosmetics.name_tags": "Μόνο ετικέτες ονομάτων",
|
||||
|
||||
@@ -97,6 +97,25 @@
|
||||
"horizon.mods.settings": "Settings",
|
||||
"horizon.mods.in_world": " (leave the world to change)",
|
||||
"horizon.mods.open_folder": "Open Mod Folder",
|
||||
"horizon.mods.browse": "Browse Mods...",
|
||||
"horizon.mods.browse.title": "Browse Mods",
|
||||
"horizon.mods.browse.search": "Search mods...",
|
||||
"horizon.mods.browse.project": "Mod",
|
||||
"horizon.mods.browse.loading": "Reading Horizon's mod registry...",
|
||||
"horizon.mods.browse.failed": "Couldn't read the mod registry: %s",
|
||||
"horizon.mods.browse.none": "No mod found",
|
||||
"horizon.mods.browse.has_update": " (update)",
|
||||
"horizon.mods.browse.is_installed": " (installed)",
|
||||
"horizon.mods.browse.by": "by %s",
|
||||
"horizon.mods.browse.installing": "Installing...",
|
||||
"horizon.mods.browse.install": "Install",
|
||||
"horizon.mods.browse.update": "Update",
|
||||
"horizon.mods.browse.installed": "Installed",
|
||||
"horizon.mods.browse.open_page": "Open Page",
|
||||
"horizon.mods.browse.enabled": "Mod",
|
||||
"horizon.mods.browse.changelog": "Changelog",
|
||||
"horizon.mods.browse.done": "%s %s is installed and on. Its content loads when the game starts again.",
|
||||
"horizon.mods.browse.install_failed": "Couldn't install it: %s",
|
||||
"horizon.shader_packs.empty": "No shader packs: put them in the shaderpacks folder",
|
||||
"horizon.shader_packs.kind_horizon": " (Horizon)",
|
||||
"horizon.shader_options.open": "Shader Options...",
|
||||
@@ -557,10 +576,14 @@
|
||||
"horizon.extras.header.cosmetics": "Cosmetics",
|
||||
"horizon.extras.cosmetics_mine": "My cosmetics",
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Hidden: every Horizon player sees you without your cosmetics, you included. The same switch as the launcher's Wardrobe.",
|
||||
"key.emote": "Emote Wheel",
|
||||
"horizon.emote_wheel.title": "Emotes",
|
||||
"horizon.emote_wheel.empty": "No emotes yet",
|
||||
"horizon.emote_wheel.help": "Release or click to play · 1–8 · Esc closes",
|
||||
"horizon.extras.cosmetics_others": "Other players' cosmetics",
|
||||
"horizon.extras.cosmetics_others.tooltip": "What other Horizon players wear: everything, only their name tag styles, or nothing. Hidden unless you own cosmetics, until you choose. Hidden, the game never asks what they wear.",
|
||||
"horizon.extras.cosmetics_auras": "Others' auras and trails",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auras and trails other players wear. Off by default.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Hide other players' trails and auras",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Other players' trails and auras stay hidden; their other cosmetics still show. Off by default.",
|
||||
"horizon.extras.cosmetics.shown": "Shown",
|
||||
"horizon.extras.cosmetics.hidden": "Hidden",
|
||||
"horizon.extras.cosmetics.name_tags": "Name tags only",
|
||||
@@ -676,6 +699,12 @@
|
||||
"horizon.pvp.hud_held_corner.tooltip": "Where the count shows.",
|
||||
"horizon.pvp.hud_background": "Widget backgrounds",
|
||||
"horizon.pvp.hud_background.tooltip": "A dark box behind each widget.",
|
||||
"horizon.pvp.hud_opacity": "Background opacity",
|
||||
"horizon.pvp.hud_opacity.tooltip": "How dark the boxes behind the widgets are.",
|
||||
"horizon.pvp.hud_text_colour": "Text colour",
|
||||
"horizon.pvp.hud_text_colour.tooltip": "The widgets' text colour. Durability keeps its green to red.",
|
||||
"horizon.pvp.hud_shadow": "Text shadow",
|
||||
"horizon.pvp.hud_shadow.tooltip": "A shadow under the widgets' text, as vanilla's HUD text has.",
|
||||
"horizon.pvp.header.hud": "HUD widgets",
|
||||
"horizon.pvp.corner.top_left": "Top left",
|
||||
"horizon.pvp.corner.top_right": "Top right",
|
||||
@@ -705,6 +734,37 @@
|
||||
"horizon.pvp.hud.direction.south": "South",
|
||||
"horizon.pvp.hud.direction.east": "East",
|
||||
"horizon.pvp.hud.direction.west": "West",
|
||||
"horizon.pvp.hud_fps": "FPS",
|
||||
"horizon.pvp.hud_fps.tooltip": "Frames drawn in the last second.",
|
||||
"horizon.pvp.hud_fps_corner": "FPS corner",
|
||||
"horizon.pvp.hud_fps_corner.tooltip": "Where the FPS shows.",
|
||||
"horizon.pvp.hud_cps": "Clicks per second",
|
||||
"horizon.pvp.hud_cps.tooltip": "Your left and right clicks in the last second.",
|
||||
"horizon.pvp.hud_cps_corner": "CPS corner",
|
||||
"horizon.pvp.hud_cps_corner.tooltip": "Where the clicks per second show.",
|
||||
"horizon.pvp.hud_keys": "Keystrokes",
|
||||
"horizon.pvp.hud_keys.tooltip": "Your movement keys, mouse buttons and jump, lit while you hold them, with clicks per second.",
|
||||
"horizon.pvp.hud_keys_corner": "Keystrokes corner",
|
||||
"horizon.pvp.hud_keys_corner.tooltip": "Where the keystrokes show.",
|
||||
"horizon.pvp.hud_clock": "Clock",
|
||||
"horizon.pvp.hud_clock.tooltip": "Your computer's time.",
|
||||
"horizon.pvp.hud_clock_corner": "Clock corner",
|
||||
"horizon.pvp.hud_clock_corner.tooltip": "Where the clock shows.",
|
||||
"horizon.pvp.hud_reach": "Reach",
|
||||
"horizon.pvp.hud_reach.tooltip": "How far from your eyes your last hit landed, for 3 seconds. It only measures: your reach stays vanilla's.",
|
||||
"horizon.pvp.hud_reach_corner": "Reach corner",
|
||||
"horizon.pvp.hud_reach_corner.tooltip": "Where the reach shows.",
|
||||
"horizon.pvp.hud_speed": "Speed",
|
||||
"horizon.pvp.hud_speed.tooltip": "How fast you move across the ground, in blocks per second.",
|
||||
"horizon.pvp.hud_speed_corner": "Speed corner",
|
||||
"horizon.pvp.hud_speed_corner.tooltip": "Where the speed shows.",
|
||||
"horizon.pvp.hud.fps": "%s FPS",
|
||||
"horizon.pvp.hud.cps": "CPS: %s | %s",
|
||||
"horizon.pvp.hud.reach": "Reach: %s",
|
||||
"horizon.pvp.hud.speed": "Speed: %s b/s",
|
||||
"horizon.pvp.hud.keys.attack": "LMB",
|
||||
"horizon.pvp.hud.keys.use": "RMB",
|
||||
"horizon.pvp.hud.keys.cps": "%s CPS",
|
||||
"horizon.pvp.numeric_enchants": "Enchantment levels as numbers",
|
||||
"horizon.pvp.numeric_enchants.tooltip": "Tooltips and the enchanting table show Sharpness 5 instead of Sharpness V.",
|
||||
"horizon.pvp.header.items": "Items",
|
||||
|
||||
@@ -468,8 +468,8 @@
|
||||
"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_hide_fx": "Kaŝi spurojn kaj aŭrojn de aliaj ludantoj",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Spuroj kaj aŭroj de aliaj ludantoj restas kaŝitaj; iliaj aliaj kosmetikaĵoj ankoraŭ montriĝas. Defaŭlte malŝaltita.",
|
||||
"horizon.extras.cosmetics.shown": "Montrita",
|
||||
"horizon.extras.cosmetics.hidden": "Kaŝita",
|
||||
"horizon.extras.cosmetics.name_tags": "Nomaj etikedoj nur"
|
||||
|
||||
@@ -464,8 +464,8 @@
|
||||
"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_hide_fx": "Peida teiste mängijate jäljed ja aurad",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Teiste mängijate jäljed ja aurad jäävad peidetuks; nende muu kosmeetika on endiselt näha. Vaikimisi väljas.",
|
||||
"horizon.extras.cosmetics.shown": "Näidatud",
|
||||
"horizon.extras.cosmetics.hidden": "Peidetud",
|
||||
"horizon.extras.cosmetics.name_tags": "Ainult nimesildid"
|
||||
|
||||
@@ -469,8 +469,8 @@
|
||||
"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_hide_fx": "Ezkutatu beste jokalarien arrastoak eta aurak",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Beste jokalarien arrastoak eta aurak ezkutuan geratzen dira; haien beste kosmetikoak ikusten jarraitzen dute. Desaktibatuta lehenespenez.",
|
||||
"horizon.extras.cosmetics.shown": "Erakutsita",
|
||||
"horizon.extras.cosmetics.hidden": "Ezkutuan",
|
||||
"horizon.extras.cosmetics.name_tags": "Izena etiketak soilik"
|
||||
|
||||
@@ -474,8 +474,8 @@
|
||||
"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_hide_fx": "پنهان کردن ردها و هالههای بازیکنان دیگر",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "ردها و هالههای بازیکنان دیگر پنهان میمانند؛ دیگر آرایههایشان همچنان دیده میشوند. بهطور پیشفرض خاموش.",
|
||||
"horizon.extras.cosmetics.shown": "نشان داده شده است",
|
||||
"horizon.extras.cosmetics.hidden": "پنهان شده است",
|
||||
"horizon.extras.cosmetics.name_tags": "فقط برچسب های نام"
|
||||
|
||||
@@ -449,8 +449,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Piilotettu: jokainen Horizon-pelaaja näkee sinut ilman kosmetiikkaasi, sinä mukaan lukien. Sama kytkin kuin kantoraketin vaatekaappi.",
|
||||
"horizon.extras.cosmetics_others": "Muiden pelaajien kosmetiikkaa",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Mitä muut Horizon-pelaajat käyttävät: kaikki, vain heidän nimilaputyylinsä tai ei mitään. Piilotettu, peli ei koskaan kysy, mitä he käyttävät.",
|
||||
"horizon.extras.cosmetics_auras": "Toisten aurat ja polut",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Aurat ja jäljet, joita muut pelaajat käyttävät. Oletuksena pois päältä.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Piilota muiden pelaajien jäljet ja aurat",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Muiden pelaajien jäljet ja aurat pysyvät piilossa; heidän muut kosmetiikkansa näkyvät yhä. Oletuksena pois.",
|
||||
"horizon.extras.cosmetics.shown": "Näytetään",
|
||||
"horizon.extras.cosmetics.hidden": "Piilotettu",
|
||||
"horizon.extras.cosmetics.name_tags": "Vain nimilaput",
|
||||
|
||||
@@ -421,8 +421,8 @@
|
||||
"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_hide_fx": "Itago ang mga bakas at aura ng ibang manlalaro",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Nakatago ang mga bakas at aura ng ibang manlalaro; nakikita pa rin ang iba nilang cosmetics. Naka-off bilang default.",
|
||||
"horizon.extras.cosmetics.shown": "Ipinakita",
|
||||
"horizon.extras.cosmetics.hidden": "Nakatago",
|
||||
"horizon.extras.cosmetics.name_tags": "Mga name tag lang",
|
||||
|
||||
@@ -471,8 +471,8 @@
|
||||
"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_hide_fx": "Fjal spor og aurur hjá øðrum leikarum",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Spor og aurur hjá øðrum leikarum verða fjald; teirra aðrir prýðislutir síggjast framvegis. Sløkt sum standard.",
|
||||
"horizon.extras.cosmetics.shown": "Víst",
|
||||
"horizon.extras.cosmetics.hidden": "Dult",
|
||||
"horizon.extras.cosmetics.name_tags": "Einans navnamerki"
|
||||
|
||||
@@ -46,6 +46,25 @@
|
||||
"horizon.mods.dev": " (dév.)",
|
||||
"horizon.mods.settings": "Options",
|
||||
"horizon.mods.open_folder": "Ouvrir le dossier des mods",
|
||||
"horizon.mods.browse": "Parcourir les mods...",
|
||||
"horizon.mods.browse.title": "Parcourir les mods",
|
||||
"horizon.mods.browse.search": "Rechercher des mods...",
|
||||
"horizon.mods.browse.project": "Mod",
|
||||
"horizon.mods.browse.loading": "Lecture du registre des mods Horizon...",
|
||||
"horizon.mods.browse.failed": "Impossible de lire le registre des mods : %s",
|
||||
"horizon.mods.browse.none": "Aucun mod trouvé",
|
||||
"horizon.mods.browse.has_update": " (mise à jour)",
|
||||
"horizon.mods.browse.is_installed": " (installé)",
|
||||
"horizon.mods.browse.by": "par %s",
|
||||
"horizon.mods.browse.installing": "Installation...",
|
||||
"horizon.mods.browse.install": "Installer",
|
||||
"horizon.mods.browse.update": "Mettre à jour",
|
||||
"horizon.mods.browse.installed": "Installé",
|
||||
"horizon.mods.browse.open_page": "Ouvrir la page",
|
||||
"horizon.mods.browse.enabled": "Mod",
|
||||
"horizon.mods.browse.changelog": "Nouveautés",
|
||||
"horizon.mods.browse.done": "%s %s est installé et activé. Son contenu se charge au prochain démarrage du jeu.",
|
||||
"horizon.mods.browse.install_failed": "Installation impossible : %s",
|
||||
"horizon.shader_packs.empty": "Aucun pack de shaders : placez-les dans le dossier shaderpacks",
|
||||
"horizon.shader_options.open": "Options des shaders...",
|
||||
"horizon.shader_options.profile": "Profil",
|
||||
@@ -394,10 +413,14 @@
|
||||
"horizon.extras.header.cosmetics": "Cosmétiques",
|
||||
"horizon.extras.cosmetics_mine": "Mes cosmétiques",
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Masqués : tous les joueurs Horizon vous voient sans vos cosmétiques, vous compris. Le même réglage que la garde-robe du launcher.",
|
||||
"key.emote": "Roue des emotes",
|
||||
"horizon.emote_wheel.title": "Emotes",
|
||||
"horizon.emote_wheel.empty": "Pas encore d'emote",
|
||||
"horizon.emote_wheel.help": "Relâche ou clique pour jouer · 1–8 · Échap ferme",
|
||||
"horizon.extras.cosmetics_others": "Cosmétiques des autres",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Ce que portent les autres joueurs Horizon : tout, seulement leur style d'étiquette de nom, ou rien. Masqués si vous n'avez pas de cosmétiques, tant que vous n'avez pas choisi. Masqués, le jeu ne demande jamais ce qu'ils portent.",
|
||||
"horizon.extras.cosmetics_auras": "Auras et traînées des autres",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Les auras et traînées que portent les autres joueurs. Désactivé par défaut.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Masquer les traînées et auras des autres joueurs",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Les traînées et auras des autres joueurs restent masquées ; leurs autres cosmétiques restent visibles. Désactivé par défaut.",
|
||||
"horizon.extras.cosmetics.shown": "Affichés",
|
||||
"horizon.extras.cosmetics.hidden": "Masqués",
|
||||
"horizon.extras.cosmetics.name_tags": "Étiquettes seulement",
|
||||
|
||||
@@ -467,8 +467,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Scuindût: ogni zuiadôr Horizon al viôt cence il to cosmetic, tu ancje. Il stes interrutôr dal Armadi dal lançadôr.",
|
||||
"horizon.extras.cosmetics_others": "I cosmetics dai altris zuiadôrs",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Ce che a metin sù altris zuiadôrs Horizon: dut, dome i lôr stîi di etichetis, o nuie. Scuindût, il zûc nol domande mai ce che a metin sù.",
|
||||
"horizon.extras.cosmetics_auras": "Auris e sentiers di altris",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auris e pistis che a metin sù altris zuiadôrs. Disactivât par automatic.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Plate lis olmis e lis aureis dai altris zuiadôrs",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Lis olmis e lis aureis dai altris zuiadôrs a restin platadis; i lôr altris cosmetics si viodin ancjemò. Distudât di predefinît.",
|
||||
"horizon.extras.cosmetics.shown": "Mostrât",
|
||||
"horizon.extras.cosmetics.hidden": "Scuindût",
|
||||
"horizon.extras.cosmetics.name_tags": "Dome lis etichetis dai nons"
|
||||
|
||||
@@ -399,8 +399,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Ferburgen: elke Horizon-spiler sjocht jo sûnder jo kosmetika, jo ynbegrepen. Deselde switch as de Launcher syn Wardrobe.",
|
||||
"horizon.extras.cosmetics_others": "Oare spilers 'cosmetica",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Wat oare Horizon-spilers drage: alles, allinich har nammelabelstilen, of neat. Ferburgen, it spultsje freget noait wat se drage.",
|
||||
"horizon.extras.cosmetics_auras": "Oare aura's en spoaren",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auras en spoaren oare spilers drage. Standert út.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Spoaren en aura's fan oare spilers ferbergje",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Spoaren en aura's fan oare spilers bliuwe ferburgen; harren oare kosmetika bliuwt sichtber. Standert út.",
|
||||
"horizon.extras.cosmetics.shown": "Oanjûn",
|
||||
"horizon.extras.cosmetics.hidden": "Ferburgen",
|
||||
"horizon.extras.cosmetics.name_tags": "Allinnich nammelabels"
|
||||
|
||||
@@ -463,8 +463,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "I bhfolach: feiceann gach imreoirHorizon tú gan do chosmaidí, tú san áireamh. An lasc céanna le Wardrobe an tosaitheoir.",
|
||||
"horizon.extras.cosmetics_others": "Cosmaidí imreoirí eile",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Cad a chaitheann imreoirí Horizon eile: gach rud, gan ach a stíleanna clibeanna ainm, nó rud ar bith. I bhfolach, ní iarrann an cluiche riamh cad a chaitheann siad.",
|
||||
"horizon.extras.cosmetics_auras": "Auras agus cosáin daoine eile",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auras agus rianta a chaitheamh imreoirí eile. Off de réir réamhshocraithe.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Folaigh rianta agus aurataí imreoirí eile",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Fanann rianta agus aurataí imreoirí eile i bhfolach; feictear a gcuid maisithe eile fós. As de réir réamhshocraithe.",
|
||||
"horizon.extras.cosmetics.shown": "Taispeántar",
|
||||
"horizon.extras.cosmetics.hidden": "i bhfolach",
|
||||
"horizon.extras.cosmetics.name_tags": "Clibeanna ainm amháin"
|
||||
|
||||
@@ -464,8 +464,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Falaichte: chì a h-uile cluicheadair Horizon thu às aonais na cungaidhean maise agad, thug thu a-steach. An aon tionndadh ri Wardrobe an lannsair.",
|
||||
"horizon.extras.cosmetics_others": "Coltas cluicheadairean eile",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Na bhios cluicheadairean Horizon eile a’ caitheamh: a h-uile càil, dìreach na stoidhlichean tag ainm aca, no dad. Falaichte, cha bhith an geama a-riamh a’ faighneachd dè a bhios orra.",
|
||||
"horizon.extras.cosmetics_auras": "Auras agus slighean cuid eile",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auras agus slighean bidh cluicheadairean eile a’ caitheamh. Cuir dheth gu bunaiteach.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Falaich slighean is aurathan chluicheadairean eile",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Fanaidh slighean is aurathan chluicheadairean eile falaichte; chithear an cuid maise eile fhathast. Dheth a ghnàth.",
|
||||
"horizon.extras.cosmetics.shown": "Air a shealltainn",
|
||||
"horizon.extras.cosmetics.hidden": "Falaichte",
|
||||
"horizon.extras.cosmetics.name_tags": "Tagaichean ainmean a-mhàin"
|
||||
|
||||
@@ -463,8 +463,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Oculto: todos os xogadores de Horizon venche sen os teus cosméticos, ti incluído. O mesmo interruptor que o garda-roupa do lanzador.",
|
||||
"horizon.extras.cosmetics_others": "Cosmética doutros xogadores",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Que usan outros Horizon xogadores: todo, só os seus estilos de etiqueta de nome ou nada. Oculto, o xogo nunca pregunta que visten.",
|
||||
"horizon.extras.cosmetics_auras": "Auras e sendeiros doutros",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auras e rastros que levan outros xogadores. Desactivado por defecto.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Agochar as estelas e auras doutros xogadores",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "As estelas e auras doutros xogadores quedan agochadas; os seus outros cosméticos séguense vendo. Desactivado por defecto.",
|
||||
"horizon.extras.cosmetics.shown": "Amosado",
|
||||
"horizon.extras.cosmetics.hidden": "Oculto",
|
||||
"horizon.extras.cosmetics.name_tags": "Só etiquetas de nome"
|
||||
|
||||
@@ -458,8 +458,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Huna: ʻike kēlā me kēia Horizon iā ʻoe me ka ʻole o kāu mea hoʻonaninani, ua hoʻokomo ʻoe. ʻO ka hoʻololi like e like me ka mea hoʻolele lole.",
|
||||
"horizon.extras.cosmetics_others": "ʻO nā mea hoʻonani o nā mea pāʻani ʻē aʻe",
|
||||
"horizon.extras.cosmetics_others.tooltip": "He aha nā mea pāʻani Horizon ʻē aʻe e ʻaʻahu ai: nā mea āpau, nā ʻano inoa inoa wale nō, a ʻaʻohe mea. Huna, ʻaʻole nīnau ka pāʻani i kā lākou lole.",
|
||||
"horizon.extras.cosmetics_auras": "ʻO nā aura a me nā alahele o nā poʻe ʻē aʻe",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "ʻO Auras a me nā alahele e hoʻohana ai nā mea pāʻani. Paʻa ma ka paʻamau.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Hūnā i nā meheu a me nā aura o nā mea pāʻani ʻē aʻe",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Hūnā ʻia nā meheu a me nā aura o nā mea pāʻani ʻē aʻe; ʻike ʻia nō kā lākou mau mea hoʻonani ʻē aʻe. Pio ma ke ʻano maʻamau.",
|
||||
"horizon.extras.cosmetics.shown": "Hōʻike ʻia",
|
||||
"horizon.extras.cosmetics.hidden": "Huna",
|
||||
"horizon.extras.cosmetics.name_tags": "Nā inoa inoa wale nō"
|
||||
|
||||
@@ -465,8 +465,8 @@
|
||||
"horizon.extras.cosmetics_mine": "הקוסמטיקה שלי",
|
||||
"horizon.extras.cosmetics_others": "מוצרי קוסמטיקה של שחקנים אחרים",
|
||||
"horizon.extras.cosmetics_others.tooltip": "מה שחקני Horizon אחרים לובשים: הכל, רק סגנונות תג השם שלהם, או כלום. נסתר, המשחק אף פעם לא שואל מה הם לובשים.",
|
||||
"horizon.extras.cosmetics_auras": "ההילות והשבילים של אחרים",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "הילות ושבילים ששחקנים אחרים לובשים. כבוי כברירת מחדל.",
|
||||
"horizon.extras.cosmetics_hide_fx": "הסתרת שבילים והילות של שחקנים אחרים",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "השבילים וההילות של שחקנים אחרים נשארים מוסתרים; שאר הקוסמטיקה שלהם עדיין מוצגת. כבוי כברירת מחדל.",
|
||||
"horizon.extras.cosmetics.shown": "מוצג",
|
||||
"horizon.extras.cosmetics.hidden": "מוסתר",
|
||||
"horizon.extras.cosmetics.name_tags": "תגי שם בלבד",
|
||||
|
||||
@@ -458,8 +458,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Rejtett: minden Horizon játékos lát téged a kozmetikumai nélkül, beleértve Önt is. Ugyanaz a kapcsoló, mint az indító gardróbjában.",
|
||||
"horizon.extras.cosmetics_others": "Más játékosok kozmetikumai",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Amit a többi Horizon játékos visel: mindent, csak a névcímkék stílusát, vagy semmit. Rejtett, a játék soha nem kérdezi, mit viselnek.",
|
||||
"horizon.extras.cosmetics_auras": "Mások aurái és nyomai",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Más játékosok által viselt aurák és nyomvonalak. Alapértelmezés szerint ki.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Más játékosok nyomainak és auráinak elrejtése",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Más játékosok nyomai és aurái rejtve maradnak; a többi kozmetikumuk továbbra is látszik. Alapértelmezetten ki.",
|
||||
"horizon.extras.cosmetics.shown": "Megjelenítve",
|
||||
"horizon.extras.cosmetics.hidden": "Rejtett",
|
||||
"horizon.extras.cosmetics.name_tags": "Csak névcímkék"
|
||||
|
||||
@@ -464,8 +464,8 @@
|
||||
"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_hide_fx": "Թաքցնել այլ խաղացողների հետքերն ու աուրաները",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Այլ խաղացողների հետքերն ու աուրաները մնում են թաքնված. նրանց մյուս կոսմետիկան դեռ երևում է։ Լռելյայն անջատված է։",
|
||||
"horizon.extras.cosmetics.shown": "Ցուցադրված է",
|
||||
"horizon.extras.cosmetics.hidden": "Թաքնված",
|
||||
"horizon.extras.cosmetics.name_tags": "Միայն անունների պիտակներ"
|
||||
|
||||
@@ -460,8 +460,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Zoro ezo: Onye ọkpụkpọ ọ bụla Horizon na-ahụ gị na-enweghị ihe ịchọ mma gị, ị gụnyere. Otu mgbanwe dị ka Wardrobe nke onye mbido.",
|
||||
"horizon.extras.cosmetics_others": "Ngwa mma ndị egwuregwu ndị ọzọ",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Ihe ndị ọzọ Horizon Player na-eyi: ihe niile, naanị aha mkpado styles, ma ọ bụ ihe ọ bụla. Na nzuzo, egwuregwu anaghị ajụ ihe ha na-eyi.",
|
||||
"horizon.extras.cosmetics_auras": "Auras na ụzọ ndị ọzọ",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auras na ụzọ ndị egwuregwu ndị ọzọ na-eyi. Gbanyụọ na ndabara.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Zoo akara ụkwụ na aura ndị egwuregwu ndị ọzọ",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Akara ụkwụ na aura ndị egwuregwu ndị ọzọ na-anọgide zoro ezo; ihe ịchọ mma ha ndị ọzọ ka na-egosi. Gbanyụrụ na ndabara.",
|
||||
"horizon.extras.cosmetics.shown": "Egosiri",
|
||||
"horizon.extras.cosmetics.hidden": "Zoro ezo",
|
||||
"horizon.extras.cosmetics.name_tags": "Naanị mkpado aha"
|
||||
|
||||
@@ -465,8 +465,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Falinn: sérhver Horizon leikmaður sér þig án snyrtivörunnar þinnar, þú meðtalin. Sami rofi og fataskápur sjósetjunnar.",
|
||||
"horizon.extras.cosmetics_others": "Snyrtivörur annarra leikmanna",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Það sem aðrir Horizon-spilarar klæðast: allt, aðeins nafnmerkisstíllinn eða ekkert. Falinn, leikurinn spyr aldrei hverju þeir klæðist.",
|
||||
"horizon.extras.cosmetics_auras": "Aurar og slóðir annarra",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auras og slóðir sem aðrir leikmenn klæðast. Slökkt sjálfgefið.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Fela slóðir og árur annarra leikmanna",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Slóðir og árur annarra leikmanna eru faldar; aðrir skrautmunir þeirra sjást áfram. Slökkt sjálfgefið.",
|
||||
"horizon.extras.cosmetics.shown": "Sýnd",
|
||||
"horizon.extras.cosmetics.hidden": "Falinn",
|
||||
"horizon.extras.cosmetics.name_tags": "Eingöngu nafnamerki",
|
||||
|
||||
@@ -480,8 +480,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Nascosto: ogni giocatore di Horizon ti vede senza i tuoi cosmetici, incluso te. Lo stesso interruttore dell'armadio del lanciatore.",
|
||||
"horizon.extras.cosmetics_others": "Cosmetici di altri giocatori",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Cosa indossano gli altri giocatori di Horizon: tutto, solo lo stile delle etichette con il nome o niente. Nascosto, il gioco non chiede mai cosa indossano.",
|
||||
"horizon.extras.cosmetics_auras": "Aure e tracce degli altri",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Aure e tracce indossate da altri giocatori. Disattivato per impostazione predefinita.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Nascondi scie e aure degli altri giocatori",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Le scie e le aure degli altri giocatori restano nascoste; gli altri loro cosmetici restano visibili. Disattivato per impostazione predefinita.",
|
||||
"horizon.extras.cosmetics.shown": "Mostrato",
|
||||
"horizon.extras.cosmetics.hidden": "Nascosto",
|
||||
"horizon.extras.cosmetics.name_tags": "Solo targhette con nome"
|
||||
|
||||
@@ -487,8 +487,8 @@
|
||||
"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_hide_fx": "他のプレイヤーの軌跡とオーラを隠す",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "他のプレイヤーの軌跡とオーラを非表示にします。それ以外のコスメティックは表示されたままです。デフォルトはオフ。",
|
||||
"horizon.extras.cosmetics.shown": "示されている",
|
||||
"horizon.extras.cosmetics.hidden": "隠された",
|
||||
"horizon.extras.cosmetics.name_tags": "ネームタグのみ"
|
||||
|
||||
@@ -472,8 +472,8 @@
|
||||
"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_hide_fx": "სხვა მოთამაშეების კვალისა და აურების დამალვა",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "სხვა მოთამაშეების კვალი და აურები დამალული რჩება; მათი სხვა კოსმეტიკა კვლავ ჩანს. ნაგულისხმევად გამორთულია.",
|
||||
"horizon.extras.cosmetics.shown": "ნაჩვენებია",
|
||||
"horizon.extras.cosmetics.hidden": "დამალული",
|
||||
"horizon.extras.cosmetics.name_tags": "მხოლოდ სახელების ტეგები"
|
||||
|
||||
@@ -474,8 +474,8 @@
|
||||
"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_hide_fx": "Басқа ойыншылардың іздері мен аураларын жасыру",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Басқа ойыншылардың іздері мен ауралары жасырын қалады; олардың басқа косметикасы көрінеді. Әдепкі бойынша өшірулі.",
|
||||
"horizon.extras.cosmetics.shown": "Көрсетілген",
|
||||
"horizon.extras.cosmetics.hidden": "Жасырын",
|
||||
"horizon.extras.cosmetics.name_tags": "Тек атау тегтері"
|
||||
|
||||
@@ -481,8 +481,8 @@
|
||||
"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_hide_fx": "ಇತರ ಆಟಗಾರರ ಹಾದಿಗಳು ಮತ್ತು ಪ್ರಭಾವಳಿಗಳನ್ನು ಮರೆಮಾಡಿ",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "ಇತರ ಆಟಗಾರರ ಹಾದಿಗಳು ಮತ್ತು ಪ್ರಭಾವಳಿಗಳು ಮರೆಯಾಗಿರುತ್ತವೆ; ಅವರ ಇತರ ಅಲಂಕಾರಗಳು ಕಾಣುತ್ತವೆ. ಪೂರ್ವನಿಯೋಜಿತವಾಗಿ ಆಫ್.",
|
||||
"horizon.extras.cosmetics.shown": "ತೋರಿಸಲಾಗಿದೆ",
|
||||
"horizon.extras.cosmetics.hidden": "ಮರೆಮಾಡಲಾಗಿದೆ",
|
||||
"horizon.extras.cosmetics.name_tags": "ಹೆಸರು ಟ್ಯಾಗ್ಗಳು ಮಾತ್ರ"
|
||||
|
||||
@@ -448,8 +448,8 @@
|
||||
"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_hide_fx": "Башка оюнчулардын издерин жана ауралары жашыруу",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Башка оюнчулардын издери жана ауралары жашыруун калат; алардын башка косметикасы көрүнөт. Демейки боюнча өчүк.",
|
||||
"horizon.extras.cosmetics.shown": "Көрсөтүлдү",
|
||||
"horizon.extras.cosmetics.hidden": "Жашырылган",
|
||||
"horizon.extras.cosmetics.name_tags": "Аталышы гана"
|
||||
|
||||
@@ -399,8 +399,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Occultus: omnisHorizonlusor te sine tuis fucis videt, te comprehendit. Idem transitum cum vestiario launcher.",
|
||||
"horizon.extras.cosmetics_others": "Alii histriones medicamentis",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Quid aliud?Horizonhistriones gerunt: omnia, solum nomen tag stili, vel nihil. Occultus, ludus numquam quaerit quod gerunt.",
|
||||
"horizon.extras.cosmetics_auras": "Aliorum auras et calles",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auras et calles alii lusores gerunt. Off per defaltam.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Vestigia et auras aliorum lusorum celare",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Vestigia et aurae aliorum lusorum celata manent; cetera ornamenta eorum adhuc videntur. Ex defalta exstinctum.",
|
||||
"horizon.extras.cosmetics.shown": "Ostensum",
|
||||
"horizon.extras.cosmetics.hidden": "Occultus",
|
||||
"horizon.extras.cosmetics.name_tags": "Nomen tags tantum"
|
||||
|
||||
@@ -435,8 +435,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Verstoppt: all Horizon Spiller gesäit Iech ouni Är Kosmetik, Dir abegraff. Deeselwechte Schalter wéi de Launcher Kleederschaf.",
|
||||
"horizon.extras.cosmetics_others": "Aner Spiller Kosmetik",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Wat aner Horizon Spiller droen: alles, nëmmen hir Numm Tag Stiler, oder näischt. Verstoppt, d'Spill freet ni wat se droen.",
|
||||
"horizon.extras.cosmetics_auras": "Anerer hir Auraen an Trëppelweeër",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auras an Trëppelweeër aner Spiller zouzedrécken. Off par défaut.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Spueren an Auraen vun anere Spiller verstoppen",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Spueren an Auraen vun anere Spiller bleiwe verstoppt; hir aner Kosmetik ass ëmmer nach ze gesinn. Standardméisseg aus.",
|
||||
"horizon.extras.cosmetics.shown": "gewisen",
|
||||
"horizon.extras.cosmetics.hidden": "Verstoppt",
|
||||
"horizon.extras.cosmetics.name_tags": "Numm Tags nëmmen"
|
||||
|
||||
@@ -434,8 +434,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Verborge: eder Horizon speuler ziet dich zoonder dien cosmetica, inclusief dich. Dezelfde schakelaar es de garderobe vaan de launcher.",
|
||||
"horizon.extras.cosmetics_others": "Cosmetica vaan aandere speulers",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Wat aandere Horizon speulers drage: alles, allein hun stijl vaan naamlabel, of niks. Verborge, vraog ‘t spel noets wat ze drage.",
|
||||
"horizon.extras.cosmetics_auras": "Auras en spore vaan aandere",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auras en spore die aandere speulers drage. Standaard oet.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Spore en aura's van anger spelers verberge",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Spore en aura's van anger spelers blieve verborge; hun anger cosmetica blieve zichbaar. Standaard oet.",
|
||||
"horizon.extras.cosmetics.shown": "Getoend",
|
||||
"horizon.extras.cosmetics.hidden": "Verborge",
|
||||
"horizon.extras.cosmetics.name_tags": "Allein naamtags"
|
||||
|
||||
@@ -474,5 +474,5 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Ascüs: tüt i giucadur Horizon te veden senza i tò cosmetich, anca tì. L’istess interruttur del guardaroba del lanciador.",
|
||||
"horizon.extras.cosmetics_others": "Cosmetich di olter giucadur",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Che ròbb che purtan sü i olter giucadur Horizon: tüt, sultant i luur stil de etichett o nient. Ascüs, el giœugh dumanda mai cus’è che g’han sü.",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auras and trails other players wear. Disattiva per predefiniziun."
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "I strasc e i aur di olter giugadur resten scondu; i sò olter cosmetegh se veden anmò. Smorzaa de default."
|
||||
}
|
||||
@@ -464,8 +464,8 @@
|
||||
"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": "Aura ແລະ trails ຜູ້ນອື່ນໆໃສ່. ປິດຕາມຄ່າເລີ່ມຕົ້ນ.",
|
||||
"horizon.extras.cosmetics_hide_fx": "ເຊື່ອງຮອຍ ແລະ ອໍຣາຂອງຜູ້ຫຼິ້ນອື່ນ",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "ຮອຍ ແລະ ອໍຣາຂອງຜູ້ຫຼິ້ນອື່ນຖືກເຊື່ອງໄວ້; ເຄື່ອງປະດັບອື່ນຂອງເຂົາເຈົ້າຍັງເຫັນຢູ່. ປິດໄວ້ຕາມຄ່າເລີ່ມຕົ້ນ.",
|
||||
"horizon.extras.cosmetics.shown": "ສະແດງໃຫ້ເຫັນ",
|
||||
"horizon.extras.cosmetics.hidden": "ເຊື່ອງໄວ້",
|
||||
"horizon.extras.cosmetics.name_tags": "ປ້າຍຊື່ເທົ່ານັ້ນ",
|
||||
|
||||
@@ -467,8 +467,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Paslėpta: kiekvienas „Horizon“ žaidėjas mato jus be jūsų kosmetikos, įskaitant jus. Tas pats jungiklis kaip ir paleidimo priemonės drabužių spinta.",
|
||||
"horizon.extras.cosmetics_others": "Kitų žaidėjų kosmetika",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Ką dėvi kiti „Horizon“ žaidėjai: viskas, tik jų vardų etiketės arba nieko. Paslėptas, žaidimas niekada neklausia, ką jie dėvi.",
|
||||
"horizon.extras.cosmetics_auras": "Kitų auros ir takai",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auros ir takai, kuriuos nešioja kiti žaidėjai. Išjungta pagal numatytuosius nustatymus.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Slėpti kitų žaidėjų pėdsakus ir auras",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Kitų žaidėjų pėdsakai ir auros lieka paslėpti; kita jų kosmetika vis dar matoma. Numatytai išjungta.",
|
||||
"horizon.extras.cosmetics.shown": "Parodyta",
|
||||
"horizon.extras.cosmetics.hidden": "Paslėpta",
|
||||
"horizon.extras.cosmetics.name_tags": "Tik vardų žymos"
|
||||
|
||||
@@ -456,8 +456,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Slēpts: katrs Horizon spēlētājs redz jūs bez jūsu kosmētikas, ieskaitot jūs. Tas pats slēdzis kā palaišanas ierīces Garderobe.",
|
||||
"horizon.extras.cosmetics_others": "Pārējo spēlētāju kosmētika",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Ko valkā citi Horizon spēlētāji: viss, tikai viņu vārda birku stili vai nekas. Slēpts, spēle nekad nejautā, ko viņi valkā.",
|
||||
"horizon.extras.cosmetics_auras": "Citu auras un takas",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auras un takas, ko valkā citi spēlētāji. Pēc noklusējuma izslēgts.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Slēpt citu spēlētāju pēdas un auras",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Citu spēlētāju pēdas un auras paliek paslēptas; viņu pārējā kosmētika joprojām redzama. Pēc noklusējuma izslēgts.",
|
||||
"horizon.extras.cosmetics.shown": "Parādīts",
|
||||
"horizon.extras.cosmetics.hidden": "Slēpts",
|
||||
"horizon.extras.cosmetics.name_tags": "Tikai vārdu birkas"
|
||||
|
||||
@@ -480,8 +480,8 @@
|
||||
"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_hide_fx": "Скриј ги трагите и аурите на другите играчи",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Трагите и аурите на другите играчи остануваат скриени; нивната друга козметика и понатаму се гледа. Стандардно исклучено.",
|
||||
"horizon.extras.cosmetics.shown": "Прикажани",
|
||||
"horizon.extras.cosmetics.hidden": "Скриени",
|
||||
"horizon.extras.cosmetics.name_tags": "Само ознаки со имиња"
|
||||
|
||||
@@ -475,8 +475,8 @@
|
||||
"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_hide_fx": "Бусад тоглогчийн мөр ба аурыг нуух",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Бусад тоглогчийн мөр ба аур нуугдсан хэвээр байна; тэдний бусад гоёл харагдсаар байна. Анхдагчаар унтраалттай.",
|
||||
"horizon.extras.cosmetics.shown": "Үзүүлсэн",
|
||||
"horizon.extras.cosmetics.hidden": "Нуугдсан",
|
||||
"horizon.extras.cosmetics.name_tags": "Зөвхөн нэрийн шошго"
|
||||
|
||||
@@ -460,8 +460,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Tersembunyi: setiap pemain Horizon melihat anda tanpa kosmetik anda, termasuk anda. Suis yang sama seperti Almari Pakaian pelancar.",
|
||||
"horizon.extras.cosmetics_others": "Kosmetik pemain lain",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Perkara yang dipakai oleh pemain Horizon lain: segala-galanya, hanya gaya tanda nama mereka, atau tiada apa-apa. Tersembunyi, permainan tidak pernah bertanya apa yang mereka pakai.",
|
||||
"horizon.extras.cosmetics_auras": "Aura dan jejak orang lain",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Aura dan jejak yang dipakai pemain lain. Mati secara lalai.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Sembunyikan jejak dan aura pemain lain",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Jejak dan aura pemain lain kekal tersembunyi; kosmetik mereka yang lain masih kelihatan. Mati secara lalai.",
|
||||
"horizon.extras.cosmetics.shown": "Ditunjukkan",
|
||||
"horizon.extras.cosmetics.hidden": "Tersembunyi",
|
||||
"horizon.extras.cosmetics.name_tags": "Tanda nama sahaja"
|
||||
|
||||
@@ -432,8 +432,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Moħbija: kull plejer Horizon jarak mingħajr il-kożmetiċi tiegħek, int inkluż. L-istess swiċċ bħall-Gwardarobba tal-lanċjar.",
|
||||
"horizon.extras.cosmetics_others": "Kosmetiċi ta' plejers oħra",
|
||||
"horizon.extras.cosmetics_others.tooltip": "X'jilbsu plejers oħra ta' Horizon: kollox, l-istili tat-tag tal-isem tagħhom biss, jew xejn. Moħbija, il-logħba qatt ma tistaqsi dak li jilbsu.",
|
||||
"horizon.extras.cosmetics_auras": "Awra u traċċi ta’ ħaddieħor",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auras u trails plejers oħra jilbsu. Mitfi awtomatikament.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Aħbi t-traċċi u l-awri ta' plejers oħra",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "It-traċċi u l-awri ta' plejers oħra jibqgħu moħbija; il-kożmetiċi l-oħra tagħhom jibqgħu jidhru. Mitfi b'mod awtomatiku.",
|
||||
"horizon.extras.cosmetics.shown": "murija",
|
||||
"horizon.extras.cosmetics.hidden": "Moħbija",
|
||||
"horizon.extras.cosmetics.name_tags": "Tikketti tal-isem biss",
|
||||
|
||||
@@ -467,8 +467,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Verborgen: elke Horizon-speler ziet je zonder je cosmetica, inclusief jij. Dezelfde schakelaar als de kledingkast van de draagraket.",
|
||||
"horizon.extras.cosmetics_others": "Cosmetica van andere spelers",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Wat andere Horizon-spelers dragen: alles, alleen hun naamplaatjestijlen, of niets. Verborgen vraagt het spel nooit wat ze dragen.",
|
||||
"horizon.extras.cosmetics_auras": "De aura's en sporen van anderen",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Aura's en sporen die andere spelers dragen. Standaard uitgeschakeld.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Sporen en aura's van andere spelers verbergen",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Sporen en aura's van andere spelers blijven verborgen; hun andere cosmetica blijven zichtbaar. Standaard uit.",
|
||||
"horizon.extras.cosmetics.shown": "Getoond",
|
||||
"horizon.extras.cosmetics.hidden": "Verborgen",
|
||||
"horizon.extras.cosmetics.name_tags": "Alleen naamplaatjes"
|
||||
|
||||
@@ -449,8 +449,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Skjult: hver Horizon-spiller ser deg uten kosmetikken din, inkludert du. Samme bryter som bærerakettens garderobe.",
|
||||
"horizon.extras.cosmetics_others": "Andre spilleres kosmetikk",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Hva andre Horizon-spillere har på seg: alt, bare navnelappene deres, eller ingenting. Skjult spør spillet aldri hva de har på seg.",
|
||||
"horizon.extras.cosmetics_auras": "Andres auraer og stier",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auraer og stier andre spillere har på seg. Av som standard.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Skjul andre spilleres spor og auraer",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Andre spilleres spor og auraer forblir skjult; deres andre kosmetikk vises fortsatt. Av som standard.",
|
||||
"horizon.extras.cosmetics.shown": "Vist",
|
||||
"horizon.extras.cosmetics.hidden": "Skjult",
|
||||
"horizon.extras.cosmetics.name_tags": "Kun navnelapper",
|
||||
|
||||
@@ -465,8 +465,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Amagat: cada Horizon jogaire vos vei sens vòstres cosmetics, vos tanben. Lo meteis interruptor que l'armari del lançaire.",
|
||||
"horizon.extras.cosmetics_others": "Los cosmetics dels autres jogaires",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Çò que pòrtan los autres jogaires Horizon : tot, sonque lors estils de etiqueta de nom, o res. Amagat, lo jòc demanda pas jamai çò que pòrtan.",
|
||||
"horizon.extras.cosmetics_auras": "Las auras e las pistas dels autres",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auras e pistas que los autres jogaires pòrtan. Desactivat per defaut.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Amagar las traças e las auras dels autres jogaires",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Las traças e las auras dels autres jogaires demòran amagadas; lors autres cosmetics se vesan totjorn. Desactivat per defaut.",
|
||||
"horizon.extras.cosmetics.shown": "Mostrat",
|
||||
"horizon.extras.cosmetics.hidden": "Amagat",
|
||||
"horizon.extras.cosmetics.name_tags": "D'etiquetas de nom solament"
|
||||
|
||||
@@ -484,8 +484,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Oculto: todo jogador do Horizon vê você sem seus cosméticos, inclusive você. A mesma opção do guarda-roupa do lançador.",
|
||||
"horizon.extras.cosmetics_others": "Cosméticos de outros jogadores",
|
||||
"horizon.extras.cosmetics_others.tooltip": "O que os outros jogadores do Horizon usam: tudo, apenas os estilos dos crachás ou nada. Escondido, o jogo nunca pergunta o que eles vestem.",
|
||||
"horizon.extras.cosmetics_auras": "Auras e trilhas de outros",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auras e trilhas que outros jogadores usam. Desativado por padrão.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Ocultar rastros e auras de outros jogadores",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Os rastros e auras de outros jogadores ficam ocultos; os demais cosméticos deles continuam visíveis. Desativado por padrão.",
|
||||
"horizon.extras.cosmetics.shown": "Mostrado",
|
||||
"horizon.extras.cosmetics.hidden": "Oculto",
|
||||
"horizon.extras.cosmetics.name_tags": "Somente tags de nome"
|
||||
|
||||
@@ -453,8 +453,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Ascuns: fiecare jucător Horizon te vede fără cosmeticele tale, inclusiv tu. Același comutator ca și garderoba lansatorului.",
|
||||
"horizon.extras.cosmetics_others": "Cosmeticele altor jucători",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Ce poartă alți jucători de la Horizon: totul, doar stilurile lor de etichete de nume sau nimic. Ascuns, jocul nu le întreabă niciodată ce poartă.",
|
||||
"horizon.extras.cosmetics_auras": "Aurele și traseele altora",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Aurele și traseele pe care le poartă alți jucători. Oprit implicit.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Ascunde urmele și aurele altor jucători",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Urmele și aurele altor jucători rămân ascunse; celelalte cosmetice ale lor se văd în continuare. Dezactivat implicit.",
|
||||
"horizon.extras.cosmetics.shown": "Afisat",
|
||||
"horizon.extras.cosmetics.hidden": "Ascuns",
|
||||
"horizon.extras.cosmetics.name_tags": "Numai etichete de nume",
|
||||
|
||||
@@ -491,8 +491,8 @@
|
||||
"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_hide_fx": "Атын оонньооччулар суолларын уонна ааураларын кистээ",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Атын оонньооччулар суоллара уонна аауралара кистэлэҥҥэ хаалаллар; кинилэр атын киэргэллэрэ көстөр. Сүрүннээн араарыллыбыт.",
|
||||
"horizon.extras.cosmetics.shown": "Көрдөрүллүбүт",
|
||||
"horizon.extras.cosmetics.hidden": "Буккуллубут",
|
||||
"horizon.extras.cosmetics.name_tags": "Аат бэлиэлэрэ эрэ"
|
||||
|
||||
@@ -461,8 +461,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Čuohpaduvvon: juohke Horizon spealli oaidná du kosmetihka haga, maiddái don. Seamma čujuhus go álggaheaddji gárvodanrusttegis.",
|
||||
"horizon.extras.cosmetics_others": "Eará čiekčiid kosmetihkka",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Maid eará Horizon-speallit geavahit: visot, dušše sin nammamearkkaid stiillat, dahje ii mihkkege. Čuožžilan, speallu ii goassege jeara maid sii geavahit.",
|
||||
"horizon.extras.cosmetics_auras": "Earáid aurat ja čuovggat",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Aurat ja čuovggat maid eará čiekčit geavahit. Heaittihuvvon standárdan.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Čiega eará speallariid luottaid ja auraid",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Eará speallariid luohtt ja aurat čihkojuvvojit; sin eará čiŋat ain oidnojit. Jáddaduvvon álgoárvun.",
|
||||
"horizon.extras.cosmetics.shown": "Čájehuvvon",
|
||||
"horizon.extras.cosmetics.hidden": "Čuohpaduvvon",
|
||||
"horizon.extras.cosmetics.name_tags": "Dušše nammamearkkat"
|
||||
|
||||
@@ -450,8 +450,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Skryté: každý hráč Horizon vás vidí bez vašej kozmetiky vrátane vás. Rovnaký vypínač ako šatník odpaľovača.",
|
||||
"horizon.extras.cosmetics_others": "Kozmetika ostatných hráčov",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Čo nosia ostatní hráči Horizon: všetko, iba štýly menoviek alebo nič. Skryté, hra sa nikdy nepýta, čo nosia.",
|
||||
"horizon.extras.cosmetics_auras": "Aury a stopy iných",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Aury a chodníky, ktoré nosia ostatní hráči. Predvolene vypnuté.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Skryť stopy a aury ostatných hráčov",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Stopy a aury ostatných hráčov zostanú skryté; ich ostatná kozmetika je stále vidieť. Predvolene vypnuté.",
|
||||
"horizon.extras.cosmetics.shown": "Zobrazené",
|
||||
"horizon.extras.cosmetics.hidden": "Skryté",
|
||||
"horizon.extras.cosmetics.name_tags": "Iba menovky"
|
||||
|
||||
@@ -466,8 +466,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Skrito: vsak igralec Horizon vas vidi brez vaše kozmetike, vključno z vami. Enako stikalo kot Garderoba zaganjalnika.",
|
||||
"horizon.extras.cosmetics_others": "Kozmetika drugih igralcev",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Kaj nosijo drugi igralci Horizon: vse, samo njihove stile imenskih oznak ali nič. Skrita, igra nikoli ne vpraša, kaj nosijo.",
|
||||
"horizon.extras.cosmetics_auras": "Avre in sledi drugih",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Avre in sledi, ki jih nosijo drugi igralci. Privzeto izklopljeno.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Skrij sledi in avre drugih igralcev",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Sledi in avre drugih igralcev ostanejo skrite; njihova druga kozmetika je še vedno vidna. Privzeto izklopljeno.",
|
||||
"horizon.extras.cosmetics.shown": "Prikazano",
|
||||
"horizon.extras.cosmetics.hidden": "Skrito",
|
||||
"horizon.extras.cosmetics.name_tags": "Samo imenske oznake",
|
||||
|
||||
@@ -451,8 +451,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Qarsoon: Horizon ciyaaryahan kasta ayaa ku arkaa adiga oo aan la'aanteed waxyaalaha la isku qurxiyo, adiga ayaa ku daray. La mid ah sida Wardrobe-ka furaha.",
|
||||
"horizon.extras.cosmetics_others": "Isqurxinta ciyaartoyda kale",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Waxa kale ee Horizon ciyaartoy xidha: wax walba, kaliya magacooda sumadda qaababka, ama waxba. Qarsoon, ciyaartu weligeed ma waydiiso waxay xidhaan.",
|
||||
"horizon.extras.cosmetics_auras": "Auraska iyo raadadka kuwa kale",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auras iyo raad-raacyo ciyaartoy kale xidhaan. Demin sida caadiga ah.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Qari raadadka iyo auraha ciyaartoyda kale",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Raadadka iyo auraha ciyaartoyda kale way qarsoon yihiin; qurxintooda kale weli way muuqataa. Si caadi ah waa dansan yahay.",
|
||||
"horizon.extras.cosmetics.shown": "Muujiyay",
|
||||
"horizon.extras.cosmetics.hidden": "Qarsoon",
|
||||
"horizon.extras.cosmetics.name_tags": "Magac tags kaliya",
|
||||
|
||||
@@ -465,8 +465,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "I fshehur: çdo lojtar Horizon ju sheh pa kozmetikën tuaj, përfshirë ju. I njëjti çelës si veshjet e lëshuesit.",
|
||||
"horizon.extras.cosmetics_others": "Kozmetikë për lojtarë të tjerë",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Çfarë veshin lojtarët e tjerë Horizon: gjithçka, vetëm stilet e etiketave të emrave të tyre ose asgjë. E fshehur, loja nuk pyet kurrë se çfarë veshin.",
|
||||
"horizon.extras.cosmetics_auras": "Aurat dhe shtigjet e të tjerëve",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Aurat dhe shtigjet që lojtarët e tjerë veshin. Joaktiv si parazgjedhje.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Fshih gjurmët dhe aurat e lojtarëve të tjerë",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Gjurmët dhe aurat e lojtarëve të tjerë mbeten të fshehura; kozmetikat e tjera të tyre shihen ende. Joaktiv si parazgjedhje.",
|
||||
"horizon.extras.cosmetics.shown": "Treguar",
|
||||
"horizon.extras.cosmetics.hidden": "I fshehur",
|
||||
"horizon.extras.cosmetics.name_tags": "Vetëm etiketat e emrave"
|
||||
|
||||
@@ -488,8 +488,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Skriveno: svaki Horizon igrač vas vidi bez kozmetike, uključujući i vas. Isti prekidač kao Garderoba u pokretaču.",
|
||||
"horizon.extras.cosmetics_others": "Kozmetika drugih igrača",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Šta nose drugi Horizon igrači: sve, samo stilove oznaka imena ili ništa. Kada je skriveno, igra nikada 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_hide_fx": "Sakrij tragove i aure drugih igrača",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Tragovi i aure drugih igrača ostaju skriveni; njihova ostala kozmetika se i dalje vidi. Isključeno po zadanom.",
|
||||
"horizon.extras.cosmetics.shown": "Pokazano",
|
||||
"horizon.extras.cosmetics.hidden": "Skriveno",
|
||||
"horizon.extras.cosmetics.name_tags": "Samo oznake sa imenom"
|
||||
|
||||
@@ -488,8 +488,8 @@
|
||||
"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_hide_fx": "Сакриј трагове и ауре других играча",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Трагови и ауре других играча остају сакривени; њихова остала козметика се и даље види. Подразумевано искључено.",
|
||||
"horizon.extras.cosmetics.shown": "Показано",
|
||||
"horizon.extras.cosmetics.hidden": "Скривено",
|
||||
"horizon.extras.cosmetics.name_tags": "Само ознаке са именом"
|
||||
|
||||
@@ -450,8 +450,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Dolt: alla Horizon-spelare ser dig utan din kosmetika, inklusive du. Samma switch som bärraketens garderob.",
|
||||
"horizon.extras.cosmetics_others": "Andra spelares kosmetika",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Vad andra Horizon-spelare bär: allt, bara deras namnetikettstilar eller ingenting. Dolt, spelet frågar aldrig vad de har på sig.",
|
||||
"horizon.extras.cosmetics_auras": "Andras auror och spår",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Auror och spår som andra spelare bär. Av som standard.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Dölj andra spelares spår och auror",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Andra spelares spår och auror förblir dolda; deras övriga kosmetika syns fortfarande. Av som standard.",
|
||||
"horizon.extras.cosmetics.shown": "Visas",
|
||||
"horizon.extras.cosmetics.hidden": "Dold",
|
||||
"horizon.extras.cosmetics.name_tags": "Endast namnlappar",
|
||||
|
||||
@@ -480,8 +480,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Skryte: kożdy grocz Horizon widzi ci bez kosmetyki, w tym ty. Tyn sōm przełōncznik jak Garderoba ôd ôdpalacza.",
|
||||
"horizon.extras.cosmetics_others": "Kosmetyka ôd inkszych groczy",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Co inksi grocze Horizon noszōm: wszysko, ino jejich style znakōw, abo nic. Skryte, gra nikej niy pyto, co ôni noszōm.",
|
||||
"horizon.extras.cosmetics_auras": "Aury i szlaki inkszych",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Aury i szlaki noszōm inksi grocze. Wyłōnczōne domyślnie.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Skryj ślady i aury inszych grŏczy",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Ślady i aury inszych grŏczy ôstŏwajōm skryte; jejich inksze kosmetyki sōm dalij widoczne. Wychodnie zastawiōne.",
|
||||
"horizon.extras.cosmetics.shown": "Pokazano",
|
||||
"horizon.extras.cosmetics.hidden": "Skryte",
|
||||
"horizon.extras.cosmetics.name_tags": "Ino znaczniki mian"
|
||||
|
||||
@@ -481,8 +481,8 @@
|
||||
"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_hide_fx": "மற்ற விளையாட்டாளர்களின் தடங்களையும் ஒளிவட்டங்களையும் மறை",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "மற்ற விளையாட்டாளர்களின் தடங்களும் ஒளிவட்டங்களும் மறைந்திருக்கும்; அவர்களின் மற்ற அலங்காரங்கள் தெரியும். இயல்பாக அணைக்கப்பட்டுள்ளது.",
|
||||
"horizon.extras.cosmetics.shown": "காட்டப்பட்டது",
|
||||
"horizon.extras.cosmetics.hidden": "மறைக்கப்பட்டது",
|
||||
"horizon.extras.cosmetics.name_tags": "பெயர் குறிச்சொற்கள் மட்டுமே"
|
||||
|
||||
@@ -488,8 +488,8 @@
|
||||
"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_hide_fx": "ซ่อนรอยและออร่าของผู้เล่นอื่น",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "รอยและออร่าของผู้เล่นอื่นจะถูกซ่อน ส่วนไอเทมแต่งตัวอื่นยังมองเห็นได้ ปิดไว้โดยค่าเริ่มต้น",
|
||||
"horizon.extras.cosmetics.shown": "แสดงแล้ว",
|
||||
"horizon.extras.cosmetics.hidden": "ซ่อนเร้น",
|
||||
"horizon.extras.cosmetics.name_tags": "ป้ายชื่อเท่านั้น"
|
||||
|
||||
@@ -478,8 +478,8 @@
|
||||
"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_hide_fx": "Башка уенчыларның эзләрен һәм аураларын яшерү",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Башка уенчыларның эзләре һәм ауралары яшерен кала; аларның башка косметикасы күренә. Гадәттә сүндерелгән.",
|
||||
"horizon.extras.cosmetics.shown": "Күрсәтелә",
|
||||
"horizon.extras.cosmetics.hidden": "Яшерен",
|
||||
"horizon.extras.cosmetics.name_tags": "Тэгларның исеме генә"
|
||||
|
||||
@@ -463,8 +463,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Yashirin: har bir Horizon o'yinchi sizni kosmetikangizsiz ko'radi, shu jumladan. Launcherning shkafi bilan bir xil kalit.",
|
||||
"horizon.extras.cosmetics_others": "Boshqa o'yinchilarning kosmetikasi",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Boshqa Horizon o'yinchilar nima kiyishadi: hamma narsa, faqat ism yorlig'i uslublari yoki hech narsa. Yashirin, o'yin hech qachon ular nima kiyishlarini so'ramaydi.",
|
||||
"horizon.extras.cosmetics_auras": "Boshqalarning auralari va izlari",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Boshqa o'yinchilar kiygan auralar va izlar. Sukut boʻyicha oʻchirilgan.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Boshqa o‘yinchilarning izlari va auralarini yashirish",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "Boshqa o‘yinchilarning izlari va auralari yashirin qoladi; ularning boshqa kosmetikasi ko‘rinib turadi. Standart bo‘yicha o‘chiq.",
|
||||
"horizon.extras.cosmetics.shown": "Ko'rsatilgan",
|
||||
"horizon.extras.cosmetics.hidden": "Yashirin",
|
||||
"horizon.extras.cosmetics.name_tags": "Faqat nom teglari"
|
||||
|
||||
@@ -464,8 +464,8 @@
|
||||
"horizon.extras.cosmetics_mine.tooltip": "Nascosto: ogni Horizon giocatore ti vede senza i tuoi cosmetici, tu compreso. Lo stesso interruttore del Armadio del lanciatore.",
|
||||
"horizon.extras.cosmetics_others": "Cosmetici dei altri zogadori",
|
||||
"horizon.extras.cosmetics_others.tooltip": "Cossa che i altri zugadori de Horizon i ga indosso: tuto, soło i so stiłi de name tag, o gnente. Nascosto, el zogo no ghe dimanda mai cossa che i porta.",
|
||||
"horizon.extras.cosmetics_auras": "Aure e sentieri dei altri",
|
||||
"horizon.extras.cosmetics_auras.tooltip": "Aure e sentieri che altri zugadori i ga. Spento par indebołimento.",
|
||||
"horizon.extras.cosmetics_hide_fx": "Scondi i trasi e łe aure de i altri zugadori",
|
||||
"horizon.extras.cosmetics_hide_fx.tooltip": "I trasi e łe aure de i altri zugadori i resta sconti; i so altri cosmetici i se vede ancora. Destudà par defolt.",
|
||||
"horizon.extras.cosmetics.shown": "Mostrà",
|
||||
"horizon.extras.cosmetics.hidden": "Sconto",
|
||||
"horizon.extras.cosmetics.name_tags": "Solo etichete de nome",
|
||||
|
||||
Loaded 100 of 359 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user