From 209412ea3de845a2bdcba47cec0eb9632698e095 Mon Sep 17 00:00:00 2001 From: Niko Marmeladkov Date: Sat, 24 Oct 2026 13:23:42 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9D=D1=83=D0=BA=D0=BB=D0=B8=D0=BF:=20Ctrl=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=BC=D0=B5=D0=B4=D0=BB=D1=8F=D0=B5=D1=82=20(?= =?UTF-8?q?=D0=BA=D0=B0=D0=BA=20=D0=B2=20GMod),=20=D0=B0=20=D0=BD=D0=B5=20?= =?UTF-8?q?=D0=BE=D0=BF=D1=83=D1=81=D0=BA=D0=B0=D0=B5=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Пользователь: в оригинале Ctrl в ноуклипе замедляет движение. Убрано `move_up -= 1` по Ctrl; скорость: Shift ×3, Ctrl ×0.35, иначе обычная. Плюс тесты-сканы карты: поверхности с «зеркальными» материалами (0 в зале) и сущности рядом с залом — подтверждают, что func_reflective_glass один (model *6, точка -2064 -2076 -286). --- crates/client/src/map.rs | 6 +- crates/engine/tests/world_real.rs | 91 +++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/crates/client/src/map.rs b/crates/client/src/map.rs index 7c6e821..ddb0b6f 100644 --- a/crates/client/src/map.rs +++ b/crates/client/src/map.rs @@ -879,9 +879,6 @@ pub fn map(assets: Option, args: MapArgs) -> anyhow::Result<()> { if pressed.contains(&Keycode::A) { move_right -= 1.0; } - if pressed.contains(&Keycode::LCtrl) { - move_up -= 1.0; - } // Сенсорный стик складывается с клавиатурой: на Android клавиш нет, // а на десктопе `--touch` удобно проверять рядом с WASD. let (touch_forward, touch_right) = touch.movement(); @@ -892,8 +889,11 @@ pub fn map(assets: Option, args: MapArgs) -> anyhow::Result<()> { } let touch_jump = touch.take_jump(); if player.noclip { + // Как в GMod: Shift ускоряет, Ctrl замедляет (а не опускает). let speed = if pressed.contains(&Keycode::LShift) { args.speed * 3.0 + } else if pressed.contains(&Keycode::LCtrl) { + args.speed * 0.35 } else { args.speed }; diff --git a/crates/engine/tests/world_real.rs b/crates/engine/tests/world_real.rs index 16fd56c..b2c9e20 100644 --- a/crates/engine/tests/world_real.rs +++ b/crates/engine/tests/world_real.rs @@ -1472,3 +1472,94 @@ fn real_map_visual_entities() { } assert!(!classes.is_empty(), "сущностей нет"); } + +/// Где на карте поверхности с «зеркальными» материалами. +#[test] +fn real_map_mirror_materials() { + let Some(root) = assets_root() else { + eprintln!("пропуск: ассеты не найдены"); + return; + }; + let map = root.join("garrysmod/maps/gm_construct.bsp"); + if !map.is_file() { + eprintln!("пропуск: нет {}", map.display()); + return; + } + let bsp = formats::bsp::Bsp::from_path(&map).expect("разбор карты"); + let geometry = bsp.geometry().expect("геометрия"); + let texdata = bsp.texdata().expect("texdata"); + let names = bsp.texture_names().expect("имена текстур"); + let mut found = 0usize; + for (index, face) in geometry.faces.iter().enumerate() { + if face.num_edges < 3 { + continue; + } + let Some(texinfo) = geometry.texinfos.get(face.texinfo.max(0) as usize) else { + continue; + }; + let name = texdata + .get(texinfo.texdata.max(0) as usize) + .map(|data| data.name_id.max(0) as usize) + .and_then(|id| names.get(id)) + .cloned() + .unwrap_or_default(); + let lower = name.to_ascii_lowercase(); + if !(lower.contains("mirror") || lower.contains("reflect") || lower.contains("glass")) { + continue; + } + let Ok(indices) = geometry.face_vertex_indices(index) else { + continue; + }; + let mut center = math::Vec3::ZERO; + let mut count = 0.0f32; + for &v in &indices { + if let Some(vertex) = geometry.vertices.get(v as usize) { + center += math::Vec3::new(vertex.pos[0], vertex.pos[1], vertex.pos[2]); + count += 1.0; + } + } + if count == 0.0 { + continue; + } + let center = center / count; + // Интересуют только вертикальные поверхности в зале (z около -300..-500). + if (-600.0..=-200.0).contains(¢er.z) { + eprintln!("грань {index}: {name} в {center:?}"); + found += 1; + } + if found > 20 { + break; + } + } + eprintln!("найдено зеркальных граней в зале: {found}"); +} + +/// Сущности рядом с залом, где пользователь ждёт зеркало. +#[test] +fn real_map_entities_near_hall() { + let Some(root) = assets_root() else { + eprintln!("пропуск: ассеты не найдены"); + return; + }; + let map = root.join("garrysmod/maps/gm_construct.bsp"); + if !map.is_file() { + eprintln!("пропуск: нет {}", map.display()); + return; + } + let vfs = mount_game(&root); + let bsp = formats::bsp::Bsp::from_path(&map).expect("разбор карты"); + let world = engine::build_world(&bsp, &vfs).expect("сборка мира"); + let hall = math::Vec3::new(-1717.0, -1693.0, -400.0); + for entity in world.entities.iter() { + let distance = (entity.origin - hall).length(); + if distance < 700.0 { + eprintln!( + "{:.0}: {} @ {:?} model={:?}", + distance, + entity.classname, + entity.origin, + entity.key("model") + ); + } + } +}