From 0548384d2abd953f0657ee03da052c097adc788c Mon Sep 17 00:00:00 2001 From: Kp Date: Fri, 8 Sep 2017 00:56:37 +0000 Subject: [PATCH] Fix excessive lavafall damage Like many things in the main game loop, lavafall handling was historically done on a per-frame basis, then its effects were scaled to FrameTime to normalize the results. Ignoring rounding errors, this produced roughly equivalent damage for high framerate users (who experienced many but small damage hits) and low framerate users (who experienced few but large damage hits). However, the randomized movement was not scaled to FrameTime, which caused differing results for high framerate users versus low framerate users. Commit b36c6f20c705 tried to fix this by forcing scrape_player_on_wall to run at a capped maximum effective frame rate, then scaling the damage in check_volatile_wall accordingly, so that high framerate users would experience fewer damage hits, but the ones they received were larger, thus maintaining approximately the same damage as before. Prior to b36c6f20c705, damage was always scaled to FrameTime. In b36c6f20c705 and later, damage scaled to max(FrameTime, DESIGNATED_GAME_FRAMETIME), causing users with a high frame rate (and thus low FrameTime) to take more damage on each pass. This damage increase was balanced by an added hack in scrape_player_on_wall to limit the frequency of the scrape so that high framerate users would skip some scrapes, giving them a virtual frame rate appropriate to DESIGNATED_GAME_FRAMETIME. However, the damage is only balanced if the new governor is used consistently. It is not used consistently, so it caused a regression for passable lava surfaces. Scraping on a solid lava surface goes through scrape_player_on_wall and respects the governor. Touching a passable lava surface (only available in Descent 2) bypasses scrape_player_on_wall and jumps directly to check_volatile_wall, thereby bypassing the governor. This allows high framerate users touching a passable lava surface to take many hits (as they always did), but not receive the full benefit of downward scaling the damage (as they once did) due to the new max() expression. Thus, they are damaged frequently, but still take damage consistent with being damaged infrequently. Fix this by moving the hack from scrape_player_on_wall to check_volatile_wall, so that both solid lava surfaces and passable lava surfaces respect the governor. The governor is still an ugly hack that should not be global, but this is a spot fix for the immediate problem. Fixes: b36c6f20c705d5eae78f349794a337612c9c8d14 ("Made scrape_player_on_wall() based on a timer. Due to the player being pushed away from the lava/water surface in every frame in a random vector (wrong, too), player movement per frame was not enough to counter this on FPS rates > ~120 which made damage scaling per frame nonsensical in these situations. Instead, execute scrape results in intevals based on DESIGNATED_GAME_FRAMETIME (or per frame if FrameTime>DESIGNATED_GAME_FRAMETIME) which fixes the issues and generally works much better for the purpose of this function.") --- similar/main/collide.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/similar/main/collide.cpp b/similar/main/collide.cpp index 8121974f8..54d47bd9a 100644 --- a/similar/main/collide.cpp +++ b/similar/main/collide.cpp @@ -443,6 +443,10 @@ volatile_wall_result check_volatile_wall(const vmobjptridx_t obj, const segment if (get_player_id(obj) == Player_num) #endif { + if (!((GameTime64 > Last_volatile_scrape_time + DESIGNATED_GAME_FRAMETIME) || (GameTime64 < Last_volatile_scrape_time))) + return volatile_wall_result::none; + Last_volatile_scrape_time = GameTime64; + #if defined(DXX_BUILD_DESCENT_II) if (d > 0) #endif @@ -484,10 +488,6 @@ bool scrape_player_on_wall(const vmobjptridx_t obj, const vmsegptridx_t hitseg, if (obj->type != OBJ_PLAYER || get_player_id(obj) != Player_num) return false; - if (!((GameTime64 > Last_volatile_scrape_time + DESIGNATED_GAME_FRAMETIME) || (GameTime64 < Last_volatile_scrape_time))) - return false; - Last_volatile_scrape_time = GameTime64; - const auto type = check_volatile_wall(obj, hitseg, hitside); if (type != volatile_wall_result::none) {