prevent players from firing through walls

This commit is contained in:
jacob 2026-02-10 19:31:34 -06:00
parent 45ac2bf1af
commit 8405d039af
2 changed files with 84 additions and 38 deletions

View File

@ -1378,7 +1378,8 @@ P_Space P_SpaceFromWalls(Arena *arena, P_Frame *frame)
{ {
Vec2 p0 = VEC2(wall->start.x / P_TilesPerMeter - P_WorldPitch / 2, wall->start.y / P_TilesPerMeter - P_WorldPitch / 2); Vec2 p0 = VEC2(wall->start.x / P_TilesPerMeter - P_WorldPitch / 2, wall->start.y / P_TilesPerMeter - P_WorldPitch / 2);
Vec2 p1 = VEC2(wall->end.x / P_TilesPerMeter - P_WorldPitch / 2, wall->end.y / P_TilesPerMeter - P_WorldPitch / 2); Vec2 p1 = VEC2(wall->end.x / P_TilesPerMeter - P_WorldPitch / 2, wall->end.y / P_TilesPerMeter - P_WorldPitch / 2);
P_Shape shape = P_ShapeFromDesc(.count = 2, .points = { p0, p1 }, .radius = 0.01 ); // P_Shape shape = P_ShapeFromDesc(.count = 2, .points = { p0, p1 }, .radius = 0.01 );
P_Shape shape = P_ShapeFromDesc(.count = 2, .points = { p0, p1 }, .radius = 0.0 );
Rng2 aabb = P_BoundingBoxFromShape(shape); Rng2 aabb = P_BoundingBoxFromShape(shape);
aabb = AddRng2Vec2(aabb, VEC2(P_WorldPitch / 2.0, P_WorldPitch / 2.0)); aabb = AddRng2Vec2(aabb, VEC2(P_WorldPitch / 2.0, P_WorldPitch / 2.0));
aabb.p0 = FloorVec2(aabb.p0); aabb.p0 = FloorVec2(aabb.p0);
@ -1729,6 +1730,7 @@ void P_SpawnEntsFromList(P_Frame *frame, P_EntList ents)
dst->next_in_bin = old_next_in_bin; dst->next_in_bin = old_next_in_bin;
dst->prev_in_bin = old_prev_in_bin; dst->prev_in_bin = old_prev_in_bin;
dst->created_at_ns = frame->time_ns; dst->created_at_ns = frame->time_ns;
dst->created_at_tick = frame->tick;
++frame->ents_count; ++frame->ents_count;
} }
} }
@ -2717,6 +2719,7 @@ void P_StepFrame(P_Frame *frame)
Vec2 fire_pos = Zi; Vec2 fire_pos = Zi;
Vec2 fire_dir = Zi; Vec2 fire_dir = Zi;
Vec2 fire_base = Zi;
if (can_fire) if (can_fire)
{ {
Vec2 look = firer->control.look; Vec2 look = firer->control.look;
@ -2764,6 +2767,13 @@ void P_StepFrame(P_Frame *frame)
SPR_Ray fire_ray = wep.rays[SPR_RayKind_Ap]; SPR_Ray fire_ray = wep.rays[SPR_RayKind_Ap];
fire_pos = MulAffineVec2(wep_pix_to_world_af, fire_ray.pos); fire_pos = MulAffineVec2(wep_pix_to_world_af, fire_ray.pos);
fire_dir = NormRot(MulAffineBasisVec2(wep_pix_to_world_af, fire_ray.dir)); fire_dir = NormRot(MulAffineBasisVec2(wep_pix_to_world_af, fire_ray.dir));
fire_base = MulVec2(PerpVec2(NegVec2(firer->xf.r)), WedgeVec2(firer->xf.r, SubVec2(firer->xf.t, fire_pos)));
fire_base = AddVec2(fire_base, firer->xf.t);
P_DebugDrawLine(fire_base, fire_pos, Color_Yellow);
P_DebugDrawPoint(fire_base, Color_Yellow);
P_DebugDrawPoint(fire_pos, Color_Red);
} }
// FIXME: Prevent obstructed weapons from firing through walls // FIXME: Prevent obstructed weapons from firing through walls
@ -2795,6 +2805,7 @@ void P_StepFrame(P_Frame *frame)
Vec2 dir = Vec2FromAngle(angle); Vec2 dir = Vec2FromAngle(angle);
bullet->bullet_base = fire_base;
bullet->bullet_start = fire_pos; bullet->bullet_start = fire_pos;
bullet->bullet_end = AddVec2(bullet->bullet_start, MulVec2(dir, speed)); bullet->bullet_end = AddVec2(bullet->bullet_start, MulVec2(dir, speed));
bullet->bullet_firer = firer->key; bullet->bullet_firer = firer->key;
@ -2817,30 +2828,56 @@ void P_StepFrame(P_Frame *frame)
if (bullet->is_bullet) if (bullet->is_bullet)
{ {
bullet->has_hit = 0; bullet->has_hit = 0;
Vec2 ray_start = bullet->bullet_start;
Vec2 ray_end = bullet->bullet_end; Struct(BulletPath)
Vec2 ray_dir = SubVec2(ray_end, ray_start); {
BulletPath *next;
Vec2 start;
Vec2 end;
};
BulletPath *first_bullet_path = 0;
BulletPath *last_bullet_path = 0;
if (bullet->created_at_tick == frame->tick)
{
// On bullet's first tick, we want to ensure that the firer/weapon
// wasn't obstructed (e.g. to prevent shooting through walls), so we
// insert a path from the bullet's base to its starting position before
// its actual firing path
BulletPath *path = PushStruct(scratch.arena, BulletPath);
SllQueuePush(first_bullet_path, last_bullet_path, path);
path->start = bullet->bullet_base;
path->end = bullet->bullet_start;
}
{
BulletPath *path = PushStruct(scratch.arena, BulletPath);
SllQueuePush(first_bullet_path, last_bullet_path, path);
path->start = bullet->bullet_start;
path->end = bullet->bullet_end;
}
P_DebugDrawLine(bullet->bullet_start, bullet->bullet_end, Color_Red); P_DebugDrawLine(bullet->bullet_start, bullet->bullet_end, Color_Red);
P_EntKey victim_key = Zi;
P_RaycastResult victim_raycast = Zi;
{
for (BulletPath *path = first_bullet_path; path; path = path->next)
{
Vec2 path_dir = SubVec2(path->end, path->start);
P_Space *cast_spaces[] = { P_Space *cast_spaces[] = {
&world->walls_space, &world->walls_space,
&post_solve_ents_space, &post_solve_ents_space,
}; };
P_SpaceEntryList cast_entries = Zi; P_SpaceEntryList cast_entries = Zi;
P_UniqueSpaceEntriesFromRay( P_UniqueSpaceEntriesFromRay(
scratch.arena, scratch.arena,
&cast_entries, &cast_entries,
countof(cast_spaces), countof(cast_spaces),
cast_spaces, cast_spaces,
ray_start, path->start,
ray_end path->end
); );
P_EntKey victim_key = Zi;
P_RaycastResult victim_raycast = Zi;
{
f32 closest_len_sq = Inf; f32 closest_len_sq = Inf;
for (P_SpaceEntryNode *entry_node = cast_entries.first; entry_node; entry_node = entry_node->next) for (P_SpaceEntryNode *entry_node = cast_entries.first; entry_node; entry_node = entry_node->next)
{ {
@ -2849,17 +2886,17 @@ void P_StepFrame(P_Frame *frame)
if (!P_MatchEntKey(potential_victim_key, bullet->bullet_firer)) if (!P_MatchEntKey(potential_victim_key, bullet->bullet_firer))
{ {
P_Shape potential_victim_shape = entry->shape; P_Shape potential_victim_shape = entry->shape;
P_RaycastResult entrance_raycast = P_RaycastShape(potential_victim_shape, ray_start, ray_dir); P_RaycastResult entrance_raycast = P_RaycastShape(potential_victim_shape, path->start, path_dir);
Vec2 entrance = entrance_raycast.p; Vec2 entrance = entrance_raycast.p;
if (entrance_raycast.is_intersecting) if (entrance_raycast.is_intersecting)
{ {
P_RaycastResult exit_raycast = P_RaycastShape(potential_victim_shape, ray_start, NegVec2(ray_dir)); P_RaycastResult exit_raycast = P_RaycastShape(potential_victim_shape, path->start, NegVec2(path_dir));
Vec2 exit = exit_raycast.p; Vec2 exit = exit_raycast.p;
f32 da = DotVec2(ray_dir, SubVec2(entrance, ray_start)); f32 da = DotVec2(path_dir, SubVec2(entrance, path->start));
f32 db = DotVec2(ray_dir, SubVec2(exit, ray_start)); f32 db = DotVec2(path_dir, SubVec2(exit, path->start));
if (db > 0 && (da <= Vec2LenSq(ray_dir) || da <= 0)) if (db > 0 && (da <= Vec2LenSq(path_dir) || da <= 0))
{ {
f32 len_sq = Vec2LenSq(SubVec2(entrance_raycast.p, ray_start)); f32 len_sq = Vec2LenSq(SubVec2(entrance_raycast.p, path->start));
if (len_sq < closest_len_sq) if (len_sq < closest_len_sq)
{ {
closest_len_sq = len_sq; closest_len_sq = len_sq;
@ -2870,7 +2907,14 @@ void P_StepFrame(P_Frame *frame)
} }
} }
} }
if (!P_IsEntKeyNil(victim_key))
{
// No need to check future paths if this path hit
break;
} }
}
}
P_Ent *victim = P_EntFromKey(frame, victim_key); P_Ent *victim = P_EntFromKey(frame, victim_key);
// TODO: Truncate bullet trail // TODO: Truncate bullet trail

View File

@ -110,6 +110,7 @@ Struct(P_Ent)
P_EntKey key; P_EntKey key;
u64 rand_seq; u64 rand_seq;
i64 created_at_ns; i64 created_at_ns;
i64 created_at_tick;
//- Build data //- Build data
@ -129,6 +130,7 @@ Struct(P_Ent)
P_EntKey bullet_firer; P_EntKey bullet_firer;
b32 is_bullet; b32 is_bullet;
Vec2 bullet_base;
Vec2 bullet_start; Vec2 bullet_start;
Vec2 bullet_end; Vec2 bullet_end;