Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6afb160
Revert "Fix zoom interpolation overshoot (#4908)"
jwt27 Jun 13, 2026
1819531
Use linear frame interpolation
jwt27 Jun 13, 2026
9ed776d
Remove minimap interpolation code
jwt27 Jun 14, 2026
7372a2e
Fix minimap position for off-screen creatures
jwt27 Jun 14, 2026
367f5dd
Don't extrapolate on network lag
jwt27 Jun 16, 2026
e55acd1
Simplify motion interpolation on lights
jwt27 Jun 16, 2026
bc1549c
Rename some things in light_data.c
jwt27 Jun 16, 2026
3c24f37
Update local player's mouse light on every frame
jwt27 Jun 16, 2026
4046a46
Allow mouse light to move while the game is paused
jwt27 Jun 16, 2026
caf7912
Remove unnecessary fields from struct Thing
jwt27 Jun 16, 2026
94edfa9
Use interpolate_synced for lights
jwt27 Jun 16, 2026
81b66b2
Update local mouse light in frontview engine
jwt27 Jun 16, 2026
c24a780
Disable interpolation only for local mouse light
jwt27 Jun 16, 2026
853b26c
Ignore local mouse when packetload is enabled
jwt27 Jun 16, 2026
d620bbb
Also compensate game.delta_time for multiplayer speed adjustment
jwt27 Jun 16, 2026
0709ed3
Subtract process_turn_time only after processing logic
jwt27 Jun 16, 2026
19649f0
Fix armour/disease particle effect glitching when creature is dropped
jwt27 Jun 18, 2026
552e99e
Clamp process_turn_time to 2.0 when waiting for packets
jwt27 Jun 20, 2026
902644e
Interpolate light intensity randomness
jwt27 Jun 20, 2026
7e56d9e
Subtract 1 from process_turn_time here
jwt27 Jun 21, 2026
d47e297
Fix bugs in mouse-to-map coordinate translation
jwt27 Jun 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/engine_camera.c
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,5 @@ void update_all_players_cameras(void)
update_player_camera(player);
}
}
update_local_cameras();
}
/******************************************************************************/
118 changes: 56 additions & 62 deletions src/engine_render.c
Original file line number Diff line number Diff line change
Expand Up @@ -530,63 +530,69 @@ static void calculate_hud_scale(struct Camera *cam) {
hud_scale = ((range_input - range_min)) / (range_max - range_min);
}

float interpolate(float variable_to_interpolate, long previous, long current)
float interpolate(float previous, float current)
{
if (is_feature_on(Ft_DeltaTime) == false || game.frame_skip > 0) {
return current;
}
// future: by using the predicted future position in the interpolation calculation, we can remove input lag (or visual lag).
long future = current + (current - previous);
// 0.5 is definitely accurate. Tested by rotating the camera while comparing the minimap's rotation with the camera's rotation in a video recording.
float desired_value = LbLerp(current, future, 0.5);
return LbLerp(variable_to_interpolate, desired_value, game.delta_time);
return LbLerp(previous, current, game.process_turn_time);
}

float interpolate_angle(float variable_to_interpolate, float previous, float current)
float interpolate_angle(float previous, float current)
{
if (is_feature_on(Ft_DeltaTime) == false || game.frame_skip > 0) {
return current;
}
float future = current + (current - previous);
float desired_value = lerp_angle(current, future, 0.5);
float result = lerp_angle(variable_to_interpolate, desired_value, game.delta_time);
float result_change = LbFmodf((result - current) + DEGREES_180, DEGREES_360) - DEGREES_180;
if (result_change > -0.5f && result_change < 0.5f) {
return lerp_angle(previous, current, game.process_turn_time);
}

// For things that stop moving when the game is paused.
float interpolate_synced(float previous, float current)
{
if (flag_is_set(game.operation_flags, GOF_Paused))
return current;
}
return result;

return interpolate(previous, current);
}

void interpolate_thing(struct Thing *thing)
struct ThingInterpolateResult interpolate_thing(struct Thing *thing)
{
// Note: if delta_time is off the interpolated position will also reflect that
struct ThingInterpolateResult result;

if (thing->creation_turn == get_gameturn()-1 || get_gameturn() - thing->last_turn_drawn > 1 ) {
// Set initial interp position when either Thing has just been created or goes off camera then comes back on camera
thing->interp_mappos = thing->mappos;
thing->interp_floor_height = thing->floor_height;
if (get_gameturn() - thing->creation_turn <= 1)
{
// Fixes an odd bug where thing->mappos.z.val is briefly 65534 (for 1
// turn) in certain situations, which can mess up the interpolation and
// cause things to fall from the sky.
if (thing->mappos.z.val == 65534)
thing->mappos.z.val = thing->floor_height;

if (thing->interp_mappos.z.val == 65534) { // Fixes an odd bug where thing->mappos.z.val is briefly 65534 (for 1 turn) in certain situations, which can mess up the interpolation and cause things to fall from the sky.
thing->interp_mappos.z.val = thing->interp_floor_height;
}
} else {
// Interpolate position every frame
thing->interp_mappos.x.val = interpolate(thing->interp_mappos.x.val, thing->previous_mappos.x.val, thing->mappos.x.val);
thing->interp_mappos.z.val = interpolate(thing->interp_mappos.z.val, thing->previous_mappos.z.val, thing->mappos.z.val);
thing->interp_mappos.y.val = interpolate(thing->interp_mappos.y.val, thing->previous_mappos.y.val, thing->mappos.y.val);
thing->interp_floor_height = interpolate(thing->interp_floor_height, thing->previous_floor_height, thing->floor_height);
// Set initial interp position when Thing has just been created
thing->previous_mappos = thing->mappos;
thing->previous_floor_height = thing->floor_height;
}

// Cancel interpolation if distance to interpolate is too far. This is a catch-all to solve any remaining interpolation bugs.
if ((abs(thing->interp_mappos.x.val-thing->mappos.x.val) >= 10000) ||
(abs(thing->interp_mappos.y.val-thing->mappos.y.val) >= 10000) ||
(abs(thing->interp_mappos.z.val-thing->mappos.z.val) >= 10000))
{
ERRORLOG("The %s index %d owned by player %d moved an unrealistic distance((%d,%d,%d) to (%d,%d,%d)), refusing interpolation."
,thing_model_name(thing), (int)thing->index, (int)thing->owner, thing->interp_mappos.x.stl.num, thing->interp_mappos.y.stl.num, thing->interp_mappos.z.stl.num, thing->mappos.x.stl.num, thing->mappos.y.stl.num, thing->mappos.z.stl.num);
thing->interp_mappos = thing->mappos;
thing->interp_floor_height = thing->floor_height;
}
// Interpolate position every frame
result.mappos.x.val = interpolate_synced(thing->previous_mappos.x.val, thing->mappos.x.val);
result.mappos.y.val = interpolate_synced(thing->previous_mappos.y.val, thing->mappos.y.val);
result.mappos.z.val = interpolate_synced(thing->previous_mappos.z.val, thing->mappos.z.val);
result.floor_height = interpolate_synced(thing->previous_floor_height, thing->floor_height);

// Cancel interpolation if distance to interpolate is too far. This is a
// catch-all to solve any remaining interpolation bugs.
if ((abs(thing->previous_mappos.x.val - thing->mappos.x.val) >= 10000) ||
(abs(thing->previous_mappos.y.val - thing->mappos.y.val) >= 10000) ||
(abs(thing->previous_mappos.z.val - thing->mappos.z.val) >= 10000))
{
ERRORLOG("The %s index %d owned by player %d moved an unrealistic distance((%d,%d,%d) to (%d,%d,%d)), refusing interpolation.",
thing_model_name(thing), (int)thing->index, (int)thing->owner,
thing->previous_mappos.x.stl.num, thing->previous_mappos.y.stl.num, thing->previous_mappos.z.stl.num,
thing->mappos.x.stl.num, thing->mappos.y.stl.num, thing->mappos.z.stl.num);
result.mappos = thing->mappos;
result.floor_height = thing->floor_height;
}

return result;
}

static void get_floor_pointed_at(long x, long y, int32_t *floor_x, int32_t *floor_y)
Expand Down Expand Up @@ -4994,12 +5000,6 @@ static void draw_fastview_mapwho(struct Camera *cam, struct BucketKindJontySprit
{
lbDisplay.DrawFlags |= Lb_TEXT_UNDERLNSHADOW;
lbSpriteReMapPtr = red_pal;
thing->time_spent_displaying_hurt_colour += game.delta_time;
if (thing->time_spent_displaying_hurt_colour >= 1.0 || game.frame_skip > 0)
{
thing->time_spent_displaying_hurt_colour = 0;
thing->rendering_flags &= ~TRF_BeingHit; // Turns off red damage colour tint
}
}
}
thing_being_displayed_is_creature = 1;
Expand Down Expand Up @@ -8036,12 +8036,6 @@ static void draw_jonty_mapwho(struct BucketKindJontySprite *jspr)
{
lbDisplay.DrawFlags |= Lb_TEXT_UNDERLNSHADOW;
lbSpriteReMapPtr = red_pal;
thing->time_spent_displaying_hurt_colour += game.delta_time;
if (thing->time_spent_displaying_hurt_colour >= 1.0 || game.frame_skip > 0)
{
thing->time_spent_displaying_hurt_colour = 0;
thing->rendering_flags &= ~TRF_BeingHit; // Turns off red damage colour tint
}
}
}
thing_being_displayed_is_creature = 1;
Expand Down Expand Up @@ -8731,12 +8725,11 @@ static void do_map_who_for_thing(struct Thing *thing)
struct EngineCoord ecor;
struct NearestLights nearlgt;

