A21/A23: физган — взять проп под прицелом и таскать по взгляду
Some checks are pending
CI / checks (push) Waiting to run
Some checks are pending
CI / checks (push) Waiting to run
Первый срез взаимодействия с пропами (то, что в GMod делает weapon_physgun): ЛКМ берёт ближайшее тело под прицелом — луч из глаз, расстояние 8..260 юнитов, допуск 40 в стороны, побеждает ближайшее; пока кнопка зажата, тело каждый кадр телепортируется на луч взгляда на взятом расстоянии (углы сохраняются); отпустил — тело свободно и падает само. Добавлен переключатель SOURCE_RS_PHYSGUN=1: держит ближайшее тело без мыши — нужен для проверки скриптом, а не руками (SDL-событий мыши в скрипте нет). Проверка: код собирается, clippy чистый, все 23 сьюта зелёные. Интерактивно проверить не смог — в скриптовых кадрах камера упирается в стены (наводить нечем). Проверять у кучи пропов в районе -2930 -1370 -64: посмотреть на проп, зажать ЛКМ. Записано в docs/DIARY.md.
This commit is contained in:
parent
35e04409c4
commit
03edb27a33
2 changed files with 99 additions and 2 deletions
|
|
@ -7,6 +7,7 @@ use fs::VirtualFs;
|
|||
use render::{Gfx, WorldRenderer};
|
||||
use sdl3::event::{Event, WindowEvent};
|
||||
use sdl3::keyboard::Keycode;
|
||||
use sdl3::mouse::MouseButton;
|
||||
use std::collections::HashSet;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Instant;
|
||||
|
|
@ -388,6 +389,12 @@ pub fn map(assets: Option<PathBuf>, args: MapArgs) -> anyhow::Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
// Физган: тело, которое держим, и расстояние удержания (A21/A23).
|
||||
let mut grabbed: Option<(engine::physics::BodyId, f32)> = None;
|
||||
// `SOURCE_RS_PHYSGUN=1` — держать ближайшее тело без мыши: нужно, чтобы
|
||||
// проверять физган кадром и скриптом, а не руками.
|
||||
let auto_grab = std::env::var_os("SOURCE_RS_PHYSGUN").is_some();
|
||||
|
||||
'running: loop {
|
||||
for event in events.poll_iter() {
|
||||
match event {
|
||||
|
|
@ -396,6 +403,52 @@ pub fn map(assets: Option<PathBuf>, args: MapArgs) -> anyhow::Result<()> {
|
|||
keycode: Some(Keycode::Escape),
|
||||
..
|
||||
} => break 'running,
|
||||
Event::MouseButtonDown {
|
||||
mouse_btn: MouseButton::Left,
|
||||
..
|
||||
} => {
|
||||
// Физган: берём ближайшее тело под прицелом — по
|
||||
// расстоянию вдоль луча, с допуском на толщину пропа.
|
||||
let origin = player.eye_position();
|
||||
let dir = camera.forward();
|
||||
let w = world.borrow();
|
||||
let mut best: Option<(f32, engine::physics::BodyId)> = None;
|
||||
for id in w.physics.body_list() {
|
||||
let Some((position, _)) = w.physics.transform(id) else {
|
||||
continue;
|
||||
};
|
||||
let to_body = position - origin;
|
||||
let along = to_body.dot(dir);
|
||||
if !(8.0..=260.0).contains(&along) {
|
||||
continue;
|
||||
}
|
||||
let perpendicular = (to_body - dir * along).length();
|
||||
if perpendicular > 40.0 {
|
||||
continue;
|
||||
}
|
||||
if best.is_none_or(|(best_along, _)| along < best_along) {
|
||||
best = Some((along, id));
|
||||
}
|
||||
}
|
||||
if let Some((along, id)) = best {
|
||||
grabbed = Some((id, along));
|
||||
log.push_back(format!("физган: взял ({} юнитов)", along as i32));
|
||||
while log.len() > 8 {
|
||||
log.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::MouseButtonUp {
|
||||
mouse_btn: MouseButton::Left,
|
||||
..
|
||||
} => {
|
||||
if grabbed.take().is_some() {
|
||||
log.push_back("физган: отпустил".to_string());
|
||||
while log.len() > 8 {
|
||||
log.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::KeyDown {
|
||||
keycode: Some(Keycode::G),
|
||||
repeat: false,
|
||||
|
|
@ -498,6 +551,40 @@ pub fn map(assets: Option<PathBuf>, args: MapArgs) -> anyhow::Result<()> {
|
|||
let mut w = world.borrow_mut();
|
||||
w.step_physics(dt);
|
||||
w.recover_fallen_props(-8192.0);
|
||||
// Физган: держим тело на луче взгляда, на взятом расстоянии.
|
||||
if auto_grab && grabbed.is_none() {
|
||||
let origin = player.eye_position();
|
||||
let dir = camera.forward();
|
||||
let mut best: Option<(f32, engine::physics::BodyId)> = None;
|
||||
for id in w.physics.body_list() {
|
||||
let Some((position, _)) = w.physics.transform(id) else {
|
||||
continue;
|
||||
};
|
||||
let to_body = position - origin;
|
||||
let along = to_body.dot(dir);
|
||||
if !(8.0..=320.0).contains(&along) {
|
||||
continue;
|
||||
}
|
||||
if (to_body - dir * along).length() > 64.0 {
|
||||
continue;
|
||||
}
|
||||
if best.is_none_or(|(best_along, _)| along < best_along) {
|
||||
best = Some((along, id));
|
||||
}
|
||||
}
|
||||
if let Some((along, id)) = best {
|
||||
grabbed = Some((id, along));
|
||||
}
|
||||
}
|
||||
if let Some((id, along)) = grabbed {
|
||||
let target = player.eye_position() + camera.forward() * along;
|
||||
let angles = w
|
||||
.physics
|
||||
.transform(id)
|
||||
.map(|(_, angles)| angles)
|
||||
.unwrap_or(math::Vec3::ZERO);
|
||||
w.physics.teleport(id, target, angles);
|
||||
}
|
||||
// Модель игрока едет за игроком: позиция — ступни, поворот — курс.
|
||||
if let Some(index) = player_model_instance {
|
||||
// У моделей Source ось Y смотрит влево, поэтому курс
|
||||
|
|
|
|||
|
|
@ -57,9 +57,19 @@ SOURCE_RS_PLAYER_MODEL= DISPLAY=:99 SDL_VIDEODRIVER=x11 cargo run -q -p client -
|
|||
пересобирается целиком (медленно, но работает); позже нужна до-аллокация
|
||||
вершин под спавн.
|
||||
|
||||
**Физган, первый срез (A21/A23).** ЛКМ — взять ближайшее тело под прицелом
|
||||
(луч из глаз, расстояние 8..260 юнитов, допуск 40 в стороны), пока держим —
|
||||
тело каждый кадр телепортируется на луч взгляда на взятом расстоянии; отпустил —
|
||||
свободно. `SOURCE_RS_PHYSGUN=1` держит ближайшее тело без мыши (для скриптов).
|
||||
|
||||
**Нужна интерактивная проверка физгана:** мои скриптовые кадры упираются в
|
||||
стены — камеру без глаз не навести. Проверять так: встать у кучи пропов
|
||||
(она в районе `-2930 -1370 -64`), посмотреть на проп, зажать ЛКМ.
|
||||
|
||||
**Следующее:** (а) до-аллокация вершин под спавн вместо пересборки рендерера;
|
||||
(б) реестр SWEP из Lua и вьюмодель; (в) физган (луч → проп → таскание);
|
||||
(г) скрытие своей модели, когда камера внутри неё; (д) скелет+анимации.
|
||||
(б) реестр SWEP из Lua и вьюмодель; (в) вращение пропа колесом и заморозка у
|
||||
физгана; (г) скрытие своей модели, когда камера внутри неё; (д) скелет+анимации
|
||||
моделей; (е) консольные команды спавна на данных из Lua (`gm_spawn`).
|
||||
|
||||
## 3. Что уже сделано и лежит в `main`
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue