mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-22 06:23:04 +01:00
handle scenario conflicts
This commit is contained in:
@@ -438,7 +438,6 @@ void scenario_success()
|
||||
} else {
|
||||
scenario_highscore_free(scenario->highscore);
|
||||
}
|
||||
scenario->highscore->fileNameRoot = scenario->path_root;
|
||||
scenario->highscore->fileName = (utf8*)path_get_filename(scenario->path);
|
||||
scenario->highscore->name = NULL;
|
||||
scenario->highscore->company_value = companyValue;
|
||||
|
||||
@@ -421,18 +421,16 @@ enum {
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8 fileNameRoot;
|
||||
utf8 *fileName;
|
||||
utf8 *name;
|
||||
money32 company_value;
|
||||
} scenario_highscore_entry;
|
||||
|
||||
typedef struct {
|
||||
uint8 path_root;
|
||||
utf8 path[MAX_PATH];
|
||||
uint64 timestamp;
|
||||
|
||||
// Category / sequence
|
||||
uint8 flags;
|
||||
uint8 category;
|
||||
uint8 source_game;
|
||||
sint16 source_index;
|
||||
@@ -448,11 +446,6 @@ typedef struct {
|
||||
utf8 details[256];
|
||||
} scenario_index_entry;
|
||||
|
||||
enum {
|
||||
SCENARIO_ROOT_RCT2,
|
||||
SCENARIO_ROOT_USER,
|
||||
};
|
||||
|
||||
// Scenario list
|
||||
extern int gScenarioListCount;
|
||||
extern int gScenarioListCapacity;
|
||||
@@ -466,7 +459,6 @@ void scenario_load_list();
|
||||
void scenario_list_dispose();
|
||||
scenario_index_entry *scenario_list_find_by_filename(const utf8 *filename);
|
||||
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);
|
||||
|
||||
@@ -33,14 +33,15 @@ int gScenarioHighscoreListCount = 0;
|
||||
int gScenarioHighscoreListCapacity = 0;
|
||||
scenario_highscore_entry *gScenarioHighscoreList = NULL;
|
||||
|
||||
static void scenario_list_include(uint8 pathRoot, const utf8 *directory);
|
||||
static void scenario_list_add(uint8 pathRoot, const char *path);
|
||||
static void scenario_list_include(const utf8 *directory);
|
||||
static void scenario_list_add(const utf8 *path, uint64 timestamp);
|
||||
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 bool scenario_scores_load();
|
||||
static bool scenario_scores_legacy_load();
|
||||
static void scenario_scores_legacy_get_path(utf8 *outPath);
|
||||
static bool scenario_scores_legacy_load(const utf8 *path);
|
||||
static void scenario_highscore_remove(scenario_highscore_entry *higscore);
|
||||
static void scenario_highscore_list_dispose();
|
||||
static utf8 *io_read_string(SDL_RWops *file);
|
||||
@@ -59,18 +60,23 @@ void scenario_load_list()
|
||||
// Get scenario directory from RCT2
|
||||
safe_strncpy(directory, gConfigGeneral.game_path, sizeof(directory));
|
||||
safe_strcat_path(directory, "Scenarios", sizeof(directory));
|
||||
scenario_list_include(SCENARIO_ROOT_RCT2, directory);
|
||||
scenario_list_include(directory);
|
||||
|
||||
// Get scenario directory from user directory
|
||||
platform_get_user_directory(directory, "scenario");
|
||||
scenario_list_include(SCENARIO_ROOT_USER, directory);
|
||||
scenario_list_include(directory);
|
||||
|
||||
scenario_list_sort();
|
||||
scenario_scores_load();
|
||||
scenario_scores_legacy_load();
|
||||
|
||||
|
||||
utf8 scoresPath[MAX_PATH];
|
||||
scenario_scores_legacy_get_path(scoresPath);
|
||||
scenario_scores_legacy_load(scoresPath);
|
||||
scenario_scores_legacy_load(get_file_path(PATH_ID_SCORES));
|
||||
}
|
||||
|
||||
static void scenario_list_include(uint8 pathRoot, const utf8 *directory)
|
||||
static void scenario_list_include(const utf8 *directory)
|
||||
{
|
||||
int handle;
|
||||
file_info fileInfo;
|
||||
@@ -85,7 +91,7 @@ static void scenario_list_include(uint8 pathRoot, const utf8 *directory)
|
||||
utf8 path[MAX_PATH];
|
||||
safe_strncpy(path, directory, sizeof(pattern));
|
||||
safe_strcat_path(path, fileInfo.path, sizeof(pattern));
|
||||
scenario_list_add(pathRoot, path);
|
||||
scenario_list_add(path, fileInfo.last_modified);
|
||||
}
|
||||
platform_enumerate_files_end(handle);
|
||||
|
||||
@@ -96,12 +102,12 @@ static void scenario_list_include(uint8 pathRoot, const utf8 *directory)
|
||||
utf8 path[MAX_PATH];
|
||||
safe_strncpy(path, directory, sizeof(pattern));
|
||||
safe_strcat_path(path, subDirectory, sizeof(pattern));
|
||||
scenario_list_include(pathRoot, path);
|
||||
scenario_list_include(path);
|
||||
}
|
||||
platform_enumerate_directories_end(handle);
|
||||
}
|
||||
|
||||
static void scenario_list_add(uint8 pathRoot, const utf8 *path)
|
||||
static void scenario_list_add(const utf8 *path, uint64 timestamp)
|
||||
{
|
||||
// Load the basic scenario information
|
||||
rct_s6_header s6Header;
|
||||
@@ -110,19 +116,44 @@ static void scenario_list_add(uint8 pathRoot, const utf8 *path)
|
||||
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 = NULL;
|
||||
|
||||
const utf8 *filename = path_get_filename(path);
|
||||
scenario_index_entry *existingEntry = scenario_list_find_by_filename(filename);
|
||||
if (existingEntry != NULL) {
|
||||
bool bail = false;
|
||||
const utf8 *conflictPath;
|
||||
if (existingEntry->timestamp > timestamp) {
|
||||
// Existing entry is more recent
|
||||
conflictPath = existingEntry->path;
|
||||
|
||||
// Overwrite existing entry with this one
|
||||
newEntry = existingEntry;
|
||||
} else {
|
||||
// This entry is more recent
|
||||
conflictPath = path;
|
||||
bail = true;
|
||||
}
|
||||
printf("Scenario conflict: '%s' ignored because it is newer.\n", conflictPath);
|
||||
if (bail) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (newEntry == NULL) {
|
||||
// Increase list size
|
||||
if (gScenarioListCount == gScenarioListCapacity) {
|
||||
gScenarioListCapacity = max(8, gScenarioListCapacity * 2);
|
||||
gScenarioList = (scenario_index_entry*)realloc(gScenarioList, gScenarioListCapacity * sizeof(scenario_index_entry));
|
||||
}
|
||||
newEntry = &gScenarioList[gScenarioListCount];
|
||||
gScenarioListCount++;
|
||||
}
|
||||
scenario_index_entry *newEntry = &gScenarioList[gScenarioListCount];
|
||||
gScenarioListCount++;
|
||||
|
||||
// Set new entry
|
||||
newEntry->path_root = pathRoot;
|
||||
safe_strncpy(newEntry->path, path, sizeof(newEntry->path));
|
||||
newEntry->timestamp = timestamp;
|
||||
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;
|
||||
@@ -203,22 +234,6 @@ scenario_index_entry *scenario_list_find_by_path(const utf8 *path)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
scenario_index_entry *scenario_list_find_by_root_path(uint8 root, const utf8 *filename)
|
||||
{
|
||||
// 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 {
|
||||
platform_get_user_directory(path, "scenario");
|
||||
}
|
||||
safe_strcat_path(path, filename, sizeof(path));
|
||||
|
||||
// Find matching scenario entry
|
||||
return scenario_list_find_by_path(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path for the scenario scores path.
|
||||
*/
|
||||
@@ -241,18 +256,12 @@ static void scenario_scores_legacy_get_path(utf8 *outPath)
|
||||
* Loads the original scores.dat file and replaces any highscores that
|
||||
* are better for matching scenarios.
|
||||
*/
|
||||
static bool scenario_scores_legacy_load()
|
||||
static bool scenario_scores_legacy_load(const utf8 *path)
|
||||
{
|
||||
utf8 scoresPath[MAX_PATH];
|
||||
scenario_scores_legacy_get_path(scoresPath);
|
||||
|
||||
// First check user folder and then fallback to install directory
|
||||
SDL_RWops *file = SDL_RWFromFile(scoresPath, "rb");
|
||||
SDL_RWops *file = SDL_RWFromFile(path, "rb");
|
||||
if (file == NULL) {
|
||||
file = SDL_RWFromFile(get_file_path(PATH_ID_SCORES), "rb");
|
||||
if (file == NULL) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load header
|
||||
@@ -278,7 +287,7 @@ static bool scenario_scores_legacy_load()
|
||||
}
|
||||
|
||||
// Find matching scenario entry
|
||||
scenario_index_entry *scenarioIndexEntry = scenario_list_find_by_root_path(SCENARIO_ROOT_RCT2, scBasic.path);
|
||||
scenario_index_entry *scenarioIndexEntry = scenario_list_find_by_filename(scBasic.path);
|
||||
if (scenarioIndexEntry != NULL) {
|
||||
// Check if legacy highscore is better
|
||||
scenario_highscore_entry *highscore = scenarioIndexEntry->highscore;
|
||||
@@ -294,7 +303,6 @@ static bool scenario_scores_legacy_load()
|
||||
|
||||
// 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;
|
||||
@@ -341,8 +349,6 @@ static bool scenario_scores_load()
|
||||
// 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);
|
||||
@@ -379,7 +385,6 @@ bool scenario_scores_save()
|
||||
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);
|
||||
|
||||
@@ -664,8 +664,7 @@ static void window_options_mouseup(rct_window *w, int widgetIndex)
|
||||
case WIDX_DEBUGGING_TOOLS:
|
||||
gConfigGeneral.debugging_tools ^= 1;
|
||||
config_save_default();
|
||||
window_invalidate(w);
|
||||
window_invalidate_by_class(WC_TOP_TOOLBAR);
|
||||
gfx_invalidate_screen();
|
||||
break;
|
||||
case WIDX_TEST_UNFINISHED_TRACKS:
|
||||
gConfigGeneral.test_unfinished_tracks ^= 1;
|
||||
|
||||
@@ -187,14 +187,12 @@ static void window_scenarioselect_init_tabs()
|
||||
int show_pages = 0;
|
||||
for (int i = 0; i < gScenarioListCount; i++) {
|
||||
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 {
|
||||
show_pages |= 1 << scenario->category;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int x = 3;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
@@ -220,7 +218,7 @@ static void window_scenarioselect_mouseup(rct_window *w, int widgetIndex)
|
||||
{
|
||||
if (widgetIndex == WIDX_CLOSE) {
|
||||
window_close(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void window_scenarioselect_mousedown(int widgetIndex, rct_window*w, rct_widget* widget)
|
||||
@@ -263,18 +261,18 @@ static void window_scenarioselect_scrollmousedown(rct_window *w, int scrollIndex
|
||||
switch (listItem->type) {
|
||||
case LIST_ITEM_TYPE_HEADING:
|
||||
y -= 18;
|
||||
break;
|
||||
break;
|
||||
case LIST_ITEM_TYPE_SCENARIO:
|
||||
y -= 24;
|
||||
y -= 24;
|
||||
if (y < 0 && !listItem->scenario.is_locked) {
|
||||
audio_play_sound_panned(SOUND_CLICK_1, w->width / 2 + w->x, 0, 0, 0);
|
||||
audio_play_sound_panned(SOUND_CLICK_1, w->width / 2 + w->x, 0, 0, 0);
|
||||
scenario_load_and_play_from_path(listItem->scenario.scenario->path);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (y < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,11 +293,11 @@ static void window_scenarioselect_scrollmouseover(rct_window *w, int scrollIndex
|
||||
if (y < 0 && !listItem->scenario.is_locked) {
|
||||
selected = listItem->scenario.scenario;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
if (y < 0) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (w->highlighted_item != (uint32)selected) {
|
||||
@@ -329,7 +327,9 @@ static void window_scenarioselect_invalidate(rct_window *w)
|
||||
int windowHeight = w->height;
|
||||
window_scenarioselect_widgets[WIDX_BACKGROUND].bottom = windowHeight - 1;
|
||||
window_scenarioselect_widgets[WIDX_TABCONTENT].bottom = windowHeight - 1;
|
||||
window_scenarioselect_widgets[WIDX_SCENARIOLIST].bottom = windowHeight - 5;
|
||||
|
||||
const int bottomMargin = gConfigGeneral.debugging_tools ? 17 : 5;
|
||||
window_scenarioselect_widgets[WIDX_SCENARIOLIST].bottom = windowHeight - bottomMargin;
|
||||
}
|
||||
|
||||
static void window_scenarioselect_paint(rct_window *w, rct_drawpixelinfo *dpi)
|
||||
@@ -364,6 +364,12 @@ static void window_scenarioselect_paint(rct_window *w, rct_drawpixelinfo *dpi)
|
||||
if (scenario == NULL)
|
||||
return;
|
||||
|
||||
// Scenario path
|
||||
if (gConfigGeneral.debugging_tools) {
|
||||
const utf8 *path = scenario->path;
|
||||
gfx_draw_string_left(dpi, 1170, (void*)&path, w->colours[1], w->x + 3, w->y + w->height - 3 - 11);
|
||||
}
|
||||
|
||||
// Scenario name
|
||||
x = w->x + window_scenarioselect_widgets[WIDX_SCENARIOLIST].right + 4;
|
||||
y = w->y + window_scenarioselect_widgets[WIDX_TABCONTENT].top + 5;
|
||||
@@ -386,7 +392,11 @@ static void window_scenarioselect_paint(rct_window *w, rct_drawpixelinfo *dpi)
|
||||
|
||||
// Scenario score
|
||||
if (scenario->highscore != NULL) {
|
||||
safe_strncpy((char*)0x009BC677, scenario->highscore->name, 64);
|
||||
const utf8 *completedByName = "???";
|
||||
if (!str_is_null_or_empty(scenario->highscore->name)) {
|
||||
completedByName = scenario->highscore->name;
|
||||
}
|
||||
safe_strncpy((char*)0x009BC677, completedByName, 64);
|
||||
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, short) = 3165; // empty string
|
||||
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);
|
||||
@@ -421,36 +431,40 @@ static void window_scenarioselect_scrollpaint(rct_window *w, rct_drawpixelinfo *
|
||||
y += 18;
|
||||
break;
|
||||
case LIST_ITEM_TYPE_SCENARIO:;
|
||||
// Draw hover highlight
|
||||
// Draw hover highlight
|
||||
scenario_index_entry *scenario = listItem->scenario.scenario;
|
||||
bool isHighlighted = w->highlighted_item == (uint32)scenario;
|
||||
if (isHighlighted) {
|
||||
gfx_fill_rect(dpi, 0, y, w->width, y + 23, 0x02000031);
|
||||
}
|
||||
gfx_fill_rect(dpi, 0, y, w->width, y + 23, 0x02000031);
|
||||
}
|
||||
|
||||
bool isCompleted = scenario->highscore != NULL;
|
||||
bool isDisabled = listItem->scenario.is_locked;
|
||||
|
||||
// Draw scenario name
|
||||
rct_string_id placeholderStringId = 3165;
|
||||
safe_strncpy((char*)language_get_string(placeholderStringId), scenario->name, 64);
|
||||
// Draw scenario name
|
||||
rct_string_id placeholderStringId = 3165;
|
||||
safe_strncpy((char*)language_get_string(placeholderStringId), scenario->name, 64);
|
||||
int format = isDisabled ? 865 : (isHighlighted ? highlighted_format : unhighlighted_format);
|
||||
colour = isDisabled ? w->colours[1] | 0x40 : COLOUR_BLACK;
|
||||
gfx_draw_string_centred(dpi, format, wide ? 270 : 210, y + 1, colour, &placeholderStringId);
|
||||
gfx_draw_string_centred(dpi, format, wide ? 270 : 210, y + 1, colour, &placeholderStringId);
|
||||
|
||||
// Check if scenario is completed
|
||||
// Check if scenario is completed
|
||||
if (isCompleted) {
|
||||
// Draw completion tick
|
||||
gfx_draw_sprite(dpi, 0x5A9F, wide ? 500 : 395, y + 1, 0);
|
||||
// Draw completion tick
|
||||
gfx_draw_sprite(dpi, 0x5A9F, wide ? 500 : 395, y + 1, 0);
|
||||
|
||||
// Draw completion score
|
||||
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);
|
||||
}
|
||||
// Draw completion score
|
||||
const utf8 *completedByName = "???";
|
||||
if (!str_is_null_or_empty(scenario->highscore->name)) {
|
||||
completedByName = scenario->highscore->name;
|
||||
}
|
||||
safe_strncpy((char*)language_get_string(placeholderStringId), completedByName, 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);
|
||||
}
|
||||
|
||||
y += 24;
|
||||
y += 24;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -482,7 +496,7 @@ static void draw_category_heading(rct_window *w, rct_drawpixelinfo *dpi, int lef
|
||||
lineY++;
|
||||
gfx_draw_line(dpi, left, lineY, strLeft, lineY, darkColour);
|
||||
gfx_draw_line(dpi, strRight, lineY, right, lineY, darkColour);
|
||||
}
|
||||
}
|
||||
|
||||
static void initialise_list_items(rct_window *w)
|
||||
{
|
||||
@@ -560,10 +574,6 @@ static bool is_scenario_visible(rct_window *w, scenario_index_entry *scenario)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(scenario->flags & SCENARIO_FLAGS_VISIBLE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user