diff --git a/src/editor.c b/src/editor.c index 8209478e56..4d92429e2c 100644 --- a/src/editor.c +++ b/src/editor.c @@ -39,6 +39,7 @@ #include "util/util.h" #include "world/banner.h" #include "world/climate.h" +#include "world/footpath.h" #include "world/map.h" #include "world/park.h" #include "world/scenery.h" @@ -599,4 +600,67 @@ static void editor_finalise_main_view() RCT2_GLOBAL(0x009DEB7C, uint16) = 0; load_palette(); gfx_invalidate_screen(); -} \ No newline at end of file +} + +/** + * + * rct2: 0x006AB9B8 + */ +bool editor_check_object_selection() +{ + return !(RCT2_CALLPROC_EBPSAFE(0x006AB9B8) & 0x100); +} + +/** + * + * rct2: 0x0066FEAC + */ +bool editor_check_park() +{ + int parkSize = park_calculate_size(); + if (parkSize == 0) { + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_PARK_MUST_OWN_SOME_LAND; + return false; + } + + for (int i = 0; i < 4; i++) { + if (RCT2_ADDRESS(RCT2_ADDRESS_PARK_ENTRANCE_X, uint16)[i] != 0x8000) + break; + + if (i == 3) { + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_NO_PARK_ENTRANCES; + return false; + } + } + + for (int i = 0; i < 4; i++) { + if (RCT2_ADDRESS(RCT2_ADDRESS_PARK_ENTRANCE_X, uint16)[i] == 0x8000) + continue; + + int x = RCT2_ADDRESS(RCT2_ADDRESS_PARK_ENTRANCE_X, uint16)[i]; + int y = RCT2_ADDRESS(RCT2_ADDRESS_PARK_ENTRANCE_Y, uint16)[i]; + int z = RCT2_ADDRESS(RCT2_ADDRESS_PARK_ENTRANCE_Z, uint16)[i] / 8; + int direction = RCT2_ADDRESS(RCT2_ADDRESS_PARK_ENTRANCE_DIRECTION, uint8)[i] ^ 2; + + switch (footpath_is_connected_to_map_edge(x, y, z, direction, 0)) { + case FOOTPATH_SEARCH_NOT_FOUND: + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_PARK_ENTRANCE_WRONG_DIRECTION_OR_NO_PATH; + return false; + case FOOTPATH_SEARCH_INCOMPLETE: + case FOOTPATH_SEARCH_TOO_COMPLEX: + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_PARK_ENTRANCE_PATH_INCOMPLETE_OR_COMPLEX; + return false; + case FOOTPATH_SEARCH_SUCCESS: + // Run the search again and unown the path + footpath_is_connected_to_map_edge(x, y, z, direction, 0x20); + break; + } + } + + if (gPeepSpawns[0].x == 0xFFFF && gPeepSpawns[1].x == 0xFFFF) { + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_PEEP_SPAWNS_NOT_SET; + return false; + } + + return true; +} diff --git a/src/editor.h b/src/editor.h index 9232dd7c59..8c0fd56699 100644 --- a/src/editor.h +++ b/src/editor.h @@ -47,4 +47,7 @@ void sub_6BD3A4(); void editor_open_windows_for_current_step(); +bool editor_check_park(); +bool editor_check_object_selection(); + #endif diff --git a/src/localisation/string_ids.h b/src/localisation/string_ids.h index 0b0cc8eceb..73519e94d6 100644 --- a/src/localisation/string_ids.h +++ b/src/localisation/string_ids.h @@ -1625,6 +1625,7 @@ enum { STR_OBJECTIVE = 3322, + STR_PEEP_SPAWNS_NOT_SET = 3327, STR_CANT_ADVANCE_TO_NEXT_EDITOR_STAGE = 3328, STR_NO_PARK_ENTRANCES = 3329, STR_PARK_MUST_OWN_SOME_LAND = 3330, diff --git a/src/windows/editor_bottom_toolbar.c b/src/windows/editor_bottom_toolbar.c index dfc63d52db..b5c15ce0cf 100644 --- a/src/windows/editor_bottom_toolbar.c +++ b/src/windows/editor_bottom_toolbar.c @@ -25,15 +25,16 @@ #include "../scenario.h" #include "../sprites.h" #include "../localisation/localisation.h" +#include "../interface/themes.h" #include "../interface/viewport.h" #include "../interface/widget.h" #include "../interface/window.h" #include "../platform/platform.h" #include "../title.h" #include "../util/util.h" +#include "../world/footpath.h" #include "../world/scenery.h" #include "error.h" -#include "../interface/themes.h" enum { WIDX_PREVIOUS_IMAGE, // 1 @@ -153,13 +154,23 @@ void window_editor_bottom_toolbar_jump_back_to_object_selection() { gfx_invalidate_screen(); } +/** + * + * rct2: 0x006DFED0 + */ +static void sub_6DFED0() +{ + for (int i = 0; i < 56; i++) + RCT2_ADDRESS(0x01357BD0, sint32)[i] = -1; +} + /** * * rct2: 0x0066F62C */ void window_editor_bottom_toolbar_jump_back_to_landscape_editor() { window_close_all(); - RCT2_CALLPROC_EBPSAFE(0x006DFED0); + sub_6DFED0(); scenery_set_default_placement_configuration(); g_editor_step = EDITOR_STEP_LANDSCAPE_EDITOR; window_map_open(); @@ -203,9 +214,47 @@ void window_editor_bottom_toolbar_jump_back_to_options_selection() { * * rct2: 0x006AB1CE */ -int window_editor_bottom_toolbar_check_object_selection() +bool window_editor_bottom_toolbar_check_object_selection() { - return RCT2_CALLPROC_EBPSAFE(0x006AB1CE) & 0x100; + rct_window *w; + + if (editor_check_object_selection()) { + window_close_by_class(WC_EDITOR_OBJECT_SELECTION); + return true; + } + + window_error_open(STR_INVALID_SELECTION_OF_OBJECTS, RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id)); + w = window_find_by_class(WC_EDITOR_OBJECT_SELECTION); + if (w != NULL) { + // Click first tab (rides) + window_event_mouse_up_call(w, 4); + } + return false; +} + +/** + * + * rct2: 0x0066F6E3 + */ +static void sub_66F6E3() +{ + RCT2_GLOBAL(0x01357404, uint32) = 0xFFFFFFFF; + RCT2_GLOBAL(0x01357408, uint32) = 0xFFFFFFFF; + RCT2_GLOBAL(0x0135740C, uint32) = 0xFFFFFFFF; + RCT2_GLOBAL(0x01357410, uint32) = 0xFFFFFFFF; + + for (int i = 0; i < 128; i++) { + RCT2_ADDRESS(0x01357444, uint32)[i] = RCT2_ADDRESS(0x0097C468, uint32)[i]; + RCT2_ADDRESS(0x01357644, uint32)[i] = RCT2_ADDRESS(0x0097C5D4, uint32)[i]; + } + + for (int i = 0; i < 8; i++) { + RCT2_ADDRESS(0x01357424, uint32)[i] = 0xFFFFFFFF; + } + + window_new_ride_open(); + RCT2_GLOBAL(0x0141F570, uint8) = 6; + gfx_invalidate_screen(); } /** @@ -214,13 +263,13 @@ int window_editor_bottom_toolbar_check_object_selection() */ void window_editor_bottom_toolbar_jump_forward_from_object_selection() { - if (window_editor_bottom_toolbar_check_object_selection()) + if (!window_editor_bottom_toolbar_check_object_selection()) return; if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TRACK_DESIGNER) { - RCT2_CALLPROC_EBPSAFE(0x0066F6E3); + sub_66F6E3(); } else { - RCT2_CALLPROC_EBPSAFE(0x006DFED0); + sub_6DFED0(); scenery_set_default_placement_configuration(); RCT2_GLOBAL(0x00141F570, uint8) = 1; window_map_open(); @@ -230,12 +279,10 @@ void window_editor_bottom_toolbar_jump_forward_from_object_selection() /** * -* rct2: 0x0066f758 +* rct2: 0x0066F758 */ void window_editor_bottom_toolbar_jump_forward_to_invention_list_set_up() { - uint32 flags = RCT2_CALLPROC_X(0x0066FEAC, 0, 0, 0, 0, 0, 0, 0); - - if (!(flags & 0x100)) { + if (editor_check_park()) { window_close_all(); window_editor_inventions_list_open(); g_editor_step = EDITOR_STEP_INVENTIONS_LIST_SET_UP; diff --git a/src/world/footpath.c b/src/world/footpath.c index 3e5f38a2ef..a57491cfe2 100644 --- a/src/world/footpath.c +++ b/src/world/footpath.c @@ -731,3 +731,156 @@ void sub_6A759F() } } } + +/** + * + * rct2: 0x0069ADBD + */ +static void footpath_unown(int x, int y, rct_map_element *pathElement) +{ + int ownershipUnk = 0; + int z = pathElement->base_height; + rct_map_element *surfaceElement = map_get_surface_element_at(x >> 5, y >> 5); + if (surfaceElement->base_height |= z) { + z -= 2; + if (surfaceElement->base_height |= z) { + ownershipUnk = (surfaceElement->properties.surface.ownership & 0xCF) >> 4; + } + } + map_buy_land_rights(x, y, x, y, 6, 1); +} + +bool get_next_direction(int edges, int *direction) +{ + int index = bitscanforward(edges); + if (index == -1) + return false; + + *direction = index; + return true; +} + +/** + * + * rct2: 0x0069AC1A + */ +int footpath_is_connected_to_map_edge_recurse( + int x, int y, int z, int direction, int flags, + int level, int distanceFromJunction, int junctionTolerance +) { + rct_map_element *mapElement; + int edges, slopeDirection; + + x += TileDirectionDelta[direction].x; + y += TileDirectionDelta[direction].y; + if (++level > 250) + return FOOTPATH_SEARCH_TOO_COMPLEX; + + // Check if we are at edge of map + if (x < 32 || y < 32) + return FOOTPATH_SEARCH_SUCCESS; + if (x >= RCT2_GLOBAL(RCT2_ADDRESS_MAP_SIZE_UNITS, uint16) || y >= RCT2_GLOBAL(RCT2_ADDRESS_MAP_SIZE_UNITS, uint16)) + return FOOTPATH_SEARCH_SUCCESS; + + mapElement = map_get_first_element_at(x >> 5, y >> 5); + do { + if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_PATH) + continue; + + if ( + footpath_element_is_sloped(mapElement) && + (slopeDirection = footpath_element_get_slope_direction(mapElement)) != direction + ) { + if ((slopeDirection ^ 2) != direction) continue; + if (mapElement->base_height + 2 != z) continue; + } else if (mapElement->base_height != z) { + continue; + } + + if (mapElement->type & RCT2_GLOBAL(0x00F1AEE0, uint8)) continue; + + if (flags & 0x20) { + footpath_unown(x, y, mapElement); + } + edges = mapElement->properties.path.edges & 0x0F; + direction ^= 2; + if (!(flags & 0x80)) { + if (mapElement[1].type == MAP_ELEMENT_TYPE_BANNER) { + for (int i = 1; i < 4; i++) { + if (map_element_is_last_for_tile(&mapElement[i - 1])) break; + if (mapElement[i].type != MAP_ELEMENT_TYPE_BANNER) break; + edges &= mapElement[i].properties.banner.flags; + } + } + if (mapElement[2].type == MAP_ELEMENT_TYPE_BANNER && mapElement[1].type != MAP_ELEMENT_TYPE_PATH) { + for (int i = 1; i < 6; i++) { + if (map_element_is_last_for_tile(&mapElement[i - 1])) break; + if (mapElement[i].type != MAP_ELEMENT_TYPE_BANNER) break; + edges &= mapElement[i].properties.banner.flags; + } + } + } + goto searchFromFootpath; + } while (!map_element_is_last_for_tile(mapElement++)); + return level == 1 ? FOOTPATH_SEARCH_NOT_FOUND : FOOTPATH_SEARCH_INCOMPLETE; + +searchFromFootpath: + // Exclude direction we came from + z = mapElement->base_height; + edges &= ~(1 << direction); + + // Find next direction to go + if (!get_next_direction(edges, &direction)) { + return FOOTPATH_SEARCH_INCOMPLETE; + } + + edges &= ~(1 << direction); + if (edges == 0) { + // Only possible direction to go + if (footpath_element_is_sloped(mapElement) && footpath_element_get_slope_direction(mapElement) == direction) { + z += 2; + } + return footpath_is_connected_to_map_edge_recurse( + x, y, z, direction, flags, + level, distanceFromJunction + 1, junctionTolerance + ); + } else { + // We have reached a junction + if (distanceFromJunction != 0) { + junctionTolerance--; + } + junctionTolerance--; + if (junctionTolerance < 0) { + return FOOTPATH_SEARCH_TOO_COMPLEX; + } + + do { + edges &= ~(1 << direction); + if (footpath_element_is_sloped(mapElement) && footpath_element_get_slope_direction(mapElement) == direction) { + z += 2; + } + int result = footpath_is_connected_to_map_edge_recurse(x, y, z, direction, flags, level, 0, junctionTolerance); + if (result == FOOTPATH_SEARCH_SUCCESS) { + return result; + } + } while (get_next_direction(edges, &direction)); + + return FOOTPATH_SEARCH_INCOMPLETE; + } +} + +int footpath_is_connected_to_map_edge(int x, int y, int z, int direction, int flags) +{ + RCT2_GLOBAL(0x00F1AEE0, uint8) = 1; + return footpath_is_connected_to_map_edge_recurse(x, y, z, direction, flags, 0, 0, 16); +} + +bool footpath_element_is_sloped(rct_map_element *mapElement) +{ + return mapElement->properties.path.type & 4; +} + +int footpath_element_get_slope_direction(rct_map_element *mapElement) +{ + return mapElement->properties.path.type & 3; +} diff --git a/src/world/footpath.h b/src/world/footpath.h index 5d4ce3d22f..a44538db1b 100644 --- a/src/world/footpath.h +++ b/src/world/footpath.h @@ -37,6 +37,13 @@ typedef struct { uint8 flags; // 0x0B } rct_path_type; +enum { + FOOTPATH_SEARCH_SUCCESS, + FOOTPATH_SEARCH_NOT_FOUND, + FOOTPATH_SEARCH_INCOMPLETE, + FOOTPATH_SEARCH_TOO_COMPLEX +}; + #define g_pathTypeEntries ((rct_path_type**)object_entry_groups[OBJECT_TYPE_PATHS].chunks) extern const rct_xy16 word_981D6C[4]; @@ -57,4 +64,8 @@ void sub_6A742F(int rideIndex, int entranceIndex, int x, int y, rct_map_element void footpath_bridge_get_info_from_pos(int screenX, int screenY, int *x, int *y, int *direction, rct_map_element **mapElement); +int footpath_is_connected_to_map_edge(int x, int y, int z, int direction, int flags); +bool footpath_element_is_sloped(rct_map_element *mapElement); +int footpath_element_get_slope_direction(rct_map_element *mapElement); + #endif