diff --git a/projects/openrct2.vcxproj b/projects/openrct2.vcxproj index 3a650aa77d..0f494356a3 100644 --- a/projects/openrct2.vcxproj +++ b/projects/openrct2.vcxproj @@ -138,6 +138,7 @@ + @@ -204,6 +205,7 @@ + diff --git a/projects/openrct2.vcxproj.filters b/projects/openrct2.vcxproj.filters index fce4be6300..6bc6dd78b5 100644 --- a/projects/openrct2.vcxproj.filters +++ b/projects/openrct2.vcxproj.filters @@ -345,7 +345,6 @@ Source\Windows - Source\Windows @@ -371,7 +370,7 @@ Source\World - + Source @@ -427,6 +426,9 @@ Source\Windows + + Source\World + @@ -627,5 +629,8 @@ Source\World + + Source\World + \ No newline at end of file diff --git a/src/game.c b/src/game.c index dc702c2d7e..74ddbbc797 100644 --- a/src/game.c +++ b/src/game.c @@ -49,6 +49,7 @@ #include "windows/error.h" #include "windows/tooltip.h" #include "world/climate.h" +#include "world/map_animation.h" #include "world/park.h" #include "world/sprite.h" #include "world/water.h" @@ -317,7 +318,7 @@ void game_logic_update() research_update(); ride_ratings_update_all(); ride_measurements_update(); - map_invalidate_animations(); + map_animation_invalidate_all(); vehicle_sounds_update(); peep_update_crowd_noise(); climate_update_sound(); diff --git a/src/ride/ride_data.c b/src/ride/ride_data.c index 5ba1213a73..83e8f50742 100644 --- a/src/ride/ride_data.c +++ b/src/ride/ride_data.c @@ -864,4 +864,19 @@ const uint8 RideAvailableBreakdowns[] = { (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 58 Mine Ride (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 59 LIM Launched Roller Coaster (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE) // 60 (none) +}; + +const rct_ride_entrance_definition RideEntranceDefinitions[12] = { + { 22664, 32, 2 }, // RIDE_ENTRANCE_STYLE_PLAIN + { 22760, 31, 21 }, // RIDE_ENTRANCE_STYLE_WOODEN + { 22680, 43, 2 }, // RIDE_ENTRANCE_STYLE_CANVAS_TENT + { 22728, 43, 19 }, // RIDE_ENTRANCE_STYLE_CASTLE_GREY + { 22712, 33, 19 }, // RIDE_ENTRANCE_STYLE_CASTLE_BROWN + { 22776, 32, 19 }, // RIDE_ENTRANCE_STYLE_JUNGLE + { 22744, 32, 20 }, // RIDE_ENTRANCE_STYLE_LOG_CABIN + { 22696, 34, 19 }, // RIDE_ENTRANCE_STYLE_CLASSICAL_ROMAN + { 22792, 40, 22 }, // RIDE_ENTRANCE_STYLE_ABSTRACT + { 22824, 35, 23 }, // RIDE_ENTRANCE_STYLE_SNOW_ICE + { 22840, 33, 19 }, // RIDE_ENTRANCE_STYLE_PAGODA + { 22856, 33, 2 } // RIDE_ENTRANCE_STYLE_SPACE }; \ No newline at end of file diff --git a/src/ride/ride_data.h b/src/ride/ride_data.h index c2e81cc9f8..c5cac5fb9c 100644 --- a/src/ride/ride_data.h +++ b/src/ride/ride_data.h @@ -30,6 +30,12 @@ typedef struct { rct_string_id unk_name; } rct_ride_name_convention; +typedef struct { + uint32 spriteIndex; + uint16 height; + uint16 var_06; +} rct_ride_entrance_definition; + extern const bool hasRunningTrack[0x60]; extern const uint8 initialUpkeepCosts[0x60]; extern const uint8 costPerTrackPiece[0x60]; @@ -42,4 +48,6 @@ extern const rct_ride_name_convention RideNameConvention[96]; extern const uint8 RideAvailableModes[]; extern const uint8 RideAvailableBreakdowns[]; +extern const rct_ride_entrance_definition RideEntranceDefinitions[12]; + #endif \ No newline at end of file diff --git a/src/windows/ride.c b/src/windows/ride.c index de63e8bb05..0a949ee097 100644 --- a/src/windows/ride.c +++ b/src/windows/ride.c @@ -4132,7 +4132,7 @@ static void window_ride_colour_paint() spriteIndex = (trackColour.additional << 24) | (trackColour.main << 19); spriteIndex |= 0xA0000000; - spriteIndex += RCT2_GLOBAL(0x00993E7C + (ride->entrance_style * 8), uint32); + spriteIndex += RideEntranceDefinitions[ride->entrance_style].spriteIndex; // Back gfx_draw_sprite(clippedDpi, spriteIndex, 34, 20, terniaryColour); diff --git a/src/world/map.c b/src/world/map.c index c2be5a635a..c1ee8373d4 100644 --- a/src/world/map.c +++ b/src/world/map.c @@ -42,7 +42,6 @@ const rct_xy16 TileDirectionDelta[] = { }; rct_xy16 *gMapSelectionTiles = (rct_xy16*)0x009DE596; -rct_animated_object *gAnimatedObjects = (rct_animated_object*)0x013886A0; int _sub_6A876D_save_x; int _sub_6A876D_save_y; @@ -459,41 +458,6 @@ int map_coord_is_connected(int x, int y, int z, uint8 faceDirection) return 0; } -/** rct2: 0x009819DC */ -const uint32 *_animatedObjectEventHandlers = (uint32*)0x009819DC; - -/** - * @returns true if the animation should be removed. - */ -bool map_invalidate_animation(rct_animated_object *obj) -{ - uint32 address = _animatedObjectEventHandlers[obj->type]; - int result = RCT2_CALLPROC_X(address, obj->x, 0, obj->y, obj->baseZ, 0, 0, 0); - return (result & 0x100) != 0; -} - -/** - * - * rct2: 0x0068AFAD - */ -void map_invalidate_animations() -{ - rct_animated_object *aobj = &gAnimatedObjects[0]; - int numAnimatedObjects = RCT2_GLOBAL(0x0138B580, uint16); - while (numAnimatedObjects > 0) { - if (map_invalidate_animation(aobj)) { - // Remove animated object - RCT2_GLOBAL(0x0138B580, uint16)--; - numAnimatedObjects--; - if (numAnimatedObjects > 0) - memmove(aobj, aobj + 1, numAnimatedObjects); - } else { - numAnimatedObjects--; - aobj++; - } - } -} - /** * * rct2: 0x006A876D diff --git a/src/world/map.h b/src/world/map.h index 13de4499e4..cd9b08bdd3 100644 --- a/src/world/map.h +++ b/src/world/map.h @@ -181,6 +181,10 @@ enum { PATH_TILE }; +enum { + PATH_FLAG_QUEUE_BANNER = 1 << 3 +}; + enum { ENTRANCE_TYPE_RIDE_ENTRANCE, ENTRANCE_TYPE_RIDE_EXIT, @@ -229,20 +233,8 @@ typedef struct { uint8 direction; } rct2_peep_spawn; -/** - * Animated object - * size: 0x06 - */ -typedef struct { - uint8 baseZ; - uint8 type; - uint16 x; - uint16 y; -} rct_animated_object; - extern const rct_xy16 TileDirectionDelta[]; extern rct_xy16 *gMapSelectionTiles; -extern rct_animated_object *gAnimatedObjects; void map_init(int size); void map_update_tile_pointers(); @@ -258,7 +250,6 @@ rct_map_element *map_get_surface_element_at(int x, int y); int map_element_height(int x, int y); void sub_68B089(); int map_coord_is_connected(int x, int y, int z, uint8 faceDirection); -void map_invalidate_animations(); void sub_6A876D(); int map_is_location_owned(int x, int y, int z); int map_is_location_in_park(int x, int y); diff --git a/src/world/map_animation.c b/src/world/map_animation.c new file mode 100644 index 0000000000..35dc9ca2fa --- /dev/null +++ b/src/world/map_animation.c @@ -0,0 +1,392 @@ +/***************************************************************************** + * Copyright (c) 2014 Ted John + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * This file is part of OpenRCT2. + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *****************************************************************************/ + +#include "../addresses.h" +#include "../ride/ride.h" +#include "../ride/ride_data.h" +#include "map_animation.h" +#include "map.h" +#include "scenery.h" +#include "sprite.h" + +rct_animated_object *gAnimatedObjects = (rct_animated_object*)0x013886A0; + +typedef bool (*map_animation_invalidate_event_handler)(int x, int y, int baseZ); + +static bool map_animation_invalidate_ride_entrance(int x, int y, int baseZ); +static bool map_animation_invalidate_queue_banner(int x, int y, int baseZ); +static bool map_animation_invalidate_small_scenery(int x, int y, int baseZ); +static bool map_animation_invalidate_park_entrance(int x, int y, int baseZ); +static bool map_animation_invalidate_remove(int x, int y, int baseZ); +static bool map_animation_invalidate_banner(int x, int y, int baseZ); +static bool map_animation_invalidate_large_scenery(int x, int y, int baseZ); +static bool sub_6E5B50(int x, int y, int baseZ); +static bool map_animation_invalidate_wall(int x, int y, int baseZ); + +/** rct2: 0x009819DC */ +const uint32 _animatedObjectEventHandlers[] = { + (uint32)map_animation_invalidate_ride_entrance, // ride entrance + (uint32)map_animation_invalidate_queue_banner, // queue banner + (uint32)map_animation_invalidate_small_scenery, // small scenery + peep + (uint32)map_animation_invalidate_park_entrance, // park entrance + 0x006CE29E, // track + 0x006CE2F3, // track + 0x006CE39D, // track + 0x006CE348, // track + 0x006CE3FA, // track + (uint32)map_animation_invalidate_remove, // simply return true + (uint32)map_animation_invalidate_banner, // banner + (uint32)map_animation_invalidate_large_scenery, // large scenery + (uint32)sub_6E5B50, // wall + (uint32)map_animation_invalidate_wall // wall +}; + + +static bool map_animation_invalidate(rct_animated_object *obj); + +/** + * + * rct2: 0x0068AFAD + */ +void map_animation_invalidate_all() +{ + rct_animated_object *aobj = &gAnimatedObjects[0]; + int numAnimatedObjects = RCT2_GLOBAL(0x0138B580, uint16); + while (numAnimatedObjects > 0) { + if (map_animation_invalidate(aobj)) { + // Remove animated object + RCT2_GLOBAL(0x0138B580, uint16)--; + numAnimatedObjects--; + if (numAnimatedObjects > 0) + memmove(aobj, aobj + 1, numAnimatedObjects); + } else { + numAnimatedObjects--; + aobj++; + } + } +} + +/** + * @returns true if the animation should be removed. + */ +static bool map_animation_invalidate(rct_animated_object *obj) +{ + uint32 address = _animatedObjectEventHandlers[obj->type]; + if (((address >> 20) & 0xFFF) == 0x006) { + int result = RCT2_CALLPROC_X(address, obj->x, 0, obj->y, obj->baseZ, 0, 0, 0); + return (result & 0x100) != 0; + } else { + map_animation_invalidate_event_handler handler = (map_animation_invalidate_event_handler)address; + return handler(obj->x, obj->y, obj->baseZ); + } +} + +/** + * + * rct2: 0x00666670 + */ +static bool map_animation_invalidate_ride_entrance(int x, int y, int baseZ) +{ + rct_map_element *mapElement; + rct_ride *ride; + const rct_ride_entrance_definition *entranceDefinition; + + mapElement = map_get_first_element_at(x >> 5, y >> 5); + do { + if (mapElement->base_height != baseZ) + continue; + if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_ENTRANCE) + continue; + if (mapElement->properties.entrance.type != ENTRANCE_TYPE_RIDE_ENTRANCE) + continue; + + ride = GET_RIDE(mapElement->properties.entrance.ride_index); + entranceDefinition = &RideEntranceDefinitions[ride->entrance_style]; + + int height = (mapElement->base_height * 8) + entranceDefinition->height + 8; + map_invalidate_tile(x, y, height, height + 16); + return false; + } while (!map_element_is_last_for_tile(mapElement++)); + + return true; +} + +/** + * + * rct2: 0x006A7BD4 + */ +static bool map_animation_invalidate_queue_banner(int x, int y, int baseZ) +{ + rct_map_element *mapElement; + + mapElement = map_get_first_element_at(x >> 5, y >> 5); + do { + if (mapElement->base_height != baseZ) + continue; + if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_PATH) + continue; + if (!(mapElement->flags & 1)) + continue; + if (!(mapElement->properties.path.type & PATH_FLAG_QUEUE_BANNER)) + continue; + + int direction = ((mapElement->type >> 6) & 3) + RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8); + if (direction == MAP_ELEMENT_DIRECTION_NORTH || direction == MAP_ELEMENT_DIRECTION_EAST) { + baseZ = mapElement->base_height * 8; + map_invalidate_tile(x, y, baseZ + 16, baseZ + 30); + } + return false; + } while (!map_element_is_last_for_tile(mapElement++)); + + return true; +} + +/** + * + * rct2: 0x006E32C9 + */ +static bool map_animation_invalidate_small_scenery(int x, int y, int baseZ) +{ + rct_map_element *mapElement; + rct_scenery_entry *sceneryEntry; + rct_sprite *sprite; + rct_peep *peep; + + mapElement = map_get_first_element_at(x >> 5, y >> 5); + do { + if (mapElement->base_height != baseZ) + continue; + if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_SCENERY) + continue; + if (mapElement->flags & (1 << 4)) + continue; + + sceneryEntry = g_smallSceneryEntries[mapElement->properties.scenery.type]; + if (sceneryEntry->small_scenery.flags & 0xD800) { + map_invalidate_tile(x, y, mapElement->base_height * 8, mapElement->clearance_height * 8); + return false; + } + + if (sceneryEntry->small_scenery.flags & 0x2000) { + if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 0x3FF)) { + int direction = mapElement->type & 3; + int x2 = x - TileDirectionDelta[direction].x; + int y2 = y - TileDirectionDelta[direction].y; + + uint16 spriteIdx = RCT2_ADDRESS(0x00F1EF60, uint16)[((x2 & 0x1FE0) << 3) | (y2 >> 5)]; + for (; spriteIdx != 0xFFFF; spriteIdx = sprite->unknown.var_02) { + sprite = &g_sprite_list[spriteIdx]; + if (sprite->unknown.linked_list_type_offset != SPRITE_LINKEDLIST_OFFSET_PEEP) + continue; + + peep = &sprite->peep; + if (peep->state != PEEP_STATE_WALKING) + continue; + if (peep->z != mapElement->base_height * 8) + continue; + if (peep->action < PEEP_ACTION_NONE_1) + continue; + + peep->action = 0; + peep->action_frame = 0; + peep->var_70 = 0; + sub_693B58(peep); + RCT2_CALLPROC_X(0x006EC53F, 0, 0, 0, 0, (int)peep, 0, 0); + break; + } + } + map_invalidate_tile(x, y, mapElement->base_height * 8, mapElement->clearance_height * 8); + return false; + } + } while (!map_element_is_last_for_tile(mapElement++)); + return true; +} + +/** + * + * rct2: 0x00666C63 + */ +static bool map_animation_invalidate_park_entrance(int x, int y, int baseZ) +{ + rct_map_element *mapElement; + + mapElement = map_get_first_element_at(x >> 5, y >> 5); + do { + if (mapElement->base_height != baseZ) + continue; + if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_ENTRANCE) + continue; + if (mapElement->properties.entrance.type != ENTRANCE_TYPE_PARK_ENTRANCE) + continue; + if (mapElement->properties.entrance.index & 0x0F) + continue; + + baseZ = mapElement->base_height * 8; + map_invalidate_tile(x, y, baseZ + 32, baseZ + 64); + return false; + } while (!map_element_is_last_for_tile(mapElement++)); + + return true; +} + +/** + * + * rct2: 0x0068DF8F + */ +static bool map_animation_invalidate_remove(int x, int y, int baseZ) +{ + return true; +} + +/** + * + * rct2: 0x006BA2BB + */ +static bool map_animation_invalidate_banner(int x, int y, int baseZ) +{ + rct_map_element *mapElement; + + mapElement = map_get_first_element_at(x >> 5, y >> 5); + do { + if (mapElement->base_height != baseZ) + continue; + if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_BANNER) + continue; + + baseZ = mapElement->base_height * 8; + map_invalidate_tile(x, y, baseZ, baseZ + 16); + return false; + } while (!map_element_is_last_for_tile(mapElement++)); + + return true; +} + +/** + * + * rct2: 0x006B94EB + */ +static bool map_animation_invalidate_large_scenery(int x, int y, int baseZ) +{ + rct_map_element *mapElement; + rct_scenery_entry *sceneryEntry; + + bool wasInvalidated = false; + mapElement = map_get_first_element_at(x >> 5, y >> 5); + do { + if (mapElement->base_height != baseZ) + continue; + if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_SCENERY_MULTIPLE) + continue; + + sceneryEntry = g_largeSceneryEntries[mapElement->properties.scenery.type & 0x3FF]; + if (sceneryEntry->large_scenery.flags & (1 << 3)) { + int z = mapElement->base_height * 8; + map_invalidate_tile(x, y, z, z + 16); + wasInvalidated = true; + } + } while (!map_element_is_last_for_tile(mapElement++)); + + return !wasInvalidated; +} + +/** + * + * rct2: 0x006E5B50 + */ +static bool sub_6E5B50(int x, int y, int baseZ) +{ + rct_map_element *mapElement; + rct_scenery_entry *sceneryEntry; + + if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 1) + return false; + + bool wasInvalidated = false; + mapElement = map_get_first_element_at(x >> 5, y >> 5); + do { + if (mapElement->base_height != baseZ) + continue; + if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_FENCE) + continue; + + sceneryEntry = g_wallSceneryEntries[mapElement->properties.scenery.type]; + if (!(sceneryEntry->wall.flags & (1 << 4))) + continue; + + uint8 di = 0; + uint8 bl = mapElement->properties.fence.item[2]; + uint8 bh = bl & 0x78; + if (bh != 0) { + if (bh == 0x78) { + bl &= 0x87; + } else { + di |= 2; + if (bh != 40) { + bh += 8; + if (bh == 104 && !(sceneryEntry->wall.flags & (1 << 5))) + bh = 120; + + di |= 1; + bl &= 135; + bl |= bh; + } + } + } + + mapElement->properties.fence.item[2] = bl; + if (di & 1) { + int z = mapElement->base_height * 8; + map_invalidate_tile(x, y, z, z + 32); + } + if (di & 2) + wasInvalidated = true; + } while (!map_element_is_last_for_tile(mapElement++)); + + return !wasInvalidated; +} + +/** + * + * rct2: 0x006E5EE4 + */ +static bool map_animation_invalidate_wall(int x, int y, int baseZ) +{ + rct_map_element *mapElement; + rct_scenery_entry *sceneryEntry; + + bool wasInvalidated = false; + mapElement = map_get_first_element_at(x >> 5, y >> 5); + do { + if (mapElement->base_height != baseZ) + continue; + if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_FENCE) + continue; + + sceneryEntry = g_wallSceneryEntries[mapElement->properties.scenery.type]; + if (!(sceneryEntry->wall.flags2 & (1 << 4)) && sceneryEntry->wall.var_0D == 255) + continue; + + int z = mapElement->base_height * 8; + map_invalidate_tile(x, y, z, z + 16); + wasInvalidated = true; + } while (!map_element_is_last_for_tile(mapElement++)); + + return !wasInvalidated; +} \ No newline at end of file diff --git a/src/world/map_animation.h b/src/world/map_animation.h new file mode 100644 index 0000000000..e766f60b3b --- /dev/null +++ b/src/world/map_animation.h @@ -0,0 +1,41 @@ +/***************************************************************************** + * Copyright (c) 2014 Ted John + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * This file is part of OpenRCT2. + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *****************************************************************************/ + +#ifndef _MAP_ANIMATION_H_ +#define _MAP_ANIMATION_H_ + +#include "../common.h" + +/** + * Animated object + * size: 0x06 + */ +typedef struct { + uint8 baseZ; + uint8 type; + uint16 x; + uint16 y; +} rct_animated_object; + +extern rct_animated_object *gAnimatedObjects; + +void map_animation_invalidate_all(); + +#endif \ No newline at end of file diff --git a/src/world/sprite.h b/src/world/sprite.h index ea1ec8a105..2eca98ca63 100644 --- a/src/world/sprite.h +++ b/src/world/sprite.h @@ -66,6 +66,10 @@ typedef struct { uint8 sprite_direction; //direction of sprite? 0x1e uint8 pad_1F[3]; // 0x1f uint16 name_string_idx; // 0x22 + uint8 pad_24[7]; + uint8 var_2B; + uint8 pad_2C[0x45]; + uint8 var_71; } rct_unk_sprite; typedef struct {