From ab942236d779aa8885fcb45163038268d1255793 Mon Sep 17 00:00:00 2001 From: Robert Jordan Date: Sat, 23 May 2015 14:56:54 -0400 Subject: [PATCH] Added in-game object selection New console commands: - open (opens a window) - windows (lists windows usable with open) - load_object (loads the specified obj with the given filename) - object_count (lists the number of objects in the scenary) Console commands now have a usage variable. Use: "open object_selection" for the object selection window. Once the object selection window is closed, all objects will automatically be researched whether or not they were already in the base scenario. The object selection window will close any other windows when selecting an object to prevent a crash. --- src/interface/console.c | 183 ++++++++++++++++++++++++-- src/interface/window.c | 13 ++ src/interface/window.h | 1 + src/management/research.c | 39 +++++- src/management/research.h | 4 + src/object.h | 1 + src/object_list.c | 2 +- src/windows/editor_object_selection.c | 34 +++-- src/world/scenery.h | 1 + 9 files changed, 257 insertions(+), 21 deletions(-) diff --git a/src/interface/console.c b/src/interface/console.c index 3ae8a6fd7a..6e228db489 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;} @@ -615,6 +619,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); @@ -622,6 +767,7 @@ typedef struct { char *command; console_command_func func; char *help; + char *usage; } console_command; char* console_variable_table[] = { @@ -650,16 +796,33 @@ char* console_variable_table[] = { "game_speed", "console_small_font" }; - -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++) @@ -671,8 +834,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