Draw a falling body between its snapshots instead of in 20 Hz jumps

present_pose drew a body between its previous and current snapshot only
when both were TR_INTERPOLATE. A falling or sliding corpse (TR_GRAVITY)
was drawn at its snapshot position for a whole 50 ms and then jumped, and
so was every switch between resting and falling.

Each snapshot position of a corpse is already where the body was at that
snapshot, so corpses are now drawn between the two whatever their
trajectory type. Players keep the old rule.

A corpse slot is reused for later deaths, so an entity number can hold a
new body. A corpse whose death time (time2) or victim changes is now a new
entity and is not drawn sliding over from where the old body lay.

Seen on the Mac: a flying body's drawn position changes on every frame
while the server moves it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
JohnM
2026-10-13 20:17:54 +10:00
parent 8d93a3102e
commit 1d11c4deee
@@ -64,9 +64,11 @@ impl CEntityRuntime {
return;
};
if !self.in_next_snap()
|| !matches!(self.pose_e_type as i32, ET_PLAYER | ET_PLAYER_CORPSE)
|| previous.pos.tr_type != TR_INTERPOLATE
|| self.current.pos.tr_type != TR_INTERPOLATE
|| !lerps_between_snapshots(
self.pose_e_type as i32,
previous.pos.tr_type,
self.current.pos.tr_type,
)
{
return;
}
@@ -92,6 +94,19 @@ impl CEntityRuntime {
}
}
fn lerps_between_snapshots(e_type: i32, previous_tr_type: i32, current_tr_type: i32) -> bool {
match e_type {
ET_PLAYER_CORPSE => true,
ET_PLAYER => previous_tr_type == TR_INTERPOLATE && current_tr_type == TR_INTERPOLATE,
_ => false,
}
}
fn another_body(current: &EntityState, next: &EntityState) -> bool {
next.e_type == ET_PLAYER_CORPSE
&& (current.time2 != next.time2 || current.client_num != next.client_num)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PlayerDrawGate {
pub eyes_entity_num: i32,
@@ -246,8 +261,9 @@ pub fn apply_existing(rt: &mut CEntityRuntime, next: EntityState, at_time_ms: i3
let was_in = rt.in_next_snap();
let e_type_changed = rt.next_state.e_type != next.e_type;
let teleport = (rt.next_state.e_flags ^ next.e_flags) & EFLAGS_TELEPORT != 0;
if !was_in || e_type_changed || teleport {
let new_entity = !was_in || e_type_changed;
let other_body = another_body(&rt.next_state, &next);
if !was_in || e_type_changed || teleport || other_body {
let new_entity = !was_in || e_type_changed || other_body;
reset_entity(rt, next, at_time_ms, new_entity);
return;
}