mirror of
https://github.com/OpenRCT2/OpenRCT2.git
synced 2025-01-23 19:02:04 -05:00
redo entire scenario highscore load / saving
- load and save highscores to new file format (highscores.dat) - scan scenarios from RCT2 data and user data - load highscores from legacy file (scores.dat) - fix various issues with new scenario select window
This commit is contained in:
parent
63529a5213
commit
6a768c4a3c
13 changed files with 595 additions and 299 deletions
|
@ -264,6 +264,7 @@
|
|||
<ClInclude Include="src\ride\track_paint.h" />
|
||||
<ClInclude Include="src\ride\vehicle.h" />
|
||||
<ClInclude Include="src\scenario.h" />
|
||||
<ClInclude Include="src\scenario_sources.h" />
|
||||
<ClInclude Include="src\sprites.h" />
|
||||
<ClInclude Include="src\version.h" />
|
||||
<ClInclude Include="test\management\finance_test.h" />
|
||||
|
|
|
@ -845,5 +845,6 @@
|
|||
<ClInclude Include="src\image_io.h">
|
||||
<Filter>Source</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\scenario_sources.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -203,7 +203,7 @@ config_property_definition _generalDefinitions[] = {
|
|||
{ offsetof(general_configuration, show_fps), "show_fps", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL },
|
||||
{ offsetof(general_configuration, trap_cursor), "trap_cursor", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL },
|
||||
{ offsetof(general_configuration, auto_open_shops), "auto_open_shops", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL },
|
||||
{ offsetof(general_configuration, scenario_select_mode), "scenario_select_mode", CONFIG_VALUE_TYPE_UINT8, 1, NULL },
|
||||
{ offsetof(general_configuration, scenario_select_mode), "scenario_select_mode", CONFIG_VALUE_TYPE_UINT8, SCENARIO_SELECT_MODE_ORIGIN, NULL },
|
||||
{ offsetof(general_configuration, scenario_unlocking_enabled), "scenario_unlocking_enabled", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL },
|
||||
|
||||
};
|
||||
|
|
|
@ -128,6 +128,11 @@ enum {
|
|||
SORT_DATE_DESCENDING,
|
||||
};
|
||||
|
||||
enum {
|
||||
SCENARIO_SELECT_MODE_DIFFICULTY,
|
||||
SCENARIO_SELECT_MODE_ORIGIN,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8 play_intro;
|
||||
uint8 confirmation_prompt;
|
||||
|
|
|
@ -219,19 +219,6 @@ int scenario_load(const char *path)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x00678282
|
||||
* scenario (ebx)
|
||||
*/
|
||||
int scenario_load_and_play(const rct_scenario_basic *scenario)
|
||||
{
|
||||
char path[MAX_PATH];
|
||||
|
||||
substitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), scenario->path);
|
||||
return scenario_load_and_play_from_path(path);
|
||||
}
|
||||
|
||||
int scenario_load_and_play_from_path(const char *path)
|
||||
{
|
||||
window_close_construction_windows();
|
||||
|
@ -437,29 +424,35 @@ void scenario_failure()
|
|||
*/
|
||||
void scenario_success()
|
||||
{
|
||||
int i;
|
||||
rct_scenario_basic* scenario;
|
||||
uint32 current_val = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_COMPANY_VALUE, uint32);
|
||||
const money32 companyValue = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_COMPANY_VALUE, money32);
|
||||
|
||||
RCT2_GLOBAL(RCT2_ADDRESS_COMPLETED_COMPANY_VALUE, uint32) = current_val;
|
||||
RCT2_GLOBAL(RCT2_ADDRESS_COMPLETED_COMPANY_VALUE, uint32) = companyValue;
|
||||
peep_applause();
|
||||
|
||||
for (i = 0; i < gScenarioListCount; i++) {
|
||||
scenario = &gScenarioList[i];
|
||||
uint8 scenarioRoot = SCENARIO_ROOT_RCT2;
|
||||
scenario_index_entry *scenario = scenario_list_find_by_root_path(scenarioRoot, _scenarioFileName);
|
||||
if (scenario == NULL) {
|
||||
scenarioRoot = SCENARIO_ROOT_USER;
|
||||
scenario = scenario_list_find_by_root_path(scenarioRoot, _scenarioFileName);
|
||||
}
|
||||
|
||||
if (strequals(scenario->path, _scenarioFileName, 256, true)) {
|
||||
// Check if record company value has been broken
|
||||
if ((scenario->flags & SCENARIO_FLAGS_COMPLETED) && scenario->company_value >= current_val)
|
||||
break;
|
||||
if (scenario != NULL) {
|
||||
// Check if record company value has been broken
|
||||
if (scenario->highscore == NULL || scenario->highscore->company_value < companyValue) {
|
||||
if (scenario->highscore == NULL) {
|
||||
scenario->highscore = scenario_highscore_insert();
|
||||
} else {
|
||||
scenario_highscore_free(scenario->highscore);
|
||||
}
|
||||
scenario->highscore->fileNameRoot = scenarioRoot;
|
||||
scenario->highscore->fileName = (utf8*)path_get_filename(scenario->path);
|
||||
scenario->highscore->name = NULL;
|
||||
scenario->highscore->company_value = companyValue;
|
||||
|
||||
// Allow name entry
|
||||
RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) |= PARK_FLAGS_SCENARIO_COMPLETE_NAME_INPUT;
|
||||
scenario->company_value = current_val;
|
||||
scenario->flags |= SCENARIO_FLAGS_COMPLETED;
|
||||
scenario->completed_by[0] = 0;
|
||||
RCT2_GLOBAL(0x013587C0, uint32) = current_val;
|
||||
RCT2_GLOBAL(0x013587C0, money32) = companyValue;
|
||||
scenario_scores_save();
|
||||
break;
|
||||
}
|
||||
}
|
||||
scenario_end();
|
||||
|
@ -471,21 +464,19 @@ void scenario_success()
|
|||
*/
|
||||
void scenario_success_submit_name(const char *name)
|
||||
{
|
||||
int i;
|
||||
rct_scenario_basic* scenario;
|
||||
uint32 scenarioWinCompanyValue;
|
||||
uint8 scenarioRoot = SCENARIO_ROOT_RCT2;
|
||||
scenario_index_entry *scenario = scenario_list_find_by_root_path(scenarioRoot, _scenarioFileName);
|
||||
if (scenario == NULL) {
|
||||
scenarioRoot = SCENARIO_ROOT_USER;
|
||||
scenario = scenario_list_find_by_root_path(scenarioRoot, _scenarioFileName);
|
||||
}
|
||||
|
||||
for (i = 0; i < gScenarioListCount; i++) {
|
||||
scenario = &gScenarioList[i];
|
||||
|
||||
if (strequals(scenario->path, _scenarioFileName, 256, true)) {
|
||||
scenarioWinCompanyValue = RCT2_GLOBAL(0x013587C0, uint32);
|
||||
if (scenario->company_value == scenarioWinCompanyValue) {
|
||||
safe_strncpy(scenario->completed_by, name, 64);
|
||||
safe_strncpy((char*)0x013587D8, name, 32);
|
||||
scenario_scores_save();
|
||||
}
|
||||
break;
|
||||
if (scenario != NULL) {
|
||||
money32 scenarioWinCompanyValue = RCT2_GLOBAL(0x013587C0, money32);
|
||||
if (scenario->highscore->company_value == scenarioWinCompanyValue) {
|
||||
scenario->highscore->name = _strdup(name);
|
||||
safe_strncpy((char*)0x013587D8, name, 32);
|
||||
scenario_scores_save();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -100,8 +100,8 @@ typedef struct {
|
|||
sint32 flags; // 0x0268
|
||||
uint32 company_value; // 0x026C
|
||||
char completed_by[64]; // 0x0270
|
||||
uint8 source_game; // new in OpenRCT2
|
||||
sint16 source_index; // new in OpenRCT2
|
||||
// uint8 source_game; // new in OpenRCT2
|
||||
// sint16 source_index; // new in OpenRCT2
|
||||
} rct_scenario_basic;
|
||||
|
||||
typedef struct {
|
||||
|
@ -420,20 +420,55 @@ enum {
|
|||
OBJECTIVE_MONTHLY_FOOD_INCOME
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8 fileNameRoot;
|
||||
utf8 *fileName;
|
||||
utf8 *name;
|
||||
money32 company_value;
|
||||
} scenario_highscore_entry;
|
||||
|
||||
typedef struct {
|
||||
utf8 path[MAX_PATH];
|
||||
|
||||
// Category / sequence
|
||||
uint8 flags;
|
||||
uint8 category;
|
||||
uint8 source_game;
|
||||
sint16 source_index;
|
||||
|
||||
// Objective
|
||||
uint8 objective_type;
|
||||
uint8 objective_arg_1;
|
||||
sint32 objective_arg_2;
|
||||
sint16 objective_arg_3;
|
||||
scenario_highscore_entry *highscore;
|
||||
|
||||
utf8 name[64];
|
||||
utf8 details[256];
|
||||
} scenario_index_entry;
|
||||
|
||||
enum {
|
||||
SCENARIO_ROOT_RCT2,
|
||||
SCENARIO_ROOT_USER,
|
||||
};
|
||||
|
||||
// Scenario list
|
||||
extern int gScenarioListCount;
|
||||
extern int gScenarioListCapacity;
|
||||
extern rct_scenario_basic *gScenarioList;
|
||||
extern scenario_index_entry *gScenarioList;
|
||||
|
||||
extern char gScenarioSavePath[MAX_PATH];
|
||||
extern int gFirstTimeSave;
|
||||
|
||||
int scenario_scores_save();
|
||||
bool scenario_scores_save();
|
||||
void scenario_load_list();
|
||||
rct_scenario_basic *get_scenario_by_filename(const char *filename);
|
||||
void scenario_list_dispose();
|
||||
scenario_index_entry *scenario_list_find_by_path(const utf8 *path);
|
||||
scenario_index_entry *scenario_list_find_by_root_path(uint8 root, const utf8 *filename);
|
||||
scenario_highscore_entry *scenario_highscore_insert();
|
||||
void scenario_highscore_free(scenario_highscore_entry *highscore);
|
||||
int scenario_load_basic(const char *path, rct_s6_header *header, rct_s6_info *info);
|
||||
int scenario_load(const char *path);
|
||||
int scenario_load_and_play(const rct_scenario_basic *scenario);
|
||||
int scenario_load_and_play_from_path(const char *path);
|
||||
void scenario_begin();
|
||||
void scenario_update();
|
||||
|
|
|
@ -28,55 +28,194 @@
|
|||
// Scenario list
|
||||
int gScenarioListCount = 0;
|
||||
int gScenarioListCapacity = 0;
|
||||
rct_scenario_basic *gScenarioList = NULL;
|
||||
scenario_index_entry *gScenarioList = NULL;
|
||||
|
||||
int gScenarioHighscoreListCount = 0;
|
||||
int gScenarioHighscoreListCapacity = 0;
|
||||
scenario_highscore_entry *gScenarioHighscoreList = NULL;
|
||||
|
||||
static void scenario_list_include(const utf8 *directory);
|
||||
static void scenario_list_add(const char *path);
|
||||
static void scenario_list_sort();
|
||||
static int scenario_list_sort_by_name(const void *a, const void *b);
|
||||
static int scenario_list_sort_by_index(const void *a, const void *b);
|
||||
static int scenario_scores_load();
|
||||
static sint32 get_scenario_index(utf8 *name);
|
||||
static void normalise_scenario_name(utf8 *name);
|
||||
static scenario_source source_by_index(sint32 index);
|
||||
|
||||
rct_scenario_basic *get_scenario_by_filename(const char *filename)
|
||||
static bool scenario_scores_load();
|
||||
static bool scenario_scores_legacy_load();
|
||||
static void scenario_highscore_remove(scenario_highscore_entry *higscore);
|
||||
static void scenario_highscore_list_dispose();
|
||||
static utf8 *io_read_string(SDL_RWops *file);
|
||||
static void io_write_string(SDL_RWops *file, utf8 *source);
|
||||
|
||||
/**
|
||||
* Searches and grabs the metadata for all the scenarios.
|
||||
*/
|
||||
void scenario_load_list()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < gScenarioListCount; i++)
|
||||
if (strcmp(gScenarioList[i].path, filename) == 0)
|
||||
return &gScenarioList[i];
|
||||
utf8 directory[MAX_PATH];
|
||||
|
||||
return NULL;
|
||||
// Clear scenario list
|
||||
gScenarioListCount = 0;
|
||||
|
||||
// Get scenario directory from RCT2
|
||||
safe_strncpy(directory, gConfigGeneral.game_path, sizeof(directory));
|
||||
safe_strcat_path(directory, "Scenarios", sizeof(directory));
|
||||
scenario_list_include(directory);
|
||||
|
||||
// Get scenario directory from user directory
|
||||
platform_get_user_directory(directory, "scenario");
|
||||
scenario_list_include(directory);
|
||||
|
||||
scenario_list_sort();
|
||||
scenario_scores_load();
|
||||
scenario_scores_legacy_load();
|
||||
}
|
||||
|
||||
sint16 get_scenario_index(rct_scenario_basic *scenario)
|
||||
static void scenario_list_include(const utf8 *directory)
|
||||
{
|
||||
for (int i = 0; i < NUM_ORIGINAL_SCENARIOS; i++) {
|
||||
if (strcmp(original_scenario_names[i], scenario->name) == 0)
|
||||
return i;
|
||||
int handle;
|
||||
file_info fileInfo;
|
||||
|
||||
// Scenarios in this directory
|
||||
utf8 pattern[MAX_PATH];
|
||||
safe_strncpy(pattern, directory, sizeof(pattern));
|
||||
safe_strcat_path(pattern, "*.sc6", sizeof(pattern));
|
||||
|
||||
handle = platform_enumerate_files_begin(pattern);
|
||||
while (platform_enumerate_files_next(handle, &fileInfo)) {
|
||||
utf8 path[MAX_PATH];
|
||||
safe_strncpy(path, directory, sizeof(pattern));
|
||||
safe_strcat_path(path, fileInfo.path, sizeof(pattern));
|
||||
scenario_list_add(path);
|
||||
}
|
||||
platform_enumerate_files_end(handle);
|
||||
|
||||
// Include sub-directories
|
||||
utf8 subDirectory[MAX_PATH];
|
||||
handle = platform_enumerate_directories_begin(directory);
|
||||
while (platform_enumerate_directories_next(handle, subDirectory)) {
|
||||
utf8 path[MAX_PATH];
|
||||
safe_strncpy(path, directory, sizeof(pattern));
|
||||
safe_strcat_path(path, subDirectory, sizeof(pattern));
|
||||
scenario_list_include(path);
|
||||
}
|
||||
platform_enumerate_directories_end(handle);
|
||||
}
|
||||
|
||||
static void scenario_list_add(const utf8 *path)
|
||||
{
|
||||
// Load the basic scenario information
|
||||
rct_s6_header s6Header;
|
||||
rct_s6_info s6Info;
|
||||
if (!scenario_load_basic(path, &s6Header, &s6Info)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Increase cache size
|
||||
if (gScenarioListCount == gScenarioListCapacity) {
|
||||
gScenarioListCapacity = max(8, gScenarioListCapacity * 2);
|
||||
gScenarioList = (scenario_index_entry*)realloc(gScenarioList, gScenarioListCapacity * sizeof(scenario_index_entry));
|
||||
}
|
||||
scenario_index_entry *newEntry = &gScenarioList[gScenarioListCount];
|
||||
gScenarioListCount++;
|
||||
|
||||
// Set new entry
|
||||
safe_strncpy(newEntry->path, path, sizeof(newEntry->path));
|
||||
newEntry->category = s6Info.category;
|
||||
newEntry->flags = SCENARIO_FLAGS_VISIBLE;
|
||||
newEntry->objective_type = s6Info.objective_type;
|
||||
newEntry->objective_arg_1 = s6Info.objective_arg_1;
|
||||
newEntry->objective_arg_2 = s6Info.objective_arg_2;
|
||||
newEntry->objective_arg_3 = s6Info.objective_arg_3;
|
||||
newEntry->highscore = NULL;
|
||||
safe_strncpy(newEntry->name, s6Info.name, sizeof(newEntry->name));
|
||||
safe_strncpy(newEntry->details, s6Info.details, sizeof(newEntry->details));
|
||||
|
||||
// Normalise the name to make the scenario as recognisable as possible.
|
||||
normalise_scenario_name(newEntry->name);
|
||||
|
||||
// Look up and store information regarding the origins of this scenario.
|
||||
newEntry->source_index = get_scenario_index(newEntry->name);
|
||||
newEntry->source_game = source_by_index(newEntry->source_index);
|
||||
}
|
||||
|
||||
void scenario_list_dispose()
|
||||
{
|
||||
gScenarioListCapacity = 0;
|
||||
gScenarioListCount = 0;
|
||||
SafeFree(gScenarioList);
|
||||
}
|
||||
|
||||
static void scenario_list_sort()
|
||||
{
|
||||
int(*compareFunc)(void const*, void const*);
|
||||
|
||||
compareFunc = gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN ?
|
||||
scenario_list_sort_by_index :
|
||||
scenario_list_sort_by_name;
|
||||
|
||||
qsort(gScenarioList, gScenarioListCount, sizeof(scenario_index_entry), compareFunc);
|
||||
}
|
||||
|
||||
static int scenario_list_sort_by_name(const void *a, const void *b)
|
||||
{
|
||||
const scenario_index_entry *entryA = (const scenario_index_entry*)a;
|
||||
const scenario_index_entry *entryB = (const scenario_index_entry*)b;
|
||||
|
||||
return strcmp(entryA->name, entryB->name);
|
||||
}
|
||||
|
||||
static int scenario_list_sort_by_index(const void *a, const void *b)
|
||||
{
|
||||
const scenario_index_entry *entryA = (const scenario_index_entry*)a;
|
||||
const scenario_index_entry *entryB = (const scenario_index_entry*)b;
|
||||
|
||||
if (entryA->source_game == SCENARIO_SOURCE_OTHER && entryB->source_game == SCENARIO_SOURCE_OTHER) {
|
||||
return scenario_list_sort_by_name(a, b);
|
||||
}
|
||||
return entryA->source_index - entryB->source_index;
|
||||
}
|
||||
|
||||
static sint32 get_scenario_index(utf8 *name)
|
||||
{
|
||||
for (sint32 i = 0; i < NUM_ORIGINAL_SCENARIOS; i++) {
|
||||
if (_strcmpi(original_scenario_names[i], name) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void normalise_scenario_name(rct_scenario_basic *scenario)
|
||||
static void normalise_scenario_name(utf8 *name)
|
||||
{
|
||||
char* name = scenario->name;
|
||||
size_t nameLength = strlen(name);
|
||||
|
||||
// Strip "RCT2 " prefix off scenario names.
|
||||
if (name[0] == 'R' && name[1] == 'C' && name[2] == 'T' && name[3] == '2') {
|
||||
log_verbose("Stripping RCT2 from name: %s", name);
|
||||
safe_strncpy(scenario->name, name + 5, 64);
|
||||
// Strip "RCT(1|2)? *" prefix off scenario names.
|
||||
if (nameLength >= 3 && (name[0] == 'R' && name[1] == 'C' && name[2] == 'T')) {
|
||||
if (nameLength >= 4 && (name[3] == '1' || name[3] == '2')) {
|
||||
log_verbose("Stripping RCT/1/2 from name: %s", name);
|
||||
safe_strncpy(name, name + 4, 64);
|
||||
} else {
|
||||
safe_strncpy(name, name + 3, 64);
|
||||
}
|
||||
|
||||
safe_strtrimleft(name, name, 64);
|
||||
}
|
||||
|
||||
// American scenario titles should be handled by their British counterpart, internally.
|
||||
// American scenario titles should be converted to British name
|
||||
// Don't worry, names will be translated using language packs later
|
||||
for (int i = 0; i < NUM_ALIASES; i++) {
|
||||
if (strcmp(scenario_aliases[i * 2], name) == 0)
|
||||
{
|
||||
log_verbose("Found alias: %s; will treat as: %s", scenario->name, scenario_aliases[i * 2 + 1]);
|
||||
safe_strncpy(scenario->name, scenario_aliases[i * 2 + 1], 64);
|
||||
if (strcmp(scenario_aliases[(i * 2) + 1], name) == 0) {
|
||||
log_verbose("Found alias: %s; will treat as: %s", name, scenario_aliases[i * 2]);
|
||||
safe_strncpy(name, scenario_aliases[i * 2], 64);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scenario_source source_by_index(uint8 index)
|
||||
static scenario_source source_by_index(sint32 index)
|
||||
{
|
||||
if (index >= SCENARIO_SOURCE_RCT1_INDEX && index < SCENARIO_SOURCE_RCT1_AA_INDEX) {
|
||||
return SCENARIO_SOURCE_RCT1;
|
||||
|
@ -97,168 +236,65 @@ scenario_source source_by_index(uint8 index)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006775A8
|
||||
*/
|
||||
void scenario_load_list()
|
||||
scenario_index_entry *scenario_list_find_by_path(const utf8 *path)
|
||||
{
|
||||
int i, enumFileHandle;
|
||||
file_info enumFileInfo;
|
||||
|
||||
// Load scores
|
||||
scenario_scores_load();
|
||||
|
||||
// Set all scenarios to be invisible
|
||||
for (i = 0; i < gScenarioListCount; i++)
|
||||
gScenarioList[i].flags &= ~SCENARIO_FLAGS_VISIBLE;
|
||||
|
||||
// Enumerate through each scenario in the directory
|
||||
enumFileHandle = platform_enumerate_files_begin(RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char));
|
||||
if (enumFileHandle != INVALID_HANDLE) {
|
||||
while (platform_enumerate_files_next(enumFileHandle, &enumFileInfo)) {
|
||||
scenario_list_add(enumFileInfo.path);
|
||||
for (int i = 0; i < gScenarioListCount; i++) {
|
||||
if (_strcmpi(path, gScenarioList[i].path) == 0) {
|
||||
return &gScenarioList[i];
|
||||
}
|
||||
platform_enumerate_files_end(enumFileHandle);
|
||||
}
|
||||
|
||||
// Sort alphabetically
|
||||
scenario_list_sort();
|
||||
|
||||
// Save the scores
|
||||
scenario_scores_save();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void scenario_list_add(const char *path)
|
||||
scenario_index_entry *scenario_list_find_by_root_path(uint8 root, const utf8 *filename)
|
||||
{
|
||||
char scenarioPath[MAX_PATH];
|
||||
rct_scenario_basic *scenario;
|
||||
rct_s6_header s6Header;
|
||||
rct_s6_info s6Info;
|
||||
|
||||
// Get absolute path
|
||||
substitute_path(scenarioPath, RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), path);
|
||||
|
||||
// Load the basic scenario information
|
||||
if (!scenario_load_basic(scenarioPath, &s6Header, &s6Info))
|
||||
return;
|
||||
|
||||
// Ignore scenarios where first header byte is not 255
|
||||
if (s6Info.editor_step != 255)
|
||||
return;
|
||||
|
||||
// Check if scenario already exists in list, likely if in scores
|
||||
scenario = get_scenario_by_filename(path);
|
||||
if (scenario != NULL) {
|
||||
// Update the scenario information
|
||||
scenario->flags |= SCENARIO_FLAGS_VISIBLE;
|
||||
scenario->category = s6Info.category;
|
||||
scenario->objective_type = s6Info.objective_type;
|
||||
scenario->objective_arg_1 = s6Info.objective_arg_1;
|
||||
scenario->objective_arg_2 = s6Info.objective_arg_2;
|
||||
scenario->objective_arg_3 = s6Info.objective_arg_3;
|
||||
safe_strncpy(scenario->name, s6Info.name, 64);
|
||||
safe_strncpy(scenario->details, s6Info.details, 256);
|
||||
// Derive path
|
||||
utf8 path[MAX_PATH];
|
||||
if (root == SCENARIO_ROOT_RCT2) {
|
||||
safe_strncpy(path, gConfigGeneral.game_path, sizeof(path));
|
||||
safe_strcat_path(path, "Scenarios", sizeof(path));
|
||||
} else {
|
||||
// Check if the scenario list buffer has room for another scenario
|
||||
if (gScenarioListCount >= gScenarioListCapacity) {
|
||||
// Allocate more room
|
||||
gScenarioListCapacity += 16;
|
||||
gScenarioList = realloc(gScenarioList, gScenarioListCapacity * sizeof(rct_scenario_basic));
|
||||
}
|
||||
|
||||
// Increment the number of scenarios
|
||||
scenario = &gScenarioList[gScenarioListCount];
|
||||
gScenarioListCount++;
|
||||
|
||||
// Add this new scenario to the list
|
||||
safe_strncpy(scenario->path, path, 256);
|
||||
scenario->flags = SCENARIO_FLAGS_VISIBLE;
|
||||
if (RCT2_GLOBAL(0x009AA00C, uint8) & 1)
|
||||
scenario->flags |= SCENARIO_FLAGS_SIXFLAGS;
|
||||
scenario->category = s6Info.category;
|
||||
scenario->objective_type = s6Info.objective_type;
|
||||
scenario->objective_arg_1 = s6Info.objective_arg_1;
|
||||
scenario->objective_arg_2 = s6Info.objective_arg_2;
|
||||
scenario->objective_arg_3 = s6Info.objective_arg_3;
|
||||
safe_strncpy(scenario->name, s6Info.name, 64);
|
||||
safe_strncpy(scenario->details, s6Info.details, 256);
|
||||
platform_get_user_directory(path, "scenario");
|
||||
}
|
||||
safe_strcat_path(path, filename, sizeof(path));
|
||||
|
||||
// Normalize the name to make the scenario as recognisable as possible.
|
||||
normalise_scenario_name(scenario);
|
||||
|
||||
// Look up and store information regarding the origins of this scenario.
|
||||
scenario->source_index = get_scenario_index(scenario);
|
||||
scenario->source_game = source_by_index(scenario->source_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the list of scenarios. This used to be an insertion sort which took
|
||||
* place as each scenario loaded. It has now been changed to a quicksort which
|
||||
* takes place after all the scenarios have been loaded in.
|
||||
* rct2: 0x00677C3B
|
||||
*/
|
||||
static void scenario_list_sort()
|
||||
{
|
||||
if (gConfigGeneral.scenario_select_mode == 1) // and not tabIndex > REAL, OTHER
|
||||
qsort(gScenarioList, gScenarioListCount, sizeof(rct_scenario_basic), scenario_list_sort_by_index);
|
||||
else
|
||||
qsort(gScenarioList, gScenarioListCount, sizeof(rct_scenario_basic), scenario_list_sort_by_name);
|
||||
}
|
||||
|
||||
static int scenario_list_sort_by_index(const void *a, const void *b)
|
||||
{
|
||||
if (((rct_scenario_basic*)a)->source_game == SCENARIO_SOURCE_OTHER && ((rct_scenario_basic*)b)->source_game == SCENARIO_SOURCE_OTHER)
|
||||
return scenario_list_sort_by_name(a, b);
|
||||
|
||||
return ((rct_scenario_basic*)a)->source_index - ((rct_scenario_basic*)b)->source_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic scenario information compare function for sorting.
|
||||
* rct2: 0x00677C08
|
||||
*/
|
||||
static int scenario_list_sort_by_name(const void *a, const void *b)
|
||||
{
|
||||
return strcmp(((rct_scenario_basic*)a)->name, ((rct_scenario_basic*)b)->name);
|
||||
// Find matching scenario entry
|
||||
return scenario_list_find_by_path(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path for the scenario scores path.
|
||||
*/
|
||||
static void scenario_scores_get_path(utf8 *outPath)
|
||||
{
|
||||
platform_get_user_directory(outPath, NULL);
|
||||
strcat(outPath, "highscores.dat");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path for the scenario scores path.
|
||||
*/
|
||||
static void scenario_scores_legacy_get_path(utf8 *outPath)
|
||||
{
|
||||
platform_get_user_directory(outPath, NULL);
|
||||
strcat(outPath, "scores.dat");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006775A8
|
||||
* Loads the original scores.dat file and replaces any highscores that
|
||||
* are better for matching scenarios.
|
||||
*/
|
||||
static int scenario_scores_load()
|
||||
static bool scenario_scores_legacy_load()
|
||||
{
|
||||
SDL_RWops *file;
|
||||
char scoresPath[MAX_PATH];
|
||||
|
||||
scenario_scores_get_path(scoresPath);
|
||||
|
||||
// Free scenario list if already allocated
|
||||
if (gScenarioList != NULL) {
|
||||
free(gScenarioList);
|
||||
gScenarioList = NULL;
|
||||
}
|
||||
|
||||
// Try and load the scores file
|
||||
utf8 scoresPath[MAX_PATH];
|
||||
scenario_scores_legacy_get_path(scoresPath);
|
||||
|
||||
// First check user folder and then fallback to install directory
|
||||
file = SDL_RWFromFile(scoresPath, "rb");
|
||||
SDL_RWops *file = SDL_RWFromFile(scoresPath, "rb");
|
||||
if (file == NULL) {
|
||||
file = SDL_RWFromFile(get_file_path(PATH_ID_SCORES), "rb");
|
||||
if (file == NULL) {
|
||||
log_error("Unable to load scenario scores.");
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,53 +302,208 @@ static int scenario_scores_load()
|
|||
rct_scenario_scores_header header;
|
||||
if (SDL_RWread(file, &header, 16, 1) != 1) {
|
||||
SDL_RWclose(file);
|
||||
log_error("Invalid header in scenario scores file.");
|
||||
return 0;
|
||||
}
|
||||
gScenarioListCount = header.scenario_count;
|
||||
|
||||
// Load scenario information with scores
|
||||
int scenarioListBufferSize = gScenarioListCount * sizeof(rct_scenario_basic);
|
||||
gScenarioListCapacity = gScenarioListCount;
|
||||
gScenarioList = malloc(scenarioListBufferSize);
|
||||
if (SDL_RWread(file, gScenarioList, scenarioListBufferSize, 1) == 1) {
|
||||
SDL_RWclose(file);
|
||||
return 1;
|
||||
log_error("Invalid header in legacy scenario scores file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unable to load scores, free scenario list
|
||||
// Read scenarios
|
||||
bool highscoresDirty = false;
|
||||
for (uint32 i = 0; i < header.scenario_count; i++) {
|
||||
// Read legacy entry
|
||||
rct_scenario_basic scBasic;
|
||||
if (SDL_RWread(file, &scBasic, sizeof(rct_scenario_basic), 1) != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Ignore non-completed scenarios
|
||||
if (!(scBasic.flags & SCENARIO_FLAGS_COMPLETED)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find matching scenario entry
|
||||
scenario_index_entry *scenarioIndexEntry = scenario_list_find_by_root_path(SCENARIO_ROOT_RCT2, scBasic.path);
|
||||
if (scenarioIndexEntry != NULL) {
|
||||
// Check if legacy highscore is better
|
||||
scenario_highscore_entry *highscore = scenarioIndexEntry->highscore;
|
||||
if (highscore == NULL) {
|
||||
highscore = scenario_highscore_insert();
|
||||
scenarioIndexEntry->highscore = highscore;
|
||||
} else if (highscore->company_value < (money32)scBasic.company_value) {
|
||||
scenario_highscore_free(highscore);
|
||||
// Re-use highscore entry
|
||||
} else {
|
||||
highscore = NULL;
|
||||
}
|
||||
|
||||
// Set new highscore
|
||||
if (highscore != NULL) {
|
||||
highscore->fileNameRoot = SCENARIO_ROOT_RCT2;
|
||||
highscore->fileName = _strdup(scBasic.path);
|
||||
highscore->name = _strdup(scBasic.completed_by);
|
||||
highscore->company_value = (money32)scBasic.company_value;
|
||||
highscoresDirty = true;
|
||||
}
|
||||
|
||||
// Exit loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
SDL_RWclose(file);
|
||||
gScenarioListCount = 0;
|
||||
gScenarioListCapacity = 0;
|
||||
free(gScenarioList);
|
||||
gScenarioList = NULL;
|
||||
return 0;
|
||||
|
||||
if (highscoresDirty) {
|
||||
scenario_scores_save();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool scenario_scores_load()
|
||||
{
|
||||
utf8 scoresPath[MAX_PATH];
|
||||
scenario_scores_get_path(scoresPath);
|
||||
|
||||
// Load scores file
|
||||
SDL_RWops *file = SDL_RWFromFile(scoresPath, "rb");
|
||||
if (file == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check file version
|
||||
uint32 fileVersion;
|
||||
SDL_RWread(file, &fileVersion, sizeof(fileVersion), 1);
|
||||
if (fileVersion != 1) {
|
||||
log_error("Invalid or incompatible highscores file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read and allocate the highscore list
|
||||
scenario_highscore_list_dispose();
|
||||
SDL_RWread(file, &gScenarioHighscoreListCount, sizeof(gScenarioHighscoreListCount), 1);
|
||||
gScenarioHighscoreListCapacity = gScenarioHighscoreListCount;
|
||||
gScenarioHighscoreList = malloc(gScenarioHighscoreListCapacity * sizeof(scenario_highscore_entry));
|
||||
|
||||
// Read highscores
|
||||
for (int i = 0; i < gScenarioHighscoreListCount; i++) {
|
||||
scenario_highscore_entry *highscore = &gScenarioHighscoreList[i];
|
||||
|
||||
SDL_RWread(file, &highscore->fileNameRoot, sizeof(highscore->fileNameRoot), 1);
|
||||
highscore->fileName = io_read_string(file);
|
||||
highscore->name = io_read_string(file);
|
||||
SDL_RWread(file, &highscore->company_value, sizeof(highscore->company_value), 1);
|
||||
|
||||
// Attach highscore to correct scenario entry
|
||||
scenario_index_entry *scenarioIndexEntry = scenario_list_find_by_root_path(highscore->fileNameRoot, highscore->fileName);
|
||||
if (scenarioIndexEntry != NULL) {
|
||||
scenarioIndexEntry->highscore = highscore;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_RWclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x00677B50
|
||||
*/
|
||||
int scenario_scores_save()
|
||||
bool scenario_scores_save()
|
||||
{
|
||||
SDL_RWops *file;
|
||||
utf8 scoresPath[MAX_PATH];
|
||||
|
||||
scenario_scores_get_path(scoresPath);
|
||||
|
||||
file = SDL_RWFromFile(scoresPath, "wb");
|
||||
SDL_RWops *file = SDL_RWFromFile(scoresPath, "wb");
|
||||
if (file == NULL) {
|
||||
log_error("Unable to save scenario scores.");
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
rct_scenario_scores_header header;
|
||||
header.scenario_count = gScenarioListCount;
|
||||
|
||||
SDL_RWwrite(file, &header, sizeof(header), 1);
|
||||
if (gScenarioListCount > 0)
|
||||
SDL_RWwrite(file, gScenarioList, gScenarioListCount * sizeof(rct_scenario_basic), 1);
|
||||
const uint32 fileVersion = 1;
|
||||
|
||||
SDL_RWwrite(file, &fileVersion, sizeof(fileVersion), 1);
|
||||
SDL_RWwrite(file, &gScenarioHighscoreListCount, sizeof(gScenarioHighscoreListCount), 1);
|
||||
for (int i = 0; i < gScenarioHighscoreListCount; i++) {
|
||||
scenario_highscore_entry *highscore = &gScenarioHighscoreList[i];
|
||||
SDL_RWwrite(file, &highscore->fileNameRoot, sizeof(highscore->fileNameRoot), 1);
|
||||
io_write_string(file, highscore->fileName);
|
||||
io_write_string(file, highscore->name);
|
||||
SDL_RWwrite(file, &highscore->company_value, sizeof(highscore->company_value), 1);
|
||||
}
|
||||
SDL_RWclose(file);
|
||||
return 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
scenario_highscore_entry *scenario_highscore_insert()
|
||||
{
|
||||
if (gScenarioHighscoreListCount >= gScenarioHighscoreListCapacity) {
|
||||
gScenarioHighscoreListCapacity = max(8, gScenarioHighscoreListCapacity * 2);
|
||||
gScenarioHighscoreList = realloc(gScenarioHighscoreList, gScenarioHighscoreListCapacity * sizeof(scenario_highscore_entry));
|
||||
}
|
||||
return &gScenarioHighscoreList[gScenarioHighscoreListCount++];
|
||||
}
|
||||
|
||||
static void scenario_highscore_remove(scenario_highscore_entry *highscore)
|
||||
{
|
||||
for (int i = 0; i < gScenarioHighscoreListCount; i++) {
|
||||
if (&gScenarioHighscoreList[i] == highscore) {
|
||||
size_t moveSize = (gScenarioHighscoreListCount - i - 1) * sizeof(scenario_highscore_entry);
|
||||
if (moveSize > 0) {
|
||||
memmove(&gScenarioHighscoreList[i], &gScenarioHighscoreList[i + 1], moveSize);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scenario_highscore_free(scenario_highscore_entry *highscore)
|
||||
{
|
||||
SafeFree(highscore->fileName);
|
||||
SafeFree(highscore->name);
|
||||
}
|
||||
|
||||
static void scenario_highscore_list_dispose()
|
||||
{
|
||||
for (int i = 0; i < gScenarioHighscoreListCount; i++) {
|
||||
scenario_highscore_free(&gScenarioHighscoreList[i]);
|
||||
}
|
||||
gScenarioHighscoreListCapacity = 0;
|
||||
gScenarioHighscoreListCount = 0;
|
||||
SafeFree(gScenarioHighscoreList);
|
||||
}
|
||||
|
||||
static utf8 *io_read_string(SDL_RWops *file)
|
||||
{
|
||||
size_t bufferCount = 0;
|
||||
size_t bufferCapacity = 0;
|
||||
utf8 *buffer = NULL;
|
||||
|
||||
utf8 ch;
|
||||
do {
|
||||
SDL_RWread(file, &ch, sizeof(ch), 1);
|
||||
if (ch == '\0' && buffer == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (bufferCount >= bufferCapacity) {
|
||||
bufferCapacity = max(32, bufferCapacity * 2);
|
||||
buffer = realloc(buffer, bufferCapacity * sizeof(uint8));
|
||||
}
|
||||
|
||||
buffer[bufferCount] = ch;
|
||||
bufferCount++;
|
||||
} while (ch != '\0');
|
||||
|
||||
if (bufferCount < bufferCapacity) {
|
||||
buffer = realloc(buffer, bufferCount);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void io_write_string(SDL_RWops *file, utf8 *source)
|
||||
{
|
||||
if (source == NULL) {
|
||||
utf8 empty = 0;
|
||||
SDL_RWwrite(file, &empty, sizeof(utf8), 1);
|
||||
} else {
|
||||
SDL_RWwrite(file, source, strlen(source) + 1, 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#define NUM_ORIGINAL_SCENARIOS 136
|
||||
#define NUM_ALIASES 5
|
||||
#define NUM_ALIASES 6
|
||||
|
||||
#define SCENARIO_SOURCE_RCT1_INDEX 0
|
||||
#define SCENARIO_SOURCE_RCT1_AA_INDEX 22
|
||||
|
@ -10,15 +10,16 @@
|
|||
#define SCENARIO_SOURCE_REAL_INDEX 128
|
||||
|
||||
const char * const scenario_aliases[NUM_ALIASES * 2] = {
|
||||
"Katie's World", "Katie's Dreamland",
|
||||
"Dinky Park", "Pokey Park",
|
||||
"Aqua Park", "White Water Park",
|
||||
"Mothball Mountain","Mystic Mountain",
|
||||
"Big Pier", "Paradise Pier"
|
||||
"Katie's Dreamland", "Katie's World",
|
||||
"Pokey Park", "Dinky Park",
|
||||
"White Water Park", "Aqua Park",
|
||||
"Mystic Mountain", "Mothball Mountain",
|
||||
"Paradise Pier", "Big Pier",
|
||||
"Paradise Pier 2", "Big Pier 2",
|
||||
};
|
||||
|
||||
const char * const original_scenario_names[NUM_ORIGINAL_SCENARIOS] = {
|
||||
// RCT Classic
|
||||
// RCT
|
||||
"Forest Frontiers",
|
||||
"Dynamite Dunes",
|
||||
"Leafy Lake",
|
||||
|
@ -42,7 +43,7 @@ const char * const original_scenario_names[NUM_ORIGINAL_SCENARIOS] = {
|
|||
"Thunder Rock",
|
||||
"Mega Park",
|
||||
|
||||
// RCT: Corkscrew Follies
|
||||
// RCT: Added Attractions
|
||||
"Whispering Cliffs",
|
||||
"Three Monkeys Park",
|
||||
"Canary Mines",
|
||||
|
@ -81,7 +82,7 @@ const char * const original_scenario_names[NUM_ORIGINAL_SCENARIOS] = {
|
|||
"Razor Rocks",
|
||||
"Crater Lake",
|
||||
"Vertigo Views",
|
||||
"Big Pier 2",
|
||||
"Paradise Pier 2",
|
||||
"Dragon's Cove",
|
||||
"Good Knight Park",
|
||||
"Wacky Warren",
|
||||
|
@ -106,7 +107,7 @@ const char * const original_scenario_names[NUM_ORIGINAL_SCENARIOS] = {
|
|||
"Venus Ponds",
|
||||
"Micro Park",
|
||||
|
||||
// RCT2 Vanilla
|
||||
// RCT2
|
||||
"Crazy Castle",
|
||||
"Electric Fields",
|
||||
"Factory Capers",
|
||||
|
@ -123,7 +124,7 @@ const char * const original_scenario_names[NUM_ORIGINAL_SCENARIOS] = {
|
|||
"Lucky Lake",
|
||||
"Rainbow Summit",
|
||||
|
||||
// RCT2 Wacky Worlds
|
||||
// RCT2: Wacky Worlds
|
||||
"Africa - Victoria Falls",
|
||||
"Asia - Great Wall of China Tourism Enhancement",
|
||||
"North America - Grand Canyon",
|
||||
|
@ -142,7 +143,7 @@ const char * const original_scenario_names[NUM_ORIGINAL_SCENARIOS] = {
|
|||
"N. America - Extreme Hawaiian Island",
|
||||
"South America - Rain Forest Plateau",
|
||||
|
||||
// RCT2 Time Twister
|
||||
// RCT2: Time Twister
|
||||
"Dark Age - Robin Hood",
|
||||
"Prehistoric - After the Asteroid",
|
||||
"Roaring Twenties - Prison Island",
|
||||
|
@ -158,7 +159,7 @@ const char * const original_scenario_names[NUM_ORIGINAL_SCENARIOS] = {
|
|||
"Roaring Twenties - Skyscrapers",
|
||||
"Rock 'n' Roll - Rock 'n' Roll",
|
||||
|
||||
// Real parks
|
||||
// Real parks
|
||||
"Alton Towers",
|
||||
"Heide-Park",
|
||||
"Blackpool Pleasure Beach",
|
||||
|
|
|
@ -222,6 +222,70 @@ char *safe_strncpy(char * destination, const char * source, size_t size)
|
|||
return result;
|
||||
}
|
||||
|
||||
char *safe_strcat(char *destination, const char *source, size_t size)
|
||||
{
|
||||
assert(destination != NULL);
|
||||
assert(source != NULL);
|
||||
|
||||
if (size == 0) {
|
||||
return destination;
|
||||
}
|
||||
|
||||
char *result = destination;
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < size; i++) {
|
||||
if (*destination == '\0') {
|
||||
break;
|
||||
} else {
|
||||
destination++;
|
||||
}
|
||||
}
|
||||
|
||||
bool terminated = false;
|
||||
for (; i < size; i++) {
|
||||
if (*source != '\0') {
|
||||
*destination++ = *source++;
|
||||
} else {
|
||||
*destination = *source;
|
||||
terminated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!terminated) {
|
||||
result[size - 1] = '\0';
|
||||
log_warning("Truncating string \"%s\" to %d bytes.", result, size);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
char *safe_strcat_path(char *destination, const char *source, size_t size)
|
||||
{
|
||||
const char pathSeparator = platform_get_path_separator();
|
||||
|
||||
size_t length = strlen(destination);
|
||||
if (length >= size - 1) {
|
||||
return destination;
|
||||
}
|
||||
|
||||
if (destination[length - 1] != pathSeparator) {
|
||||
destination[length] = pathSeparator;
|
||||
destination[length + 1] = '\0';
|
||||
}
|
||||
|
||||
return safe_strcat(destination, source, size);
|
||||
}
|
||||
|
||||
char *safe_strtrimleft(char *destination, const char *source, size_t size)
|
||||
{
|
||||
while (*source == ' ' && *source != '\0') {
|
||||
source++;
|
||||
}
|
||||
return safe_strncpy(destination, source, size);
|
||||
}
|
||||
|
||||
bool utf8_is_bom(const char *str)
|
||||
{
|
||||
return str[0] == (char)0xEF && str[1] == (char)0xBB && str[2] == (char)0xBF;
|
||||
|
|
|
@ -42,6 +42,9 @@ int bitcount(int source);
|
|||
bool strequals(const char *a, const char *b, int length, bool caseInsensitive);
|
||||
int strcicmp(char const *a, char const *b);
|
||||
char *safe_strncpy(char * destination, const char * source, size_t num);
|
||||
char *safe_strcat(char *destination, const char *source, size_t size);
|
||||
char *safe_strcat_path(char *destination, const char *source, size_t size);
|
||||
char *safe_strtrimleft(char *destination, const char *source, size_t size);
|
||||
|
||||
bool utf8_is_bom(const char *str);
|
||||
bool str_is_null_or_empty(const char *str);
|
||||
|
|
|
@ -1179,6 +1179,7 @@ static void window_options_dropdown(rct_window *w, int widgetIndex, int dropdown
|
|||
gConfigGeneral.scenario_select_mode = dropdownIndex;
|
||||
config_save_default();
|
||||
window_invalidate(w);
|
||||
window_close_by_class(WC_SCENARIO_SELECT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1530,7 +1531,9 @@ static void window_options_paint(rct_window *w, rct_drawpixelinfo *dpi)
|
|||
gfx_draw_string_left(dpi, STR_OPTIONS_SCENARIO_GROUPING, NULL, w->colours[1], w->x + 10, w->y + window_options_controls_and_interface_widgets[WIDX_SCENARIO_GROUPING].top + 1);
|
||||
gfx_draw_string_left_clipped(
|
||||
dpi,
|
||||
gConfigGeneral.scenario_select_mode == 0 ? STR_OPTIONS_SCENARIO_DIFFICULTY : STR_OPTIONS_SCENARIO_ORIGIN,
|
||||
gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_DIFFICULTY ?
|
||||
STR_OPTIONS_SCENARIO_DIFFICULTY :
|
||||
STR_OPTIONS_SCENARIO_ORIGIN,
|
||||
NULL,
|
||||
w->colours[1],
|
||||
w->x + window_options_controls_and_interface_widgets[WIDX_SCENARIO_GROUPING].left + 1,
|
||||
|
|
|
@ -121,8 +121,7 @@ void window_scenarioselect_open()
|
|||
scenario_load_list();
|
||||
|
||||
// Shrink the window if we're showing scenarios by difficulty level.
|
||||
if (gConfigGeneral.scenario_select_mode == 2)
|
||||
{
|
||||
if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_DIFFICULTY) {
|
||||
window_width = 610;
|
||||
window_scenarioselect_widgets[WIDX_BACKGROUND].right = 609;
|
||||
window_scenarioselect_widgets[WIDX_TITLEBAR].right = 608;
|
||||
|
@ -130,9 +129,9 @@ void window_scenarioselect_open()
|
|||
window_scenarioselect_widgets[WIDX_CLOSE].right = 607;
|
||||
window_scenarioselect_widgets[WIDX_TABCONTENT].right = 609;
|
||||
window_scenarioselect_widgets[WIDX_SCENARIOLIST].right = 433;
|
||||
}
|
||||
else
|
||||
} else {
|
||||
window_width = 733;
|
||||
}
|
||||
|
||||
window = window_create_centred(
|
||||
window_width,
|
||||
|
@ -164,15 +163,15 @@ static void window_scenarioselect_init_tabs()
|
|||
{
|
||||
int show_pages = 0;
|
||||
for (int i = 0; i < gScenarioListCount; i++) {
|
||||
rct_scenario_basic* scenario = &gScenarioList[i];
|
||||
if (scenario->flags & SCENARIO_FLAGS_VISIBLE)
|
||||
{
|
||||
if (gConfigGeneral.scenario_select_mode == 1)
|
||||
scenario_index_entry *scenario = &gScenarioList[i];
|
||||
if (scenario->flags & SCENARIO_FLAGS_VISIBLE) {
|
||||
if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN) {
|
||||
show_pages |= 1 << scenario->source_game;
|
||||
else
|
||||
} else {
|
||||
show_pages |= 1 << scenario->category;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int x = 3;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
|
@ -212,10 +211,10 @@ static void window_scenarioselect_scrollgetsize(rct_window *w, int scrollIndex,
|
|||
{
|
||||
*height = 0;
|
||||
for (int i = 0; i < gScenarioListCount; i++) {
|
||||
rct_scenario_basic *scenario = &gScenarioList[i];
|
||||
scenario_index_entry *scenario = &gScenarioList[i];
|
||||
|
||||
if ((gConfigGeneral.scenario_select_mode == 1 && scenario->source_game != w->selected_tab) ||
|
||||
(gConfigGeneral.scenario_select_mode == 2 && scenario->category != w->selected_tab))
|
||||
if ((gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN && scenario->source_game != w->selected_tab) ||
|
||||
(gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_DIFFICULTY && scenario->category != w->selected_tab))
|
||||
continue;
|
||||
|
||||
if (scenario->flags & SCENARIO_FLAGS_VISIBLE)
|
||||
|
@ -231,20 +230,20 @@ static void window_scenarioselect_scrollmousedown(rct_window *w, int scrollIndex
|
|||
{
|
||||
int num_unlocks = 5;
|
||||
for (int i = 0; i < gScenarioListCount; i++) {
|
||||
rct_scenario_basic *scenario = &gScenarioList[i];
|
||||
scenario_index_entry *scenario = &gScenarioList[i];
|
||||
|
||||
if ((gConfigGeneral.scenario_select_mode == 1 && scenario->source_game != w->selected_tab) ||
|
||||
(gConfigGeneral.scenario_select_mode == 2 && scenario->category != w->selected_tab))
|
||||
if ((gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN && scenario->source_game != w->selected_tab) ||
|
||||
(gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_DIFFICULTY && scenario->category != w->selected_tab))
|
||||
continue;
|
||||
|
||||
if (!(scenario->flags & SCENARIO_FLAGS_VISIBLE))
|
||||
continue;
|
||||
|
||||
if (gConfigGeneral.scenario_unlocking_enabled) {
|
||||
if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN && gConfigGeneral.scenario_unlocking_enabled) {
|
||||
if (num_unlocks <= 0)
|
||||
break;
|
||||
|
||||
bool is_completed = scenario->flags & SCENARIO_FLAGS_COMPLETED;
|
||||
bool is_completed = scenario->highscore != NULL;
|
||||
if (is_completed) {
|
||||
num_unlocks++;
|
||||
} else {
|
||||
|
@ -257,7 +256,7 @@ static void window_scenarioselect_scrollmousedown(rct_window *w, int scrollIndex
|
|||
continue;
|
||||
|
||||
audio_play_sound_panned(SOUND_CLICK_1, w->width / 2 + w->x, 0, 0, 0);
|
||||
scenario_load_and_play(scenario);
|
||||
scenario_load_and_play_from_path(scenario->path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -268,22 +267,22 @@ static void window_scenarioselect_scrollmousedown(rct_window *w, int scrollIndex
|
|||
*/
|
||||
static void window_scenarioselect_scrollmouseover(rct_window *w, int scrollIndex, int x, int y)
|
||||
{
|
||||
rct_scenario_basic *selected = NULL;
|
||||
scenario_index_entry *selected = NULL;
|
||||
int num_unlocks = 5;
|
||||
for (int i = 0; i < gScenarioListCount; i++) {
|
||||
rct_scenario_basic *scenario = &gScenarioList[i];
|
||||
if ((gConfigGeneral.scenario_select_mode == 1 && scenario->source_game != w->selected_tab) ||
|
||||
(gConfigGeneral.scenario_select_mode == 2 && scenario->category != w->selected_tab))
|
||||
scenario_index_entry *scenario = &gScenarioList[i];
|
||||
if ((gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN && scenario->source_game != w->selected_tab) ||
|
||||
(gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_DIFFICULTY && scenario->category != w->selected_tab))
|
||||
continue;
|
||||
|
||||
if (!(scenario->flags & SCENARIO_FLAGS_VISIBLE))
|
||||
continue;
|
||||
|
||||
if (gConfigGeneral.scenario_unlocking_enabled) {
|
||||
if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN && gConfigGeneral.scenario_unlocking_enabled) {
|
||||
if (num_unlocks <= 0)
|
||||
break;
|
||||
|
||||
bool is_completed = scenario->flags & SCENARIO_FLAGS_COMPLETED;
|
||||
bool is_completed = scenario->highscore != NULL;
|
||||
num_unlocks += is_completed ? 1 : -1;
|
||||
}
|
||||
|
||||
|
@ -315,7 +314,7 @@ static void window_scenarioselect_paint(rct_window *w, rct_drawpixelinfo *dpi)
|
|||
{
|
||||
int i, x, y, format;
|
||||
rct_widget *widget;
|
||||
rct_scenario_basic *scenario;
|
||||
scenario_index_entry *scenario;
|
||||
|
||||
window_draw_widgets(w, dpi);
|
||||
|
||||
|
@ -330,7 +329,7 @@ static void window_scenarioselect_paint(rct_window *w, rct_drawpixelinfo *dpi)
|
|||
x = (widget->left + widget->right) / 2 + w->x;
|
||||
y = (widget->top + widget->bottom) / 2 + w->y - 3;
|
||||
|
||||
if (gConfigGeneral.scenario_select_mode == 1) {
|
||||
if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN) {
|
||||
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, short) = STR_SCENARIO_CATEGORY_RCT1 + i;
|
||||
} else { // old-style
|
||||
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, short) = STR_BEGINNER_PARKS + i;
|
||||
|
@ -339,7 +338,7 @@ static void window_scenarioselect_paint(rct_window *w, rct_drawpixelinfo *dpi)
|
|||
}
|
||||
|
||||
// Return if no scenario highlighted
|
||||
scenario = w->scenario;
|
||||
scenario = (scenario_index_entry*)w->highlighted_item;
|
||||
if (scenario == NULL)
|
||||
return;
|
||||
|
||||
|
@ -364,10 +363,10 @@ static void window_scenarioselect_paint(rct_window *w, rct_drawpixelinfo *dpi)
|
|||
y += gfx_draw_string_left_wrapped(dpi, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS, x, y, 170, STR_OBJECTIVE, 0) + 5;
|
||||
|
||||
// Scenario score
|
||||
if (scenario->flags & SCENARIO_FLAGS_COMPLETED) {
|
||||
safe_strncpy((char*)0x009BC677, scenario->completed_by, 64);
|
||||
if (scenario->highscore != NULL) {
|
||||
safe_strncpy((char*)0x009BC677, scenario->highscore->name, 64);
|
||||
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, short) = 3165; // empty string
|
||||
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, int) = scenario->company_value;
|
||||
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, int) = scenario->highscore->company_value;
|
||||
y += gfx_draw_string_left_wrapped(dpi, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS, x, y, 170, STR_COMPLETED_BY_WITH_COMPANY_VALUE, 0);
|
||||
}
|
||||
}
|
||||
|
@ -382,15 +381,15 @@ static void window_scenarioselect_scrollpaint(rct_window *w, rct_drawpixelinfo *
|
|||
int unhighlighted_format = (theme_get_preset()->features.rct1_scenario_font) ? 5139 : 1191;
|
||||
int disabled_format = 5619;
|
||||
|
||||
bool wide = gConfigGeneral.scenario_select_mode == 1;
|
||||
bool wide = gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN;
|
||||
|
||||
int y = 0;
|
||||
int num_unlocks = 5;
|
||||
for (int i = 0; i < gScenarioListCount; i++) {
|
||||
rct_scenario_basic *scenario = &gScenarioList[i];
|
||||
scenario_index_entry *scenario = &gScenarioList[i];
|
||||
|
||||
if ((gConfigGeneral.scenario_select_mode == 1 && scenario->source_game != w->selected_tab) ||
|
||||
(gConfigGeneral.scenario_select_mode == 2 && scenario->category != w->selected_tab))
|
||||
if ((gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN && scenario->source_game != w->selected_tab) ||
|
||||
(gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_DIFFICULTY && scenario->category != w->selected_tab))
|
||||
continue;
|
||||
|
||||
if (!(scenario->flags & SCENARIO_FLAGS_VISIBLE))
|
||||
|
@ -401,24 +400,26 @@ static void window_scenarioselect_scrollpaint(rct_window *w, rct_drawpixelinfo *
|
|||
|
||||
// Draw hover highlight
|
||||
bool is_highlighted = w->highlighted_item == (int)scenario;
|
||||
if (is_highlighted)
|
||||
if (is_highlighted) {
|
||||
gfx_fill_rect(dpi, 0, y, w->width, y + 23, 0x02000031);
|
||||
}
|
||||
|
||||
bool is_completed = scenario->flags & SCENARIO_FLAGS_COMPLETED;
|
||||
bool is_completed = scenario->highscore != NULL;
|
||||
bool is_disabled = false;
|
||||
if (gConfigGeneral.scenario_unlocking_enabled) {
|
||||
if (num_unlocks <= 0)
|
||||
if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN && gConfigGeneral.scenario_unlocking_enabled) {
|
||||
if (num_unlocks <= 0) {
|
||||
is_disabled = true;
|
||||
|
||||
}
|
||||
num_unlocks += is_completed ? 1 : -1;
|
||||
}
|
||||
|
||||
int format = is_disabled ? 5619 : (is_highlighted ? highlighted_format : unhighlighted_format);
|
||||
|
||||
// Draw scenario name
|
||||
safe_strncpy((char*)0x009BC677, scenario->name, 64);
|
||||
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, short) = 3165;
|
||||
gfx_draw_string_centred(dpi, format, wide ? 270 : 210, y + 1, 0, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS);
|
||||
rct_string_id placeholderStringId = 3165;
|
||||
safe_strncpy((char*)language_get_string(placeholderStringId), scenario->name, 64);
|
||||
int format = is_disabled ? 865 : (is_highlighted ? highlighted_format : unhighlighted_format);
|
||||
colour = is_disabled ? w->colours[1] | 0x40 : COLOUR_BLACK;
|
||||
gfx_draw_string_centred(dpi, format, wide ? 270 : 210, y + 1, colour, &placeholderStringId);
|
||||
|
||||
// Check if scenario is completed
|
||||
if (is_completed) {
|
||||
|
@ -426,9 +427,9 @@ static void window_scenarioselect_scrollpaint(rct_window *w, rct_drawpixelinfo *
|
|||
gfx_draw_sprite(dpi, 0x5A9F, wide ? 500 : 395, y + 1, 0);
|
||||
|
||||
// Draw completion score
|
||||
safe_strncpy((char*)0x009BC677, scenario->completed_by, 64);
|
||||
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, short) = 2793;
|
||||
RCT2_GLOBAL(0x013CE954, short) = 3165;
|
||||
safe_strncpy((char*)language_get_string(placeholderStringId), scenario->highscore->name, 64);
|
||||
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, rct_string_id) = 2793;
|
||||
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, rct_string_id) = placeholderStringId;
|
||||
gfx_draw_string_centred(dpi, format, wide ? 270 : 210, y + 11, 0, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,11 +33,11 @@ int run_all_tests();
|
|||
#include "../src/scenario.h"
|
||||
|
||||
static void test_load_scenario(CuTest* tc, const char* file_name) {
|
||||
const rct_scenario_basic* scenario = get_scenario_by_filename(file_name);
|
||||
const scenario_index_entry* scenario = scenario_list_find_by_path(file_name);
|
||||
if (scenario == NULL) {
|
||||
CuFail(tc, "Could not load scenario");
|
||||
}
|
||||
scenario_load_and_play(scenario);
|
||||
scenario_load_and_play_from_path(scenario->name);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue