mirror of
https://github.com/OpenRCT2/OpenRCT2.git
synced 2024-08-30 05:56:06 +02:00
518 lines
13 KiB
C
518 lines
13 KiB
C
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
|
/*****************************************************************************
|
|
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
|
*
|
|
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
|
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
|
*
|
|
* OpenRCT2 is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* A full copy of the GNU General Public License can be found in licence.txt
|
|
*****************************************************************************/
|
|
#pragma endregion
|
|
|
|
#pragma warning(disable : 4996) // GetVersionExA deprecated
|
|
|
|
#include <time.h>
|
|
#include "addresses.h"
|
|
#include "audio/audio.h"
|
|
#include "audio/mixer.h"
|
|
#include "config.h"
|
|
#include "drawing/drawing.h"
|
|
#include "editor.h"
|
|
#include "game.h"
|
|
#include "input.h"
|
|
#include "interface/chat.h"
|
|
#include "interface/console.h"
|
|
#include "interface/viewport.h"
|
|
#include "intro.h"
|
|
#include "localisation/date.h"
|
|
#include "localisation/localisation.h"
|
|
#include "management/news_item.h"
|
|
#include "network/network.h"
|
|
#include "network/twitch.h"
|
|
#include "object.h"
|
|
#include "openrct2.h"
|
|
#include "peep/staff.h"
|
|
#include "platform/platform.h"
|
|
#include "rct1.h"
|
|
#include "ride/ride.h"
|
|
#include "ride/track.h"
|
|
#include "ride/track_design.h"
|
|
#include "scenario.h"
|
|
#include "title.h"
|
|
#include "util/util.h"
|
|
#include "world/map.h"
|
|
#include "world/park.h"
|
|
#include "world/climate.h"
|
|
#include "world/scenery.h"
|
|
#include "world/sprite.h"
|
|
|
|
// rct2: 0x0097F67C
|
|
const char * const RCT2FilePaths[PATH_ID_END] = {
|
|
"Data\\g1.dat",
|
|
"Data\\plugin.dat",
|
|
"Data\\css1.dat",
|
|
"Data\\css2.dat",
|
|
"Data\\css4.dat",
|
|
"Data\\css5.dat",
|
|
"Data\\css6.dat",
|
|
"Data\\css7.dat",
|
|
"Data\\css8.dat",
|
|
"Data\\css9.dat",
|
|
"Data\\css11.dat",
|
|
"Data\\css12.dat",
|
|
"Data\\css13.dat",
|
|
"Data\\css14.dat",
|
|
"Data\\css15.dat",
|
|
"Data\\css3.dat",
|
|
"Data\\css17.dat",
|
|
"Data\\css18.dat",
|
|
"Data\\css19.dat",
|
|
"Data\\css20.dat",
|
|
"Data\\css21.dat",
|
|
"Data\\css22.dat",
|
|
"Saved Games\\scores.dat",
|
|
"Data\\css23.dat",
|
|
"Data\\css24.dat",
|
|
"Data\\css25.dat",
|
|
"Data\\css26.dat",
|
|
"Data\\css27.dat",
|
|
"Data\\css28.dat",
|
|
"Data\\css29.dat",
|
|
"Data\\css30.dat",
|
|
"Data\\css31.dat",
|
|
"Data\\css32.dat",
|
|
"Data\\css33.dat",
|
|
"Data\\css34.dat",
|
|
"Data\\css35.dat",
|
|
"Data\\css36.dat",
|
|
"Data\\css37.dat",
|
|
"Data\\css38.dat",
|
|
"Data\\CUSTOM1.WAV",
|
|
"Data\\CUSTOM2.WAV",
|
|
"Data\\css39.dat",
|
|
"Data\\css40.dat",
|
|
"Data\\css41.dat",
|
|
"Scenarios\\Six Flags Magic Mountain.SC6",
|
|
"Data\\css42.dat",
|
|
"Data\\css43.dat",
|
|
"Data\\css44.dat",
|
|
"Data\\css45.dat",
|
|
"Data\\css46.dat",
|
|
"Data\\css50.dat"
|
|
};
|
|
|
|
uint32 gCurrentDrawCount = 0;
|
|
|
|
uint8 gScreenFlags;
|
|
uint32 gScreenAge;
|
|
uint8 gSavePromptMode;
|
|
sint32 gScreenWidth;
|
|
sint32 gScreenHeight;
|
|
|
|
typedef struct tm tm_t;
|
|
|
|
void print_launch_information();
|
|
|
|
int rct2_init_directories();
|
|
int rct2_startup_checks();
|
|
|
|
static void rct2_draw_fps();
|
|
|
|
void rct2_quit()
|
|
{
|
|
gSavePromptMode = PM_QUIT;
|
|
window_save_prompt_open();
|
|
}
|
|
|
|
void rct2_dispose()
|
|
{
|
|
gfx_unload_g2();
|
|
gfx_unload_g1();
|
|
object_unload_all();
|
|
}
|
|
|
|
int rct2_init()
|
|
{
|
|
log_verbose("initialising game");
|
|
|
|
gScenarioTicks = 0;
|
|
util_srand((unsigned int)time(0));
|
|
if (!rct2_init_directories())
|
|
return 0;
|
|
|
|
if (!rct2_startup_checks())
|
|
return 0;
|
|
|
|
config_reset_shortcut_keys();
|
|
config_shortcut_keys_load();
|
|
gInputPlaceObjectModifier = PLACE_OBJECT_MODIFIER_NONE;
|
|
// config_load();
|
|
|
|
gfx_load_g1();
|
|
gfx_load_g2();
|
|
|
|
object_list_load();
|
|
scenario_load_list();
|
|
track_design_index_create();
|
|
|
|
font_sprite_initialise_characters();
|
|
if (!gOpenRCT2Headless) {
|
|
platform_init();
|
|
audio_init_ride_sounds_and_info();
|
|
}
|
|
viewport_init_all();
|
|
news_item_init_queue();
|
|
reset_park_entrances();
|
|
user_string_clear_all();
|
|
reset_sprite_list();
|
|
ride_init_all();
|
|
window_guest_list_init_vars_a();
|
|
staff_reset_modes();
|
|
map_init(150);
|
|
park_init();
|
|
if (!gOpenRCT2Headless)
|
|
window_title_menu_open();
|
|
date_reset();
|
|
climate_reset(CLIMATE_COOL_AND_WET);
|
|
scenery_set_default_placement_configuration();
|
|
window_new_ride_init_vars();
|
|
window_guest_list_init_vars_b();
|
|
window_staff_list_init_vars();
|
|
|
|
if (!gOpenRCT2Headless) {
|
|
title_load();
|
|
|
|
gfx_clear(&gScreenDPI, 10);
|
|
}
|
|
|
|
log_verbose("initialising game finished");
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x00683499
|
|
*/
|
|
int rct2_init_directories()
|
|
{
|
|
// windows_get_registry_install_info((rct2_install_info*)0x009AA10C, "RollerCoaster Tycoon 2 Setup", "MS Sans Serif", 0);
|
|
|
|
// check install directory
|
|
if (!platform_original_game_data_exists(gConfigGeneral.game_path)) {
|
|
log_verbose("install directory does not exist or invalid directory selected, %s", gConfigGeneral.game_path);
|
|
if (!config_find_or_browse_install_directory()) {
|
|
utf8 path[MAX_PATH];
|
|
config_get_default_path(path);
|
|
log_fatal("Invalid RCT2 installation path. Please correct \"game_path\" in %s.", path);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
char separator[] = {platform_get_path_separator(), 0};
|
|
|
|
strcpy(RCT2_ADDRESS(RCT2_ADDRESS_APP_PATH, char), gConfigGeneral.game_path);
|
|
|
|
strcpy(RCT2_ADDRESS(RCT2_ADDRESS_APP_PATH_SLASH, char), RCT2_ADDRESS(RCT2_ADDRESS_APP_PATH, char));
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_APP_PATH_SLASH, char), separator);
|
|
|
|
strcpy(RCT2_ADDRESS(RCT2_ADDRESS_SAVED_GAMES_PATH, char), RCT2_ADDRESS(RCT2_ADDRESS_APP_PATH, char));
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_SAVED_GAMES_PATH, char), separator);
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_SAVED_GAMES_PATH, char), "Saved Games");
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_SAVED_GAMES_PATH, char), separator);
|
|
|
|
strcpy(RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), RCT2_ADDRESS(RCT2_ADDRESS_APP_PATH, char));
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), separator);
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), "Scenarios");
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), separator);
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), "*.SC6");
|
|
|
|
strcpy(RCT2_ADDRESS(RCT2_ADDRESS_LANDSCAPES_PATH, char), RCT2_ADDRESS(RCT2_ADDRESS_APP_PATH, char));
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_LANDSCAPES_PATH, char), separator);
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_LANDSCAPES_PATH, char), "Landscapes");
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_LANDSCAPES_PATH, char), separator);
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_LANDSCAPES_PATH, char), "*.SC6");
|
|
|
|
strcpy(RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), RCT2_ADDRESS(RCT2_ADDRESS_APP_PATH, char));
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), separator);
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), "ObjData");
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), separator);
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), "*.DAT");
|
|
|
|
strcpy(RCT2_ADDRESS(RCT2_ADDRESS_TRACKS_PATH, char), RCT2_ADDRESS(RCT2_ADDRESS_APP_PATH, char));
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_TRACKS_PATH, char), separator);
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_TRACKS_PATH, char), "Tracks");
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_TRACKS_PATH, char), separator);
|
|
strcat(RCT2_ADDRESS(RCT2_ADDRESS_TRACKS_PATH, char), "*.TD?");
|
|
|
|
strcpy(RCT2_ADDRESS(RCT2_ADDRESS_SAVED_GAMES_PATH_2, char), RCT2_ADDRESS(RCT2_ADDRESS_SAVED_GAMES_PATH, char));
|
|
return 1;
|
|
}
|
|
|
|
void substitute_path(char *dest, const char *path, const char *filename)
|
|
{
|
|
while (*path != '*') {
|
|
*dest++ = *path++;
|
|
}
|
|
strcpy(dest, filename);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x00674B42
|
|
*/
|
|
int rct2_startup_checks()
|
|
{
|
|
if (!check_file_paths())
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void rct2_draw()
|
|
{
|
|
if (gIntroState != INTRO_STATE_NONE) {
|
|
return;
|
|
}
|
|
|
|
redraw_rain();
|
|
window_update_all();
|
|
gfx_invalidate_pickedup_peep();
|
|
gfx_draw_pickedup_peep();
|
|
update_rain_animation();
|
|
update_palette_effects();
|
|
|
|
chat_draw();
|
|
console_draw(&gScreenDPI);
|
|
|
|
if (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) {
|
|
DrawOpenRCT2(0, gScreenHeight - 20);
|
|
}
|
|
|
|
if (gConfigGeneral.show_fps) {
|
|
rct2_draw_fps();
|
|
}
|
|
|
|
gCurrentDrawCount++;
|
|
}
|
|
|
|
static uint32 _lastFPSUpdateTicks;
|
|
static uint32 _lastFPSTicks;
|
|
static float _currentFPS;
|
|
|
|
static float rct2_measure_fps()
|
|
{
|
|
uint32 currentTicks = SDL_GetTicks();
|
|
if (currentTicks - _lastFPSUpdateTicks > 500) {
|
|
_lastFPSUpdateTicks = currentTicks;
|
|
|
|
uint32 frameDelta = currentTicks - _lastFPSTicks;
|
|
_currentFPS = 1000.0f / frameDelta;
|
|
}
|
|
_lastFPSTicks = currentTicks;
|
|
return _currentFPS;
|
|
}
|
|
|
|
static void rct2_draw_fps()
|
|
{
|
|
rct_drawpixelinfo *dpi = &gScreenDPI;
|
|
int x = gScreenWidth / 2;
|
|
int y = 2;
|
|
|
|
// Measure FPS
|
|
rct2_measure_fps();
|
|
|
|
// Format string
|
|
utf8 buffer[64];
|
|
utf8 *ch = buffer;
|
|
ch = utf8_write_codepoint(ch, FORMAT_MEDIUMFONT);
|
|
ch = utf8_write_codepoint(ch, FORMAT_OUTLINE);
|
|
ch = utf8_write_codepoint(ch, FORMAT_WHITE);
|
|
|
|
const char *formatString = (_currentFPS >= 100.0f ? "%.0f" : "%.1f");
|
|
sprintf(ch, formatString, _currentFPS);
|
|
|
|
// Draw Text
|
|
int stringWidth = gfx_get_string_width(buffer);
|
|
x = x - (stringWidth / 2);
|
|
gfx_draw_string(dpi, buffer, 0, x, y);
|
|
|
|
// Make area dirty so the text doesn't get drawn over the last
|
|
gfx_set_dirty_blocks(x - 16, y - 4, gLastDrawStringX + 16, 16);
|
|
}
|
|
|
|
bool rct2_open_file(const char *path)
|
|
{
|
|
char *extension = strrchr(path, '.');
|
|
if (extension == NULL) {
|
|
return false;
|
|
}
|
|
extension++;
|
|
|
|
if (_stricmp(extension, "sv6") == 0) {
|
|
strcpy((char*)RCT2_ADDRESS_SAVED_GAMES_PATH_2, path);
|
|
game_load_save(path);
|
|
gFirstTimeSave = 0;
|
|
return true;
|
|
} else if (_stricmp(extension, "sc6") == 0) {
|
|
// TODO scenario install
|
|
rct_scenario_basic scenarioBasic;
|
|
strcpy(scenarioBasic.path, path);
|
|
scenario_load_and_play_from_path(scenarioBasic.path);
|
|
return true;
|
|
} else if (_stricmp(extension, "td6") == 0 || _stricmp(extension, "td4") == 0) {
|
|
// TODO track design install
|
|
return true;
|
|
} else if (_stricmp(extension, "sv4") == 0) {
|
|
if (rct1_load_saved_game(path)) {
|
|
game_load_init();
|
|
}
|
|
} else if (_stricmp(extension, "sc4") == 0) {
|
|
if (rct1_load_scenario(path)) {
|
|
scenario_begin();
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x00674C95
|
|
*/
|
|
int check_file_paths()
|
|
{
|
|
for (int pathId = 0; pathId < PATH_ID_END; pathId++) {
|
|
if (!check_file_path(pathId)) {
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x00674CA5
|
|
*/
|
|
int check_file_path(int pathId)
|
|
{
|
|
const utf8* path = get_file_path(pathId);
|
|
SDL_RWops *file = SDL_RWFromFile(path, "rb");
|
|
|
|
switch (pathId) {
|
|
case PATH_ID_G1:
|
|
if (file == NULL) {
|
|
log_fatal("Could not find file %s", path);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case PATH_ID_CUSTOM1:
|
|
if (file != NULL) {
|
|
// Store file size in music_custom1_size @ 0x009AF164
|
|
gRideMusicInfoList[36]->length = (uint32)SDL_RWsize(file);
|
|
}
|
|
break;
|
|
|
|
case PATH_ID_CUSTOM2:
|
|
if (file != NULL) {
|
|
// Store file size in music_custom2_size @ 0x009AF16E
|
|
gRideMusicInfoList[37]->length = (uint32)SDL_RWsize(file);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (file != NULL) {
|
|
SDL_RWclose(file);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void rct2_update()
|
|
{
|
|
int tickCount = SDL_GetTicks();
|
|
gTicksSinceLastUpdate = min(tickCount - gLastTickCount, 500);
|
|
gLastTickCount = tickCount;
|
|
if (game_is_not_paused()) {
|
|
gPaletteEffectFrame += gTicksSinceLastUpdate;
|
|
}
|
|
|
|
date_update_real_time_of_day();
|
|
|
|
// TODO: screenshot countdown process
|
|
|
|
network_update();
|
|
|
|
// check_cmdline_arg();
|
|
// Screens
|
|
if (gIntroState != INTRO_STATE_NONE) {
|
|
intro_update();
|
|
} else if ((gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) && !gOpenRCT2Headless) {
|
|
title_update();
|
|
} else {
|
|
game_update();
|
|
}
|
|
|
|
//stop_completed_sounds(); // removes other sounds that are no longer playing in directsound
|
|
|
|
twitch_update();
|
|
chat_update();
|
|
console_update();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x00674E6C
|
|
*/
|
|
const utf8 *get_file_path(int pathId)
|
|
{
|
|
static utf8 path[MAX_PATH];
|
|
strcpy(path, gConfigGeneral.game_path);
|
|
|
|
// Make sure base path is terminated with a slash
|
|
if (strlen(path) == 0 || path[strlen(path) - 1] != platform_get_path_separator()) {
|
|
if (strlen(path) >= MAX_PATH - 1) {
|
|
log_error("Path for %s too long", RCT2FilePaths[pathId]);
|
|
path[0] = '\0';
|
|
return path;
|
|
}
|
|
|
|
char separator[] = {platform_get_path_separator(), 0};
|
|
strcat(path, separator);
|
|
}
|
|
|
|
// Concatenate file path
|
|
if (strlen(path) + strlen(RCT2FilePaths[pathId]) > MAX_PATH) {
|
|
log_error("Path for %s too long", RCT2FilePaths[pathId]);
|
|
path[0] = '\0';
|
|
return path;
|
|
}
|
|
|
|
char *pathp = path + strnlen(path, sizeof(path));
|
|
strcat(path, RCT2FilePaths[pathId]);
|
|
while (*pathp) {
|
|
if (*pathp == '\\') *pathp = platform_get_path_separator();
|
|
pathp++;
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
uint32 get_file_extension_type(const utf8 *path)
|
|
{
|
|
const utf8 *extension = path_get_extension(path);
|
|
if (strcicmp(extension, ".dat") == 0) return FILE_EXTENSION_DAT;
|
|
if (strcicmp(extension, ".sc4") == 0) return FILE_EXTENSION_SC4;
|
|
if (strcicmp(extension, ".sv4") == 0) return FILE_EXTENSION_SV4;
|
|
if (strcicmp(extension, ".td4") == 0) return FILE_EXTENSION_TD4;
|
|
if (strcicmp(extension, ".sc6") == 0) return FILE_EXTENSION_SC6;
|
|
if (strcicmp(extension, ".sv6") == 0) return FILE_EXTENSION_SV6;
|
|
if (strcicmp(extension, ".td6") == 0) return FILE_EXTENSION_TD6;
|
|
return FILE_EXTENSION_UNKNOWN;
|
|
}
|