diff --git a/src/interface/console.c b/src/interface/console.c index 023857e7d4..86b55eb7ed 100644 --- a/src/interface/console.c +++ b/src/interface/console.c @@ -5,6 +5,7 @@ #include "../localisation/localisation.h" #include "../platform/platform.h" #include "../world/park.h" +#include "../util/sawyercoding.h" #include "../config.h" #include "../cursors.h" #include "../game.h" @@ -12,6 +13,8 @@ #include "../object.h" #include "console.h" #include "window.h" +#include "../world/scenery.h" +#include "../management/research.h" #define CONSOLE_BUFFER_SIZE 8192 #define CONSOLE_BUFFER_2_SIZE 256 @@ -47,6 +50,7 @@ static int console_parse_int(const char *src, bool *valid); static double console_parse_double(const char *src, bool *valid); static int cc_variables(const char **argv, int argc); +static int cc_windows(const char **argv, int argc); static int cc_help(const char **argv, int argc); #define SET_FLAG(variable, flag, value) {if (value) variable |= flag; else variable &= ~flag;} @@ -631,6 +635,147 @@ static int cc_set(const char **argv, int argc) } return 0; } +static void editor_load_selected_objects_console() +{ + uint8 *selection_flags = RCT2_GLOBAL(RCT2_ADDRESS_EDITOR_OBJECT_FLAGS_LIST, uint8*); + rct_object_entry *installed_entry = RCT2_GLOBAL(RCT2_ADDRESS_INSTALLED_OBJECT_LIST, rct_object_entry*); + + if (RCT2_GLOBAL(RCT2_ADDRESS_OBJECT_LIST_NO_ITEMS, uint32) == 0) + return; + + for (int i = RCT2_GLOBAL(RCT2_ADDRESS_OBJECT_LIST_NO_ITEMS, uint32); i != 0; i--, selection_flags++) { + if (*selection_flags & 1) { + uint8 entry_index, entry_type; + if (!find_object_in_entry_group(installed_entry, &entry_type, &entry_index)){ + int chunk_size; + if (!object_load(-1, installed_entry, &chunk_size)) { + log_error("Failed to load entry %.8s", installed_entry->name); + } + } + } + + installed_entry = object_get_next(installed_entry); + } +} + +static int cc_load_object(const char **argv, int argc) { + if (argc > 0) { + char path[260]; + + subsitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), argv[0]); + // Require pointer to start of filename + char* last_char = path + strlen(path); + strcat(path, ".DAT\0"); + + rct_object_entry entry; + if (object_load_entry(path, &entry)) { + uint8 type = entry.flags & 0xF; + uint8 index; + + if (check_object_entry(&entry)) { + if (!find_object_in_entry_group(&entry, &type, &index)){ + + int entryGroupIndex = 0; + for (; entryGroupIndex < object_entry_group_counts[type]; entryGroupIndex++){ + if (object_entry_groups[type].chunks[entryGroupIndex] == (uint8*)-1){ + break; + } + } + + if (entryGroupIndex >= object_entry_group_counts[type]) { + console_writeline_error("Too many objects of that type."); + } + else { + // Load the obect + if (!object_load(entryGroupIndex, &entry, NULL)) { + console_writeline_error("Could not load object file."); + } + else { + reset_loaded_objects(); + if (type == OBJECT_TYPE_RIDE) { + // Automatically research the ride so it's supported by the game. + + rct_ride_type *rideEntry; + int rideType; + + rideEntry = GET_RIDE_ENTRY(entryGroupIndex); + + for (int j = 0; j < 3; j++) { + rideType = rideEntry->ride_type[j]; + if (rideType != 255) + research_insert(true, 0x10000 | (rideType << 8) | entryGroupIndex, rideEntry->category[0]); + } + + gSilentResearch = true; + sub_684AC3(); + gSilentResearch = false; + } + else if (type == OBJECT_TYPE_SCENERY_SETS) { + rct_scenery_set_entry *scenerySetEntry; + + scenerySetEntry = g_scenerySetEntries[entryGroupIndex]; + + research_insert(true, entryGroupIndex, RESEARCH_CATEGORY_SCENERYSET); + + gSilentResearch = true; + sub_684AC3(); + gSilentResearch = false; + } + scenery_set_default_placement_configuration(); + window_new_ride_init_vars(); + + RCT2_GLOBAL(0x009DEB7C, uint16) = 0; + gfx_invalidate_screen(); + console_writeline("Object file loaded."); + } + } + } + else { + console_writeline_error("Object is already in scenario."); + } + } + else { + console_writeline_error("The object file was invalid."); + } + } + else { + console_writeline_error("Could not find the object file."); + } + } + + return 0; +} +static int cc_object_count(const char **argv, int argc) { + const char* object_type_names[] = { "Rides", "Small scenery", "Large scenery", "Walls", "Banners", "Paths", "Path Additions", "Scenery groups", "Park entrances", "Water" }; + for (int i = 0; i < 10; i++) { + + int entryGroupIndex = 0; + for (; entryGroupIndex < object_entry_group_counts[i]; entryGroupIndex++){ + if (object_entry_groups[i].chunks[entryGroupIndex] == (uint8*)-1){ + break; + } + } + console_printf("%s: %d/%d", object_type_names[i], entryGroupIndex, object_entry_group_counts[i]); + } + + return 0; +} +static int cc_open(const char **argv, int argc) { + if (argc > 0) { + if (strcmp(argv[0], "object_selection") == 0) { + // Only this window should be open for safety reasons + window_close_all(); + window_editor_object_selection_open(); + } else if (strcmp(argv[0], "inventions_list") == 0) { + window_editor_inventions_list_open(); + } else if (strcmp(argv[0], "options") == 0) { + window_options_open(); + } else { + console_writeline_error("Invalid window."); + } + } + return 0; +} typedef int (*console_command_func)(const char **argv, int argc); @@ -638,6 +783,7 @@ typedef struct { char *command; console_command_func func; char *help; + char *usage; } console_command; char* console_variable_table[] = { @@ -668,16 +814,33 @@ char* console_variable_table[] = { "test_unfinished_tracks", "no_test_crashes" }; - -console_command console_command_table[] = { - { "clear", cc_clear, "Clears the console." }, - { "echo", cc_echo, "Echos the text to the console.\necho text" }, - { "help", cc_help, "Lists commands or info about a command.\nhelp [command]" }, - { "get", cc_get, "Gets the value of the specified variable.\nget variable" }, - { "set", cc_set, "Sets the variable to the specified value.\nset variable value" }, - { "variables", cc_variables, "Lists all the variables that can be used with get and sometimes set." } +char* console_window_table[] = { + "object_selection", + "inventions_list", + "options" }; +console_command console_command_table[] = { + { "clear", cc_clear, "Clears the console." "clear"}, + { "echo", cc_echo, "Echos the text to the console.", "echo " }, + { "help", cc_help, "Lists commands or info about a command.", "help [command]" }, + { "get", cc_get, "Gets the value of the specified variable.", "get " }, + { "set", cc_set, "Sets the variable to the specified value.", "set " }, + { "open", cc_open, "Opens the window with the give name.", "open ." }, + { "variables", cc_variables, "Lists all the variables that can be used with get and sometimes set.", "variables" }, + { "windows", cc_windows, "Lists all the windows that can be opened.", "windows" }, + { "load_object", cc_load_object, "Loads the object file into the scenario.\n" + "Loading a scenery group will not load its associated objects.\n" + "This is a safer method opposed to \"open object_selection\".", + "load_object " }, + { "object_count", cc_object_count, "Shows the number of objects of each type in the scenario.", "object_count" } +}; + +static int cc_windows(const char **argv, int argc) { + for (int i = 0; i < countof(console_window_table); i++) + console_writeline(console_window_table[i]); + return 0; +} static int cc_variables(const char **argv, int argc) { for (int i = 0; i < countof(console_variable_table); i++) @@ -689,8 +852,10 @@ static int cc_help(const char **argv, int argc) { if (argc > 0) { for (int i = 0; i < countof(console_command_table); i++) { - if (strcmp(console_command_table[i].command, argv[0]) == 0) + if (strcmp(console_command_table[i].command, argv[0]) == 0) { console_writeline(console_command_table[i].help); + console_printf("\nUsage: %s", console_command_table[i].usage); + } } } else { diff --git a/src/interface/window.c b/src/interface/window.c index edc97909b6..335d425add 100644 --- a/src/interface/window.c +++ b/src/interface/window.c @@ -602,6 +602,19 @@ void window_close_all() { } } +void window_close_all_except_class(rct_windowclass cls) { + rct_window* w; + + window_close_by_class(WC_DROPDOWN); + + for (w = g_window_list; w < RCT2_LAST_WINDOW; w++){ + if (w->classification != cls && !(w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT))) { + window_close(w); + w = g_window_list; + } + } +} + /** * * rct2: 0x006EA845 diff --git a/src/interface/window.h b/src/interface/window.h index e0c2e8e75f..c38ebde179 100644 --- a/src/interface/window.h +++ b/src/interface/window.h @@ -449,6 +449,7 @@ void window_close_by_class(rct_windowclass cls); void window_close_by_number(rct_windowclass cls, rct_windownumber number); void window_close_top(); void window_close_all(); +void window_close_all_except_class(rct_windowclass cls); rct_window *window_find_by_class(rct_windowclass cls); rct_window *window_find_by_number(rct_windowclass cls, rct_windownumber number); rct_window *window_find_from_point(int x, int y); diff --git a/src/management/research.c b/src/management/research.c index fe1668c14f..d15b4cab91 100644 --- a/src/management/research.c +++ b/src/management/research.c @@ -36,6 +36,8 @@ rct_research_item *gResearchItems = (rct_research_item*)RCT2_RESEARCH_ITEMS; // 0x00EE787C uint8 gResearchUncompletedCategories; +bool gSilentResearch = false; + /** * * rct2: 0x006671AD, part of 0x00667132 @@ -198,7 +200,8 @@ void research_finish_item(sint32 entryIndex) if (RCT2_GLOBAL(0x009AC06C, uint8) == 0) { RCT2_GLOBAL(0x013CE952, rct_string_id) = rideEntry->var_008 & 0x1000 ? rideEntry->name : ecx + 2; - news_item_add_to_queue(NEWS_ITEM_RESEARCH, 2249, entryIndex); + if (!gSilentResearch) + news_item_add_to_queue(NEWS_ITEM_RESEARCH, 2249, entryIndex); } research_invalidate_related_windows(); @@ -213,7 +216,8 @@ void research_finish_item(sint32 entryIndex) // I don't think 0x009AC06C is ever not 0, so probably redundant if (RCT2_GLOBAL(0x009AC06C, uint8) == 0) { RCT2_GLOBAL(0x013CE952, rct_string_id) = scenerySetEntry->name; - news_item_add_to_queue(NEWS_ITEM_RESEARCH, 2250, entryIndex); + if (!gSilentResearch) + news_item_add_to_queue(NEWS_ITEM_RESEARCH, 2250, entryIndex); } research_invalidate_related_windows(); @@ -426,7 +430,7 @@ static void research_insert_researched(int entryIndex, int category) } while (entryIndex != (researchItem++)->entryIndex); } -static void research_insert(int researched, int entryIndex, int category) +void research_insert(int researched, int entryIndex, int category) { if (researched) research_insert_researched(entryIndex, category); @@ -469,6 +473,35 @@ void research_populate_list_random() } } +void research_populate_list_researched() +{ + rct_ride_type *rideEntry; + rct_scenery_set_entry *scenerySetEntry; + int rideType; + + // Rides + for (int i = 0; i < 128; i++) { + rideEntry = GET_RIDE_ENTRY(i); + if (rideEntry == (rct_ride_type*)-1) + continue; + + for (int j = 0; j < 3; j++) { + rideType = rideEntry->ride_type[j]; + if (rideType != 255) + research_insert(true, 0x10000 | (rideType << 8) | i, rideEntry->category[0]); + } + } + + // Scenery + for (int i = 0; i < 19; i++) { + scenerySetEntry = g_scenerySetEntries[i]; + if (scenerySetEntry == (rct_scenery_set_entry*)-1) + continue; + + research_insert(true, i, RESEARCH_CATEGORY_SCENERYSET); + } +} + void research_set_funding(int amount) { game_do_command(0, GAME_COMMAND_FLAG_APPLY, 0, amount, GAME_COMMAND_SET_RESEARCH_FUNDING, 0, 0); diff --git a/src/management/research.h b/src/management/research.h index 55f858bd1e..02dcf714a9 100644 --- a/src/management/research.h +++ b/src/management/research.h @@ -60,6 +60,7 @@ enum { extern rct_research_item *gResearchItems; extern uint8 gResearchUncompletedCategories; +extern bool gSilentResearch; void research_reset_items(); void research_update_uncompleted_types(); @@ -67,9 +68,12 @@ void research_update(); void sub_684AC3(); void research_remove_non_separate_vehicle_types(); void research_populate_list_random(); +void research_populate_list_researched(); void research_set_funding(int amount); void research_set_priority(int activeCategories); void game_command_set_research_funding(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); +void research_finish_item(sint32 entryIndex); +void research_insert(int researched, int entryIndex, int category); #endif \ No newline at end of file diff --git a/src/object.h b/src/object.h index 9de3c7238e..0a846f140c 100644 --- a/src/object.h +++ b/src/object.h @@ -76,6 +76,7 @@ int object_read_and_load_entries(FILE *file); int object_load_packed(FILE *file); void object_unload_all(); +int check_object_entry(rct_object_entry *entry); int object_load(int groupIndex, rct_object_entry *entry, int* chunk_size); int object_load_file(int groupIndex, const rct_object_entry *entry, int* chunkSize, const rct_object_entry *installedObject); void object_unload(int groupIndex, rct_object_entry_extended *entry); diff --git a/src/object_list.c b/src/object_list.c index 480d9a78ac..9dcbf199d0 100644 --- a/src/object_list.c +++ b/src/object_list.c @@ -385,7 +385,7 @@ static int object_list_cache_save(int fileCount, uint64 totalFileSize, int fileD return 1; } -static int check_object_entry(rct_object_entry *entry) +int check_object_entry(rct_object_entry *entry) { uint32 *dwords = (uint32*)entry; return (0xFFFFFFFF & dwords[0] & dwords[1] & dwords[2] & dwords[3]) + 1 != 0; diff --git a/src/windows/editor_object_selection.c b/src/windows/editor_object_selection.c index 2b53a55c57..6ab2fadda4 100644 --- a/src/windows/editor_object_selection.c +++ b/src/windows/editor_object_selection.c @@ -245,19 +245,26 @@ static void window_editor_object_selection_close() rct_window* w; window_get_register(w); - if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & - (SCREEN_FLAGS_SCENARIO_EDITOR | - SCREEN_FLAGS_TRACK_DESIGNER | - SCREEN_FLAGS_TRACK_MANAGER)) - )return; + //if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_EDITOR)) + // return; RCT2_CALLPROC_EBPSAFE(0x6ABB66); editor_load_selected_objects(); reset_loaded_objects(); object_free_scenario_text(); RCT2_CALLPROC_EBPSAFE(0x6AB316); - research_populate_list_random(); - research_remove_non_separate_vehicle_types(); + if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_EDITOR) { + research_populate_list_random(); + research_remove_non_separate_vehicle_types(); + } + else { + // Used for in-game object selection cheat + research_reset_items(); + research_populate_list_researched(); + gSilentResearch = true; + sub_684AC3(); + gSilentResearch = false; + } window_new_ride_init_vars(); } @@ -274,7 +281,13 @@ static void window_editor_object_selection_mouseup() switch (widgetIndex) { case WIDX_CLOSE: - game_do_command(0, 1, 0, 0, GAME_COMMAND_LOAD_OR_QUIT, 1, 0); + if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_EDITOR) { + game_do_command(0, 1, 0, 0, GAME_COMMAND_LOAD_OR_QUIT, 1, 0); + } + else { + // Used for in-game object selection cheat + window_close(w); + } break; case WIDX_TAB_1: @@ -340,6 +353,10 @@ static void window_editor_object_selection_scroll_mousedown() window_scrollmouse_get_registers(w, scrollIndex, x, y); + // Used for in-game object selection cheat to prevent crashing the game + // when windows attempt to draw objects that don't exist any more + window_close_all_except_class(WC_EDITOR_OBJECT_SELECTION); + uint8 object_selection_flags; rct_object_entry* installed_entry; int selected_object = get_object_from_object_selection((w->selected_tab & 0xFF), y, &object_selection_flags, &installed_entry); @@ -350,6 +367,7 @@ static void window_editor_object_selection_scroll_mousedown() sound_play_panned(SOUND_CLICK_1, RCT2_GLOBAL(0x142406C,uint32), 0, 0, 0); + if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TRACK_MANAGER) { if (!window_editor_object_selection_select_object(1, installed_entry)) return; diff --git a/src/world/scenery.h b/src/world/scenery.h index 3d2e6a98e9..12251b2738 100644 --- a/src/world/scenery.h +++ b/src/world/scenery.h @@ -176,5 +176,6 @@ void scenery_update_tile(int x, int y); void scenery_update_age(int x, int y, rct_map_element *mapElement); void scenery_set_default_placement_configuration(); void scenery_remove_ghost_tool_placement(); +void scenery_set_default_placement_configuration(); #endif \ No newline at end of file