interpolate_thing(thing);
int render_pos_x, render_floorpos, render_pos_y, render_pos_z;
render_pos_x = thing->interp_mappos.x.val;
render_pos_y = thing->interp_mappos.z.val;
render_pos_z = thing->interp_mappos.y.val;
render_floorpos = thing->interp_floor_height;
const struct ThingInterpolateResult interp = interpolate_thing(thing);
const int render_pos_x = interp.mappos.x.val;
const int render_pos_y = interp.mappos.z.val;
const int render_pos_z = interp.mappos.y.val;
const int render_floorpos = interp.floor_height;

switch (thing->draw_class)
{
Expand Down Expand Up @@ -8895,7 +8888,7 @@ static void do_map_who(short tnglist_idx)
static void draw_frontview_thing_on_element(struct Thing *thing, struct Map *map, struct Camera *cam)
{
// The draw_frontview_thing_on_element() function is the FrontView equivalent of do_map_who_for_thing()
interpolate_thing(thing);
struct ThingInterpolateResult interp = interpolate_thing(thing);

int32_t cx;
int32_t cy;
Expand All @@ -8905,7 +8898,7 @@ static void draw_frontview_thing_on_element(struct Thing *thing, struct Map *map
switch (thing->draw_class)
{
case ODC_Default: // Things
convert_world_coord_to_front_view_screen_coord(&thing->interp_mappos,cam,&cx,&cy,&cz);
convert_world_coord_to_front_view_screen_coord(&interp.mappos, cam, &cx, &cy, &cz);
if (is_free_space_in_poly_pool(1))
{
add_thing_sprite_to_polypool(thing, cx, cy, cy, cz-3);
Expand All @@ -8916,7 +8909,7 @@ static void draw_frontview_thing_on_element(struct Thing *thing, struct Map *map
}
break;
case ODC_RoomPrice: // Floating gold text when buying and selling
convert_world_coord_to_front_view_screen_coord(&thing->interp_mappos,cam,&cx,&cy,&cz);
convert_world_coord_to_front_view_screen_coord(&interp.mappos, cam, &cx, &cy, &cz);
if (is_free_space_in_poly_pool(1))
{
add_number_to_polypool(cx, cy, thing->creature.gold_carried, 1);
Expand All @@ -8933,7 +8926,7 @@ static void draw_frontview_thing_on_element(struct Thing *thing, struct Map *map
break;
}

convert_world_coord_to_front_view_screen_coord(&thing->interp_mappos,cam,&cx,&cy,&cz);
convert_world_coord_to_front_view_screen_coord(&interp.mappos, cam, &cx, &cy, &cz);
if (is_free_space_in_poly_pool(1))
{
if (get_gameturn() - thing->roomflag.last_turn_drawn == 1)
Expand All @@ -8958,7 +8951,7 @@ static void draw_frontview_thing_on_element(struct Thing *thing, struct Map *map
}
break;
case ODC_SpinningKey:
convert_world_coord_to_front_view_screen_coord(&thing->interp_mappos,cam,&cx,&cy,&cz);
convert_world_coord_to_front_view_screen_coord(&interp.mappos, cam, &cx, &cy, &cz);
if (is_free_space_in_poly_pool(1))
{
add_spinning_key_to_polypool(thing, cx, cy, cy, cz-3);
Expand Down Expand Up @@ -9090,6 +9083,7 @@ void draw_frontview_engine(struct Camera *cam)
}

update_frontview_pointed_block(zoom, qdrant, px, py, qx, qy);
update_local_mouse_light();
if ( (map_volume_box.visible) && (!game_is_busy_doing_gui()) )
{
process_frontview_map_volume_box(cam, ((zoom >> 8) & 0xFF), player->id_number);
Expand Down
12 changes: 10 additions & 2 deletions src/engine_render.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ struct MapVolumeBox { // sizeof = 24
long floor_height_z;
};

struct ThingInterpolateResult
{
struct Coord3d mappos;
int32_t floor_height;
};

/******************************************************************************/
// Stripey Line Color Arrays

Expand Down Expand Up @@ -136,8 +142,10 @@ extern unsigned char temp_cluedo_mode;

extern TbSpriteData keepersprite_add[KEEPERSPRITE_ADD_NUM];
/*****************************************************************************/
float interpolate(float variable_to_interpolate, long previous, long current);
float interpolate_angle(float variable_to_interpolate, float previous, float current);
float interpolate(float previous, float current);
float interpolate_angle(float previous, float current);
float interpolate_synced(float previous, float current);
struct ThingInterpolateResult interpolate_thing(struct Thing *thing);

int floor_height_for_volume_box(PlayerNumber plyr_idx, MapSlabCoord slb_x, MapSlabCoord slb_y);
void frame_wibble_generate(void);
Expand Down
Loading