From bb99c978c5f982e80a3007101fd61fe23635e242 Mon Sep 17 00:00:00 2001 From: salmonik Date: Tue, 27 Oct 2026 13:19:00 +0100 Subject: Jumpbug: automatic duck+jump on damaging falls - Predict the landing speed from the current vertical velocity and a swept standing-hull ground trace; engage only when it exceeds PLAYER_MAX_SAFE_FALL_SPEED. - Press duck one command before the unduck window so FinishDuck's 36-unit origin raise places the feet ~37 units above the floor. - On the next command release duck and press jump: PlayerMove runs Duck() before CheckJumpButton, so FinishUnDuck sets ground, the jump executes, and CheckFalling never sees ground for the fall-damage check. - Measure the per-command fall from consecutive airborne origins instead of a fixed tick interval; clear IN_JUMP while waiting so the pogo latch is clear. - New Movement -> Jumpbug -> Active toggle (default on), config [jumpbug]. --- src/vischeck.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'src/vischeck.cpp') diff --git a/src/vischeck.cpp b/src/vischeck.cpp index 428f54f..6d453c5 100644 --- a/src/vischeck.cpp +++ b/src/vischeck.cpp @@ -95,6 +95,9 @@ constexpr unsigned MASK_VISIBLE = 0x1u | 0x4000u | 0x80u | 0x2000u; // MASK_SHOT = SOLID|MOVEABLE|MONSTER|WINDOW|DEBRIS|HITBOX constexpr unsigned MASK_SHOT = 0x1u | 0x4000u | 0x2000000u | 0x4u | 0x4000000u | 0x40000000u; +// MASK_PLAYERSOLID = SOLID|MOVEABLE|PLAYERCLIP|WINDOW|MONSTER|GRATE +constexpr unsigned MASK_PLAYERSOLID = + 0x1u | 0x4000u | 0x10000u | 0x4u | 0x2000000u | 0x8u; static void make_ray(RayCompat &ray, const Vector &start, const Vector &end) { float dx = end.x - start.x, dy = end.y - start.y, dz = end.z - start.z; @@ -107,6 +110,21 @@ static void make_ray(RayCompat &ray, const Vector &start, const Vector &end) { ray.isSwept = (dx * dx + dy * dy + dz * dz) > 0.0f; } +// Swept AABB matching the standing player hull used by CGameMovement. +// Ray_t::Init shifts m_Start to the box center and stores the negated center +// offset in m_StartOffset. With mins=(-16,-16,0), maxs=(16,16,72), the +// extents are (16,16,36) and m_StartOffset is (0,0,-36). +static void make_standing_hull_ray(RayCompat &ray, const Vector &origin, + float distance) { + ray = RayCompat{}; + ray.start = {origin.x, origin.y, origin.z + 36.0f, 0.0f}; + ray.delta = {0.0f, 0.0f, -distance, 0.0f}; + ray.startOffset = {0.0f, 0.0f, -36.0f, 0.0f}; + ray.extents = {16.0f, 16.0f, 36.0f, 0.0f}; + ray.isRay = false; + ray.isSwept = true; +} + } // namespace bool is_visible(const Vector &eye, const Vector &point) { @@ -147,3 +165,30 @@ void *trace_entity(const Vector &start, const Vector &end, void *ignore) { return nullptr; return tr.ent; } + +// Vertical distance from the standing player hull's feet to the first +// standable surface below. Matches CategorizePosition's 2-unit probe and its +// plane.normal.z >= 0.7 rule, but sweeps much farther so the caller can +// predict the landing. Returns -1 when there is no usable ground. +float ground_distance(const Vector &origin, void *ignore, float max_dist) { + if (!g_iface.enginetrace || max_dist <= 0.0f) + return -1.0f; + RayCompat ray; + make_standing_hull_ray(ray, origin, max_dist); + GameTraceCompat tr{}; + EntityFilter filter(ignore); + using TraceRayFn = void (*)(void *, const RayCompat *, unsigned, void *, + GameTraceCompat *); + vfunc(g_iface.enginetrace, 4)(g_iface.enginetrace, &ray, + MASK_PLAYERSOLID, &filter, &tr); + if (tr.startsolid || tr.fraction >= 1.0f || tr.normal.z < 0.7f) { + static int solid_logs = 0; + if (tr.startsolid && solid_logs < 4) { + solid_logs++; + logf("ground trace: startsolid at (%.1f %.1f %.1f)", origin.x, + origin.y, origin.z); + } + return -1.0f; + } + return tr.fraction * max_dist; +} -- cgit v1.2.3