mirror of https://github.com/OpenRCT2/OpenRCT2.git
497 lines
12 KiB
C
497 lines
12 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 "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 "object/ObjectManager.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" PATH_SEPARATOR "g1.dat",
|
|
"Data" PATH_SEPARATOR "plugin.dat",
|
|
"Data" PATH_SEPARATOR "css1.dat",
|
|
"Data" PATH_SEPARATOR "css2.dat",
|
|
"Data" PATH_SEPARATOR "css4.dat",
|
|
"Data" PATH_SEPARATOR "css5.dat",
|
|
"Data" PATH_SEPARATOR "css6.dat",
|
|
"Data" PATH_SEPARATOR "css7.dat",
|
|
"Data" PATH_SEPARATOR "css8.dat",
|
|
"Data" PATH_SEPARATOR "css9.dat",
|
|
"Data" PATH_SEPARATOR "css11.dat",
|
|
"Data" PATH_SEPARATOR "css12.dat",
|
|
"Data" PATH_SEPARATOR "css13.dat",
|
|
"Data" PATH_SEPARATOR "css14.dat",
|
|
"Data" PATH_SEPARATOR "css15.dat",
|
|
"Data" PATH_SEPARATOR "css3.dat",
|
|
"Data" PATH_SEPARATOR "css17.dat",
|
|
"Data" PATH_SEPARATOR "css18.dat",
|
|
"Data" PATH_SEPARATOR "css19.dat",
|
|
"Data" PATH_SEPARATOR "css20.dat",
|
|
"Data" PATH_SEPARATOR "css21.dat",
|
|
"Data" PATH_SEPARATOR "css22.dat",
|
|
"Saved Games" PATH_SEPARATOR "scores.dat",
|
|
"Data" PATH_SEPARATOR "css23.dat",
|
|
"Data" PATH_SEPARATOR "css24.dat",
|
|
"Data" PATH_SEPARATOR "css25.dat",
|
|
"Data" PATH_SEPARATOR "css26.dat",
|
|
"Data" PATH_SEPARATOR "css27.dat",
|
|
"Data" PATH_SEPARATOR "css28.dat",
|
|
"Data" PATH_SEPARATOR "css29.dat",
|
|
"Data" PATH_SEPARATOR "css30.dat",
|
|
"Data" PATH_SEPARATOR "css31.dat",
|
|
"Data" PATH_SEPARATOR "css32.dat",
|
|
"Data" PATH_SEPARATOR "css33.dat",
|
|
"Data" PATH_SEPARATOR "css34.dat",
|
|
"Data" PATH_SEPARATOR "css35.dat",
|
|
"Data" PATH_SEPARATOR "css36.dat",
|
|
"Data" PATH_SEPARATOR "css37.dat",
|
|
"Data" PATH_SEPARATOR "css38.dat",
|
|
"Data" PATH_SEPARATOR "CUSTOM1.WAV",
|
|
"Data" PATH_SEPARATOR "CUSTOM2.WAV",
|
|
"Data" PATH_SEPARATOR "css39.dat",
|
|
"Data" PATH_SEPARATOR "css40.dat",
|
|
"Data" PATH_SEPARATOR "css41.dat",
|
|
"Scenarios" PATH_SEPARATOR "Six Flags Magic Mountain.SC6",
|
|
"Data" PATH_SEPARATOR "css42.dat",
|
|
"Data" PATH_SEPARATOR "css43.dat",
|
|
"Data" PATH_SEPARATOR "css44.dat",
|
|
"Data" PATH_SEPARATOR "css45.dat",
|
|
"Data" PATH_SEPARATOR "css46.dat",
|
|
"Data" PATH_SEPARATOR "css50.dat"
|
|
};
|
|
|
|
uint32 gCurrentDrawCount = 0;
|
|
|
|
uint8 gScreenFlags;
|
|
uint32 gScreenAge;
|
|
uint8 gSavePromptMode;
|
|
sint32 gScreenWidth;
|
|
sint32 gScreenHeight;
|
|
|
|
char gRCT2AddressAppPath[MAX_PATH];
|
|
char gRCT2AddressSavedGamesPath[MAX_PATH];
|
|
char gRCT2AddressSavedGamesPath2[MAX_PATH];
|
|
char gRCT2AddressScenariosPath[MAX_PATH];
|
|
char gRCT2AddressLandscapesPath[MAX_PATH];
|
|
char gRCT2AddressObjectDataPath[MAX_PATH];
|
|
char gRCT2AddressTracksPath[MAX_PATH];
|
|
|
|
typedef struct tm tm_t;
|
|
|
|
void print_launch_information();
|
|
|
|
int rct2_init_directories();
|
|
int rct2_startup_checks();
|
|
|
|
static void rct2_draw_fps(rct_drawpixelinfo *dpi);
|
|
|
|
void rct2_quit()
|
|
{
|
|
gSavePromptMode = PM_QUIT;
|
|
window_save_prompt_open();
|
|
}
|
|
|
|
void rct2_dispose()
|
|
{
|
|
object_manager_unload_all_objects();
|
|
gfx_unload_g2();
|
|
gfx_unload_g1();
|
|
}
|
|
|
|
bool rct2_init()
|
|
{
|
|
log_verbose("initialising game");
|
|
|
|
gScenarioTicks = 0;
|
|
util_srand((unsigned int)time(0));
|
|
if (!rct2_init_directories())
|
|
return false;
|
|
|
|
if (!rct2_startup_checks())
|
|
return false;
|
|
|
|
config_reset_shortcut_keys();
|
|
config_shortcut_keys_load();
|
|
gInputPlaceObjectModifier = PLACE_OBJECT_MODIFIER_NONE;
|
|
// config_load();
|
|
|
|
if (!gfx_load_g1()) {
|
|
return false;
|
|
}
|
|
if (!gfx_load_g2()) {
|
|
return false;
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
log_verbose("initialising game finished");
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x00683499
|
|
*/
|
|
int rct2_init_directories()
|
|
{
|
|
// windows_get_registry_install_info((rct2_install_info*)0x009AA10C, "RollerCoaster Tycoon 2 Setup", "MS Sans Serif", 0);
|
|
|
|
char separator[] = {platform_get_path_separator(), 0};
|
|
|
|
if (str_is_null_or_empty(gCustomRCT2DataPath)) {
|
|
// 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;
|
|
}
|
|
}
|
|
strcpy(gRCT2AddressAppPath, gConfigGeneral.game_path);
|
|
} else {
|
|
strcpy(gRCT2AddressAppPath, gCustomRCT2DataPath);
|
|
}
|
|
strcat(gRCT2AddressAppPath, separator);
|
|
|
|
strcpy(gRCT2AddressSavedGamesPath, gRCT2AddressAppPath);
|
|
strcat(gRCT2AddressSavedGamesPath, "Saved Games");
|
|
strcat(gRCT2AddressSavedGamesPath, separator);
|
|
|
|
strcpy(gRCT2AddressScenariosPath, gRCT2AddressAppPath);
|
|
strcat(gRCT2AddressScenariosPath, "Scenarios");
|
|
strcat(gRCT2AddressScenariosPath, separator);
|
|
strcat(gRCT2AddressScenariosPath, "*.SC6");
|
|
|
|
strcpy(gRCT2AddressLandscapesPath, gRCT2AddressAppPath);
|
|
strcat(gRCT2AddressLandscapesPath, "Landscapes");
|
|
strcat(gRCT2AddressLandscapesPath, separator);
|
|
strcat(gRCT2AddressLandscapesPath, "*.SC6");
|
|
|
|
strcpy(gRCT2AddressObjectDataPath, gRCT2AddressAppPath);
|
|
strcat(gRCT2AddressObjectDataPath, "ObjData");
|
|
strcat(gRCT2AddressObjectDataPath, separator);
|
|
strcat(gRCT2AddressObjectDataPath, "*.DAT");
|
|
|
|
strcpy(gRCT2AddressTracksPath, gRCT2AddressAppPath);
|
|
strcat(gRCT2AddressTracksPath, "Tracks");
|
|
strcat(gRCT2AddressTracksPath, separator);
|
|
strcat(gRCT2AddressTracksPath, "*.TD?");
|
|
|
|
strcpy(gRCT2AddressSavedGamesPath2, gRCT2AddressSavedGamesPath);
|
|
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(rct_drawpixelinfo *dpi)
|
|
{
|
|
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(dpi);
|
|
console_draw(dpi);
|
|
|
|
if (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) {
|
|
DrawOpenRCT2(dpi, 0, gScreenHeight - 20);
|
|
}
|
|
|
|
if (gConfigGeneral.show_fps) {
|
|
rct2_draw_fps(dpi);
|
|
}
|
|
|
|
gCurrentDrawCount++;
|
|
}
|
|
|
|
static time_t _lastSecond;
|
|
static int _currentFPS;
|
|
static int _frames;
|
|
|
|
static void rct2_measure_fps()
|
|
{
|
|
_frames++;
|
|
|
|
time_t currentTime = time(NULL);
|
|
|
|
if (currentTime != _lastSecond) {
|
|
_currentFPS = _frames;
|
|
_frames = 0;
|
|
}
|
|
|
|
_lastSecond = currentTime;
|
|
}
|
|
|
|
static void rct2_draw_fps(rct_drawpixelinfo *dpi)
|
|
{
|
|
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);
|
|
|
|
sprintf(ch, "%d", _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*)gRCT2AddressSavedGamesPath2, 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, gRCT2AddressAppPath);
|
|
safe_strcat_path(path, RCT2FilePaths[pathId], sizeof(path));
|
|
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;
|
|
}
|