summaryrefslogtreecommitdiffstats
path: root/src/vischeck.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/vischeck.cpp62
1 files changed, 53 insertions, 9 deletions
diff --git a/src/vischeck.cpp b/src/vischeck.cpp
index 6b531b9..6126c88 100644
--- a/src/vischeck.cpp
+++ b/src/vischeck.cpp
@@ -88,12 +88,29 @@ struct EntityFilter : ITraceFilterCompat {
88 int GetTraceType() const override { return 0; } // TRACE_EVERYTHING 88 int GetTraceType() const override { return 0; } // TRACE_EVERYTHING
89}; 89};
90 90
91// True when pEntity is a child of target: held weapons are separate entities
92// and heavy NPCs use phys_bone_follower entities for their collision model, so
93// a trace toward an NPC's torso otherwise stops on the weapon/follower and the
94// target reads as occluded. CBaseHandle stores the entity index in the low
95// bits, so the owner handle resolves through the client entity list.
96static bool owned_by(void *pEntity, void *target) {
97 if (!pEntity || !target || g_net.owner < 0)
98 return false;
99 uint32_t h = *(uint32_t *)((uintptr_t)pEntity + g_net.owner);
100 int idx = (int)(h & 0x1FFFu);
101 if (idx <= 0)
102 return false;
103 return entitylist_get(idx) == target;
104}
105
91struct VisibilityFilter : ITraceFilterCompat { 106struct VisibilityFilter : ITraceFilterCompat {
92 void *self; 107 void *self;
93 void *target; 108 void *target;
94 VisibilityFilter(void *s, void *t) : self(s), target(t) {} 109 VisibilityFilter(void *s, void *t) : self(s), target(t) {}
95 bool ShouldHitEntity(void *pEntity, int) override { 110 bool ShouldHitEntity(void *pEntity, int) override {
96 return pEntity != self && pEntity != target; 111 if (pEntity == self || pEntity == target)
112 return false;
113 return !owned_by(pEntity, target);
97 } 114 }
98 int GetTraceType() const override { return 0; } // TRACE_EVERYTHING 115 int GetTraceType() const override { return 0; } // TRACE_EVERYTHING
99}; 116};
@@ -176,14 +193,14 @@ bool is_visible(const Vector &eye, const Vector &point, void *self,
176 // Trace everything that bullets care about: world, static props, crates and 193 // Trace everything that bullets care about: world, static props, crates and
177 // NPCs. Non-solid glass and glass whose material name marks it breakable is 194 // NPCs. Non-solid glass and glass whose material name marks it breakable is
178 // skipped; solid non-break glass (bulletproof) blocks. 195 // skipped; solid non-break glass (bulletproof) blocks.
196 using TraceRayFn = void (*)(void *, const RayCompat *, unsigned, void *,
197 GameTraceCompat *);
179 Vector start = eye; 198 Vector start = eye;
180 for (int pass = 0; pass < 5; pass++) { 199 for (int pass = 0; pass < 5; pass++) {
181 RayCompat ray; 200 RayCompat ray;
182 make_ray(ray, start, point); 201 make_ray(ray, start, point);
183 GameTraceCompat tr{}; 202 GameTraceCompat tr{};
184 VisibilityFilter filter(self, target); 203 VisibilityFilter filter(self, target);
185 using TraceRayFn = void (*)(void *, const RayCompat *, unsigned, void *,
186 GameTraceCompat *);
187 vfunc<TraceRayFn>(g_iface.enginetrace, 4)(g_iface.enginetrace, &ray, 204 vfunc<TraceRayFn>(g_iface.enginetrace, 4)(g_iface.enginetrace, &ray,
188 MASK_SHOT, &filter, &tr); 205 MASK_SHOT, &filter, &tr);
189 if (tr.startsolid || tr.allsolid) 206 if (tr.startsolid || tr.allsolid)
@@ -196,11 +213,34 @@ bool is_visible(const Vector &eye, const Vector &point, void *self,
196 bool solid = (tr.contents & CONTENTS_SOLID) != 0; 213 bool solid = (tr.contents & CONTENTS_SOLID) != 0;
197 bool breakable_glass = glass && surface_is_breakable_glass(tr.surface); 214 bool breakable_glass = glass && surface_is_breakable_glass(tr.surface);
198 if (glass && (!solid || breakable_glass)) { 215 if (glass && (!solid || breakable_glass)) {
199 // Step just past the pane and look for the next obstruction. 216 // Glass brushes are commonly 1-4 units thick, so the old fixed 0.5-unit
200 start = tr.endpos; 217 // step landed inside the brush and the next pass reported startsolid,
201 start.x += dir.x * 0.5f; 218 // which read as "blocked". Advance until a probe from the new start is
202 start.y += dir.y * 0.5f; 219 // clear, doubling the step, and stop if we pass the target.
203 start.z += dir.z * 0.5f; 220 Vector next = tr.endpos;
221 float step = 1.0f;
222 bool clear = false;
223 for (int guard = 0; guard < 16 && !clear; guard++) {
224 next.x += dir.x * step;
225 next.y += dir.y * step;
226 next.z += dir.z * step;
227 float ox = next.x - point.x, oy = next.y - point.y,
228 oz = next.z - point.z;
229 if (ox * ox + oy * oy + oz * oz < 0.25f)
230 return true; // stepped past the target; nothing left to block it
231 RayCompat probe;
232 make_ray(probe, next, point);
233 GameTraceCompat ptr{};
234 VisibilityFilter pfilter(self, target);
235 vfunc<TraceRayFn>(g_iface.enginetrace, 4)(
236 g_iface.enginetrace, &probe, MASK_SHOT, &pfilter, &ptr);
237 clear = !ptr.startsolid && !ptr.allsolid;
238 if (!clear)
239 step *= 2.0f;
240 }
241 if (!clear)
242 return false; // still inside geometry; treat as blocked
243 start = next;
204 logd("vischeck: skipped glass surface=%s contents=0x%x solid=%d break=%d", 244 logd("vischeck: skipped glass surface=%s contents=0x%x solid=%d break=%d",
205 tr.surface.name ? tr.surface.name : "?", tr.contents, (int)solid, 245 tr.surface.name ? tr.surface.name : "?", tr.contents, (int)solid,
206 (int)breakable_glass); 246 (int)breakable_glass);
@@ -270,7 +310,11 @@ bool point_ground(const Vector &point, void *ignore) {
270 vfunc<TraceRayFn>(g_iface.enginetrace, 4)(g_iface.enginetrace, &ray, 310 vfunc<TraceRayFn>(g_iface.enginetrace, 4)(g_iface.enginetrace, &ray,
271 MASK_PLAYERSOLID, &filter, &tr); 311 MASK_PLAYERSOLID, &filter, &tr);
272 // A start inside geometry means a wall/step there, not a ledge. 312 // A start inside geometry means a wall/step there, not a ledge.
273 return tr.startsolid || tr.fraction < 1.0f; 313 if (tr.startsolid)
314 return true;
315 // Only a standable surface counts: a steep cliff face hit within 96 units is
316 // not a floor, and treating it as one suppressed the edge jump.
317 return tr.fraction < 1.0f && tr.normal.z >= 0.7f;
274} 318}
275 319
276// Vertical distance from the standing player hull's feet to the first 320// Vertical distance from the standing player hull's feet to the first