diff --git a/src/game.c b/src/game.c index 3ba4e54996..02876215e0 100644 --- a/src/game.c +++ b/src/game.c @@ -670,6 +670,7 @@ int game_load_sv6(const char *path) reset_loaded_objects(); map_update_tile_pointers(); reset_0x69EBE4(); + gOpenRCT2ResetFrameSmoothing = true; return 1; } diff --git a/src/interface/window.c b/src/interface/window.c index 264d89beac..f5a23c6a66 100644 --- a/src/interface/window.c +++ b/src/interface/window.c @@ -156,6 +156,13 @@ void window_dispatch_update_all() RCT2_CALLPROC_EBPSAFE(0x006EE411); // handle_text_input } +void window_update_all_viewports() +{ + for (rct_window *w = g_window_list; w < RCT2_NEW_WINDOW; w++) + if (w->viewport != NULL) + viewport_update_position(w); +} + /** * * rct2: 0x006E77A1 @@ -169,10 +176,7 @@ void window_update_all() return; gfx_draw_all_dirty_blocks(); - - for (w = g_window_list; w < RCT2_NEW_WINDOW; w++) - if (w->viewport != NULL) - viewport_update_position(w); + window_update_all_viewports(); // 1000 tick update RCT2_GLOBAL(0x009DEB7C, sint16) += RCT2_GLOBAL(0x009DE588, sint16); diff --git a/src/interface/window.h b/src/interface/window.h index 9423d7c31c..6093d52ee1 100644 --- a/src/interface/window.h +++ b/src/interface/window.h @@ -459,6 +459,7 @@ extern rct_window* g_window_list; extern ride_list_item _window_track_list_item; void window_dispatch_update_all(); +void window_update_all_viewports(); void window_update_all(); rct_window *window_create(int x, int y, int width, int height, uint32 *event_handlers, rct_windowclass cls, uint16 flags); rct_window *window_create_auto_pos(int width, int height, uint32 *event_handlers, rct_windowclass cls, uint16 flags); diff --git a/src/openrct2.c b/src/openrct2.c index 40e09b9ba0..acea0970e8 100644 --- a/src/openrct2.c +++ b/src/openrct2.c @@ -42,6 +42,10 @@ bool gOpenRCT2Headless = false; bool gOpenRCT2ShowChangelog; +// This needs to be set when a park is loaded. It could also be used to reset other important states, it should then be renamed +// to something more general. +bool gOpenRCT2ResetFrameSmoothing = false; + /** If set, will end the OpenRCT2 game loop. Intentially private to this module so that the flag can not be set back to 0. */ int _finished; @@ -231,6 +235,21 @@ void openrct2_dispose() platform_free(); } +/** + * Determines whether its worth tweening a sprite or not when frame smoothing is on. + */ +static bool sprite_should_tween(rct_sprite *sprite) +{ + if (sprite->unknown.linked_list_type_offset == SPRITE_LINKEDLIST_OFFSET_VEHICLE) + return true; + if (sprite->unknown.linked_list_type_offset == SPRITE_LINKEDLIST_OFFSET_PEEP) + return true; + if (sprite->unknown.linked_list_type_offset == SPRITE_LINKEDLIST_OFFSET_UNKNOWN) + return true; + + return false; +} + /** * Run the main game loop until the finished flag is set at 40fps (25ms interval). */ @@ -250,6 +269,7 @@ static void openrct2_loop() if (gConfigGeneral.uncap_fps) { currentTick = SDL_GetTicks(); if (!uncappedinitialized) { + // Reset sprite locations uncapTick = SDL_GetTicks(); for (uint16 i = 0; i < MAX_SPRITES; i++) { spritelocations1[i].x = spritelocations2[i].x = g_sprite_list[i].unknown.x; @@ -258,28 +278,51 @@ static void openrct2_loop() } uncappedinitialized = 1; } + while (uncapTick <= currentTick && currentTick - uncapTick > 25) { + // Get the original position of each sprite for (uint16 i = 0; i < MAX_SPRITES; i++) { spritelocations1[i].x = g_sprite_list[i].unknown.x; spritelocations1[i].y = g_sprite_list[i].unknown.y; spritelocations1[i].z = g_sprite_list[i].unknown.z; } + + // Update the game so the sprite positions update rct2_update(); + if (gOpenRCT2ResetFrameSmoothing) { + gOpenRCT2ResetFrameSmoothing = false; + continue; + } + + // Get the next position of each sprite for (uint16 i = 0; i < MAX_SPRITES; i++) { spritelocations2[i].x = g_sprite_list[i].unknown.x; spritelocations2[i].y = g_sprite_list[i].unknown.y; spritelocations2[i].z = g_sprite_list[i].unknown.z; } + uncapTick += 25; } + + // Tween the position of each sprite from the last position to the new position based on the time between the last + // tick and the next tick. float nudge = 1 - ((float)(currentTick - uncapTick) / 25); for (uint16 i = 0; i < MAX_SPRITES; i++) { - if (!(g_sprite_list[i].unknown.linked_list_type_offset == SPRITE_LINKEDLIST_OFFSET_VEHICLE || g_sprite_list[i].unknown.linked_list_type_offset == SPRITE_LINKEDLIST_OFFSET_PEEP || g_sprite_list[i].unknown.linked_list_type_offset == SPRITE_LINKEDLIST_OFFSET_UNKNOWN)) { + if (!sprite_should_tween(&g_sprite_list[i])) continue; - } - sprite_move(spritelocations2[i].x + (sint16)((spritelocations1[i].x - spritelocations2[i].x) * nudge), spritelocations2[i].y + (sint16)((spritelocations1[i].y - spritelocations2[i].y) * nudge), spritelocations2[i].z + (sint16)((spritelocations1[i].z - spritelocations2[i].z) * nudge), &g_sprite_list[i]); + + sprite_move( + spritelocations2[i].x + (sint16)((spritelocations1[i].x - spritelocations2[i].x) * nudge), + spritelocations2[i].y + (sint16)((spritelocations1[i].y - spritelocations2[i].y) * nudge), + spritelocations2[i].z + (sint16)((spritelocations1[i].z - spritelocations2[i].z) * nudge), + &g_sprite_list[i] + ); invalidate_sprite(&g_sprite_list[i]); } + + // Viewports need to be updated to reduce chopiness of those which follow sprites + window_update_all_viewports(); + platform_process_messages(); rct2_draw(); platform_draw(); @@ -288,10 +331,12 @@ static void openrct2_loop() fps = 0; secondTick = SDL_GetTicks(); } + + // Restore the real positions of the sprites so they aren't left at the mid-tween positions for (uint16 i = 0; i < MAX_SPRITES; i++) { - if (!(g_sprite_list[i].unknown.linked_list_type_offset == SPRITE_LINKEDLIST_OFFSET_VEHICLE || g_sprite_list[i].unknown.linked_list_type_offset == SPRITE_LINKEDLIST_OFFSET_PEEP || g_sprite_list[i].unknown.linked_list_type_offset == SPRITE_LINKEDLIST_OFFSET_UNKNOWN)) { + if (!sprite_should_tween(&g_sprite_list[i])) continue; - } + sprite_move(spritelocations2[i].x, spritelocations2[i].y, spritelocations2[i].z, &g_sprite_list[i]); } } else { @@ -307,7 +352,10 @@ static void openrct2_loop() lastTick = currentTick; platform_process_messages(); + rct2_update(); + gOpenRCT2ResetFrameSmoothing = false; + rct2_draw(); platform_draw(); } diff --git a/src/openrct2.h b/src/openrct2.h index 120b09051f..227ca0cba4 100644 --- a/src/openrct2.h +++ b/src/openrct2.h @@ -35,6 +35,7 @@ extern char gOpenRCT2StartupActionPath[512]; extern char gExePath[MAX_PATH]; extern bool gOpenRCT2Headless; extern bool gOpenRCT2ShowChangelog; +extern bool gOpenRCT2ResetFrameSmoothing; bool openrct2_initialise(); void openrct2_launch(); diff --git a/src/scenario.c b/src/scenario.c index fde84c4687..e15ed1911b 100644 --- a/src/scenario.c +++ b/src/scenario.c @@ -30,6 +30,7 @@ #include "management/research.h" #include "management/news_item.h" #include "object.h" +#include "openrct2.h" #include "peep/staff.h" #include "platform/platform.h" #include "ride/ride.h" @@ -178,6 +179,7 @@ int scenario_load(const char *path) reset_loaded_objects(); map_update_tile_pointers(); reset_0x69EBE4(); + gOpenRCT2ResetFrameSmoothing = true; return 1; }