2017-07-23 01:19:09 +02:00
|
|
|
/*****************************************************************************
|
2019-03-17 08:16:15 +01:00
|
|
|
* Copyright (c) 2014-2019 OpenRCT2 developers
|
2018-06-15 14:07:34 +02:00
|
|
|
*
|
|
|
|
* For a complete list of all authors, please refer to contributors.md
|
|
|
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
|
|
*
|
|
|
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
|
|
*****************************************************************************/
|
2017-07-23 01:19:09 +02:00
|
|
|
|
|
|
|
#include "GameState.h"
|
2018-06-22 23:25:16 +02:00
|
|
|
|
2017-07-23 01:19:09 +02:00
|
|
|
#include "Context.h"
|
|
|
|
#include "Editor.h"
|
2018-11-19 23:10:54 +01:00
|
|
|
#include "Game.h"
|
2019-05-11 21:31:34 +02:00
|
|
|
#include "GameStateSnapshots.h"
|
2017-07-23 01:19:09 +02:00
|
|
|
#include "Input.h"
|
2018-06-22 23:25:16 +02:00
|
|
|
#include "OpenRCT2.h"
|
2018-12-06 06:34:27 +01:00
|
|
|
#include "ReplayManager.h"
|
2019-08-20 23:51:12 +02:00
|
|
|
#include "actions/GameAction.h"
|
2019-05-11 21:31:34 +02:00
|
|
|
#include "config/Config.h"
|
2017-07-23 01:19:09 +02:00
|
|
|
#include "interface/Screenshot.h"
|
2017-07-23 17:10:09 +02:00
|
|
|
#include "localisation/Date.h"
|
2018-05-27 17:11:54 +02:00
|
|
|
#include "localisation/Localisation.h"
|
2017-07-23 01:19:09 +02:00
|
|
|
#include "management/NewsItem.h"
|
|
|
|
#include "network/network.h"
|
|
|
|
#include "platform/Platform2.h"
|
|
|
|
#include "scenario/Scenario.h"
|
|
|
|
#include "title/TitleScreen.h"
|
|
|
|
#include "title/TitleSequencePlayer.h"
|
2018-06-22 23:25:16 +02:00
|
|
|
#include "ui/UiContext.h"
|
2018-05-27 17:11:54 +02:00
|
|
|
#include "windows/Intent.h"
|
2017-07-23 01:19:09 +02:00
|
|
|
#include "world/Climate.h"
|
|
|
|
#include "world/MapAnimation.h"
|
|
|
|
#include "world/Park.h"
|
2018-05-27 17:11:54 +02:00
|
|
|
#include "world/Scenery.h"
|
2017-07-23 01:19:09 +02:00
|
|
|
|
2018-08-12 13:50:40 +02:00
|
|
|
#include <algorithm>
|
|
|
|
|
2017-07-23 01:19:09 +02:00
|
|
|
using namespace OpenRCT2;
|
|
|
|
|
2017-07-23 02:06:24 +02:00
|
|
|
GameState::GameState()
|
|
|
|
{
|
|
|
|
_park = std::make_unique<Park>();
|
|
|
|
}
|
|
|
|
|
2018-05-27 17:11:54 +02:00
|
|
|
/**
|
|
|
|
* Initialises the map, park etc. basically all S6 data.
|
|
|
|
*/
|
2018-06-20 17:28:51 +02:00
|
|
|
void GameState::InitAll(int32_t mapSize)
|
2018-05-27 17:11:54 +02:00
|
|
|
{
|
|
|
|
gInMapInitCode = true;
|
|
|
|
|
|
|
|
map_init(mapSize);
|
|
|
|
_park->Initialise();
|
|
|
|
finance_init();
|
|
|
|
banner_init();
|
|
|
|
ride_init_all();
|
|
|
|
reset_sprite_list();
|
|
|
|
staff_reset_modes();
|
|
|
|
date_reset();
|
|
|
|
climate_reset(CLIMATE_COOL_AND_WET);
|
|
|
|
news_item_init_queue();
|
|
|
|
|
|
|
|
gInMapInitCode = false;
|
|
|
|
|
|
|
|
gNextGuestNumber = 1;
|
|
|
|
|
|
|
|
context_init();
|
|
|
|
scenery_set_default_placement_configuration();
|
|
|
|
|
|
|
|
auto intent = Intent(INTENT_ACTION_CLEAR_TILE_INSPECTOR_CLIPBOARD);
|
|
|
|
context_broadcast_intent(&intent);
|
|
|
|
|
|
|
|
load_palette();
|
|
|
|
}
|
|
|
|
|
2019-01-17 23:22:27 +01:00
|
|
|
/**
|
|
|
|
* Function will be called every GAME_UPDATE_TIME_MS.
|
|
|
|
* It has its own loop which might run multiple updates per call such as
|
|
|
|
* when operating as a client it may run multiple updates to catch up with the server tick,
|
|
|
|
* another influence can be the game speed setting.
|
|
|
|
*/
|
2017-07-23 01:19:09 +02:00
|
|
|
void GameState::Update()
|
|
|
|
{
|
|
|
|
gInUpdateCode = true;
|
|
|
|
|
2019-01-17 23:22:27 +01:00
|
|
|
// Normal game play will update only once every GAME_UPDATE_TIME_MS
|
|
|
|
uint32_t numUpdates = 1;
|
2017-07-23 01:19:09 +02:00
|
|
|
|
|
|
|
// 0x006E3AEC // screen_game_process_mouse_input();
|
|
|
|
screenshot_check();
|
|
|
|
game_handle_keyboard_input();
|
|
|
|
|
|
|
|
if (game_is_not_paused() && gPreviewingTitleSequenceInGame)
|
|
|
|
{
|
2018-06-02 13:33:20 +02:00
|
|
|
auto player = GetContext()->GetUiContext()->GetTitleSequencePlayer();
|
|
|
|
if (player != nullptr)
|
|
|
|
{
|
|
|
|
player->Update();
|
|
|
|
}
|
2017-07-23 01:19:09 +02:00
|
|
|
}
|
|
|
|
|
2018-12-01 16:36:00 +01:00
|
|
|
uint32_t realtimeTicksElapsed = gCurrentDeltaTime / GAME_UPDATE_TIME_MS;
|
|
|
|
realtimeTicksElapsed = std::clamp<uint32_t>(realtimeTicksElapsed, 1, GAME_MAX_UPDATES);
|
|
|
|
|
|
|
|
// We use this variable to always advance ticks in normal speed.
|
|
|
|
gCurrentRealTimeTicks += realtimeTicksElapsed;
|
|
|
|
|
2019-08-06 00:28:45 +02:00
|
|
|
network_update();
|
2017-07-23 01:19:09 +02:00
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
if (network_get_mode() == NETWORK_MODE_CLIENT && network_get_status() == NETWORK_STATUS_CONNECTED
|
|
|
|
&& network_get_authstatus() == NETWORK_AUTH_OK)
|
2017-07-23 01:19:09 +02:00
|
|
|
{
|
2019-08-06 00:28:45 +02:00
|
|
|
numUpdates = std::clamp<uint32_t>(network_get_server_tick() - gCurrentTicks, 0, 10);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Determine how many times we need to update the game
|
|
|
|
if (gGameSpeed > 1)
|
|
|
|
{
|
|
|
|
// Update more often if game speed is above normal.
|
|
|
|
numUpdates = 1 << (gGameSpeed - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isPaused = game_is_paused();
|
|
|
|
if (network_get_mode() == NETWORK_MODE_SERVER && gConfigNetwork.pause_server_if_no_clients)
|
|
|
|
{
|
|
|
|
// If we are headless we always have 1 player (host), pause if no one else is around.
|
|
|
|
if (gOpenRCT2Headless && network_get_num_players() == 1)
|
2017-07-23 01:19:09 +02:00
|
|
|
{
|
2019-08-06 00:28:45 +02:00
|
|
|
isPaused |= true;
|
2017-07-23 01:19:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-23 23:40:47 +02:00
|
|
|
bool didRunSingleFrame = false;
|
2019-08-06 00:28:45 +02:00
|
|
|
if (isPaused)
|
2017-07-23 01:19:09 +02:00
|
|
|
{
|
2018-09-23 23:40:47 +02:00
|
|
|
if (gDoSingleUpdate && network_get_mode() == NETWORK_MODE_NONE)
|
|
|
|
{
|
|
|
|
didRunSingleFrame = true;
|
|
|
|
pause_toggle();
|
|
|
|
numUpdates = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
numUpdates = 0;
|
|
|
|
// Update the animation list. Note this does not
|
|
|
|
// increment the map animation.
|
|
|
|
map_animation_invalidate_all();
|
2017-07-23 01:19:09 +02:00
|
|
|
|
2018-09-23 23:40:47 +02:00
|
|
|
// Special case because we set numUpdates to 0, otherwise in game_logic_update.
|
2018-12-09 20:39:44 +01:00
|
|
|
network_process_pending();
|
2019-08-20 23:51:12 +02:00
|
|
|
|
|
|
|
GameActions::ProcessQueue();
|
2018-09-23 23:40:47 +02:00
|
|
|
}
|
2017-07-23 01:19:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Update the game one or more times
|
2018-06-20 17:28:51 +02:00
|
|
|
for (uint32_t i = 0; i < numUpdates; i++)
|
2017-07-23 01:19:09 +02:00
|
|
|
{
|
|
|
|
UpdateLogic();
|
|
|
|
if (gGameSpeed == 1)
|
|
|
|
{
|
2018-06-22 23:25:16 +02:00
|
|
|
if (input_get_state() == INPUT_STATE_RESET || input_get_state() == INPUT_STATE_NORMAL)
|
2017-07-23 01:19:09 +02:00
|
|
|
{
|
|
|
|
if (input_test_flag(INPUT_FLAG_VIEWPORT_SCROLLING))
|
|
|
|
{
|
|
|
|
input_set_flag(INPUT_FLAG_VIEWPORT_SCROLLING, false);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!gOpenRCT2Headless)
|
|
|
|
{
|
|
|
|
input_set_flag(INPUT_FLAG_VIEWPORT_SCROLLING, false);
|
|
|
|
|
|
|
|
// the flickering frequency is reduced by 4, compared to the original
|
|
|
|
// it was done due to inability to reproduce original frequency
|
|
|
|
// and decision that the original one looks too fast
|
2018-12-01 17:00:11 +01:00
|
|
|
if (gCurrentRealTimeTicks % 4 == 0)
|
2017-07-23 01:19:09 +02:00
|
|
|
gWindowMapFlashingFlags ^= (1 << 15);
|
|
|
|
|
|
|
|
// Handle guest map flashing
|
|
|
|
gWindowMapFlashingFlags &= ~(1 << 1);
|
|
|
|
if (gWindowMapFlashingFlags & (1 << 0))
|
|
|
|
gWindowMapFlashingFlags |= (1 << 1);
|
|
|
|
gWindowMapFlashingFlags &= ~(1 << 0);
|
|
|
|
|
|
|
|
// Handle staff map flashing
|
|
|
|
gWindowMapFlashingFlags &= ~(1 << 3);
|
|
|
|
if (gWindowMapFlashingFlags & (1 << 2))
|
|
|
|
gWindowMapFlashingFlags |= (1 << 3);
|
|
|
|
gWindowMapFlashingFlags &= ~(1 << 2);
|
|
|
|
|
|
|
|
context_update_map_tooltip();
|
|
|
|
|
|
|
|
context_handle_input();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Always perform autosave check, even when paused
|
2018-06-22 23:25:16 +02:00
|
|
|
if (!(gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) && !(gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER)
|
|
|
|
&& !(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER))
|
2017-07-23 01:19:09 +02:00
|
|
|
{
|
|
|
|
scenario_autosave_check();
|
|
|
|
}
|
|
|
|
|
|
|
|
window_dispatch_update_all();
|
|
|
|
|
2018-09-23 23:40:47 +02:00
|
|
|
if (didRunSingleFrame && game_is_not_paused() && !(gScreenFlags & SCREEN_FLAGS_TITLE_DEMO))
|
|
|
|
{
|
|
|
|
pause_toggle();
|
|
|
|
}
|
|
|
|
|
2017-07-23 01:19:09 +02:00
|
|
|
gGameCommandNestLevel = 0;
|
2018-09-23 23:40:47 +02:00
|
|
|
gDoSingleUpdate = false;
|
2018-06-22 23:25:16 +02:00
|
|
|
gInUpdateCode = false;
|
2017-07-23 01:19:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void GameState::UpdateLogic()
|
|
|
|
{
|
|
|
|
gScreenAge++;
|
|
|
|
if (gScreenAge == 0)
|
|
|
|
gScreenAge--;
|
|
|
|
|
2018-12-06 06:34:27 +01:00
|
|
|
GetContext()->GetReplayManager()->Update();
|
|
|
|
|
2019-08-08 17:49:30 +02:00
|
|
|
network_update();
|
|
|
|
|
2017-07-23 01:19:09 +02:00
|
|
|
if (network_get_mode() == NETWORK_MODE_SERVER)
|
|
|
|
{
|
2019-05-11 21:31:34 +02:00
|
|
|
if (network_gamestate_snapshots_enabled())
|
|
|
|
{
|
|
|
|
CreateStateSnapshot();
|
|
|
|
}
|
|
|
|
|
2017-07-23 01:19:09 +02:00
|
|
|
// Send current tick out.
|
|
|
|
network_send_tick();
|
|
|
|
}
|
|
|
|
else if (network_get_mode() == NETWORK_MODE_CLIENT)
|
|
|
|
{
|
|
|
|
// Check desync.
|
2019-05-11 21:31:34 +02:00
|
|
|
bool desynced = network_check_desynchronisation();
|
|
|
|
if (desynced)
|
|
|
|
{
|
|
|
|
// If desync debugging is enabled and we are still connected request the specific game state from server.
|
|
|
|
if (network_gamestate_snapshots_enabled() && network_get_status() == NETWORK_STATUS_CONNECTED)
|
|
|
|
{
|
|
|
|
// Create snapshot from this tick so we can compare it later
|
|
|
|
// as we won't pause the game on this event.
|
|
|
|
CreateStateSnapshot();
|
|
|
|
|
|
|
|
network_request_gamestate_snapshot();
|
|
|
|
}
|
|
|
|
}
|
2017-07-23 01:19:09 +02:00
|
|
|
}
|
|
|
|
|
2017-07-23 17:10:09 +02:00
|
|
|
date_update();
|
|
|
|
_date = Date(gDateMonthTicks, gDateMonthTicks);
|
|
|
|
|
2017-07-23 01:19:09 +02:00
|
|
|
scenario_update();
|
|
|
|
climate_update();
|
|
|
|
map_update_tiles();
|
|
|
|
// Temporarily remove provisional paths to prevent peep from interacting with them
|
|
|
|
map_remove_provisional_elements();
|
|
|
|
map_update_path_wide_flags();
|
|
|
|
peep_update_all();
|
|
|
|
map_restore_provisional_elements();
|
|
|
|
vehicle_update_all();
|
|
|
|
sprite_misc_update_all();
|
2019-04-20 22:22:39 +02:00
|
|
|
Ride::UpdateAll();
|
2017-07-23 02:06:24 +02:00
|
|
|
|
2019-03-20 20:58:08 +01:00
|
|
|
if (!(gScreenFlags & SCREEN_FLAGS_EDITOR))
|
2017-07-23 02:06:24 +02:00
|
|
|
{
|
2017-07-23 17:10:09 +02:00
|
|
|
_park->Update(_date);
|
2017-07-23 02:06:24 +02:00
|
|
|
}
|
|
|
|
|
2017-07-23 01:19:09 +02:00
|
|
|
research_update();
|
|
|
|
ride_ratings_update_all();
|
|
|
|
ride_measurements_update();
|
|
|
|
news_item_update_current();
|
|
|
|
|
|
|
|
map_animation_invalidate_all();
|
|
|
|
vehicle_sounds_update();
|
|
|
|
peep_update_crowd_noise();
|
|
|
|
climate_update_sound();
|
|
|
|
editor_open_windows_for_current_step();
|
|
|
|
|
|
|
|
// Update windows
|
2018-06-22 23:25:16 +02:00
|
|
|
// window_dispatch_update_all();
|
2017-07-23 01:19:09 +02:00
|
|
|
|
|
|
|
// Start autosave timer after update
|
|
|
|
if (gLastAutoSaveUpdate == AUTOSAVE_PAUSE)
|
|
|
|
{
|
|
|
|
gLastAutoSaveUpdate = Platform::GetTicks();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Separated out processing commands in network_update which could call scenario_rand where gInUpdateCode is false.
|
|
|
|
// All commands that are received are first queued and then executed where gInUpdateCode is set to true.
|
2018-12-09 20:39:44 +01:00
|
|
|
network_process_pending();
|
2019-08-20 23:51:12 +02:00
|
|
|
GameActions::ProcessQueue();
|
2017-07-23 01:19:09 +02:00
|
|
|
|
|
|
|
network_flush();
|
|
|
|
|
|
|
|
gCurrentTicks++;
|
|
|
|
gScenarioTicks++;
|
|
|
|
gSavedAge++;
|
|
|
|
}
|
2019-05-11 21:31:34 +02:00
|
|
|
|
|
|
|
void GameState::CreateStateSnapshot()
|
|
|
|
{
|
|
|
|
IGameStateSnapshots* snapshots = GetContext()->GetGameStateSnapshots();
|
|
|
|
|
|
|
|
auto& snapshot = snapshots->CreateSnapshot();
|
|
|
|
snapshots->Capture(snapshot);
|
|
|
|
snapshots->LinkSnapshot(snapshot, gCurrentTicks, scenario_rand_state().s0);
|
|
|
|
}
|