From 7e17f4fcf94e36bbdbdb79a22acb642aaf6a0f13 Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Fri, 31 Oct 2014 02:33:33 +0000 Subject: [PATCH 01/11] start implementing ride update and station update --- src/audio/audio.h | 1 + src/ride/ride.c | 340 +++++++++++++++++++++++++++++++++++++++++++++- src/ride/ride.h | 9 +- 3 files changed, 345 insertions(+), 5 deletions(-) diff --git a/src/audio/audio.h b/src/audio/audio.h index 44e52fbaf6..eaa1797413 100644 --- a/src/audio/audio.h +++ b/src/audio/audio.h @@ -215,6 +215,7 @@ void unpause_sounds(); void stop_vehicle_sounds(); void sub_6BC348(); void sub_6BC6D8(); +int sub_6BC3AC(sint16 x, sint16 y, sint16 z, uint16 bx, uint32 ebp, uint16 di); // 0x009AF59C probably does the same job // once it's confirmed and calls in pause_sounds() are reversed, it can be used instead of this diff --git a/src/ride/ride.c b/src/ride/ride.c index 9eaca0a549..3176fc8ac6 100644 --- a/src/ride/ride.c +++ b/src/ride/ride.c @@ -20,6 +20,7 @@ #include #include "../addresses.h" +#include "../audio/audio.h" #include "../game.h" #include "../interface/window.h" #include "../localisation/localisation.h" @@ -106,6 +107,8 @@ const uint8 gRideClassifications[255] = { rct_ride_type **gRideTypeList = RCT2_ADDRESS(0x009ACFA4, rct_ride_type*); rct_ride* g_ride_list = RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride); +static void ride_init_vehicle_speed(rct_ride *ride); + int ride_get_count() { rct_ride *ride; @@ -201,13 +204,346 @@ void ride_update_favourited_stat() window_invalidate_by_class(WC_RIDE_LIST); } +/** + * + * rct2: 0x006BC6D8 + */ +void ride_play_music() +{ + +} + +rct_map_element *ride_get_station_start_track_element(rct_ride *ride, int stationIndex) +{ + int x, y, z; + rct_map_element *mapElement; + + x = (ride->station_starts[stationIndex] & 0xFF) * 32; + y = (ride->station_starts[stationIndex] >> 8) * 32; + z = ride->station_heights[stationIndex] * 8; + + // Get first element of the tile + mapElement = TILE_MAP_ELEMENT_POINTER(y * 256 + x); + + // Find the station track element + do { + if ((mapElement->type & MAP_ELEMENT_TYPE_MASK) == MAP_ELEMENT_TYPE_TRACK && x == mapElement->base_height) + return mapElement; + + mapElement++; + } while (!((mapElement - 1)->flags & MAP_ELEMENT_FLAG_LAST_TILE)); + + return NULL; +} + +/** + * + * rct2: 0x006ABFFB + */ +void ride_update_station(rct_ride *ride, int stationIndex) +{ + int x, y, i, dl, dh, dx; + rct_map_element *mapElement; + rct_vehicle *vehicle; + rct_peep *peep; + + if (ride->station_starts[stationIndex] == 0xFFFF) + return; + + dl = 0; + if (ride->mode == RIDE_MODE_RACE) + goto loc_6AC1DF; + if (ride->mode == RIDE_MODE_BUMPERCAR) + goto loc_6AC12B; + if (ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED) + goto loc_6AC0A1; + if (ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED) + goto loc_6AC0A1; + + if (!(ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED))) { + if (ride->status != RIDE_STATUS_CLOSED) + goto loc_6AC076; + if (ride->num_riders != 0) + goto loc_6AC076; + } + + dh = ride->var_062[stationIndex] & 0x7F; + if (dh != 0 && dh != 0x7F && !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 7)) + ride->var_062[stationIndex]--; + goto loc_6AC2AF; + +loc_6AC076: + dl = 1; + dh = ride->var_062[stationIndex] & 0x7F; + if (dh != 0) { + if (dh != 0x7F && !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 31)) + ride->var_062[stationIndex]--; + dl = 0; + } + goto loc_6AC2AF; + +loc_6AC0A1: + mapElement = ride_get_station_start_track_element(ride, stationIndex); + + if (ride->status == RIDE_STATUS_CLOSED && ride->num_riders == 0) + goto loc_6AC10D; + + if (mapElement->flags & 0x20) + goto loc_6AC10D; + + dl = 1; + if (!(ride->var_062[stationIndex] & (1 << 7))) { + ride->var_062[stationIndex] |= (1 << 7); + goto loc_6AC2C7; + } + + if (mapElement->properties.track.sequence & 0x80) + goto loc_6AC2C7; + + goto loc_6AC2AF; + +loc_6AC10D: + dl = 0; + if (ride->var_062[stationIndex] & (1 << 7)) { + ride->var_062[stationIndex] &= ~(1 << 7); + goto loc_6AC2C7; + } + + if (mapElement->properties.track.sequence & 0x80) + goto loc_6AC2C7; + + goto loc_6AC2AF; + +loc_6AC12B: + if (ride->status == RIDE_STATUS_CLOSED) + goto loc_6AC2AF; + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) + goto loc_6AC2AF; + if (ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) + goto loc_6AC17B; + + for (i = 0; i < ride->num_vehicles; i++) { + vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); + if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART) + goto loc_6AC2AF; + } + goto loc_6AC1CA; + +loc_6AC17B: + dx = ride->var_0D0 * 32; + dl = dx & 0xFF; + dh = (dx >> 8) & 0xFF; + for (i = 0; i < ride->num_vehicles; i++) { + vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); + if (dh > vehicle->var_CE) + continue; + if (dh < vehicle->var_CE) + goto loc_6AC1B5; + if (dl > vehicle->var_51) + continue; + } + goto loc_6AC1C3; + +loc_6AC1B5: + ride->lifecycle_flags &= ~VEHICLE_STATUS_WAITING_TO_DEPART; + dh = 0; + goto loc_6AC2AF; + +loc_6AC1C3: + dl = 1; + goto loc_6AC2AF; + +loc_6AC1CA: + ride->lifecycle_flags |= VEHICLE_STATUS_WAITING_TO_DEPART; + ride->var_14D = 12; + dl = 1; + goto loc_6AC2AF; + +loc_6AC1DF: + if (ride->status == RIDE_STATUS_CLOSED) + goto loc_6AC2AF; + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) + goto loc_6AC2AF; + if (ride->lifecycle_flags & VEHICLE_STATUS_WAITING_TO_DEPART) + goto loc_6AC236; + + for (i = 0; i < ride->num_vehicles; i++) { + vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); + if (vehicle->status == VEHICLE_STATUS_WAITING_TO_DEPART) + continue; + if (vehicle->status == VEHICLE_STATUS_DEPARTING) + continue; + + goto loc_6AC2AF; + } + goto loc_6AC29A; + +loc_6AC236: + dh = ride->var_0D0; + for (i = 0; i < ride->num_vehicles; i++) { + vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); + if (vehicle->status == VEHICLE_STATUS_WAITING_TO_DEPART) + continue; + if (dh > vehicle->var_CE) + continue; + + ride->lifecycle_flags &= ~VEHICLE_STATUS_WAITING_TO_DEPART; + if (vehicle->var_B3 == 0) + goto loc_6AC2AF; + + peep = &(g_sprite_list[vehicle->peep].peep); + ride->race_winner = peep->sprite_index; + ride->var_14D = 12; + goto loc_6AC2AF; + } + goto loc_6AC2AD; + +loc_6AC29A: + ride_init_vehicle_speed(ride); + ride->lifecycle_flags |= VEHICLE_STATUS_WAITING_TO_DEPART; + ride->var_14D = 12; + +loc_6AC2AD: + dl = 1; + +loc_6AC2AF: + if (dl != 0) { + if (ride->var_062[stationIndex] & (1 << 7)) + return; + ride->var_062[stationIndex] |= (1 << 7); + } else { + ride->var_062[stationIndex] &= ~(1 << 7); + return; + } + +loc_6AC2C7: + x = (ride->station_starts[stationIndex] & 0xFF) * 32; + y = (ride->station_starts[stationIndex] >> 8) * 32; + mapElement = ride_get_station_start_track_element(ride, stationIndex); + + mapElement->properties.track.sequence &= 0x7F; + if (dl != 0) + mapElement->properties.track.sequence |= 0x80; + + RCT2_CALLPROC_X(0x006ECB60, x, 0, y, 0, mapElement->clearance_height * 8, mapElement->base_height * 8, 0); +} + /** * * rct2: 0x006ABE4C */ void ride_update_all() { - RCT2_CALLPROC_EBPSAFE(0x006ABE4C); + // RCT2_CALLPROC_EBPSAFE(0x006ABE4C); + + rct_s6_info *s6Info = (rct_s6_info*)0x0141F570; + rct_ride *ride; + rct_vehicle *vehicle; + int i, j, x, y, z; + + // Remove all rides if certain flags are set (possible scenario editor?) + int *esi = (int*)0x9DCE9E; + if (esi[0x1BCA] & 2) { + if (s6Info->var_000 <= 2) + FOR_ALL_RIDES(i, ride) + ride->type = RIDE_TYPE_NULL; + return; + } + + // Something related to windows + RCT2_CALLPROC_EBPSAFE(0x006BC348); + + FOR_ALL_RIDES(i, ride) { + if (ride->var_1CA != 0) + ride->var_1CA--; + + if (RCT2_GLOBAL(0x0097D4F2 + (ride->type * 8), uint16) & 6) + goto loc_6ABFF0; + if (ride->status != RIDE_STATUS_OPEN) + goto loc_6ABF6F; + if (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) + goto loc_6ABF6F; + if (ride->type == RIDE_TYPE_CIRCUS_SHOW) + goto loc_6ABF21; + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN)) + goto loc_6ABF37; + if (ride->var_18C == 7) + goto loc_6ABF04; + if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) + goto loc_6ABEEA; + if (ride->var_18C == 6) + goto loc_6ABEEA; + if (ride->var_18C == 7) + goto loc_6ABEF9; + + loc_6ABEEA: + if (ride->var_1AC != 255) + ride->var_1AC++; + + loc_6ABEF9: + if (ride->var_1AC != 255) + goto loc_6ABF37; + + goto loc_6ABF6F; + + loc_6ABF04: + if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 7) + goto loc_6ABF37; + + if (ride->var_1AC != 255) + ride->var_1AC++; + + goto loc_6ABF37; + + loc_6ABF21: + vehicle = &(g_sprite_list[ride->vehicles[0]].vehicle); + if (vehicle->status != VEHICLE_STATUS_DOING_CIRCUS_SHOW) + goto loc_6ABF6F; + + loc_6ABF37: + if (ride->var_15C != 255) + goto loc_6ABF76; + + uint8 *someKindOfMusicInfo = RCT2_ADDRESS(0x009AEF28, uint8*)[ride->music]; + uint8 someKindOfMusicInfoCount = *someKindOfMusicInfo++; + ride->var_15C = someKindOfMusicInfo[scenario_rand() % someKindOfMusicInfoCount]; + ride->var_188 = 0; + goto loc_6ABF76; + + loc_6ABF6F: + ride->var_15C = 255; + + loc_6ABF76: + if (ride->var_15C != 255) { + // Ride music of some sort, possibly break down power cut effect + x = (ride->station_starts[0] & 0xFF) * 32 + 16; + y = (ride->station_starts[0] >> 8) * 32 + 16; + z = ride->station_heights[0] * 8; + + uint8 bh = ride->var_15C; + int sampleRate = 22050; + if (!(ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN))) { + sampleRate = ride->var_1AC * 70; + if (ride->var_18C != 7) + sampleRate *= -1; + sampleRate += 22050; + } + + ride->var_188 = sub_6BC3AC(x, y, z, (ride->var_15C << 8) | i, ride->var_188, sampleRate); + ride->var_15C = bh; + } + + loc_6ABFF0: + if (ride->type != RIDE_TYPE_MAZE) { + // Update stations + for (j = 0; j < 4; j++) + ride_update_station(ride, j); + } + + // loc_6AC335: + } + + ride_play_music(); } /** @@ -600,7 +936,7 @@ vehicle_colour ride_get_vehicle_colour(rct_ride *ride, int vehicleIndex) * set the speed of the go kart type vehicle at the start to a random value or alter if peep name is an easter egg * @param ride (esi) */ -void ride_init_vehicle_speed(rct_ride *ride) +static void ride_init_vehicle_speed(rct_ride *ride) { rct_ride_type *rideEntry; rct_vehicle *vehicle; diff --git a/src/ride/ride.h b/src/ride/ride.h index fe255e6920..dae21429a8 100644 --- a/src/ride/ride.h +++ b/src/ride/ride.h @@ -187,7 +187,8 @@ typedef struct { sint16 build_date; // 0x180 money16 upkeep_cost; // 0x182 uint16 race_winner; // 0x184 - uint8 pad_186[0x06]; + uint8 pad_186[0x02]; + uint32 var_188; uint8 var_18C; uint8 mechanic_status; // 0x18D uint16 mechanic; // 0x18E @@ -202,7 +203,9 @@ typedef struct { uint8 last_inspection; // 0x19B uint8 pad_19C[0x8]; uint32 var_1A4; - uint8 pad_1A8[6]; + uint8 pad_1A8[4]; + uint8 var_1AC; + uint8 pad_1AD; uint8 var_1AE; uint8 connected_message_throttle; // 0x1AF money32 income_per_hour; // 0x1B0 @@ -261,7 +264,7 @@ enum { RIDE_LIFECYCLE_NO_RAW_STATS = 1 << 3, RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING = 1 << 4, RIDE_LIFECYCLE_ON_RIDE_PHOTO = 1 << 5, - + RIDE_LIFECYCLE_6 = 1 << 6, RIDE_LIFECYCLE_BROKEN_DOWN = 1 << 7, RIDE_LIFECYCLE_CRASHED = 1 << 10, From 7f7c380bd6981d2bf1f0da9b1b7bdde26fcb3161 Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Fri, 31 Oct 2014 02:37:28 +0000 Subject: [PATCH 02/11] fix ride_get_station_start_track_element --- src/ride/ride.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ride/ride.c b/src/ride/ride.c index 3176fc8ac6..5e6d5f4148 100644 --- a/src/ride/ride.c +++ b/src/ride/ride.c @@ -218,16 +218,16 @@ rct_map_element *ride_get_station_start_track_element(rct_ride *ride, int statio int x, y, z; rct_map_element *mapElement; - x = (ride->station_starts[stationIndex] & 0xFF) * 32; - y = (ride->station_starts[stationIndex] >> 8) * 32; - z = ride->station_heights[stationIndex] * 8; + x = ride->station_starts[stationIndex] & 0xFF; + y = ride->station_starts[stationIndex] >> 8; + z = ride->station_heights[stationIndex]; // Get first element of the tile mapElement = TILE_MAP_ELEMENT_POINTER(y * 256 + x); // Find the station track element do { - if ((mapElement->type & MAP_ELEMENT_TYPE_MASK) == MAP_ELEMENT_TYPE_TRACK && x == mapElement->base_height) + if ((mapElement->type & MAP_ELEMENT_TYPE_MASK) == MAP_ELEMENT_TYPE_TRACK && z == mapElement->base_height) return mapElement; mapElement++; From 1095c1d33ad171dde024c7f5be448aafd5b201bf Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sat, 1 Nov 2014 01:25:25 +0000 Subject: [PATCH 03/11] finish update ride, still testing and refactoring to go --- src/ride/ride.c | 761 ++++++++++++++++++++++++++++++++---------------- src/ride/ride.h | 23 +- 2 files changed, 536 insertions(+), 248 deletions(-) diff --git a/src/ride/ride.c b/src/ride/ride.c index 5e6d5f4148..c09e59d85a 100644 --- a/src/ride/ride.c +++ b/src/ride/ride.c @@ -104,6 +104,10 @@ const uint8 gRideClassifications[255] = { #pragma endregion +static const int RideInspectionInterval[] = { + 10, 20, 30, 45, 60, 120, 0, 0 +}; + rct_ride_type **gRideTypeList = RCT2_ADDRESS(0x009ACFA4, rct_ride_type*); rct_ride* g_ride_list = RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride); @@ -204,6 +208,53 @@ void ride_update_favourited_stat() window_invalidate_by_class(WC_RIDE_LIST); } +money32 get_shop_item_cost(int shopItem) +{ + return shopItem < 32 ? + RCT2_GLOBAL(0x00982164 + (shopItem * 8), uint16) : + RCT2_GLOBAL(0x00982144 + (shopItem * 8), uint16); +} + +/** + * + * rct2: 0x006AC3AB + */ +money32 ride_calculate_income_per_hour(rct_ride *ride) +{ + rct_ride_type *entry; + money32 incomePerHour, priceMinusCost; + int shopItem; + + entry = GET_RIDE_ENTRY(ride->subtype); + incomePerHour = + ride->var_124 + + ride->var_126 + + ride->var_128 + + ride->var_12A + + ride->var_12C + + ride->var_12E + + ride->age + + ride->running_cost + + ride->var_134 + + ride->var_136; + incomePerHour *= 12; + priceMinusCost = ride->price; + + shopItem = entry->shop_item; + if (shopItem != 255) { + priceMinusCost -= get_shop_item_cost(shopItem); + + shopItem = entry->shop_item_secondary; + if (shopItem != 255) { + priceMinusCost -= get_shop_item_cost(shopItem); + priceMinusCost /= 2; + } + } + + incomePerHour *= priceMinusCost; + return incomePerHour; +} + /** * * rct2: 0x006BC6D8 @@ -238,185 +289,23 @@ rct_map_element *ride_get_station_start_track_element(rct_ride *ride, int statio /** * - * rct2: 0x006ABFFB + * rct2: 0x006ECB60 + * NOTE: x, y and z are in pixels, not tile units */ -void ride_update_station(rct_ride *ride, int stationIndex) +void map_invalidate_tile(int x, int y, int zLow, int zHigh) { - int x, y, i, dl, dh, dx; + RCT2_CALLPROC_X(0x006ECB60, x, 0, y, 0, zHigh, zLow, 0); +} + +/** + * + * rct2: 0x006AC2C7 + */ +void ride_invalidate_station_start(rct_ride *ride, int stationIndex, int dl) +{ + int x, y; rct_map_element *mapElement; - rct_vehicle *vehicle; - rct_peep *peep; - if (ride->station_starts[stationIndex] == 0xFFFF) - return; - - dl = 0; - if (ride->mode == RIDE_MODE_RACE) - goto loc_6AC1DF; - if (ride->mode == RIDE_MODE_BUMPERCAR) - goto loc_6AC12B; - if (ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED) - goto loc_6AC0A1; - if (ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED) - goto loc_6AC0A1; - - if (!(ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED))) { - if (ride->status != RIDE_STATUS_CLOSED) - goto loc_6AC076; - if (ride->num_riders != 0) - goto loc_6AC076; - } - - dh = ride->var_062[stationIndex] & 0x7F; - if (dh != 0 && dh != 0x7F && !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 7)) - ride->var_062[stationIndex]--; - goto loc_6AC2AF; - -loc_6AC076: - dl = 1; - dh = ride->var_062[stationIndex] & 0x7F; - if (dh != 0) { - if (dh != 0x7F && !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 31)) - ride->var_062[stationIndex]--; - dl = 0; - } - goto loc_6AC2AF; - -loc_6AC0A1: - mapElement = ride_get_station_start_track_element(ride, stationIndex); - - if (ride->status == RIDE_STATUS_CLOSED && ride->num_riders == 0) - goto loc_6AC10D; - - if (mapElement->flags & 0x20) - goto loc_6AC10D; - - dl = 1; - if (!(ride->var_062[stationIndex] & (1 << 7))) { - ride->var_062[stationIndex] |= (1 << 7); - goto loc_6AC2C7; - } - - if (mapElement->properties.track.sequence & 0x80) - goto loc_6AC2C7; - - goto loc_6AC2AF; - -loc_6AC10D: - dl = 0; - if (ride->var_062[stationIndex] & (1 << 7)) { - ride->var_062[stationIndex] &= ~(1 << 7); - goto loc_6AC2C7; - } - - if (mapElement->properties.track.sequence & 0x80) - goto loc_6AC2C7; - - goto loc_6AC2AF; - -loc_6AC12B: - if (ride->status == RIDE_STATUS_CLOSED) - goto loc_6AC2AF; - if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) - goto loc_6AC2AF; - if (ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) - goto loc_6AC17B; - - for (i = 0; i < ride->num_vehicles; i++) { - vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); - if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART) - goto loc_6AC2AF; - } - goto loc_6AC1CA; - -loc_6AC17B: - dx = ride->var_0D0 * 32; - dl = dx & 0xFF; - dh = (dx >> 8) & 0xFF; - for (i = 0; i < ride->num_vehicles; i++) { - vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); - if (dh > vehicle->var_CE) - continue; - if (dh < vehicle->var_CE) - goto loc_6AC1B5; - if (dl > vehicle->var_51) - continue; - } - goto loc_6AC1C3; - -loc_6AC1B5: - ride->lifecycle_flags &= ~VEHICLE_STATUS_WAITING_TO_DEPART; - dh = 0; - goto loc_6AC2AF; - -loc_6AC1C3: - dl = 1; - goto loc_6AC2AF; - -loc_6AC1CA: - ride->lifecycle_flags |= VEHICLE_STATUS_WAITING_TO_DEPART; - ride->var_14D = 12; - dl = 1; - goto loc_6AC2AF; - -loc_6AC1DF: - if (ride->status == RIDE_STATUS_CLOSED) - goto loc_6AC2AF; - if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) - goto loc_6AC2AF; - if (ride->lifecycle_flags & VEHICLE_STATUS_WAITING_TO_DEPART) - goto loc_6AC236; - - for (i = 0; i < ride->num_vehicles; i++) { - vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); - if (vehicle->status == VEHICLE_STATUS_WAITING_TO_DEPART) - continue; - if (vehicle->status == VEHICLE_STATUS_DEPARTING) - continue; - - goto loc_6AC2AF; - } - goto loc_6AC29A; - -loc_6AC236: - dh = ride->var_0D0; - for (i = 0; i < ride->num_vehicles; i++) { - vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); - if (vehicle->status == VEHICLE_STATUS_WAITING_TO_DEPART) - continue; - if (dh > vehicle->var_CE) - continue; - - ride->lifecycle_flags &= ~VEHICLE_STATUS_WAITING_TO_DEPART; - if (vehicle->var_B3 == 0) - goto loc_6AC2AF; - - peep = &(g_sprite_list[vehicle->peep].peep); - ride->race_winner = peep->sprite_index; - ride->var_14D = 12; - goto loc_6AC2AF; - } - goto loc_6AC2AD; - -loc_6AC29A: - ride_init_vehicle_speed(ride); - ride->lifecycle_flags |= VEHICLE_STATUS_WAITING_TO_DEPART; - ride->var_14D = 12; - -loc_6AC2AD: - dl = 1; - -loc_6AC2AF: - if (dl != 0) { - if (ride->var_062[stationIndex] & (1 << 7)) - return; - ride->var_062[stationIndex] |= (1 << 7); - } else { - ride->var_062[stationIndex] &= ~(1 << 7); - return; - } - -loc_6AC2C7: x = (ride->station_starts[stationIndex] & 0xFF) * 32; y = (ride->station_starts[stationIndex] >> 8) * 32; mapElement = ride_get_station_start_track_element(ride, stationIndex); @@ -425,92 +314,264 @@ loc_6AC2C7: if (dl != 0) mapElement->properties.track.sequence |= 0x80; - RCT2_CALLPROC_X(0x006ECB60, x, 0, y, 0, mapElement->clearance_height * 8, mapElement->base_height * 8, 0); + // Invalidate map tile + map_invalidate_tile(x, y, mapElement->base_height * 8, mapElement->clearance_height * 8); +} + +void sub_6AC2AF(rct_ride *ride, int stationIndex, int dl) +{ + if (dl != 0) + ride->var_062[stationIndex] |= (1 << 7); + else + ride->var_062[stationIndex] &= ~(1 << 7); } /** * - * rct2: 0x006ABE4C + * rct2: 0x006AC1DF */ -void ride_update_all() +void ride_update_station_race(rct_ride *ride, int stationIndex, int dl) { - // RCT2_CALLPROC_EBPSAFE(0x006ABE4C); - - rct_s6_info *s6Info = (rct_s6_info*)0x0141F570; - rct_ride *ride; + int i, dh; rct_vehicle *vehicle; - int i, j, x, y, z; + rct_peep *peep; - // Remove all rides if certain flags are set (possible scenario editor?) - int *esi = (int*)0x9DCE9E; - if (esi[0x1BCA] & 2) { - if (s6Info->var_000 <= 2) - FOR_ALL_RIDES(i, ride) - ride->type = RIDE_TYPE_NULL; + if ( + ride->status == RIDE_STATUS_CLOSED || + (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) + ) { + sub_6AC2AF(ride, stationIndex, dl); return; } - // Something related to windows - RCT2_CALLPROC_EBPSAFE(0x006BC348); + if (!(ride->lifecycle_flags & VEHICLE_STATUS_WAITING_TO_DEPART)) { + for (i = 0; i < ride->num_vehicles; i++) { + vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); + if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART && vehicle->status != VEHICLE_STATUS_DEPARTING) { + sub_6AC2AF(ride, stationIndex, dl); + return; + } + } + + ride_init_vehicle_speed(ride); + ride->lifecycle_flags |= VEHICLE_STATUS_WAITING_TO_DEPART; + ride->var_14D = 12; + } else { + dh = ride->var_0D0; + for (i = 0; i < ride->num_vehicles; i++) { + vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); + if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART && dh <= vehicle->var_CE) { + ride->lifecycle_flags &= ~VEHICLE_STATUS_WAITING_TO_DEPART; + if (vehicle->var_B3 != 0) { + peep = &(g_sprite_list[vehicle->peep].peep); + ride->race_winner = peep->sprite_index; + ride->var_14D = 12; + } + sub_6AC2AF(ride, stationIndex, dl); + return; + } + } + } + dl = 1; + sub_6AC2AF(ride, stationIndex, dl); +} - FOR_ALL_RIDES(i, ride) { - if (ride->var_1CA != 0) - ride->var_1CA--; +/** + * + * rct2: 0x006AC12B + */ +void ride_update_station_bumpercar(rct_ride *ride, int stationIndex, int dl) +{ + int i, dx, dh; + rct_vehicle *vehicle; - if (RCT2_GLOBAL(0x0097D4F2 + (ride->type * 8), uint16) & 6) - goto loc_6ABFF0; - if (ride->status != RIDE_STATUS_OPEN) - goto loc_6ABF6F; - if (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) - goto loc_6ABF6F; - if (ride->type == RIDE_TYPE_CIRCUS_SHOW) - goto loc_6ABF21; - if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN)) - goto loc_6ABF37; - if (ride->var_18C == 7) - goto loc_6ABF04; - if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) - goto loc_6ABEEA; - if (ride->var_18C == 6) - goto loc_6ABEEA; - if (ride->var_18C == 7) - goto loc_6ABEF9; + if ( + ride->status == RIDE_STATUS_CLOSED || + (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) + ) { + sub_6AC2AF(ride, stationIndex, dl); + return; + } - loc_6ABEEA: - if (ride->var_1AC != 255) - ride->var_1AC++; + if (ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) { + dx = ride->var_0D0 * 32; + dl = dx & 0xFF; + dh = (dx >> 8) & 0xFF; + for (i = 0; i < ride->num_vehicles; i++) { + vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); + if (dh > vehicle->var_CE) + continue; - loc_6ABEF9: - if (ride->var_1AC != 255) - goto loc_6ABF37; + if (dh < vehicle->var_CE) { + ride->lifecycle_flags &= ~VEHICLE_STATUS_WAITING_TO_DEPART; + dh = 0; + sub_6AC2AF(ride, stationIndex, dl); + return; + } - goto loc_6ABF6F; + if (dl > vehicle->var_51) + continue; + } - loc_6ABF04: - if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 7) - goto loc_6ABF37; + dl = 1; + } else { + for (i = 0; i < ride->num_vehicles; i++) { + vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); + if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART) { + sub_6AC2AF(ride, stationIndex, dl); + return; + } + } - if (ride->var_1AC != 255) - ride->var_1AC++; + ride->lifecycle_flags |= VEHICLE_STATUS_WAITING_TO_DEPART; + ride->var_14D = 12; + dl = 1; + } + sub_6AC2AF(ride, stationIndex, dl); +} - goto loc_6ABF37; +/** + * + * rct2: 0x006AC0A1 + */ +void ride_update_station_blocksection(rct_ride *ride, int stationIndex, int dl) +{ + rct_map_element *mapElement; - loc_6ABF21: - vehicle = &(g_sprite_list[ride->vehicles[0]].vehicle); - if (vehicle->status != VEHICLE_STATUS_DOING_CIRCUS_SHOW) - goto loc_6ABF6F; + mapElement = ride_get_station_start_track_element(ride, stationIndex); - loc_6ABF37: - if (ride->var_15C != 255) + if ((ride->status == RIDE_STATUS_CLOSED && ride->num_riders == 0) || mapElement->flags & 0x20) { + dl = 0; + if (ride->var_062[stationIndex] & (1 << 7)) { + ride->var_062[stationIndex] &= ~(1 << 7); + ride_invalidate_station_start(ride, stationIndex, dl); + return; + } + + if (mapElement->properties.track.sequence & 0x80) { + ride_invalidate_station_start(ride, stationIndex, dl); + return; + } + } else { + dl = 1; + if (!(ride->var_062[stationIndex] & (1 << 7))) { + ride->var_062[stationIndex] |= (1 << 7); + ride_invalidate_station_start(ride, stationIndex, dl); + return; + } + + if (mapElement->properties.track.sequence & 0x80) { + ride_invalidate_station_start(ride, stationIndex, dl); + return; + } + } + + sub_6AC2AF(ride, stationIndex, dl); +} + +/** + * + * rct2: 0x006ABFFB + */ +void ride_update_station(rct_ride *ride, int stationIndex) +{ + int dl, dh; + + if (ride->station_starts[stationIndex] == 0xFFFF) + return; + + dl = 0; + switch (ride->mode) { + case RIDE_MODE_RACE: + ride_update_station_race(ride, stationIndex, dl); + break; + case RIDE_MODE_BUMPERCAR: + ride_update_station_bumpercar(ride, stationIndex, dl); + break; + case RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED: + case RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED: + ride_update_station_blocksection(ride, stationIndex, dl); + break; + default: + if ( + (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) && + (ride->status == RIDE_STATUS_CLOSED && ride->num_riders == 0) + ) { + dh = ride->var_062[stationIndex] & 0x7F; + if (dh != 0 && dh != 0x7F && !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 7)) + ride->var_062[stationIndex]--; + } else { + dl = 1; + dh = ride->var_062[stationIndex] & 0x7F; + if (dh != 0) { + if (dh != 0x7F && !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 31)) + ride->var_062[stationIndex]--; + dl = 0; + } + } + sub_6AC2AF(ride, stationIndex, dl); + break; + } +} + +int sub_6B7294(rct_ride *ride) +{ + int eax, ebx, ecx, edx, esi, edi, ebp; + + esi = (int)ride; + RCT2_CALLFUNC_X(0x006B7294, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + return ebx; +} + +/** + * + * rct2: 0x006ABE73 + */ +void ride_update(int index) +{ + rct_vehicle *vehicle; + int j, x, y, z; + rct_map_element *mapElement; + rct_ride *ride = GET_RIDE(index); + + if (ride->var_1CA != 0) + ride->var_1CA--; + + if (!(RCT2_GLOBAL(0x0097D4F2 + (ride->type * 8), uint16) & 6)) { + if (ride->status == RIDE_STATUS_OPEN && !(ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC)) { + if (ride->type == RIDE_TYPE_CIRCUS_SHOW) { + vehicle = &(g_sprite_list[ride->vehicles[0]].vehicle); + if (vehicle->status != VEHICLE_STATUS_DOING_CIRCUS_SHOW) { + ride->var_15C = 255; + goto loc_6ABF76; + } + } + + if (!(ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN))) { + if (ride->var_18C == 7) { + if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 7)) + if (ride->var_1AC != 255) + ride->var_1AC++; + } else { + if ((ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) || ride->var_18C == 6 || ride->var_18C != 7) + if (ride->var_1AC != 255) + ride->var_1AC++; + + if (ride->var_1AC == 255) { + ride->var_15C = 255; + goto loc_6ABF76; + } + } + } + + if (ride->var_15C == 255) { + uint8 *someKindOfMusicInfo = RCT2_ADDRESS(0x009AEF28, uint8*)[ride->music]; + uint8 someKindOfMusicInfoCount = *someKindOfMusicInfo++; + ride->var_15C = someKindOfMusicInfo[scenario_rand() % someKindOfMusicInfoCount]; + ride->var_188 = 0; + } goto loc_6ABF76; - - uint8 *someKindOfMusicInfo = RCT2_ADDRESS(0x009AEF28, uint8*)[ride->music]; - uint8 someKindOfMusicInfoCount = *someKindOfMusicInfo++; - ride->var_15C = someKindOfMusicInfo[scenario_rand() % someKindOfMusicInfoCount]; - ride->var_188 = 0; - goto loc_6ABF76; - - loc_6ABF6F: + } ride->var_15C = 255; loc_6ABF76: @@ -529,20 +590,228 @@ void ride_update_all() sampleRate += 22050; } - ride->var_188 = sub_6BC3AC(x, y, z, (ride->var_15C << 8) | i, ride->var_188, sampleRate); + ride->var_188 = sub_6BC3AC(x, y, z, (ride->var_15C << 8) | index, ride->var_188, sampleRate); ride->var_15C = bh; } + } - loc_6ABFF0: - if (ride->type != RIDE_TYPE_MAZE) { - // Update stations - for (j = 0; j < 4; j++) - ride_update_station(ride, j); + // Update stations + if (ride->type != RIDE_TYPE_MAZE) + for (j = 0; j < 4; j++) + ride_update_station(ride, j); + + // + ride->var_122++; + if (ride->var_122 >= 960) { + ride->var_122 = 0; + + ride->var_136 = ride->var_134; + ride->var_134 = ride->running_cost; + ride->running_cost = ride->age; + ride->age = ride->var_12E; + ride->var_12E = ride->var_12C; + ride->var_12C = ride->var_12A; + ride->var_12A = ride->var_128; + ride->var_128 = ride->var_126; + ride->var_126 = ride->var_124; + ride->var_124 = ride->var_120; + ride->var_14D |= 1; + + ride->income_per_hour = ride_calculate_income_per_hour(ride); + ride->var_14D |= 2; + + if (ride->upkeep_cost != (money16)0xFFFF) { + ride->upkeep_cost *= -16; + ride->upkeep_cost += (money16)ride->income_per_hour; + } + } + + if (ride->type == RIDE_TYPE_CHAIRLIFT && (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) { + if ( + (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) || + ride->var_18C != 0 + ) { + int ax = ride->var_0D0 * 2048; + int bx = ride->var_148; + int cx = bx + ax; + ride->var_148 = cx; + if (bx >> 14 != cx >> 14) { + x = (ride->var_13A & 0xFF) * 32; + y = (ride->var_13A >> 8) * 32; + z = ride->var_13E * 8; + map_invalidate_tile(x, y, z, z + (4 * 8)); + + x = (ride->var_13C & 0xFF) * 32; + y = (ride->var_13C >> 8) * 32; + z = ride->var_13F * 8; + map_invalidate_tile(x, y, z, z + (4 * 8)); + } + } + } + + if ( + !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 3) && + ride->type == RIDE_TYPE_SPIRAL_SLIDE && + ride->var_15D != 0 + ) { + ride->var_176++; + if (ride->var_176 >= 48) { + ride->var_15D--; + + // TODO find out what type of sprite is stored here + rct_sprite *sprite = &g_sprite_list[ride->maze_tiles]; + RCT2_GLOBAL((int)sprite + 0x32, uint16)++; } - // loc_6AC335: + // Invalidate something related to station start + for (j = 0; j < 4; j++) { + if (ride->station_starts[j] == 0xFFFF) + continue; + + x = ride->station_starts[j] & 0xFF; + y = ride->station_starts[j] >> 8; + z = ride->station_heights[j]; + + mapElement = ride_get_station_start_track_element(ride, j); + int rotation = ((mapElement->type & 3) << 2) | RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8); + x += RCT2_GLOBAL(0x0098DDB8 + (rotation * 4), uint16); + y += RCT2_GLOBAL(0x0098DDBA + (rotation * 4), uint16); + + map_invalidate_tile(x, y, mapElement->base_height * 8, mapElement->clearance_height * 8); + } } + if ( + !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 255) && + !(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TRACK_DESIGNER) + ) { + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) + ride->var_19C++; + + if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 8191)) { + int ax = + ride->var_19C + + ride->var_19D + + ride->var_19E + + ride->var_1A0 + + ride->var_1A2 + + ride->var_1A3; + ride->var_199 = min(ax / 2, 100); + + ride->var_1A3 = ride->var_1A2; + ride->var_1A2 = ride->var_1A1; + ride->var_1A1 = ride->var_1A0; + ride->var_1A0 = ride->var_19F; + ride->var_19F = ride->var_19E; + ride->var_19E = ride->var_19D; + ride->var_19D = ride->var_19C; + ride->var_19C = 0; + ride->var_14D |= 32; + } + + if ( + !(ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) && + ride->status != RIDE_STATUS_CLOSED + ) { + int ax = ride->var_198; + int dx = 0; + int cx = (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint16) - ride->build_date) >> 3; + if (cx != 0) { + dx = ax >> 3; + if (cx != 1) { + dx = ax >> 2; + if (cx != 2) { + dx = ax >> 1; + if (cx > 4) { + dx = ax; + if (cx > 7) { + dx = ax << 1; + } + } + } + } + } + ax += dx; + ride->var_196 = max(0, ride->var_196 - ax); + ride->var_14D |= 32; + + int ebx = ride->var_196; + if (ebx == 0) { + goto loc_6AC787; + } else { + ebx *= -1; + ebx += 25856; + if ((scenario_rand() & 0x2FFFFF) <= ebx) { + loc_6AC787: + if (sub_6B7294(ride) != -1) { + // Possibly do breakdown function + RCT2_CALLPROC_X(0x006B75C8, 0, 0, 0, index, 0, 0, 0); + } + } + } + } + } + + // ? + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_DUE_INSPECTION)) + if (((RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) >> 1) & 255) == index) + RCT2_CALLPROC_X(0x006B75C8, 0, 0, 0, index, 0, 0, 0); + + // Inspection + if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 2047)) { + if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TRACK_DESIGNER)) { + ride->last_inspection++; + if (ride->last_inspection == 0) + ride->last_inspection--; + + int inspectionIntervalMinutes = RideInspectionInterval[ride->inspection_interval]; + if (inspectionIntervalMinutes != 0) { + if (RCT2_ADDRESS(0x0097C740, uint32)[ride->type] != 0) { + if (inspectionIntervalMinutes <= ride->last_inspection) { + if (!(ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_DUE_INSPECTION | RIDE_LIFECYCLE_CRASHED))) { + ride->lifecycle_flags |= RIDE_LIFECYCLE_DUE_INSPECTION; + + ride->var_190 = 0; + for (j = 0; j < 4; j++) { + if (ride->exits[j] != 0xFFFF) { + ride->var_190 = j; + break; + } + } + } + } + } + } + } + } +} + +/** + * + * rct2: 0x006ABE4C + */ +void ride_update_all() +{ + rct_s6_info *s6Info = (rct_s6_info*)0x0141F570; + rct_ride *ride; + int i; + + // Remove all rides if certain flags are set (possible scenario editor?) + int *esi = (int*)0x9DCE9E; + if (esi[0x1BCA] & 2) { + if (s6Info->var_000 <= 2) + FOR_ALL_RIDES(i, ride) + ride->type = RIDE_TYPE_NULL; + return; + } + + // Something related to windows + RCT2_CALLPROC_EBPSAFE(0x006BC348); + + // Update rides + FOR_ALL_RIDES(i, ride) + ride_update(i); + ride_play_music(); } diff --git a/src/ride/ride.h b/src/ride/ride.h index dae21429a8..5a214f1f4d 100644 --- a/src/ride/ride.h +++ b/src/ride/ride.h @@ -192,7 +192,8 @@ typedef struct { uint8 var_18C; uint8 mechanic_status; // 0x18D uint16 mechanic; // 0x18E - uint8 pad_190[0x03]; + uint8 var_190; + uint8 pad_191[0x02]; uint8 breakdown_reason; // 0x193 money16 price_secondary; // 0x194 uint16 var_196; @@ -201,7 +202,14 @@ typedef struct { uint8 var_199; uint8 inspection_interval; // 0x19A uint8 last_inspection; // 0x19B - uint8 pad_19C[0x8]; + uint8 var_19C; + uint8 var_19D; + uint8 var_19E; + uint8 var_19F; + uint8 var_1A0; + uint8 var_1A1; + uint8 var_1A2; + uint8 var_1A3; uint32 var_1A4; uint8 pad_1A8[4]; uint8 var_1AC; @@ -266,6 +274,7 @@ enum { RIDE_LIFECYCLE_ON_RIDE_PHOTO = 1 << 5, RIDE_LIFECYCLE_6 = 1 << 6, RIDE_LIFECYCLE_BROKEN_DOWN = 1 << 7, + RIDE_LIFECYCLE_DUE_INSPECTION = 1 << 8, RIDE_LIFECYCLE_CRASHED = 1 << 10, RIDE_LIFECYCLE_11 = 1 << 11, @@ -525,6 +534,16 @@ enum { RIDE_ENTRANCE_STYLE_SPACE }; +enum { + RIDE_INSPECTION_EVERY_10_MINUTES, + RIDE_INSPECTION_EVERY_20_MINUTES, + RIDE_INSPECTION_EVERY_30_MINUTES, + RIDE_INSPECTION_EVERY_45_MINUTES, + RIDE_INSPECTION_EVERY_HOUR, + RIDE_INSPECTION_EVERY_2_HOURS, + RIDE_INSPECTION_NEVER +}; + typedef struct { uint8 main; uint8 additional; From 1d3fc842e301f9858e0864ffadeca58c7ef0d198 Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sat, 1 Nov 2014 03:32:57 +0000 Subject: [PATCH 04/11] fix bugs and refactor ride update --- src/audio/audio.c | 53 +++-- src/audio/audio.h | 2 +- src/ride/ride.c | 520 ++++++++++++++++++++++++++-------------------- src/ride/ride.h | 6 +- 4 files changed, 334 insertions(+), 247 deletions(-) diff --git a/src/audio/audio.c b/src/audio/audio.c index 4a0f5fd2de..54bdd956d2 100644 --- a/src/audio/audio.c +++ b/src/audio/audio.c @@ -1874,17 +1874,32 @@ void sub_6BC348() * @param x (ax) * @param y (cx) * @param z (dx) - * @param bx - * @param ebp - * @param di - * @returns ebp register + * @param sampleRate (di) + * @param rideIndex (bl) + * @param position (ebp) + * @param tuneId (bh) + * @returns new position (ebp) */ -int sub_6BC3AC(sint16 x, sint16 y, sint16 z, uint16 bx, uint32 ebp, uint16 di) +int sub_6BC3AC(sint16 x, sint16 y, sint16 z, uint8 rideIndex, uint16 sampleRate, uint32 position, uint8 *tuneId) { - uint8 bl = LOBYTE(bx); - uint8 bh = HIBYTE(bx); + { + int a_eax, a_ebx, a_ecx, a_edx, a_esi, a_edi, a_ebp; + + a_eax = x; + a_ebx = (*tuneId << 8) | rideIndex; + a_ecx = y; + a_edx = z; + a_edi = sampleRate; + a_ebp = position; + RCT2_CALLFUNC_X(0x006BC3AC, &a_eax, &a_ebx, &a_ecx, &a_edx, &a_esi, &a_edi, &a_ebp); + + *tuneId = (a_ebx >> 8) & 0xFF; + return a_ebp; + } + + // TODO fix / bh needs returning too! if(!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR) && !RCT2_GLOBAL(0x009AF59C, uint8) && RCT2_GLOBAL(0x00F438A4, rct_viewport*) != (rct_viewport*)-1) { - RCT2_GLOBAL(0x009AF47C, uint16) = di; + RCT2_GLOBAL(0x009AF47C, uint16) = sampleRate; sint16 v11; sint16 v12; switch (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint32)) { @@ -1988,12 +2003,12 @@ int sub_6BC3AC(sint16 x, sint16 y, sint16 z, uint16 bx, uint32 ebp, uint16 di) rct_music_info2* music_info2 = &RCT2_GLOBAL(0x009AF46C, rct_music_info2); int channel = 0; uint32 a1; - while (music_info2->id != bl && music_info2->var_1 != bh) { + while (music_info2->id != rideIndex && music_info2->var_1 != *tuneId) { music_info2++; channel++; if (channel >= 2) { - rct_music_info3* music_info3 = &RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[bh]; - a1 = ebp + music_info3->var_4; + rct_music_info3* music_info3 = &RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[*tuneId]; + a1 = position + music_info3->var_4; goto label51; } } @@ -2007,11 +2022,11 @@ int sub_6BC3AC(sint16 x, sint16 y, sint16 z, uint16 bx, uint32 ebp, uint16 di) a1 = sub_401B46(channel); RCT2_GLOBAL(0x014241BC, uint32) = 0; label51: - if (a1 < RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[bh].var_0) { + if (a1 < RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[*tuneId].var_0) { rct_music_info* music_info = RCT2_GLOBAL(0x009AF42C, rct_music_info*); if (music_info < (rct_music_info*)0x009AF46C/*music_info list end*/) { - music_info->id = bl; - music_info->var_1 = bh; + music_info->id = rideIndex; + music_info->var_1 = *tuneId; music_info->offset = a1; music_info->volume = v32; music_info->pan = panx; @@ -2022,16 +2037,16 @@ int sub_6BC3AC(sint16 x, sint16 y, sint16 z, uint16 bx, uint32 ebp, uint16 di) } else { uint32 eax; label58: - eax = ebp; - ebp = bh; - rct_music_info3* music_info3 = &RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[bh]; + eax = position; + position = *tuneId; + rct_music_info3* music_info3 = &RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[*tuneId]; eax += music_info3->var_4; if (eax < music_info3->var_0) { - ebp = eax; + position = eax; } } } - return ebp; + return position; } /** diff --git a/src/audio/audio.h b/src/audio/audio.h index eaa1797413..5821ce051f 100644 --- a/src/audio/audio.h +++ b/src/audio/audio.h @@ -215,7 +215,7 @@ void unpause_sounds(); void stop_vehicle_sounds(); void sub_6BC348(); void sub_6BC6D8(); -int sub_6BC3AC(sint16 x, sint16 y, sint16 z, uint16 bx, uint32 ebp, uint16 di); +int sub_6BC3AC(sint16 x, sint16 y, sint16 z, uint8 rideIndex, uint16 sampleRate, uint32 position, uint8 *tuneId); // 0x009AF59C probably does the same job // once it's confirmed and calls in pause_sounds() are reversed, it can be used instead of this diff --git a/src/ride/ride.c b/src/ride/ride.c index c09e59d85a..e976e89d42 100644 --- a/src/ride/ride.c +++ b/src/ride/ride.c @@ -23,6 +23,7 @@ #include "../audio/audio.h" #include "../game.h" #include "../interface/window.h" +#include "../localisation/date.h" #include "../localisation/localisation.h" #include "../management/news_item.h" #include "../peep/peep.h" @@ -261,7 +262,7 @@ money32 ride_calculate_income_per_hour(rct_ride *ride) */ void ride_play_music() { - + RCT2_CALLPROC_EBPSAFE(0x006BC6D8); } rct_map_element *ride_get_station_start_track_element(rct_ride *ride, int stationIndex) @@ -523,84 +524,299 @@ int sub_6B7294(rct_ride *ride) return ebx; } +/** + * + * rct2: 0x006B75C8 + */ +void sub_6B75C8(int rideIndex) +{ + RCT2_CALLPROC_X(0x006B75C8, 0, 0, 0, rideIndex, 0, 0, 0); +} + +/** + * + * rct2: 0x006AC622 + */ +void ride_breakdown_update(int rideIndex) +{ + int agePenalty, years, ax; + rct_ride *ride = GET_RIDE(rideIndex); + + if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 255) + return; + if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TRACK_DESIGNER) + return; + + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) + ride->var_19C++; + + if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 8191)) { + int ax = + ride->var_19C + + ride->var_19D + + ride->var_19E + + ride->var_1A0 + + ride->var_1A2 + + ride->var_1A3; + ride->var_199 = min(ax / 2, 100); + + ride->var_1A3 = ride->var_1A2; + ride->var_1A2 = ride->var_1A1; + ride->var_1A1 = ride->var_1A0; + ride->var_1A0 = ride->var_19F; + ride->var_19F = ride->var_19E; + ride->var_19E = ride->var_19D; + ride->var_19D = ride->var_19C; + ride->var_19C = 0; + ride->var_14D |= 32; + } + + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) + return; + if (ride->status == RIDE_STATUS_CLOSED) + return; + + // Calculate breakdown probability? + ax = ride->var_198; + agePenalty; + years = date_get_year(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint16) - ride->build_date); + switch (years) { + case 0: + agePenalty = 0; + break; + case 1: + agePenalty = ax >> 3; + break; + case 2: + agePenalty = ax >> 2; + break; + case 3: + agePenalty = ax >> 1; + break; + case 4: + case 5: + case 6: + agePenalty = ax >> 0; + break; + default: + agePenalty = ax << 1; + break; + } + ax += agePenalty; + ride->var_196 = max(0, ride->var_196 - ax); + ride->var_14D |= 32; + + // Break down based on probability? + if (ride->var_196 == 0 || (scenario_rand() & 0x2FFFFF) <= 25856 - ride->var_196) + if (sub_6B7294(ride) != -1) + sub_6B75C8(rideIndex); +} + +/** + * + * rct2: 0x006AC7C2 + */ +void ride_inspection_update(rct_ride *ride) +{ + int i; + + if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 2047) + return; + if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TRACK_DESIGNER) + return; + + ride->last_inspection++; + if (ride->last_inspection == 0) + ride->last_inspection--; + + int inspectionIntervalMinutes = RideInspectionInterval[ride->inspection_interval]; + if (inspectionIntervalMinutes == 0) + return; + + if (RCT2_ADDRESS(0x0097C740, uint32)[ride->type] == 0) + return; + + if (inspectionIntervalMinutes > ride->last_inspection) + return; + + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_DUE_INSPECTION | RIDE_LIFECYCLE_CRASHED)) + return; + + // Inspect the first station that has an exit + ride->lifecycle_flags |= RIDE_LIFECYCLE_DUE_INSPECTION; + ride->inspection_station = 0; + for (i = 0; i < 4; i++) { + if (ride->exits[i] != 0xFFFF) { + ride->inspection_station = i; + break; + } + } +} + +/** + * + * rct2: 0x006AC489 + */ +void ride_chairlift_update(rct_ride *ride) +{ + int x, y, z, ax, bx, cx; + + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) + return; + if (!(ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED))) + return; + if (ride->var_18C == 0) + return; + + ax = ride->var_0D0 * 2048; + bx = ride->var_148; + cx = bx + ax; + ride->var_148 = cx; + if (bx >> 14 == cx >> 14) + return; + + x = (ride->var_13A & 0xFF) * 32; + y = (ride->var_13A >> 8) * 32; + z = ride->var_13E * 8; + map_invalidate_tile(x, y, z, z + (4 * 8)); + + x = (ride->var_13C & 0xFF) * 32; + y = (ride->var_13C >> 8) * 32; + z = ride->var_13F * 8; + map_invalidate_tile(x, y, z, z + (4 * 8)); +} + +/** + * + * rct2: 0x006AC545 + */ +void ride_spiral_slide_update(rct_ride *ride) +{ + int i, x, y, z; + rct_map_element *mapElement; + + if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 3) + return; + if (ride->var_15D == 0) + return; + + ride->var_176++; + if (ride->var_176 >= 48) { + ride->var_15D--; + + // TODO find out what type of sprite is stored here + rct_sprite *sprite = &g_sprite_list[ride->maze_tiles]; + RCT2_GLOBAL((int)sprite + 0x32, uint16)++; + } + + // Invalidate something related to station start + for (i = 0; i < 4; i++) { + if (ride->station_starts[i] == 0xFFFF) + continue; + + x = ride->station_starts[i] & 0xFF; + y = ride->station_starts[i] >> 8; + z = ride->station_heights[i]; + + mapElement = ride_get_station_start_track_element(ride, i); + int rotation = ((mapElement->type & 3) << 2) | RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8); + x += RCT2_GLOBAL(0x0098DDB8 + (rotation * 4), uint16); + y += RCT2_GLOBAL(0x0098DDBA + (rotation * 4), uint16); + + map_invalidate_tile(x, y, mapElement->base_height * 8, mapElement->clearance_height * 8); + } +} + +/** + * + * rct2: 0x006ABE85 + */ +void ride_music_update(int rideIndex) +{ + int x, y, z; + rct_vehicle *vehicle; + rct_ride *ride = GET_RIDE(rideIndex); + + if (!(RCT2_GLOBAL(0x0097D4F2 + (ride->type * 8), uint16) & 6)) + return; + + if (ride->status != RIDE_STATUS_OPEN || !(ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC)) { + ride->music_tune_id = 255; + return; + } + + if (ride->type == RIDE_TYPE_CIRCUS_SHOW) { + vehicle = &(g_sprite_list[ride->vehicles[0]].vehicle); + if (vehicle->status != VEHICLE_STATUS_DOING_CIRCUS_SHOW) { + ride->music_tune_id = 255; + return; + } + } + + // Oscillate parameters for a power cut effect when breaking down + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN)) { + if (ride->var_18C == 7) { + if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 7)) + if (ride->var_1AC != 255) + ride->var_1AC++; + } else { + if ((ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) || ride->var_18C == 6 || ride->var_18C == 7) + if (ride->var_1AC != 255) + ride->var_1AC++; + + if (ride->var_1AC == 255) { + ride->music_tune_id = 255; + return; + } + } + } + + // Select random tune from available tunes for a music style (of course only merry-go-rounds have more than one tune) + if (ride->music_tune_id == 255) { + uint8 *musicStyleTunes = RCT2_ADDRESS(0x009AEF28, uint8*)[ride->music]; + uint8 numTunes = *musicStyleTunes++; + ride->music_tune_id = musicStyleTunes[scenario_rand() % numTunes]; + ride->music_position = 0; + return; + } + + x = (ride->station_starts[0] & 0xFF) * 32 + 16; + y = (ride->station_starts[0] >> 8) * 32 + 16; + z = ride->station_heights[0] * 8; + + int sampleRate = 22050; + + // Alter sample rate for a power cut effect + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN)) { + sampleRate = ride->var_1AC * 70; + if (ride->var_18C != 7) + sampleRate *= -1; + sampleRate += 22050; + } + + ride->music_position = sub_6BC3AC(x, y, z, rideIndex, sampleRate, ride->music_position, &ride->music_tune_id); +} + /** * * rct2: 0x006ABE73 */ -void ride_update(int index) +void ride_update(int rideIndex) { - rct_vehicle *vehicle; - int j, x, y, z; - rct_map_element *mapElement; - rct_ride *ride = GET_RIDE(index); + int i; + rct_ride *ride = GET_RIDE(rideIndex); if (ride->var_1CA != 0) ride->var_1CA--; - if (!(RCT2_GLOBAL(0x0097D4F2 + (ride->type * 8), uint16) & 6)) { - if (ride->status == RIDE_STATUS_OPEN && !(ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC)) { - if (ride->type == RIDE_TYPE_CIRCUS_SHOW) { - vehicle = &(g_sprite_list[ride->vehicles[0]].vehicle); - if (vehicle->status != VEHICLE_STATUS_DOING_CIRCUS_SHOW) { - ride->var_15C = 255; - goto loc_6ABF76; - } - } - - if (!(ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN))) { - if (ride->var_18C == 7) { - if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 7)) - if (ride->var_1AC != 255) - ride->var_1AC++; - } else { - if ((ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) || ride->var_18C == 6 || ride->var_18C != 7) - if (ride->var_1AC != 255) - ride->var_1AC++; - - if (ride->var_1AC == 255) { - ride->var_15C = 255; - goto loc_6ABF76; - } - } - } - - if (ride->var_15C == 255) { - uint8 *someKindOfMusicInfo = RCT2_ADDRESS(0x009AEF28, uint8*)[ride->music]; - uint8 someKindOfMusicInfoCount = *someKindOfMusicInfo++; - ride->var_15C = someKindOfMusicInfo[scenario_rand() % someKindOfMusicInfoCount]; - ride->var_188 = 0; - } - goto loc_6ABF76; - } - ride->var_15C = 255; - - loc_6ABF76: - if (ride->var_15C != 255) { - // Ride music of some sort, possibly break down power cut effect - x = (ride->station_starts[0] & 0xFF) * 32 + 16; - y = (ride->station_starts[0] >> 8) * 32 + 16; - z = ride->station_heights[0] * 8; - - uint8 bh = ride->var_15C; - int sampleRate = 22050; - if (!(ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN))) { - sampleRate = ride->var_1AC * 70; - if (ride->var_18C != 7) - sampleRate *= -1; - sampleRate += 22050; - } - - ride->var_188 = sub_6BC3AC(x, y, z, (ride->var_15C << 8) | index, ride->var_188, sampleRate); - ride->var_15C = bh; - } - } + ride_music_update(rideIndex); // Update stations if (ride->type != RIDE_TYPE_MAZE) - for (j = 0; j < 4; j++) - ride_update_station(ride, j); + for (i = 0; i < 4; i++) + ride_update_station(ride, i); - // + // Update financial statistics ride->var_122++; if (ride->var_122 >= 960) { ride->var_122 = 0; @@ -620,170 +836,24 @@ void ride_update(int index) ride->income_per_hour = ride_calculate_income_per_hour(ride); ride->var_14D |= 2; - if (ride->upkeep_cost != (money16)0xFFFF) { - ride->upkeep_cost *= -16; - ride->upkeep_cost += (money16)ride->income_per_hour; - } + if (ride->upkeep_cost != (money16)0xFFFF) + ride->upkeep_cost = (money16)ride->income_per_hour - (ride->upkeep_cost * 16); } - if (ride->type == RIDE_TYPE_CHAIRLIFT && (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) { - if ( - (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) || - ride->var_18C != 0 - ) { - int ax = ride->var_0D0 * 2048; - int bx = ride->var_148; - int cx = bx + ax; - ride->var_148 = cx; - if (bx >> 14 != cx >> 14) { - x = (ride->var_13A & 0xFF) * 32; - y = (ride->var_13A >> 8) * 32; - z = ride->var_13E * 8; - map_invalidate_tile(x, y, z, z + (4 * 8)); + // Ride specific updates + if (ride->type == RIDE_TYPE_CHAIRLIFT) + ride_chairlift_update(ride); + else if (ride->type == RIDE_TYPE_SPIRAL_SLIDE) + ride_spiral_slide_update(ride); - x = (ride->var_13C & 0xFF) * 32; - y = (ride->var_13C >> 8) * 32; - z = ride->var_13F * 8; - map_invalidate_tile(x, y, z, z + (4 * 8)); - } - } - } + ride_breakdown_update(rideIndex); - if ( - !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 3) && - ride->type == RIDE_TYPE_SPIRAL_SLIDE && - ride->var_15D != 0 - ) { - ride->var_176++; - if (ride->var_176 >= 48) { - ride->var_15D--; - - // TODO find out what type of sprite is stored here - rct_sprite *sprite = &g_sprite_list[ride->maze_tiles]; - RCT2_GLOBAL((int)sprite + 0x32, uint16)++; - } - - // Invalidate something related to station start - for (j = 0; j < 4; j++) { - if (ride->station_starts[j] == 0xFFFF) - continue; - - x = ride->station_starts[j] & 0xFF; - y = ride->station_starts[j] >> 8; - z = ride->station_heights[j]; - - mapElement = ride_get_station_start_track_element(ride, j); - int rotation = ((mapElement->type & 3) << 2) | RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8); - x += RCT2_GLOBAL(0x0098DDB8 + (rotation * 4), uint16); - y += RCT2_GLOBAL(0x0098DDBA + (rotation * 4), uint16); - - map_invalidate_tile(x, y, mapElement->base_height * 8, mapElement->clearance_height * 8); - } - } - - if ( - !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 255) && - !(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TRACK_DESIGNER) - ) { - if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) - ride->var_19C++; - - if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 8191)) { - int ax = - ride->var_19C + - ride->var_19D + - ride->var_19E + - ride->var_1A0 + - ride->var_1A2 + - ride->var_1A3; - ride->var_199 = min(ax / 2, 100); - - ride->var_1A3 = ride->var_1A2; - ride->var_1A2 = ride->var_1A1; - ride->var_1A1 = ride->var_1A0; - ride->var_1A0 = ride->var_19F; - ride->var_19F = ride->var_19E; - ride->var_19E = ride->var_19D; - ride->var_19D = ride->var_19C; - ride->var_19C = 0; - ride->var_14D |= 32; - } - - if ( - !(ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) && - ride->status != RIDE_STATUS_CLOSED - ) { - int ax = ride->var_198; - int dx = 0; - int cx = (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint16) - ride->build_date) >> 3; - if (cx != 0) { - dx = ax >> 3; - if (cx != 1) { - dx = ax >> 2; - if (cx != 2) { - dx = ax >> 1; - if (cx > 4) { - dx = ax; - if (cx > 7) { - dx = ax << 1; - } - } - } - } - } - ax += dx; - ride->var_196 = max(0, ride->var_196 - ax); - ride->var_14D |= 32; - - int ebx = ride->var_196; - if (ebx == 0) { - goto loc_6AC787; - } else { - ebx *= -1; - ebx += 25856; - if ((scenario_rand() & 0x2FFFFF) <= ebx) { - loc_6AC787: - if (sub_6B7294(ride) != -1) { - // Possibly do breakdown function - RCT2_CALLPROC_X(0x006B75C8, 0, 0, 0, index, 0, 0, 0); - } - } - } - } - } - - // ? + // Various things include news messages if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_DUE_INSPECTION)) - if (((RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) >> 1) & 255) == index) - RCT2_CALLPROC_X(0x006B75C8, 0, 0, 0, index, 0, 0, 0); + if (((RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) >> 1) & 255) == rideIndex) + sub_6B75C8(rideIndex); - // Inspection - if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 2047)) { - if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TRACK_DESIGNER)) { - ride->last_inspection++; - if (ride->last_inspection == 0) - ride->last_inspection--; - - int inspectionIntervalMinutes = RideInspectionInterval[ride->inspection_interval]; - if (inspectionIntervalMinutes != 0) { - if (RCT2_ADDRESS(0x0097C740, uint32)[ride->type] != 0) { - if (inspectionIntervalMinutes <= ride->last_inspection) { - if (!(ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_DUE_INSPECTION | RIDE_LIFECYCLE_CRASHED))) { - ride->lifecycle_flags |= RIDE_LIFECYCLE_DUE_INSPECTION; - - ride->var_190 = 0; - for (j = 0; j < 4; j++) { - if (ride->exits[j] != 0xFFFF) { - ride->var_190 = j; - break; - } - } - } - } - } - } - } - } + ride_inspection_update(ride); } /** @@ -796,6 +866,8 @@ void ride_update_all() rct_ride *ride; int i; + // RCT2_CALLPROC_EBPSAFE(0x006ABE4C); return; + // Remove all rides if certain flags are set (possible scenario editor?) int *esi = (int*)0x9DCE9E; if (esi[0x1BCA] & 2) { diff --git a/src/ride/ride.h b/src/ride/ride.h index 5a214f1f4d..9d25f00d1d 100644 --- a/src/ride/ride.h +++ b/src/ride/ride.h @@ -178,7 +178,7 @@ typedef struct { uint16 var_158; uint8 pad_15A; uint8 num_riders; // 0x15B - uint8 var_15C; + uint8 music_tune_id; // 0x15C uint8 var_15D; uint16 maze_tiles; // 0x15E uint8 pad_160[0x16]; @@ -188,11 +188,11 @@ typedef struct { money16 upkeep_cost; // 0x182 uint16 race_winner; // 0x184 uint8 pad_186[0x02]; - uint32 var_188; + uint32 music_position; // 0x188 uint8 var_18C; uint8 mechanic_status; // 0x18D uint16 mechanic; // 0x18E - uint8 var_190; + uint8 inspection_station; // 0x190 uint8 pad_191[0x02]; uint8 breakdown_reason; // 0x193 money16 price_secondary; // 0x194 From e4dc9b8c8e7fa8cee6bf8069c95ddcfe8957e19f Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sat, 1 Nov 2014 22:21:28 +0000 Subject: [PATCH 05/11] improve ride update and add some breakdown functions --- src/ride/ride.c | 181 +++++++++++++++++++++++++++++++++++++++---- src/ride/ride.h | 13 +++- src/ride/ride_data.c | 94 ++++++++++++++++++++++ src/ride/ride_data.h | 1 + 4 files changed, 268 insertions(+), 21 deletions(-) diff --git a/src/ride/ride.c b/src/ride/ride.c index e976e89d42..39c4da9132 100644 --- a/src/ride/ride.c +++ b/src/ride/ride.c @@ -515,13 +515,85 @@ void ride_update_station(rct_ride *ride, int stationIndex) } } -int sub_6B7294(rct_ride *ride) +int bitscanforward(int source) { - int eax, ebx, ecx, edx, esi, edi, ebp; + int i; - esi = (int)ride; - RCT2_CALLFUNC_X(0x006B7294, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); - return ebx; + for (i = 0; i < 32; i++) + if (source & (1 << i)) + return i; + + return -1; +} + +static uint8 _breakdownProblemProbabilities[] = { + 25, // BREAKDOWN_SAFETY_CUT_OUT + 12, // BREAKDOWN_RESTRAINTS_STUCK_CLOSED + 10, // BREAKDOWN_RESTRAINTS_STUCK_OPEN + 13, // BREAKDOWN_DOORS_STUCK_CLOSED + 10, // BREAKDOWN_DOORS_STUCK_OPEN + 6, // BREAKDOWN_VEHICLE_MALFUNCTION + 0, // BREAKDOWN_BRAKES_FAILURE + 3 // BREAKDOWN_CONTROL_FAILURE +}; + +/** + * + * rct2: 0x006B7294 + */ +int ride_get_new_breakdown_problem(rct_ride *ride) +{ + int availableBreakdownProblems, monthsOld, totalProbability, randomProbability, problemBits, breakdownProblem; + rct_ride_type *entry; + + // Brake failure is more likely when its raining + _breakdownProblemProbabilities[BREAKDOWN_BRAKES_FAILURE] = + RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_RAIN_LEVEL, uint8) == 0 ? 3 : 20; + + entry = ride_get_entry(ride); + if (entry->var_008 & 0x4000) + return -1; + + availableBreakdownProblems = RideAvailableBreakdowns[ride->type]; + + // Calculate the total probability range for all possible breakdown problems + totalProbability = 0; + problemBits = availableBreakdownProblems; + while (problemBits != 0) { + breakdownProblem = bitscanforward(problemBits); + problemBits &= ~(1 << breakdownProblem); + totalProbability += _breakdownProblemProbabilities[breakdownProblem]; + } + if (totalProbability == 0) + return -1; + + // Choose a random number within this range + randomProbability = scenario_rand() % totalProbability; + + // Find which problem range the random number lies + problemBits = availableBreakdownProblems; + do { + breakdownProblem = bitscanforward(problemBits); + problemBits &= ~(1 << breakdownProblem); + randomProbability -= _breakdownProblemProbabilities[breakdownProblem]; + } while (randomProbability >= 0); + + if (breakdownProblem != BREAKDOWN_BRAKES_FAILURE) + return breakdownProblem; + + // Breaks failure can not happen if block breaks are used (so long as there is more than one vehicle) + // However if this is the case, break failure should be taken out the equation, otherwise block brake + // rides have a lower probability to break down due to a random implementation reason. + if (ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED || ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED) + if (ride->num_vehicles != 1) + return -1; + + // Again the probability is lower, this time if young or two other unknown reasons... + monthsOld = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint8) - ride->build_date; + if (monthsOld < 16 || ride->var_196 > 12800 || ride->lifecycle_flags & RIDE_LIFECYCLE_19) + return -1; + + return BREAKDOWN_BRAKES_FAILURE; } /** @@ -533,13 +605,81 @@ void sub_6B75C8(int rideIndex) RCT2_CALLPROC_X(0x006B75C8, 0, 0, 0, rideIndex, 0, 0, 0); } +/** + * + * rct2: 0x006B7348 + */ +void ride_prepare_breakdown(int rideIndex, int breakdownReason) +{ + int i; + rct_ride *ride; + rct_vehicle *vehicle; + + ride = GET_RIDE(rideIndex); + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) + return; + + ride->lifecycle_flags &= ~RIDE_LIFECYCLE_DUE_INSPECTION; + ride->lifecycle_flags |= RIDE_LIFECYCLE_6; + + ride->breakdown_reason_pending = breakdownReason; + ride->mechanic_status = RIDE_MECHANIC_STATUS_UNDEFINED; + ride->var_1AC = 0; + ride->var_1AD = 0; + + switch (breakdownReason) { + case BREAKDOWN_SAFETY_CUT_OUT: + case BREAKDOWN_CONTROL_FAILURE: + // Inspect first station with an exit + for (i = 0; i < 4; i++) { + if (ride->exits[i] != 0xFFFF) { + ride->inspection_station = i; + break; + } + } + break; + case BREAKDOWN_RESTRAINTS_STUCK_CLOSED: + case BREAKDOWN_RESTRAINTS_STUCK_OPEN: + case BREAKDOWN_DOORS_STUCK_CLOSED: + case BREAKDOWN_DOORS_STUCK_OPEN: + // Choose a random train and car + ride->broken_vehicle = scenario_rand() % ride->num_vehicles; + ride->broken_car = scenario_rand() % ride->num_cars_per_train; + + // Set flag on broken car + vehicle = &(g_sprite_list[ride->vehicles[ride->broken_vehicle]].vehicle); + for (i = ride->broken_car; i >= 0; i--) + vehicle = &(g_sprite_list[ride->vehicles[vehicle->next_vehicle_on_train]].vehicle); + vehicle->var_48 |= 0x100; + break; + case BREAKDOWN_VEHICLE_MALFUNCTION: + // Choose a random train + ride->broken_vehicle = scenario_rand() % ride->num_vehicles; + ride->broken_car = 0; + + // Set flag on broken train, first car + vehicle = &(g_sprite_list[ride->vehicles[ride->broken_vehicle]].vehicle); + vehicle->var_48 |= 0x200; + break; + case BREAKDOWN_BRAKES_FAILURE: + // Original code generates a random number but does not use it + // Unsure if this was supposed to choose a random station (or random station with an exit) + for (i = 0; i < 4; i++) { + ride->inspection_station = i; + if (ride->exits[i] != 0xFFFF) + break; + } + break; + } +} + /** * * rct2: 0x006AC622 */ void ride_breakdown_update(int rideIndex) { - int agePenalty, years, ax; + int agePenalty, years, ax, breakdownReason; rct_ride *ride = GET_RIDE(rideIndex); if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 255) @@ -606,10 +746,12 @@ void ride_breakdown_update(int rideIndex) ride->var_196 = max(0, ride->var_196 - ax); ride->var_14D |= 32; - // Break down based on probability? - if (ride->var_196 == 0 || (scenario_rand() & 0x2FFFFF) <= 25856 - ride->var_196) - if (sub_6B7294(ride) != -1) - sub_6B75C8(rideIndex); + // Random probability of a breakdown + if (ride->var_196 == 0 || (scenario_rand() & 0x2FFFFF) <= 25856 - ride->var_196) { + breakdownReason = ride_get_new_breakdown_problem(ride); + if (breakdownReason != -1) + ride_prepare_breakdown(rideIndex, breakdownReason); + } } /** @@ -665,7 +807,7 @@ void ride_chairlift_update(rct_ride *ride) return; if (!(ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED))) return; - if (ride->var_18C == 0) + if (ride->breakdown_reason_pending == 0) return; ax = ride->var_0D0 * 2048; @@ -694,6 +836,7 @@ void ride_spiral_slide_update(rct_ride *ride) { int i, x, y, z; rct_map_element *mapElement; + rct_peep *peep; if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 3) return; @@ -704,9 +847,8 @@ void ride_spiral_slide_update(rct_ride *ride) if (ride->var_176 >= 48) { ride->var_15D--; - // TODO find out what type of sprite is stored here - rct_sprite *sprite = &g_sprite_list[ride->maze_tiles]; - RCT2_GLOBAL((int)sprite + 0x32, uint16)++; + peep = &(g_sprite_list[ride->maze_tiles].peep); + peep->var_32++; } // Invalidate something related to station start @@ -755,14 +897,19 @@ void ride_music_update(int rideIndex) // Oscillate parameters for a power cut effect when breaking down if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN)) { - if (ride->var_18C == 7) { + if (ride->breakdown_reason_pending == BREAKDOWN_CONTROL_FAILURE) { if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 7)) if (ride->var_1AC != 255) ride->var_1AC++; } else { - if ((ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) || ride->var_18C == 6 || ride->var_18C == 7) + if ( + (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) || + ride->breakdown_reason_pending == BREAKDOWN_BRAKES_FAILURE || + ride->breakdown_reason_pending == BREAKDOWN_CONTROL_FAILURE + ) { if (ride->var_1AC != 255) ride->var_1AC++; + } if (ride->var_1AC == 255) { ride->music_tune_id = 255; @@ -789,7 +936,7 @@ void ride_music_update(int rideIndex) // Alter sample rate for a power cut effect if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN)) { sampleRate = ride->var_1AC * 70; - if (ride->var_18C != 7) + if (ride->breakdown_reason_pending != BREAKDOWN_CONTROL_FAILURE) sampleRate *= -1; sampleRate += 22050; } diff --git a/src/ride/ride.h b/src/ride/ride.h index 9d25f00d1d..e4c04e5723 100644 --- a/src/ride/ride.h +++ b/src/ride/ride.h @@ -180,7 +180,10 @@ typedef struct { uint8 num_riders; // 0x15B uint8 music_tune_id; // 0x15C uint8 var_15D; - uint16 maze_tiles; // 0x15E + union { + uint16 slide_peep; // 0x15E + uint16 maze_tiles; // 0x15E + }; uint8 pad_160[0x16]; uint8 var_176; uint8 pad_177[0x9]; @@ -189,11 +192,12 @@ typedef struct { uint16 race_winner; // 0x184 uint8 pad_186[0x02]; uint32 music_position; // 0x188 - uint8 var_18C; + uint8 breakdown_reason_pending; // 0x18C uint8 mechanic_status; // 0x18D uint16 mechanic; // 0x18E uint8 inspection_station; // 0x190 - uint8 pad_191[0x02]; + uint8 broken_vehicle; // 0x191 + uint8 broken_car; // 0x192 uint8 breakdown_reason; // 0x193 money16 price_secondary; // 0x194 uint16 var_196; @@ -213,7 +217,7 @@ typedef struct { uint32 var_1A4; uint8 pad_1A8[4]; uint8 var_1AC; - uint8 pad_1AD; + uint8 var_1AD; uint8 var_1AE; uint8 connected_message_throttle; // 0x1AF money32 income_per_hour; // 0x1B0 @@ -492,6 +496,7 @@ enum { }; enum { + RIDE_MECHANIC_STATUS_UNDEFINED = 0, RIDE_MECHANIC_STATUS_CALLING = 1, RIDE_MECHANIC_STATUS_HEADING = 2, RIDE_MECHANIC_STATUS_FIXING = 3, diff --git a/src/ride/ride_data.c b/src/ride/ride_data.c index b1ae1d8b85..d6645eef1a 100644 --- a/src/ride/ride_data.c +++ b/src/ride/ride_data.c @@ -771,4 +771,98 @@ const uint8 RideAvailableModes[] = { RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 58 Mine Ride RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 59 LIM Launched Roller Coaster RIDE_MODE_POWERED_LAUNCH_35, RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED, 0xFF // 60 (none) +}; + +const uint8 RideAvailableBreakdowns[] = { + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 00 Spiral 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), // 01 Stand Up Coaster + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 02 Suspended Swinging + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 03 Inverted + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 04 Steel Mini Coaster + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 05 Mini Railroad + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_DOORS_STUCK_CLOSED) | (1 << BREAKDOWN_DOORS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 06 Monorail + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 07 Mini Suspended Coaster + (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 08 Bumper Boats + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 09 Wooden Wild Mine/Mouse + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 0A Steeplechase/Motorbike/Soap Box Derby + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 0B Car Ride + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 0C Launched Freefall + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 0D Bobsleigh Coaster + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 0E Observation Tower + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 0F Looping Roller Coaster + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 10 Dinghy Slide + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 11 Mine Train Coaster + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 12 Chairlift + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 13 Corkscrew Roller Coaster + 0, // 14 Maze + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 15 Spiral Slide + (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 16 Go Karts + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_CONTROL_FAILURE), // 17 Log Flume + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_CONTROL_FAILURE), // 18 River Rapids + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 19 Bumper Cars + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 1A Pirate Ship + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 1B Swinging Inverter Ship + 0, // 1C Food Stall + 0, // 1D (none) + 0, // 1E Drink Stall + 0, // 1F (none) + 0, // 20 Shop (all types) + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_CONTROL_FAILURE), // 21 Merry Go Round + 0, // 22 Balloon Stall (maybe) + 0, // 23 Information Kiosk + 0, // 24 Bathroom + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 25 Ferris Wheel + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 26 Motion Simulator + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 27 3D Cinema + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 28 Gravitron + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 29 Space Rings + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 2A Reverse Freefall Coaster + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_DOORS_STUCK_CLOSED) | (1 << BREAKDOWN_DOORS_STUCK_OPEN), // 2B Elevator + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 2C Vertical Drop Roller Coaster + 0, // 2D ATM + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 2E Twist + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 2F Haunted House + 0, // 30 First Aid + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 31 Circus Show + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 32 Ghost Train + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 33 Twister 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), // 34 Wooden Roller Coaster + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 35 Side-Friction 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), // 36 Wild Mouse + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 37 Multi Dimension Coaster + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 38 (none) + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 39 Flying 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), // 3A (none) + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 3B Virginia Reel + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_CONTROL_FAILURE), // 3C Splash Boats + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 3D Mini Helicopters + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 3E Lay-down Roller Coaster + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_DOORS_STUCK_CLOSED) | (1 << BREAKDOWN_DOORS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 3F Suspended Monorail + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 40 (none) + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 41 Reverser Roller Coaster + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 42 Heartline Twister Roller Coaster + 0, // 43 Mini Golf + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 44 Giga Coaster + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 45 Roto-Drop + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 46 Flying Saucers + 0, // 47 Crooked House + (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 48 Monorail Cycles + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 49 Compact Inverted Coaster + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 4A Water Coaster + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 4B Air Powered Vertical Coaster + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 4C Inverted Hairpin Coaster + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 4D Magic Carpet + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 4E Submarine Ride + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 4F River Rafts + 0, // 50 (none) + (1 << BREAKDOWN_SAFETY_CUT_OUT), // 51 Enterprise + 0, // 52 (none) + 0, // 53 (none) + 0, // 54 (none) + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 55 (none) + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 56 Inverted Impulse Coaster + (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 57 Mini Roller Coaster + (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) }; \ No newline at end of file diff --git a/src/ride/ride_data.h b/src/ride/ride_data.h index d6b1617b04..d45438480d 100644 --- a/src/ride/ride_data.h +++ b/src/ride/ride_data.h @@ -41,5 +41,6 @@ extern const uint8 rideUnknownData3[0x60]; extern const rct_ride_name_convention RideNameConvention[96]; extern const uint8 RideAvailableModes[]; +extern const uint8 RideAvailableBreakdowns[]; #endif \ No newline at end of file From 3d40c7e3ee8f3944616b6249a7812ea3638c0e52 Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sun, 2 Nov 2014 01:41:00 +0000 Subject: [PATCH 06/11] add various ride mechanic functions --- src/addresses.h | 2 +- src/peep/peep.c | 4 +- src/peep/peep.h | 6 +- src/peep/staff.c | 25 ++++++ src/peep/staff.h | 2 + src/ride/ride.c | 204 +++++++++++++++++++++++++++++++++++++++++++- src/ride/ride.h | 9 +- src/scenario.c | 2 +- src/scenario.h | 2 +- src/windows/guest.c | 4 +- src/windows/staff.c | 2 +- src/world/map.c | 53 ++++++++---- src/world/map.h | 1 + src/world/park.c | 4 +- 14 files changed, 282 insertions(+), 38 deletions(-) diff --git a/src/addresses.h b/src/addresses.h index b82a9d6a44..7166cddab7 100644 --- a/src/addresses.h +++ b/src/addresses.h @@ -489,7 +489,7 @@ static void RCT2_CALLPROC_EBPSAFE(int address) /* Returns the flags register * *Flags register is as follows: - *0bSZ0A_0P0C_0000_00000 + *0bSZ0A_0P0C_0000_0000 *S = Signed flag *Z = Zero flag *C = Carry flag diff --git a/src/peep/peep.c b/src/peep/peep.c index 7412b278f5..59c60138cc 100644 --- a/src/peep/peep.c +++ b/src/peep/peep.c @@ -558,11 +558,11 @@ void get_arguments_from_action(rct_peep* peep, uint32 *argument_1, uint32* argum *argument_2 = 0; break; case PEEP_STATE_ANSWERING: - if (peep->pad_2C == 0){ + if (peep->var_2C == 0){ *argument_1 = STR_WALKING; *argument_2 = 0; } - else if (peep->pad_2C == 1){ + else if (peep->var_2C == 1){ *argument_1 = STR_ANSWERING_RADIO_CALL; *argument_2 = 0; } diff --git a/src/peep/peep.h b/src/peep/peep.h index e9e665e9bb..24f2cac38c 100644 --- a/src/peep/peep.h +++ b/src/peep/peep.h @@ -304,7 +304,7 @@ typedef struct { typedef struct { uint8 sprite_identifier; // 0x00 - uint8 pad_01; + uint8 var_01; uint16 var_02; // 0x02 uint16 next; // 0x04 uint16 previous; // 0x06 @@ -329,7 +329,7 @@ typedef struct { uint16 next_z; // 0x28 uint8 var_2A; uint8 state; // 0x2B - uint8 pad_2C; + uint8 var_2C; uint8 sprite_type; // 0x2D uint8 type; // 0x2E union{ @@ -363,7 +363,7 @@ typedef struct { uint8 photo4_ride_ref; // 0x5E uint8 pad_5F[0x09]; // 0x5C uint8 current_ride; // 0x68 - uint8 pad_69; + uint8 current_ride_station; // 0x69 uint8 current_train; // 0x6A uint8 current_car; // 0x6B uint8 current_seat; // 0x6C diff --git a/src/peep/staff.c b/src/peep/staff.c index 4bbcf64275..d8b7756f67 100644 --- a/src/peep/staff.c +++ b/src/peep/staff.c @@ -290,3 +290,28 @@ void sub_6C0C3F() } } } + +/** + * + * rct2: 0x006C0905 + */ +int mechanic_is_location_in_patrol(rct_peep *mechanic, int x, int y) +{ + int eax, ebx, ecx; + + if (!sub_664F72(x, y, mechanic->z)) + return 0; + + if (!(RCT2_ADDRESS(RCT2_ADDRESS_STAFF_MODE_ARRAY, uint8)[mechanic->staff_id] & 2)) + return 1; + + // Check patrol area? + ebx = mechanic->staff_id << 9; + eax = ((x & 0x1F80) >> 7) | ((y & 0x1F80) >> 1); + ecx = eax & 0x1F; + eax >>= 5; + if (RCT2_ADDRESS(0x013B0E72, uint32)[x * 4 + ebx] & (1 << y)) + return 1; + + return 0; +} \ No newline at end of file diff --git a/src/peep/staff.h b/src/peep/staff.h index 72e9f0c965..758159489c 100644 --- a/src/peep/staff.h +++ b/src/peep/staff.h @@ -22,6 +22,7 @@ #define _STAFF_H_ #include "../common.h" +#include "peep.h" #define STAFF_MAX_COUNT 0xC8 #define STAFF_TYPE_COUNT 0x04 @@ -45,5 +46,6 @@ void game_command_hire_new_staff_member(int* eax, int* ebx, int* ecx, int* edx, void update_staff_colour(uint8 staff_type, uint16 color); uint16 hire_new_staff_member(uint8 staff_type); void sub_6C0C3F(); +int mechanic_is_location_in_patrol(rct_peep *mechanic, int x, int y); #endif \ No newline at end of file diff --git a/src/ride/ride.c b/src/ride/ride.c index 39c4da9132..f8485837f6 100644 --- a/src/ride/ride.c +++ b/src/ride/ride.c @@ -596,13 +596,209 @@ int ride_get_new_breakdown_problem(rct_ride *ride) return BREAKDOWN_BRAKES_FAILURE; } +/** + * + * rct2: 0x006B74FA + */ +void ride_breakdown_add_news_item(int rideIndex) +{ + rct_ride *ride = GET_RIDE(rideIndex); + + RCT2_GLOBAL(0x0013CE952 + 0, uint16) = ride->name; + RCT2_GLOBAL(0x0013CE952 + 2, uint32) = ride->name_arguments; + news_item_add_to_queue(NEWS_ITEM_RIDE, 1927, rideIndex); +} + +/** + * + * rct2: 0x006B774B (forInspection = 0) + * rct2: 0x006B78C3 (forInspection = 1) + */ +rct_peep *find_closest_mechanic(int x, int y, int forInspection) +{ + unsigned int closestDistance, distance; + uint16 spriteIndex; + rct_peep *peep, *closestMechanic; + + closestDistance = -1; + FOR_ALL_STAFF(spriteIndex, peep) { + if (forInspection) { + if ((peep->state != PEEP_STATE_HEADING_TO_INSPECTION || peep->var_2C >= 4) && peep->state != PEEP_STATE_PATROLLING) + continue; + + if (!(peep->staff_orders & 2)) + continue; + } else { + if (peep->state != PEEP_STATE_PATROLLING && !(peep->staff_orders & 1)) + continue; + } + + if (map_is_location_in_park(x, y)) + if (!mechanic_is_location_in_patrol(peep, x & 0xFFE0, y & 0xFFE0)) + continue; + + if (peep->x == (sint16)0x8000) + continue; + + // Should probably be euclidean or manhattan distance, this seems a bit naive + distance = max(abs(peep->x - x), abs(peep->y - y)); + if (distance < closestDistance) { + closestDistance = distance; + closestMechanic = peep; + } + } + + return closestDistance == -1 ? NULL : closestMechanic; +} + +/** + * + * rct2: 0x006B76AB + */ +void ride_call_mechanic(int rideIndex) +{ + int x, y, z, stationIndex, direction, inspecting; + uint16 xy; + rct_ride *ride; + rct_map_element *mapElement; + rct_peep *mechanic; + + ride = GET_RIDE(rideIndex); + + inspecting = (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN)) == 0; + + // Get either exit position or entrance position if there is no exit + stationIndex = ride->inspection_station; + xy = ride->exits[stationIndex]; + if (xy == 0xFFFF) { + xy = ride->entrances[stationIndex]; + if (xy == 0xFFFF) + return; + } + + // Get station start track element and position + x = (xy & 0xFF) * 32; + y = (xy >> 8) * 32; + z = ride->station_heights[stationIndex] * 8; + mapElement = ride_get_station_start_track_element(ride, stationIndex); + if (mapElement == NULL) + return; + + direction = mapElement->type & 3; + x -= RCT2_ADDRESS(0x00993CCC, sint16)[direction * 2]; + y -= RCT2_ADDRESS(0x00993CCE, sint16)[direction * 2]; + x += 16; + y += 16; + + // Find closest mechanic + mechanic = find_closest_mechanic(x, y, inspecting); + if (mechanic == NULL) + return; + + RCT2_CALLPROC_X(0x0069A409, 0, 0, 0, 0, (int)mechanic, 0, 0); + mechanic->state = inspecting ? PEEP_STATE_HEADING_TO_INSPECTION : PEEP_STATE_ANSWERING; + RCT2_CALLPROC_X(0x0069A42F, 0, 0, 0, 0, (int)mechanic, 0, 0); + mechanic->var_2C = 0; + ride->mechanic_status = RIDE_MECHANIC_STATUS_HEADING; + ride->var_14D |= 0x20; + ride->mechanic = mechanic->sprite_index; + mechanic->current_ride = rideIndex; + mechanic->current_ride_station = ride->inspection_station; +} + +/** + * + * rct2: 0x006B762F + */ +void ride_mechanic_status_update(int rideIndex, int mechanicStatus) +{ + int breakdownReason; + rct_ride *ride; + rct_peep *mechanic; + + ride = GET_RIDE(rideIndex); + switch (mechanicStatus) { + case RIDE_MECHANIC_STATUS_UNDEFINED: + breakdownReason = ride->breakdown_reason_pending; + if ( + breakdownReason == BREAKDOWN_SAFETY_CUT_OUT || + breakdownReason == BREAKDOWN_BRAKES_FAILURE || + breakdownReason == BREAKDOWN_CONTROL_FAILURE + ) { + ride->lifecycle_flags |= 0x80; + ride->var_14D |= 0x2C; + ride->mechanic_status = RIDE_MECHANIC_STATUS_CALLING; + ride->breakdown_reason = breakdownReason; + ride_breakdown_add_news_item(rideIndex); + } + break; + case RIDE_MECHANIC_STATUS_CALLING: + if (RideAvailableBreakdowns[ride->type] == 0) { + ride->lifecycle_flags &= ~(RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_DUE_INSPECTION); + break; + } + + ride_call_mechanic(rideIndex); + break; + case RIDE_MECHANIC_STATUS_HEADING: + mechanic = &(g_sprite_list[ride->mechanic].peep); + if ( + !peep_is_mechanic(mechanic) || + (mechanic->state != PEEP_STATE_HEADING_TO_INSPECTION && mechanic->state != PEEP_STATE_ANSWERING) || + mechanic->current_ride != rideIndex + ) { + ride->mechanic_status = RIDE_MECHANIC_STATUS_CALLING; + ride->var_14D |= 0x20; + ride_mechanic_status_update(rideIndex, RIDE_MECHANIC_STATUS_CALLING); + } + break; + case RIDE_MECHANIC_STATUS_FIXING: + mechanic = &(g_sprite_list[ride->mechanic].peep); + if ( + !peep_is_mechanic(mechanic) || + ( + mechanic->state != PEEP_STATE_HEADING_TO_INSPECTION && + mechanic->state != PEEP_STATE_FIXING && + mechanic->state != PEEP_STATE_INSPECTING && + mechanic->state != PEEP_STATE_ANSWERING + ) + ) { + ride->mechanic_status = RIDE_MECHANIC_STATUS_CALLING; + ride->var_14D |= 0x20; + ride_mechanic_status_update(rideIndex, RIDE_MECHANIC_STATUS_CALLING); + } + break; + } +} + /** * * rct2: 0x006B75C8 */ -void sub_6B75C8(int rideIndex) +void ride_breakdown_status_update(int rideIndex) { - RCT2_CALLPROC_X(0x006B75C8, 0, 0, 0, rideIndex, 0, 0, 0); + // RCT2_CALLPROC_X(0x006B75C8, 0, 0, 0, rideIndex, 0, 0, 0); + + rct_ride *ride = GET_RIDE(rideIndex); + + // Warn player if ride hasnt been fixed for ages + if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) { + ride->var_1AD++; + if (ride->var_1AD == 0) + ride->var_1AD -= 16; + + if ( + !(ride->var_1AD & 15) && + ride->mechanic_status != RIDE_MECHANIC_STATUS_FIXING && + ride->mechanic_status != RIDE_MECHANIC_STATUS_4 + ) { + RCT2_GLOBAL(0x0013CE952 + 0, uint16) = ride->name; + RCT2_GLOBAL(0x0013CE952 + 2, uint32) = ride->name_arguments; + news_item_add_to_queue(NEWS_ITEM_RIDE, 1929, rideIndex); + } + } + + ride_mechanic_status_update(rideIndex, ride->mechanic_status); } /** @@ -747,7 +943,7 @@ void ride_breakdown_update(int rideIndex) ride->var_14D |= 32; // Random probability of a breakdown - if (ride->var_196 == 0 || (scenario_rand() & 0x2FFFFF) <= 25856 - ride->var_196) { + if (ride->var_196 == 0 || (int)(scenario_rand() & 0x2FFFFF) <= 25856 - ride->var_196) { breakdownReason = ride_get_new_breakdown_problem(ride); if (breakdownReason != -1) ride_prepare_breakdown(rideIndex, breakdownReason); @@ -998,7 +1194,7 @@ void ride_update(int rideIndex) // Various things include news messages if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_DUE_INSPECTION)) if (((RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) >> 1) & 255) == rideIndex) - sub_6B75C8(rideIndex); + ride_breakdown_status_update(rideIndex); ride_inspection_update(ride); } diff --git a/src/ride/ride.h b/src/ride/ride.h index e4c04e5723..582b8ee118 100644 --- a/src/ride/ride.h +++ b/src/ride/ride.h @@ -496,10 +496,11 @@ enum { }; enum { - RIDE_MECHANIC_STATUS_UNDEFINED = 0, - RIDE_MECHANIC_STATUS_CALLING = 1, - RIDE_MECHANIC_STATUS_HEADING = 2, - RIDE_MECHANIC_STATUS_FIXING = 3, + RIDE_MECHANIC_STATUS_UNDEFINED, + RIDE_MECHANIC_STATUS_CALLING, + RIDE_MECHANIC_STATUS_HEADING, + RIDE_MECHANIC_STATUS_FIXING, + RIDE_MECHANIC_STATUS_4 }; enum { diff --git a/src/scenario.c b/src/scenario.c index 1a81cbe709..829f69c32e 100644 --- a/src/scenario.c +++ b/src/scenario.c @@ -642,7 +642,7 @@ void scenario_update() * * rct2: 0x006E37D2 */ -int scenario_rand() +unsigned int scenario_rand() { int eax = RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_SRAND_0, uint32); RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_SRAND_0, uint32) += ror32(RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_SRAND_1, uint32) ^ 0x1234567F, 7); diff --git a/src/scenario.h b/src/scenario.h index e969e8b34d..221a673a22 100644 --- a/src/scenario.h +++ b/src/scenario.h @@ -407,6 +407,6 @@ int scenario_load(const char *path); int scenario_load_and_play(const rct_scenario_basic *scenario); int scenario_load_and_play_from_path(const char *path); void scenario_update(); -int scenario_rand(); +unsigned int scenario_rand(); #endif diff --git a/src/windows/guest.c b/src/windows/guest.c index 4baa96add5..842a936510 100644 --- a/src/windows/guest.c +++ b/src/windows/guest.c @@ -627,7 +627,7 @@ void window_guest_overview_mouse_up(){ RCT2_CALLPROC_X(0x0069E9D3, 0x8000, 0, peep->y, peep->z, (int)peep, 0, 0); RCT2_CALLPROC_X(0x0069A409, 0, 0, 0, 0, (int)peep, 0, 0); peep->state = 9; - peep->pad_2C = 0; + peep->var_2C = 0; RCT2_CALLPROC_X(0x0069A42F, 0, 0, 0, 0, (int)peep, 0, 0); break; case WIDX_RENAME: @@ -1204,7 +1204,7 @@ void window_guest_overview_tool_down(){ int dest_z = ((uint8*)edx)[2] * 8 + 16; - if (sub_664F72(tile_x, tile_y, dest_z)){ + if (!sub_664F72(tile_x, tile_y, dest_z)){ window_error_open(0x785,-1); return; } diff --git a/src/windows/staff.c b/src/windows/staff.c index a22ad15bab..c6a8e2141f 100644 --- a/src/windows/staff.c +++ b/src/windows/staff.c @@ -1124,7 +1124,7 @@ void window_staff_overview_tool_down(){ int dest_z = ((uint8*)edx)[2] * 8 + 16; - if (sub_664F72(tile_x, tile_y, dest_z)){ + if (!sub_664F72(tile_x, tile_y, dest_z)){ window_error_open(0x785, -1); return; } diff --git a/src/world/map.c b/src/world/map.c index b889a96bd5..88da65fafe 100644 --- a/src/world/map.c +++ b/src/world/map.c @@ -434,25 +434,44 @@ static void sub_6A87BB(int x, int y) RCT2_CALLPROC_X(0x006A87BB, x, 0, y, 0, 0, 0, 0); } -/* rct2: 0x664F72 */ -int sub_664F72(int x, int y, int z){ - if (x > 0x1FFF || y > 0x1FFF){ - RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = 0x6C1; - return 1; +/** + * + * rct2: 0x00664F72 + */ +int sub_664F72(int x, int y, int z) +{ + rct_map_element *mapElement; + + if (x < (256 * 32) && y < (256 * 32)) { + mapElement = map_get_surface_element_at(x / 32, y / 32); + if (mapElement->properties.surface.ownership & 0x20) + return 1; + + if (mapElement->properties.surface.ownership & 0x10) { + z /= 8; + if (z < mapElement->base_height || z - 2 > mapElement->base_height) + return 1; + } } - rct_map_element* map_element = map_get_surface_element_at(x / 32, y / 32); - if (map_element->properties.surface.ownership & 0x20) return 0; - if (!(map_element->properties.surface.ownership & 0x10)){ - RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = 0x6C1; - return 1; + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = 1729; + return 0; +} + +/** + * + * rct2: 0x00664F2C + */ +int map_is_location_in_park(int x, int y) +{ + rct_map_element *mapElement; + + if (x < (256 * 32) && y < (256 * 32)) { + mapElement = map_get_surface_element_at(x / 32, y / 32); + if (mapElement->properties.surface.ownership & 0x20) + return 1; } - z >>= 3; - if ((z & 0xFF) < map_element->base_height)return 0; - z = (z & 0xFF) - 2; - if (z > map_element->base_height)return 0; - - RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = 0x6C1; - return 1; + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = 1729; + return 0; } \ No newline at end of file diff --git a/src/world/map.h b/src/world/map.h index 394063d6e6..3bef2a673e 100644 --- a/src/world/map.h +++ b/src/world/map.h @@ -206,6 +206,7 @@ int map_coord_is_connected(uint16 coordinate, uint8 height, uint8 face_direction void map_invalidate_animations(); void sub_6A876D(); int sub_664F72(int x, int y, int z); +int map_is_location_in_park(int x, int y); void fountain_update_all(); diff --git a/src/world/park.c b/src/world/park.c index 1cf7dfb678..9130a3fb64 100644 --- a/src/world/park.c +++ b/src/world/park.c @@ -508,7 +508,7 @@ static rct_peep *park_generate_new_guest_due_to_campaign(int campaign) static void park_generate_new_guests() { // Generate a new guest for some probability - if ((scenario_rand() & 0xFFFF) < _guestGenerationProbability) { + if ((int)(scenario_rand() & 0xFFFF) < _guestGenerationProbability) { int difficultGeneration = (RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_DIFFICULT_GUEST_GENERATION) != 0; if (!difficultGeneration || _suggestedGuestMaximum + 150 >= RCT2_GLOBAL(RCT2_ADDRESS_GUESTS_IN_PARK, uint16)) park_generate_new_guest(); @@ -519,7 +519,7 @@ static void park_generate_new_guests() for (campaign = 0; campaign < ADVERTISING_CAMPAIGN_COUNT; campaign++) { if (RCT2_ADDRESS(0x01358102, uint8)[campaign] != 0) { // Random chance of guest generation - if ((scenario_rand() & 0xFFFF) < marketing_get_campaign_guest_generation_probability(campaign)) + if ((int)(scenario_rand() & 0xFFFF) < marketing_get_campaign_guest_generation_probability(campaign)) park_generate_new_guest_due_to_campaign(campaign); } } From cf59455d6914de47b095ef974eb0f9b8d76056cc Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sun, 2 Nov 2014 03:37:56 +0000 Subject: [PATCH 07/11] refactor ride update and fix bugs --- projects/openrct2.vcxproj | 4 +- projects/openrct2.vcxproj.filters | 13 +- src/peep/staff.c | 28 +- src/ride/ride.c | 2311 +++++++++++++---------------- src/ride/ride.h | 3 +- src/ride/station.c | 321 ++++ src/ride/station.h | 31 + src/util/util.c | 11 + src/util/util.h | 2 + src/world/map.c | 10 + src/world/map.h | 1 + 11 files changed, 1428 insertions(+), 1307 deletions(-) create mode 100644 src/ride/station.c create mode 100644 src/ride/station.h diff --git a/projects/openrct2.vcxproj b/projects/openrct2.vcxproj index d43b9d3f75..eff1866b1e 100644 --- a/projects/openrct2.vcxproj +++ b/projects/openrct2.vcxproj @@ -89,6 +89,7 @@ + @@ -188,6 +189,7 @@ + @@ -291,4 +293,4 @@ - + \ No newline at end of file diff --git a/projects/openrct2.vcxproj.filters b/projects/openrct2.vcxproj.filters index 40ea9706d1..397f06c03d 100644 --- a/projects/openrct2.vcxproj.filters +++ b/projects/openrct2.vcxproj.filters @@ -382,7 +382,6 @@ - Source\Drawing @@ -393,9 +392,6 @@ - - Libraries\lodepng - @@ -431,6 +427,10 @@ Source\Windows + + + Source\Ride + @@ -628,5 +628,8 @@ Source\World + + Source\Ride + - + \ No newline at end of file diff --git a/src/peep/staff.c b/src/peep/staff.c index d8b7756f67..bd176a3781 100644 --- a/src/peep/staff.c +++ b/src/peep/staff.c @@ -291,27 +291,33 @@ void sub_6C0C3F() } } +int staff_is_location_in_patrol_area(rct_peep *peep, int x, int y) +{ + // Patrol quads are stored in a bit map (8 patrol quads per byte) + // Each patrol quad is 4x4 + // Therefore there are in total 64 x 64 patrol quads in the 256 x 256 map + int patrolOffset = peep->staff_id * (64 * 64 / 8); + int patrolIndex = ((x & 0x1F80) >> 7) | ((y & 0x1F80) >> 1); + int mask = 1 << (patrolIndex & 0x1F); + int base = patrolIndex >> 5; + + uint32 *patrolBits = (uint32*)(0x013B0E72 + patrolOffset + (base * 4)); + return (*patrolBits & mask) != 0; +} + /** * * rct2: 0x006C0905 */ int mechanic_is_location_in_patrol(rct_peep *mechanic, int x, int y) { - int eax, ebx, ecx; - + // Check if location is in the park if (!sub_664F72(x, y, mechanic->z)) return 0; + // Check if mechanic has patrol area if (!(RCT2_ADDRESS(RCT2_ADDRESS_STAFF_MODE_ARRAY, uint8)[mechanic->staff_id] & 2)) return 1; - // Check patrol area? - ebx = mechanic->staff_id << 9; - eax = ((x & 0x1F80) >> 7) | ((y & 0x1F80) >> 1); - ecx = eax & 0x1F; - eax >>= 5; - if (RCT2_ADDRESS(0x013B0E72, uint32)[x * 4 + ebx] & (1 << y)) - return 1; - - return 0; + return staff_is_location_in_patrol_area(mechanic, x, y); } \ No newline at end of file diff --git a/src/ride/ride.c b/src/ride/ride.c index f8485837f6..04a2d95666 100644 --- a/src/ride/ride.c +++ b/src/ride/ride.c @@ -29,10 +29,12 @@ #include "../peep/peep.h" #include "../peep/staff.h" #include "../scenario.h" +#include "../util/util.h" #include "../world/map.h" #include "../world/sprite.h" #include "ride.h" #include "ride_data.h" +#include "station.h" #pragma region Ride classification table @@ -112,7 +114,40 @@ static const int RideInspectionInterval[] = { rct_ride_type **gRideTypeList = RCT2_ADDRESS(0x009ACFA4, rct_ride_type*); rct_ride* g_ride_list = RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride); -static void ride_init_vehicle_speed(rct_ride *ride); +// Static function declarations +rct_peep *find_closest_mechanic(int x, int y, int forInspection); +static void ride_breakdown_status_update(int rideIndex); +static void ride_breakdown_update(int rideIndex); +static void ride_call_mechanic(int rideIndex); +static void ride_chairlift_update(rct_ride *ride); +static void ride_entrance_exit_connected(rct_ride* ride, int ride_idx); +static int ride_get_new_breakdown_problem(rct_ride *ride); +static void ride_inspection_update(rct_ride *ride); +static void ride_mechanic_status_update(int rideIndex, int mechanicStatus); +static void ride_music_update(int rideIndex); +static void ride_play_music(); +static void ride_prepare_breakdown(int rideIndex, int breakdownReason); +static void ride_shop_connected(rct_ride* ride, int ride_idx); +static void ride_spiral_slide_update(rct_ride *ride); +static void ride_update(int rideIndex); + +rct_ride_type *ride_get_entry(rct_ride *ride) +{ + return GET_RIDE_ENTRY(ride->subtype); +} + +uint8 *get_ride_entry_indices_for_ride_type(uint8 rideType) +{ + uint8 *typeToRideEntryIndexMap = (uint8*)0x009E32F8; + uint8 *entryIndexList = typeToRideEntryIndexMap; + while (rideType > 0) { + do { + entryIndexList++; + } while (*(entryIndexList - 1) != 255); + rideType--; + } + return entryIndexList; +} int ride_get_count() { @@ -143,44 +178,6 @@ int ride_get_max_queue_time(rct_ride *ride) return queueTime; } -/** - * - * rct2: 0x006ACA89 - */ -void ride_init_all() -{ - int i; - rct_ride *ride; - rct_ride_measurement *ride_measurement; - - for (i = 0; i < MAX_RIDES; i++) { - ride = &g_ride_list[i]; - ride->type = RIDE_TYPE_NULL; - } - - RCT2_GLOBAL(0x0138B590, sint8) = 0; - RCT2_GLOBAL(0x0138B591, sint8) = 0; - - for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) { - ride_measurement = GET_RIDE_MEASUREMENT(i); - ride_measurement->ride_index = 255; - } -} - -/** -* -* rct2: 0x006B7A38 -*/ -void reset_all_ride_build_dates() { - int i; - rct_ride *ride; - FOR_ALL_RIDES(i, ride) { - //mov ax, current_month_year - //sub [esi + 180h], ax - ride->build_date -= RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint16); - } -} - /** * rct2: 0x006AC916 */ @@ -256,1124 +253,6 @@ money32 ride_calculate_income_per_hour(rct_ride *ride) return incomePerHour; } -/** - * - * rct2: 0x006BC6D8 - */ -void ride_play_music() -{ - RCT2_CALLPROC_EBPSAFE(0x006BC6D8); -} - -rct_map_element *ride_get_station_start_track_element(rct_ride *ride, int stationIndex) -{ - int x, y, z; - rct_map_element *mapElement; - - x = ride->station_starts[stationIndex] & 0xFF; - y = ride->station_starts[stationIndex] >> 8; - z = ride->station_heights[stationIndex]; - - // Get first element of the tile - mapElement = TILE_MAP_ELEMENT_POINTER(y * 256 + x); - - // Find the station track element - do { - if ((mapElement->type & MAP_ELEMENT_TYPE_MASK) == MAP_ELEMENT_TYPE_TRACK && z == mapElement->base_height) - return mapElement; - - mapElement++; - } while (!((mapElement - 1)->flags & MAP_ELEMENT_FLAG_LAST_TILE)); - - return NULL; -} - -/** - * - * rct2: 0x006ECB60 - * NOTE: x, y and z are in pixels, not tile units - */ -void map_invalidate_tile(int x, int y, int zLow, int zHigh) -{ - RCT2_CALLPROC_X(0x006ECB60, x, 0, y, 0, zHigh, zLow, 0); -} - -/** - * - * rct2: 0x006AC2C7 - */ -void ride_invalidate_station_start(rct_ride *ride, int stationIndex, int dl) -{ - int x, y; - rct_map_element *mapElement; - - x = (ride->station_starts[stationIndex] & 0xFF) * 32; - y = (ride->station_starts[stationIndex] >> 8) * 32; - mapElement = ride_get_station_start_track_element(ride, stationIndex); - - mapElement->properties.track.sequence &= 0x7F; - if (dl != 0) - mapElement->properties.track.sequence |= 0x80; - - // Invalidate map tile - map_invalidate_tile(x, y, mapElement->base_height * 8, mapElement->clearance_height * 8); -} - -void sub_6AC2AF(rct_ride *ride, int stationIndex, int dl) -{ - if (dl != 0) - ride->var_062[stationIndex] |= (1 << 7); - else - ride->var_062[stationIndex] &= ~(1 << 7); -} - -/** - * - * rct2: 0x006AC1DF - */ -void ride_update_station_race(rct_ride *ride, int stationIndex, int dl) -{ - int i, dh; - rct_vehicle *vehicle; - rct_peep *peep; - - if ( - ride->status == RIDE_STATUS_CLOSED || - (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) - ) { - sub_6AC2AF(ride, stationIndex, dl); - return; - } - - if (!(ride->lifecycle_flags & VEHICLE_STATUS_WAITING_TO_DEPART)) { - for (i = 0; i < ride->num_vehicles; i++) { - vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); - if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART && vehicle->status != VEHICLE_STATUS_DEPARTING) { - sub_6AC2AF(ride, stationIndex, dl); - return; - } - } - - ride_init_vehicle_speed(ride); - ride->lifecycle_flags |= VEHICLE_STATUS_WAITING_TO_DEPART; - ride->var_14D = 12; - } else { - dh = ride->var_0D0; - for (i = 0; i < ride->num_vehicles; i++) { - vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); - if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART && dh <= vehicle->var_CE) { - ride->lifecycle_flags &= ~VEHICLE_STATUS_WAITING_TO_DEPART; - if (vehicle->var_B3 != 0) { - peep = &(g_sprite_list[vehicle->peep].peep); - ride->race_winner = peep->sprite_index; - ride->var_14D = 12; - } - sub_6AC2AF(ride, stationIndex, dl); - return; - } - } - } - dl = 1; - sub_6AC2AF(ride, stationIndex, dl); -} - -/** - * - * rct2: 0x006AC12B - */ -void ride_update_station_bumpercar(rct_ride *ride, int stationIndex, int dl) -{ - int i, dx, dh; - rct_vehicle *vehicle; - - if ( - ride->status == RIDE_STATUS_CLOSED || - (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) - ) { - sub_6AC2AF(ride, stationIndex, dl); - return; - } - - if (ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) { - dx = ride->var_0D0 * 32; - dl = dx & 0xFF; - dh = (dx >> 8) & 0xFF; - for (i = 0; i < ride->num_vehicles; i++) { - vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); - if (dh > vehicle->var_CE) - continue; - - if (dh < vehicle->var_CE) { - ride->lifecycle_flags &= ~VEHICLE_STATUS_WAITING_TO_DEPART; - dh = 0; - sub_6AC2AF(ride, stationIndex, dl); - return; - } - - if (dl > vehicle->var_51) - continue; - } - - dl = 1; - } else { - for (i = 0; i < ride->num_vehicles; i++) { - vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); - if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART) { - sub_6AC2AF(ride, stationIndex, dl); - return; - } - } - - ride->lifecycle_flags |= VEHICLE_STATUS_WAITING_TO_DEPART; - ride->var_14D = 12; - dl = 1; - } - sub_6AC2AF(ride, stationIndex, dl); -} - -/** - * - * rct2: 0x006AC0A1 - */ -void ride_update_station_blocksection(rct_ride *ride, int stationIndex, int dl) -{ - rct_map_element *mapElement; - - mapElement = ride_get_station_start_track_element(ride, stationIndex); - - if ((ride->status == RIDE_STATUS_CLOSED && ride->num_riders == 0) || mapElement->flags & 0x20) { - dl = 0; - if (ride->var_062[stationIndex] & (1 << 7)) { - ride->var_062[stationIndex] &= ~(1 << 7); - ride_invalidate_station_start(ride, stationIndex, dl); - return; - } - - if (mapElement->properties.track.sequence & 0x80) { - ride_invalidate_station_start(ride, stationIndex, dl); - return; - } - } else { - dl = 1; - if (!(ride->var_062[stationIndex] & (1 << 7))) { - ride->var_062[stationIndex] |= (1 << 7); - ride_invalidate_station_start(ride, stationIndex, dl); - return; - } - - if (mapElement->properties.track.sequence & 0x80) { - ride_invalidate_station_start(ride, stationIndex, dl); - return; - } - } - - sub_6AC2AF(ride, stationIndex, dl); -} - -/** - * - * rct2: 0x006ABFFB - */ -void ride_update_station(rct_ride *ride, int stationIndex) -{ - int dl, dh; - - if (ride->station_starts[stationIndex] == 0xFFFF) - return; - - dl = 0; - switch (ride->mode) { - case RIDE_MODE_RACE: - ride_update_station_race(ride, stationIndex, dl); - break; - case RIDE_MODE_BUMPERCAR: - ride_update_station_bumpercar(ride, stationIndex, dl); - break; - case RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED: - case RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED: - ride_update_station_blocksection(ride, stationIndex, dl); - break; - default: - if ( - (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) && - (ride->status == RIDE_STATUS_CLOSED && ride->num_riders == 0) - ) { - dh = ride->var_062[stationIndex] & 0x7F; - if (dh != 0 && dh != 0x7F && !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 7)) - ride->var_062[stationIndex]--; - } else { - dl = 1; - dh = ride->var_062[stationIndex] & 0x7F; - if (dh != 0) { - if (dh != 0x7F && !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 31)) - ride->var_062[stationIndex]--; - dl = 0; - } - } - sub_6AC2AF(ride, stationIndex, dl); - break; - } -} - -int bitscanforward(int source) -{ - int i; - - for (i = 0; i < 32; i++) - if (source & (1 << i)) - return i; - - return -1; -} - -static uint8 _breakdownProblemProbabilities[] = { - 25, // BREAKDOWN_SAFETY_CUT_OUT - 12, // BREAKDOWN_RESTRAINTS_STUCK_CLOSED - 10, // BREAKDOWN_RESTRAINTS_STUCK_OPEN - 13, // BREAKDOWN_DOORS_STUCK_CLOSED - 10, // BREAKDOWN_DOORS_STUCK_OPEN - 6, // BREAKDOWN_VEHICLE_MALFUNCTION - 0, // BREAKDOWN_BRAKES_FAILURE - 3 // BREAKDOWN_CONTROL_FAILURE -}; - -/** - * - * rct2: 0x006B7294 - */ -int ride_get_new_breakdown_problem(rct_ride *ride) -{ - int availableBreakdownProblems, monthsOld, totalProbability, randomProbability, problemBits, breakdownProblem; - rct_ride_type *entry; - - // Brake failure is more likely when its raining - _breakdownProblemProbabilities[BREAKDOWN_BRAKES_FAILURE] = - RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_RAIN_LEVEL, uint8) == 0 ? 3 : 20; - - entry = ride_get_entry(ride); - if (entry->var_008 & 0x4000) - return -1; - - availableBreakdownProblems = RideAvailableBreakdowns[ride->type]; - - // Calculate the total probability range for all possible breakdown problems - totalProbability = 0; - problemBits = availableBreakdownProblems; - while (problemBits != 0) { - breakdownProblem = bitscanforward(problemBits); - problemBits &= ~(1 << breakdownProblem); - totalProbability += _breakdownProblemProbabilities[breakdownProblem]; - } - if (totalProbability == 0) - return -1; - - // Choose a random number within this range - randomProbability = scenario_rand() % totalProbability; - - // Find which problem range the random number lies - problemBits = availableBreakdownProblems; - do { - breakdownProblem = bitscanforward(problemBits); - problemBits &= ~(1 << breakdownProblem); - randomProbability -= _breakdownProblemProbabilities[breakdownProblem]; - } while (randomProbability >= 0); - - if (breakdownProblem != BREAKDOWN_BRAKES_FAILURE) - return breakdownProblem; - - // Breaks failure can not happen if block breaks are used (so long as there is more than one vehicle) - // However if this is the case, break failure should be taken out the equation, otherwise block brake - // rides have a lower probability to break down due to a random implementation reason. - if (ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED || ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED) - if (ride->num_vehicles != 1) - return -1; - - // Again the probability is lower, this time if young or two other unknown reasons... - monthsOld = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint8) - ride->build_date; - if (monthsOld < 16 || ride->var_196 > 12800 || ride->lifecycle_flags & RIDE_LIFECYCLE_19) - return -1; - - return BREAKDOWN_BRAKES_FAILURE; -} - -/** - * - * rct2: 0x006B74FA - */ -void ride_breakdown_add_news_item(int rideIndex) -{ - rct_ride *ride = GET_RIDE(rideIndex); - - RCT2_GLOBAL(0x0013CE952 + 0, uint16) = ride->name; - RCT2_GLOBAL(0x0013CE952 + 2, uint32) = ride->name_arguments; - news_item_add_to_queue(NEWS_ITEM_RIDE, 1927, rideIndex); -} - -/** - * - * rct2: 0x006B774B (forInspection = 0) - * rct2: 0x006B78C3 (forInspection = 1) - */ -rct_peep *find_closest_mechanic(int x, int y, int forInspection) -{ - unsigned int closestDistance, distance; - uint16 spriteIndex; - rct_peep *peep, *closestMechanic; - - closestDistance = -1; - FOR_ALL_STAFF(spriteIndex, peep) { - if (forInspection) { - if ((peep->state != PEEP_STATE_HEADING_TO_INSPECTION || peep->var_2C >= 4) && peep->state != PEEP_STATE_PATROLLING) - continue; - - if (!(peep->staff_orders & 2)) - continue; - } else { - if (peep->state != PEEP_STATE_PATROLLING && !(peep->staff_orders & 1)) - continue; - } - - if (map_is_location_in_park(x, y)) - if (!mechanic_is_location_in_patrol(peep, x & 0xFFE0, y & 0xFFE0)) - continue; - - if (peep->x == (sint16)0x8000) - continue; - - // Should probably be euclidean or manhattan distance, this seems a bit naive - distance = max(abs(peep->x - x), abs(peep->y - y)); - if (distance < closestDistance) { - closestDistance = distance; - closestMechanic = peep; - } - } - - return closestDistance == -1 ? NULL : closestMechanic; -} - -/** - * - * rct2: 0x006B76AB - */ -void ride_call_mechanic(int rideIndex) -{ - int x, y, z, stationIndex, direction, inspecting; - uint16 xy; - rct_ride *ride; - rct_map_element *mapElement; - rct_peep *mechanic; - - ride = GET_RIDE(rideIndex); - - inspecting = (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN)) == 0; - - // Get either exit position or entrance position if there is no exit - stationIndex = ride->inspection_station; - xy = ride->exits[stationIndex]; - if (xy == 0xFFFF) { - xy = ride->entrances[stationIndex]; - if (xy == 0xFFFF) - return; - } - - // Get station start track element and position - x = (xy & 0xFF) * 32; - y = (xy >> 8) * 32; - z = ride->station_heights[stationIndex] * 8; - mapElement = ride_get_station_start_track_element(ride, stationIndex); - if (mapElement == NULL) - return; - - direction = mapElement->type & 3; - x -= RCT2_ADDRESS(0x00993CCC, sint16)[direction * 2]; - y -= RCT2_ADDRESS(0x00993CCE, sint16)[direction * 2]; - x += 16; - y += 16; - - // Find closest mechanic - mechanic = find_closest_mechanic(x, y, inspecting); - if (mechanic == NULL) - return; - - RCT2_CALLPROC_X(0x0069A409, 0, 0, 0, 0, (int)mechanic, 0, 0); - mechanic->state = inspecting ? PEEP_STATE_HEADING_TO_INSPECTION : PEEP_STATE_ANSWERING; - RCT2_CALLPROC_X(0x0069A42F, 0, 0, 0, 0, (int)mechanic, 0, 0); - mechanic->var_2C = 0; - ride->mechanic_status = RIDE_MECHANIC_STATUS_HEADING; - ride->var_14D |= 0x20; - ride->mechanic = mechanic->sprite_index; - mechanic->current_ride = rideIndex; - mechanic->current_ride_station = ride->inspection_station; -} - -/** - * - * rct2: 0x006B762F - */ -void ride_mechanic_status_update(int rideIndex, int mechanicStatus) -{ - int breakdownReason; - rct_ride *ride; - rct_peep *mechanic; - - ride = GET_RIDE(rideIndex); - switch (mechanicStatus) { - case RIDE_MECHANIC_STATUS_UNDEFINED: - breakdownReason = ride->breakdown_reason_pending; - if ( - breakdownReason == BREAKDOWN_SAFETY_CUT_OUT || - breakdownReason == BREAKDOWN_BRAKES_FAILURE || - breakdownReason == BREAKDOWN_CONTROL_FAILURE - ) { - ride->lifecycle_flags |= 0x80; - ride->var_14D |= 0x2C; - ride->mechanic_status = RIDE_MECHANIC_STATUS_CALLING; - ride->breakdown_reason = breakdownReason; - ride_breakdown_add_news_item(rideIndex); - } - break; - case RIDE_MECHANIC_STATUS_CALLING: - if (RideAvailableBreakdowns[ride->type] == 0) { - ride->lifecycle_flags &= ~(RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_DUE_INSPECTION); - break; - } - - ride_call_mechanic(rideIndex); - break; - case RIDE_MECHANIC_STATUS_HEADING: - mechanic = &(g_sprite_list[ride->mechanic].peep); - if ( - !peep_is_mechanic(mechanic) || - (mechanic->state != PEEP_STATE_HEADING_TO_INSPECTION && mechanic->state != PEEP_STATE_ANSWERING) || - mechanic->current_ride != rideIndex - ) { - ride->mechanic_status = RIDE_MECHANIC_STATUS_CALLING; - ride->var_14D |= 0x20; - ride_mechanic_status_update(rideIndex, RIDE_MECHANIC_STATUS_CALLING); - } - break; - case RIDE_MECHANIC_STATUS_FIXING: - mechanic = &(g_sprite_list[ride->mechanic].peep); - if ( - !peep_is_mechanic(mechanic) || - ( - mechanic->state != PEEP_STATE_HEADING_TO_INSPECTION && - mechanic->state != PEEP_STATE_FIXING && - mechanic->state != PEEP_STATE_INSPECTING && - mechanic->state != PEEP_STATE_ANSWERING - ) - ) { - ride->mechanic_status = RIDE_MECHANIC_STATUS_CALLING; - ride->var_14D |= 0x20; - ride_mechanic_status_update(rideIndex, RIDE_MECHANIC_STATUS_CALLING); - } - break; - } -} - -/** - * - * rct2: 0x006B75C8 - */ -void ride_breakdown_status_update(int rideIndex) -{ - // RCT2_CALLPROC_X(0x006B75C8, 0, 0, 0, rideIndex, 0, 0, 0); - - rct_ride *ride = GET_RIDE(rideIndex); - - // Warn player if ride hasnt been fixed for ages - if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) { - ride->var_1AD++; - if (ride->var_1AD == 0) - ride->var_1AD -= 16; - - if ( - !(ride->var_1AD & 15) && - ride->mechanic_status != RIDE_MECHANIC_STATUS_FIXING && - ride->mechanic_status != RIDE_MECHANIC_STATUS_4 - ) { - RCT2_GLOBAL(0x0013CE952 + 0, uint16) = ride->name; - RCT2_GLOBAL(0x0013CE952 + 2, uint32) = ride->name_arguments; - news_item_add_to_queue(NEWS_ITEM_RIDE, 1929, rideIndex); - } - } - - ride_mechanic_status_update(rideIndex, ride->mechanic_status); -} - -/** - * - * rct2: 0x006B7348 - */ -void ride_prepare_breakdown(int rideIndex, int breakdownReason) -{ - int i; - rct_ride *ride; - rct_vehicle *vehicle; - - ride = GET_RIDE(rideIndex); - if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) - return; - - ride->lifecycle_flags &= ~RIDE_LIFECYCLE_DUE_INSPECTION; - ride->lifecycle_flags |= RIDE_LIFECYCLE_6; - - ride->breakdown_reason_pending = breakdownReason; - ride->mechanic_status = RIDE_MECHANIC_STATUS_UNDEFINED; - ride->var_1AC = 0; - ride->var_1AD = 0; - - switch (breakdownReason) { - case BREAKDOWN_SAFETY_CUT_OUT: - case BREAKDOWN_CONTROL_FAILURE: - // Inspect first station with an exit - for (i = 0; i < 4; i++) { - if (ride->exits[i] != 0xFFFF) { - ride->inspection_station = i; - break; - } - } - break; - case BREAKDOWN_RESTRAINTS_STUCK_CLOSED: - case BREAKDOWN_RESTRAINTS_STUCK_OPEN: - case BREAKDOWN_DOORS_STUCK_CLOSED: - case BREAKDOWN_DOORS_STUCK_OPEN: - // Choose a random train and car - ride->broken_vehicle = scenario_rand() % ride->num_vehicles; - ride->broken_car = scenario_rand() % ride->num_cars_per_train; - - // Set flag on broken car - vehicle = &(g_sprite_list[ride->vehicles[ride->broken_vehicle]].vehicle); - for (i = ride->broken_car; i >= 0; i--) - vehicle = &(g_sprite_list[ride->vehicles[vehicle->next_vehicle_on_train]].vehicle); - vehicle->var_48 |= 0x100; - break; - case BREAKDOWN_VEHICLE_MALFUNCTION: - // Choose a random train - ride->broken_vehicle = scenario_rand() % ride->num_vehicles; - ride->broken_car = 0; - - // Set flag on broken train, first car - vehicle = &(g_sprite_list[ride->vehicles[ride->broken_vehicle]].vehicle); - vehicle->var_48 |= 0x200; - break; - case BREAKDOWN_BRAKES_FAILURE: - // Original code generates a random number but does not use it - // Unsure if this was supposed to choose a random station (or random station with an exit) - for (i = 0; i < 4; i++) { - ride->inspection_station = i; - if (ride->exits[i] != 0xFFFF) - break; - } - break; - } -} - -/** - * - * rct2: 0x006AC622 - */ -void ride_breakdown_update(int rideIndex) -{ - int agePenalty, years, ax, breakdownReason; - rct_ride *ride = GET_RIDE(rideIndex); - - if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 255) - return; - if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TRACK_DESIGNER) - return; - - if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) - ride->var_19C++; - - if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 8191)) { - int ax = - ride->var_19C + - ride->var_19D + - ride->var_19E + - ride->var_1A0 + - ride->var_1A2 + - ride->var_1A3; - ride->var_199 = min(ax / 2, 100); - - ride->var_1A3 = ride->var_1A2; - ride->var_1A2 = ride->var_1A1; - ride->var_1A1 = ride->var_1A0; - ride->var_1A0 = ride->var_19F; - ride->var_19F = ride->var_19E; - ride->var_19E = ride->var_19D; - ride->var_19D = ride->var_19C; - ride->var_19C = 0; - ride->var_14D |= 32; - } - - if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) - return; - if (ride->status == RIDE_STATUS_CLOSED) - return; - - // Calculate breakdown probability? - ax = ride->var_198; - agePenalty; - years = date_get_year(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint16) - ride->build_date); - switch (years) { - case 0: - agePenalty = 0; - break; - case 1: - agePenalty = ax >> 3; - break; - case 2: - agePenalty = ax >> 2; - break; - case 3: - agePenalty = ax >> 1; - break; - case 4: - case 5: - case 6: - agePenalty = ax >> 0; - break; - default: - agePenalty = ax << 1; - break; - } - ax += agePenalty; - ride->var_196 = max(0, ride->var_196 - ax); - ride->var_14D |= 32; - - // Random probability of a breakdown - if (ride->var_196 == 0 || (int)(scenario_rand() & 0x2FFFFF) <= 25856 - ride->var_196) { - breakdownReason = ride_get_new_breakdown_problem(ride); - if (breakdownReason != -1) - ride_prepare_breakdown(rideIndex, breakdownReason); - } -} - -/** - * - * rct2: 0x006AC7C2 - */ -void ride_inspection_update(rct_ride *ride) -{ - int i; - - if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 2047) - return; - if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TRACK_DESIGNER) - return; - - ride->last_inspection++; - if (ride->last_inspection == 0) - ride->last_inspection--; - - int inspectionIntervalMinutes = RideInspectionInterval[ride->inspection_interval]; - if (inspectionIntervalMinutes == 0) - return; - - if (RCT2_ADDRESS(0x0097C740, uint32)[ride->type] == 0) - return; - - if (inspectionIntervalMinutes > ride->last_inspection) - return; - - if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_DUE_INSPECTION | RIDE_LIFECYCLE_CRASHED)) - return; - - // Inspect the first station that has an exit - ride->lifecycle_flags |= RIDE_LIFECYCLE_DUE_INSPECTION; - ride->inspection_station = 0; - for (i = 0; i < 4; i++) { - if (ride->exits[i] != 0xFFFF) { - ride->inspection_station = i; - break; - } - } -} - -/** - * - * rct2: 0x006AC489 - */ -void ride_chairlift_update(rct_ride *ride) -{ - int x, y, z, ax, bx, cx; - - if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) - return; - if (!(ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED))) - return; - if (ride->breakdown_reason_pending == 0) - return; - - ax = ride->var_0D0 * 2048; - bx = ride->var_148; - cx = bx + ax; - ride->var_148 = cx; - if (bx >> 14 == cx >> 14) - return; - - x = (ride->var_13A & 0xFF) * 32; - y = (ride->var_13A >> 8) * 32; - z = ride->var_13E * 8; - map_invalidate_tile(x, y, z, z + (4 * 8)); - - x = (ride->var_13C & 0xFF) * 32; - y = (ride->var_13C >> 8) * 32; - z = ride->var_13F * 8; - map_invalidate_tile(x, y, z, z + (4 * 8)); -} - -/** - * - * rct2: 0x006AC545 - */ -void ride_spiral_slide_update(rct_ride *ride) -{ - int i, x, y, z; - rct_map_element *mapElement; - rct_peep *peep; - - if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 3) - return; - if (ride->var_15D == 0) - return; - - ride->var_176++; - if (ride->var_176 >= 48) { - ride->var_15D--; - - peep = &(g_sprite_list[ride->maze_tiles].peep); - peep->var_32++; - } - - // Invalidate something related to station start - for (i = 0; i < 4; i++) { - if (ride->station_starts[i] == 0xFFFF) - continue; - - x = ride->station_starts[i] & 0xFF; - y = ride->station_starts[i] >> 8; - z = ride->station_heights[i]; - - mapElement = ride_get_station_start_track_element(ride, i); - int rotation = ((mapElement->type & 3) << 2) | RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8); - x += RCT2_GLOBAL(0x0098DDB8 + (rotation * 4), uint16); - y += RCT2_GLOBAL(0x0098DDBA + (rotation * 4), uint16); - - map_invalidate_tile(x, y, mapElement->base_height * 8, mapElement->clearance_height * 8); - } -} - -/** - * - * rct2: 0x006ABE85 - */ -void ride_music_update(int rideIndex) -{ - int x, y, z; - rct_vehicle *vehicle; - rct_ride *ride = GET_RIDE(rideIndex); - - if (!(RCT2_GLOBAL(0x0097D4F2 + (ride->type * 8), uint16) & 6)) - return; - - if (ride->status != RIDE_STATUS_OPEN || !(ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC)) { - ride->music_tune_id = 255; - return; - } - - if (ride->type == RIDE_TYPE_CIRCUS_SHOW) { - vehicle = &(g_sprite_list[ride->vehicles[0]].vehicle); - if (vehicle->status != VEHICLE_STATUS_DOING_CIRCUS_SHOW) { - ride->music_tune_id = 255; - return; - } - } - - // Oscillate parameters for a power cut effect when breaking down - if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN)) { - if (ride->breakdown_reason_pending == BREAKDOWN_CONTROL_FAILURE) { - if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 7)) - if (ride->var_1AC != 255) - ride->var_1AC++; - } else { - if ( - (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) || - ride->breakdown_reason_pending == BREAKDOWN_BRAKES_FAILURE || - ride->breakdown_reason_pending == BREAKDOWN_CONTROL_FAILURE - ) { - if (ride->var_1AC != 255) - ride->var_1AC++; - } - - if (ride->var_1AC == 255) { - ride->music_tune_id = 255; - return; - } - } - } - - // Select random tune from available tunes for a music style (of course only merry-go-rounds have more than one tune) - if (ride->music_tune_id == 255) { - uint8 *musicStyleTunes = RCT2_ADDRESS(0x009AEF28, uint8*)[ride->music]; - uint8 numTunes = *musicStyleTunes++; - ride->music_tune_id = musicStyleTunes[scenario_rand() % numTunes]; - ride->music_position = 0; - return; - } - - x = (ride->station_starts[0] & 0xFF) * 32 + 16; - y = (ride->station_starts[0] >> 8) * 32 + 16; - z = ride->station_heights[0] * 8; - - int sampleRate = 22050; - - // Alter sample rate for a power cut effect - if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN)) { - sampleRate = ride->var_1AC * 70; - if (ride->breakdown_reason_pending != BREAKDOWN_CONTROL_FAILURE) - sampleRate *= -1; - sampleRate += 22050; - } - - ride->music_position = sub_6BC3AC(x, y, z, rideIndex, sampleRate, ride->music_position, &ride->music_tune_id); -} - -/** - * - * rct2: 0x006ABE73 - */ -void ride_update(int rideIndex) -{ - int i; - rct_ride *ride = GET_RIDE(rideIndex); - - if (ride->var_1CA != 0) - ride->var_1CA--; - - ride_music_update(rideIndex); - - // Update stations - if (ride->type != RIDE_TYPE_MAZE) - for (i = 0; i < 4; i++) - ride_update_station(ride, i); - - // Update financial statistics - ride->var_122++; - if (ride->var_122 >= 960) { - ride->var_122 = 0; - - ride->var_136 = ride->var_134; - ride->var_134 = ride->running_cost; - ride->running_cost = ride->age; - ride->age = ride->var_12E; - ride->var_12E = ride->var_12C; - ride->var_12C = ride->var_12A; - ride->var_12A = ride->var_128; - ride->var_128 = ride->var_126; - ride->var_126 = ride->var_124; - ride->var_124 = ride->var_120; - ride->var_14D |= 1; - - ride->income_per_hour = ride_calculate_income_per_hour(ride); - ride->var_14D |= 2; - - if (ride->upkeep_cost != (money16)0xFFFF) - ride->upkeep_cost = (money16)ride->income_per_hour - (ride->upkeep_cost * 16); - } - - // Ride specific updates - if (ride->type == RIDE_TYPE_CHAIRLIFT) - ride_chairlift_update(ride); - else if (ride->type == RIDE_TYPE_SPIRAL_SLIDE) - ride_spiral_slide_update(ride); - - ride_breakdown_update(rideIndex); - - // Various things include news messages - if (ride->lifecycle_flags & (RIDE_LIFECYCLE_6 | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_DUE_INSPECTION)) - if (((RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) >> 1) & 255) == rideIndex) - ride_breakdown_status_update(rideIndex); - - ride_inspection_update(ride); -} - -/** - * - * rct2: 0x006ABE4C - */ -void ride_update_all() -{ - rct_s6_info *s6Info = (rct_s6_info*)0x0141F570; - rct_ride *ride; - int i; - - // RCT2_CALLPROC_EBPSAFE(0x006ABE4C); return; - - // Remove all rides if certain flags are set (possible scenario editor?) - int *esi = (int*)0x9DCE9E; - if (esi[0x1BCA] & 2) { - if (s6Info->var_000 <= 2) - FOR_ALL_RIDES(i, ride) - ride->type = RIDE_TYPE_NULL; - return; - } - - // Something related to windows - RCT2_CALLPROC_EBPSAFE(0x006BC348); - - // Update rides - FOR_ALL_RIDES(i, ride) - ride_update(i); - - ride_play_music(); -} - -/** - * rct2: 0x006B7C59 - * @return 1 if the coordinate is reachable or has no entrance, 0 otherwise - */ -int ride_entrance_exit_is_reachable(uint16 coordinate, rct_ride* ride, int index) { - int x = ((coordinate >> 8) & 0xFF) << 5, // cx - y = (coordinate & 0xFF) << 5; // ax - uint8 station_height = ride->station_heights[index]; - int tile_idx = ((x << 8) | y) >> 5; - rct_map_element* tile = RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, rct_map_element*)[tile_idx]; - - while(1) { - uint8 element_type = tile->type & MAP_ELEMENT_TYPE_MASK; - if (element_type == MAP_ELEMENT_TYPE_ENTRANCE && station_height == tile->base_height) { - break; - } else if (tile->flags & MAP_ELEMENT_FLAG_LAST_TILE) { - return 1; - } - tile++; - } - - uint8 face_direction = tile->type & 3; - y -= RCT2_ADDRESS(0x00993CCC, sint16)[face_direction * 2]; - x -= RCT2_ADDRESS(0x00993CCE, sint16)[face_direction * 2]; - tile_idx = ((x << 8) | y) >> 5; - - return map_coord_is_connected(tile_idx, station_height, face_direction); -} - - -void ride_entrance_exit_connected(rct_ride* ride, int ride_idx) -{ - for (int i = 0; i < 4; ++i) { - uint16 station_start = ride->station_starts[i], - entrance = ride->entrances[i], - exit = ride->exits[i]; - - if (station_start == -1 ) - continue; - if (entrance != -1 && !ride_entrance_exit_is_reachable(entrance, ride, i)) { - // name of ride is parameter of the format string - RCT2_GLOBAL(0x013CE952, uint16) = ride->name; - RCT2_GLOBAL(0x013CE954, uint32) = ride->name_arguments; - news_item_add_to_queue(1, STR_ENTRANCE_NOT_CONNECTED, ride_idx); - ride->connected_message_throttle = 3; - } - - if (exit != -1 && !ride_entrance_exit_is_reachable(exit, ride, i)) { - // name of ride is parameter of the format string - RCT2_GLOBAL(0x013CE952, uint16) = ride->name; - RCT2_GLOBAL(0x013CE954, uint32) = ride->name_arguments; - news_item_add_to_queue(1, STR_EXIT_NOT_CONNECTED, ride_idx); - ride->connected_message_throttle = 3; - } - - } -} - - -void ride_shop_connected(rct_ride* ride, int ride_idx) -{ - rct_ride* ride_back = ride; - uint16 coordinate = ride->station_starts[0]; - if (coordinate == 0xFFFF) - return; - - int x = ((coordinate >> 8) & 0xFF) << 5, // cx - y = (coordinate & 0xFF) << 5; // ax - - rct_map_element* tile = RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, rct_map_element*)[coordinate]; - - for (; ; tile++){ - uint8 element_type = tile->type & MAP_ELEMENT_TYPE_MASK; - if(element_type == MAP_ELEMENT_TYPE_TRACK && tile->properties.track.ride_index == ride_idx) - break; - if(tile->flags & MAP_ELEMENT_FLAG_LAST_TILE) - return; - } - - uint16 entrance_directions = 0; - uint8 track_type = tile->properties.track.type; - ride = &g_ride_list[tile->properties.track.ride_index]; - if (RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + ride->type * 8, uint32) & 0x80000) { - entrance_directions = RCT2_ADDRESS(0x0099CA64, uint8)[track_type * 16]; - } else { - entrance_directions = RCT2_ADDRESS(0x0099BA64, uint8)[track_type * 16]; - } - - - uint8 tile_direction = tile->type & MAP_ELEMENT_DIRECTION_MASK; - entrance_directions <<= tile_direction; - entrance_directions = ((entrance_directions >> 12) | entrance_directions) & 0xF; - - // now each bit in entrance_directions stands for an entrance direction to check - if (entrance_directions == 0) - return; - - for (int count = 0; entrance_directions != 0; ++count) { - if (!(entrance_directions & 1)) { - entrance_directions >>= 1; - continue; - } - entrance_directions >>= 1; - - uint8 face_direction = count ^ 2; // flip direction north<->south, east<->west - int y2 = y - RCT2_ADDRESS(0x00993CCC, sint16)[face_direction * 2]; - int x2 = x - RCT2_ADDRESS(0x00993CCE, sint16)[face_direction * 2]; - int tile_idx = ((x2 << 8) | y2) >> 5; - - if (map_coord_is_connected(tile_idx, tile->base_height, face_direction)) - return; - } - - // name of ride is parameter of the format string - RCT2_GLOBAL(0x013CE952, uint16) = ride->name; - RCT2_GLOBAL(0x013CE954, uint32) = ride->name_arguments; - news_item_add_to_queue(1, STR_ENTRANCE_NOT_CONNECTED, ride_idx); - - ride->connected_message_throttle = 3; -} - - - -/** - * rct2: 0x006B7A5E - **/ -void ride_check_all_reachable() -{ - rct_ride *ride; - int i; - - FOR_ALL_RIDES(i, ride) { - if (ride->connected_message_throttle != 0) - ride->connected_message_throttle--; - if (ride->status != RIDE_STATUS_OPEN || ride->connected_message_throttle != 0) - continue; - - if (RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + ride->type * 8, uint32) & 0x20000) - ride_shop_connected(ride, i); - else - ride_entrance_exit_connected(ride, i); - } -} - /** * * rct2: 0x006CAF80 @@ -1452,6 +331,124 @@ rct_map_element *ride_find_track_gap(rct_map_element *startTrackElement, int *ou return (rct_map_element*)esi; } +/** + * + * rct2: 0x006AF561 + */ +void ride_get_status(int rideIndex, int *formatSecondary, int *argument) +{ + rct_ride *ride = &g_ride_list[rideIndex]; + + if (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED) { + *formatSecondary = STR_CRASHED; + return; + } + if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) { + *formatSecondary = STR_BROKEN_DOWN; + return; + } + if (ride->status == RIDE_STATUS_CLOSED) { + *formatSecondary = STR_CLOSED; + return; + } + if (ride->status == RIDE_STATUS_TESTING) { + *formatSecondary = STR_TEST_RUN; + return; + } + rct_peep *peep = GET_PEEP(ride->race_winner); + if (ride->mode == RIDE_MODE_RACE && !(ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) && ride->race_winner != 0xFFFF && peep->sprite_identifier == SPRITE_IDENTIFIER_PEEP) { + if (peep->name_string_idx == STR_GUEST) { + *argument = peep->id; + *formatSecondary = STR_RACE_WON_BY_GUEST; + } else { + *argument = peep->name_string_idx; + *formatSecondary = STR_RACE_WON_BY; + } + } else { + if (!(RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + ride->type * 8, uint32) & 0x20000)) { + *argument = ride->num_riders; + *formatSecondary = STR_PERSON_ON_RIDE; + if(*argument != 1) + *formatSecondary = STR_PEOPLE_ON_RIDE; + + } else { + *formatSecondary = STR_OPEN; + } + } +} + +int ride_get_total_length(rct_ride *ride) +{ + int i, totalLength = 0; + for (i = 0; i < ride->num_stations; i++) + totalLength += ride->length[i]; + return totalLength; +} + +int ride_can_have_multiple_circuits(rct_ride *ride) +{ + if (!(RCT2_GLOBAL(0x0097D4F2 + (ride->type * 8), uint16) & 0x200)) + return 0; + + // Only allow circuit or launch modes + if ( + ride->mode != RIDE_MODE_CONTINUOUS_CIRCUIT && + ride->mode != RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE && + ride->mode != RIDE_MODE_POWERED_LAUNCH + ) { + return 0; + } + + // Must have no more than one vehicle and one station + if (ride->num_vehicles > 1 || ride->num_stations > 1) + return 0; + + return 1; +} + +#pragma region Initialisation functions + +/** + * + * rct2: 0x006ACA89 + */ +void ride_init_all() +{ + int i; + rct_ride *ride; + rct_ride_measurement *ride_measurement; + + for (i = 0; i < MAX_RIDES; i++) { + ride = &g_ride_list[i]; + ride->type = RIDE_TYPE_NULL; + } + + RCT2_GLOBAL(0x0138B590, sint8) = 0; + RCT2_GLOBAL(0x0138B591, sint8) = 0; + + for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) { + ride_measurement = GET_RIDE_MEASUREMENT(i); + ride_measurement->ride_index = 255; + } +} + +/** +* +* rct2: 0x006B7A38 +*/ +void reset_all_ride_build_dates() +{ + int i; + rct_ride *ride; + + FOR_ALL_RIDES(i, ride) + ride->build_date -= RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint16); +} + +#pragma endregion + +#pragma region Construction + int ride_create_ride(ride_list_item listItem) { int eax, ebx, ecx, edx, esi, edi, ebp; @@ -1502,50 +499,639 @@ int ride_try_construct(rct_map_element *trackMapElement) return 1; } +#pragma endregion + +#pragma region Update functions + /** - * - * rct2: 0x006AF561 + * + * rct2: 0x006ABE4C */ -void ride_get_status(int rideIndex, int *formatSecondary, int *argument) +void ride_update_all() { - rct_ride *ride = &g_ride_list[rideIndex]; + rct_s6_info *s6Info = (rct_s6_info*)0x0141F570; + rct_ride *ride; + int i; - if (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED) { - *formatSecondary = STR_CRASHED; + // RCT2_CALLPROC_EBPSAFE(0x006ABE4C); return; + + // Remove all rides if certain flags are set (possible scenario editor?) + int *esi = (int*)0x9DCE9E; + if (esi[0x1BCA] & 2) { + if (s6Info->var_000 <= 2) + FOR_ALL_RIDES(i, ride) + ride->type = RIDE_TYPE_NULL; return; } + + // Something related to windows + RCT2_CALLPROC_EBPSAFE(0x006BC348); + + // Update rides + FOR_ALL_RIDES(i, ride) + ride_update(i); + + ride_play_music(); +} + +/** + * + * rct2: 0x006ABE73 + */ +static void ride_update(int rideIndex) +{ + int i; + rct_ride *ride = GET_RIDE(rideIndex); + + if (ride->var_1CA != 0) + ride->var_1CA--; + + ride_music_update(rideIndex); + + // Update stations + if (ride->type != RIDE_TYPE_MAZE) + for (i = 0; i < 4; i++) + ride_update_station(ride, i); + + // Update financial statistics + ride->var_122++; + if (ride->var_122 >= 960) { + ride->var_122 = 0; + + ride->var_136 = ride->var_134; + ride->var_134 = ride->running_cost; + ride->running_cost = ride->age; + ride->age = ride->var_12E; + ride->var_12E = ride->var_12C; + ride->var_12C = ride->var_12A; + ride->var_12A = ride->var_128; + ride->var_128 = ride->var_126; + ride->var_126 = ride->var_124; + ride->var_124 = ride->var_120; + ride->var_14D |= 1; + + ride->income_per_hour = ride_calculate_income_per_hour(ride); + ride->var_14D |= 2; + + if (ride->upkeep_cost != (money16)0xFFFF) + ride->upkeep_cost = (money16)ride->income_per_hour - (ride->upkeep_cost * 16); + } + + // Ride specific updates + if (ride->type == RIDE_TYPE_CHAIRLIFT) + ride_chairlift_update(ride); + else if (ride->type == RIDE_TYPE_SPIRAL_SLIDE) + ride_spiral_slide_update(ride); + + ride_breakdown_update(rideIndex); + + // Various things include news messages + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_DUE_INSPECTION)) + if (((RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) >> 1) & 255) == rideIndex) + ride_breakdown_status_update(rideIndex); + + ride_inspection_update(ride); +} + +/** + * + * rct2: 0x006AC489 + */ +static void ride_chairlift_update(rct_ride *ride) +{ + int x, y, z, ax, bx, cx; + + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) + return; + if (!(ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED))) + return; + if (ride->breakdown_reason_pending == 0) + return; + + ax = ride->var_0D0 * 2048; + bx = ride->var_148; + cx = bx + ax; + ride->var_148 = cx; + if (bx >> 14 == cx >> 14) + return; + + x = (ride->var_13A & 0xFF) * 32; + y = (ride->var_13A >> 8) * 32; + z = ride->var_13E * 8; + map_invalidate_tile(x, y, z, z + (4 * 8)); + + x = (ride->var_13C & 0xFF) * 32; + y = (ride->var_13C >> 8) * 32; + z = ride->var_13F * 8; + map_invalidate_tile(x, y, z, z + (4 * 8)); +} + +/** + * + * rct2: 0x006AC545 + */ +static void ride_spiral_slide_update(rct_ride *ride) +{ + int i, x, y, z; + rct_map_element *mapElement; + rct_peep *peep; + + if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 3) + return; + if (ride->var_15D == 0) + return; + + ride->var_176++; + if (ride->var_176 >= 48) { + ride->var_15D--; + + peep = &(g_sprite_list[ride->maze_tiles].peep); + peep->var_32++; + } + + // Invalidate something related to station start + for (i = 0; i < 4; i++) { + if (ride->station_starts[i] == 0xFFFF) + continue; + + x = ride->station_starts[i] & 0xFF; + y = ride->station_starts[i] >> 8; + z = ride->station_heights[i]; + + mapElement = ride_get_station_start_track_element(ride, i); + int rotation = ((mapElement->type & 3) << 2) | RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8); + x += RCT2_GLOBAL(0x0098DDB8 + (rotation * 4), uint16); + y += RCT2_GLOBAL(0x0098DDBA + (rotation * 4), uint16); + + map_invalidate_tile(x, y, mapElement->base_height * 8, mapElement->clearance_height * 8); + } +} + +#pragma endregion + +#pragma region Breakdown and inspection functions + +static uint8 _breakdownProblemProbabilities[] = { + 25, // BREAKDOWN_SAFETY_CUT_OUT + 12, // BREAKDOWN_RESTRAINTS_STUCK_CLOSED + 10, // BREAKDOWN_RESTRAINTS_STUCK_OPEN + 13, // BREAKDOWN_DOORS_STUCK_CLOSED + 10, // BREAKDOWN_DOORS_STUCK_OPEN + 6, // BREAKDOWN_VEHICLE_MALFUNCTION + 0, // BREAKDOWN_BRAKES_FAILURE + 3 // BREAKDOWN_CONTROL_FAILURE +}; + +/** + * + * rct2: 0x006AC7C2 + */ +static void ride_inspection_update(rct_ride *ride) +{ + int i; + + if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 2047) + return; + if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TRACK_DESIGNER) + return; + + ride->last_inspection++; + if (ride->last_inspection == 0) + ride->last_inspection--; + + int inspectionIntervalMinutes = RideInspectionInterval[ride->inspection_interval]; + if (inspectionIntervalMinutes == 0) + return; + + if (RCT2_ADDRESS(0x0097C740, uint32)[ride->type] == 0) + return; + + if (inspectionIntervalMinutes > ride->last_inspection) + return; + + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_DUE_INSPECTION | RIDE_LIFECYCLE_CRASHED)) + return; + + // Inspect the first station that has an exit + ride->lifecycle_flags |= RIDE_LIFECYCLE_DUE_INSPECTION; + ride->inspection_station = 0; + for (i = 0; i < 4; i++) { + if (ride->exits[i] != 0xFFFF) { + ride->inspection_station = i; + break; + } + } +} + +/** + * + * rct2: 0x006AC622 + */ +static void ride_breakdown_update(int rideIndex) +{ + int agePenalty, years, ax, breakdownReason; + rct_ride *ride = GET_RIDE(rideIndex); + + if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 255) + return; + if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TRACK_DESIGNER) + return; + + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) + ride->var_19C++; + + if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 8191)) { + int ax = + ride->var_19C + + ride->var_19D + + ride->var_19E + + ride->var_1A0 + + ride->var_1A2 + + ride->var_1A3; + ride->var_199 = min(ax / 2, 100); + + ride->var_1A3 = ride->var_1A2; + ride->var_1A2 = ride->var_1A1; + ride->var_1A1 = ride->var_1A0; + ride->var_1A0 = ride->var_19F; + ride->var_19F = ride->var_19E; + ride->var_19E = ride->var_19D; + ride->var_19D = ride->var_19C; + ride->var_19C = 0; + ride->var_14D |= 32; + } + + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) + return; + if (ride->status == RIDE_STATUS_CLOSED) + return; + + // Calculate breakdown probability? + ax = ride->var_198; + agePenalty; + years = date_get_year(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint16) - ride->build_date); + switch (years) { + case 0: + agePenalty = 0; + break; + case 1: + agePenalty = ax >> 3; + break; + case 2: + agePenalty = ax >> 2; + break; + case 3: + agePenalty = ax >> 1; + break; + case 4: + case 5: + case 6: + agePenalty = ax >> 0; + break; + default: + agePenalty = ax << 1; + break; + } + ax += agePenalty; + ride->var_196 = max(0, ride->var_196 - ax); + ride->var_14D |= 32; + + // Random probability of a breakdown + if (ride->var_196 == 0 || (int)(scenario_rand() & 0x2FFFFF) <= 25856 - ride->var_196) { + breakdownReason = ride_get_new_breakdown_problem(ride); + if (breakdownReason != -1) + ride_prepare_breakdown(rideIndex, breakdownReason); + } +} + +/** + * + * rct2: 0x006B7294 + */ +static int ride_get_new_breakdown_problem(rct_ride *ride) +{ + int availableBreakdownProblems, monthsOld, totalProbability, randomProbability, problemBits, breakdownProblem; + rct_ride_type *entry; + + // Brake failure is more likely when its raining + _breakdownProblemProbabilities[BREAKDOWN_BRAKES_FAILURE] = + RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_RAIN_LEVEL, uint8) == 0 ? 3 : 20; + + entry = ride_get_entry(ride); + if (entry->var_008 & 0x4000) + return -1; + + availableBreakdownProblems = RideAvailableBreakdowns[ride->type]; + + // Calculate the total probability range for all possible breakdown problems + totalProbability = 0; + problemBits = availableBreakdownProblems; + while (problemBits != 0) { + breakdownProblem = bitscanforward(problemBits); + problemBits &= ~(1 << breakdownProblem); + totalProbability += _breakdownProblemProbabilities[breakdownProblem]; + } + if (totalProbability == 0) + return -1; + + // Choose a random number within this range + randomProbability = scenario_rand() % totalProbability; + + // Find which problem range the random number lies + problemBits = availableBreakdownProblems; + do { + breakdownProblem = bitscanforward(problemBits); + problemBits &= ~(1 << breakdownProblem); + randomProbability -= _breakdownProblemProbabilities[breakdownProblem]; + } while (randomProbability >= 0); + + if (breakdownProblem != BREAKDOWN_BRAKES_FAILURE) + return breakdownProblem; + + // Breaks failure can not happen if block breaks are used (so long as there is more than one vehicle) + // However if this is the case, break failure should be taken out the equation, otherwise block brake + // rides have a lower probability to break down due to a random implementation reason. + if (ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED || ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED) + if (ride->num_vehicles != 1) + return -1; + + // Again the probability is lower, this time if young or two other unknown reasons... + monthsOld = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint8) - ride->build_date; + if (monthsOld < 16 || ride->var_196 > 12800 || ride->lifecycle_flags & RIDE_LIFECYCLE_19) + return -1; + + return BREAKDOWN_BRAKES_FAILURE; +} + +/** + * + * rct2: 0x006B7348 + */ +static void ride_prepare_breakdown(int rideIndex, int breakdownReason) +{ + int i; + rct_ride *ride; + rct_vehicle *vehicle; + + ride = GET_RIDE(rideIndex); + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) + return; + + ride->lifecycle_flags &= ~RIDE_LIFECYCLE_DUE_INSPECTION; + ride->lifecycle_flags |= RIDE_LIFECYCLE_BREAKDOWN_PENDING; + + ride->breakdown_reason_pending = breakdownReason; + ride->mechanic_status = RIDE_MECHANIC_STATUS_UNDEFINED; + ride->var_1AC = 0; + ride->var_1AD = 0; + + switch (breakdownReason) { + case BREAKDOWN_SAFETY_CUT_OUT: + case BREAKDOWN_CONTROL_FAILURE: + // Inspect first station with an exit + for (i = 0; i < 4; i++) { + if (ride->exits[i] != 0xFFFF) { + ride->inspection_station = i; + break; + } + } + break; + case BREAKDOWN_RESTRAINTS_STUCK_CLOSED: + case BREAKDOWN_RESTRAINTS_STUCK_OPEN: + case BREAKDOWN_DOORS_STUCK_CLOSED: + case BREAKDOWN_DOORS_STUCK_OPEN: + // Choose a random train and car + ride->broken_vehicle = scenario_rand() % ride->num_vehicles; + ride->broken_car = scenario_rand() % ride->num_cars_per_train; + + // Set flag on broken car + vehicle = &(g_sprite_list[ride->vehicles[ride->broken_vehicle]].vehicle); + for (i = ride->broken_car; i > 0; i--) + vehicle = &(g_sprite_list[vehicle->next_vehicle_on_train].vehicle); + vehicle->var_48 |= 0x100; + break; + case BREAKDOWN_VEHICLE_MALFUNCTION: + // Choose a random train + ride->broken_vehicle = scenario_rand() % ride->num_vehicles; + ride->broken_car = 0; + + // Set flag on broken train, first car + vehicle = &(g_sprite_list[ride->vehicles[ride->broken_vehicle]].vehicle); + vehicle->var_48 |= 0x200; + break; + case BREAKDOWN_BRAKES_FAILURE: + // Original code generates a random number but does not use it + // Unsure if this was supposed to choose a random station (or random station with an exit) + for (i = 0; i < 4; i++) { + ride->inspection_station = i; + if (ride->exits[i] != 0xFFFF) + break; + } + break; + } +} + +/** + * + * rct2: 0x006B74FA + */ +void ride_breakdown_add_news_item(int rideIndex) +{ + rct_ride *ride = GET_RIDE(rideIndex); + + RCT2_GLOBAL(0x0013CE952 + 0, uint16) = ride->name; + RCT2_GLOBAL(0x0013CE952 + 2, uint32) = ride->name_arguments; + news_item_add_to_queue(NEWS_ITEM_RIDE, 1927, rideIndex); +} + +/** + * + * rct2: 0x006B75C8 + */ +static void ride_breakdown_status_update(int rideIndex) +{ + rct_ride *ride = GET_RIDE(rideIndex); + + // Warn player if ride hasnt been fixed for ages if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) { - *formatSecondary = STR_BROKEN_DOWN; - return; - } - if (ride->status == RIDE_STATUS_CLOSED) { - *formatSecondary = STR_CLOSED; - return; - } - if (ride->status == RIDE_STATUS_TESTING) { - *formatSecondary = STR_TEST_RUN; - return; - } - rct_peep *peep = GET_PEEP(ride->race_winner); - if (ride->mode == RIDE_MODE_RACE && !(ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) && ride->race_winner != 0xFFFF && peep->sprite_identifier == SPRITE_IDENTIFIER_PEEP) { - if (peep->name_string_idx == STR_GUEST) { - *argument = peep->id; - *formatSecondary = STR_RACE_WON_BY_GUEST; - } else { - *argument = peep->name_string_idx; - *formatSecondary = STR_RACE_WON_BY; - } - } else { - if (!(RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + ride->type * 8, uint32) & 0x20000)) { - *argument = ride->num_riders; - *formatSecondary = STR_PERSON_ON_RIDE; - if(*argument != 1) - *formatSecondary = STR_PEOPLE_ON_RIDE; + ride->var_1AD++; + if (ride->var_1AD == 0) + ride->var_1AD -= 16; - } else { - *formatSecondary = STR_OPEN; + if ( + !(ride->var_1AD & 15) && + ride->mechanic_status != RIDE_MECHANIC_STATUS_FIXING && + ride->mechanic_status != RIDE_MECHANIC_STATUS_4 + ) { + RCT2_GLOBAL(0x0013CE952 + 0, uint16) = ride->name; + RCT2_GLOBAL(0x0013CE952 + 2, uint32) = ride->name_arguments; + news_item_add_to_queue(NEWS_ITEM_RIDE, 1929, rideIndex); } } + + ride_mechanic_status_update(rideIndex, ride->mechanic_status); +} + +/** + * + * rct2: 0x006B762F + */ +static void ride_mechanic_status_update(int rideIndex, int mechanicStatus) +{ + int breakdownReason; + rct_ride *ride; + rct_peep *mechanic; + + ride = GET_RIDE(rideIndex); + switch (mechanicStatus) { + case RIDE_MECHANIC_STATUS_UNDEFINED: + breakdownReason = ride->breakdown_reason_pending; + if ( + breakdownReason == BREAKDOWN_SAFETY_CUT_OUT || + breakdownReason == BREAKDOWN_BRAKES_FAILURE || + breakdownReason == BREAKDOWN_CONTROL_FAILURE + ) { + ride->lifecycle_flags |= RIDE_LIFECYCLE_BROKEN_DOWN; + ride->var_14D |= 0x2C; + ride->mechanic_status = RIDE_MECHANIC_STATUS_CALLING; + ride->breakdown_reason = breakdownReason; + ride_breakdown_add_news_item(rideIndex); + } + break; + case RIDE_MECHANIC_STATUS_CALLING: + if (RideAvailableBreakdowns[ride->type] == 0) { + ride->lifecycle_flags &= ~(RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_DUE_INSPECTION); + break; + } + + ride_call_mechanic(rideIndex); + break; + case RIDE_MECHANIC_STATUS_HEADING: + mechanic = &(g_sprite_list[ride->mechanic].peep); + if ( + !peep_is_mechanic(mechanic) || + (mechanic->state != PEEP_STATE_HEADING_TO_INSPECTION && mechanic->state != PEEP_STATE_ANSWERING) || + mechanic->current_ride != rideIndex + ) { + ride->mechanic_status = RIDE_MECHANIC_STATUS_CALLING; + ride->var_14D |= 0x20; + ride_mechanic_status_update(rideIndex, RIDE_MECHANIC_STATUS_CALLING); + } + break; + case RIDE_MECHANIC_STATUS_FIXING: + mechanic = &(g_sprite_list[ride->mechanic].peep); + if ( + !peep_is_mechanic(mechanic) || + ( + mechanic->state != PEEP_STATE_HEADING_TO_INSPECTION && + mechanic->state != PEEP_STATE_FIXING && + mechanic->state != PEEP_STATE_INSPECTING && + mechanic->state != PEEP_STATE_ANSWERING + ) + ) { + ride->mechanic_status = RIDE_MECHANIC_STATUS_CALLING; + ride->var_14D |= 0x20; + ride_mechanic_status_update(rideIndex, RIDE_MECHANIC_STATUS_CALLING); + } + break; + } +} + +/** + * + * rct2: 0x006B76AB + */ +static void ride_call_mechanic(int rideIndex) +{ + int x, y, z, stationIndex, direction, inspecting; + uint16 xy; + rct_ride *ride; + rct_map_element *mapElement; + rct_peep *mechanic; + + ride = GET_RIDE(rideIndex); + + inspecting = (ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN)) == 0; + + // Get either exit position or entrance position if there is no exit + stationIndex = ride->inspection_station; + xy = ride->exits[stationIndex]; + if (xy == 0xFFFF) { + xy = ride->entrances[stationIndex]; + if (xy == 0xFFFF) + return; + } + + // Get station start track element and position + x = (xy & 0xFF) * 32; + y = (xy >> 8) * 32; + z = ride->station_heights[stationIndex] * 8; + mapElement = ride_get_station_start_track_element(ride, stationIndex); + if (mapElement == NULL) + return; + + direction = mapElement->type & 3; + x -= RCT2_ADDRESS(0x00993CCC, sint16)[direction * 2]; + y -= RCT2_ADDRESS(0x00993CCE, sint16)[direction * 2]; + x += 16; + y += 16; + + // Find closest mechanic + mechanic = find_closest_mechanic(x, y, inspecting); + if (mechanic == NULL) + return; + + RCT2_CALLPROC_X(0x0069A409, 0, 0, 0, 0, (int)mechanic, 0, 0); + mechanic->state = inspecting ? PEEP_STATE_HEADING_TO_INSPECTION : PEEP_STATE_ANSWERING; + RCT2_CALLPROC_X(0x0069A42F, 0, 0, 0, 0, (int)mechanic, 0, 0); + mechanic->var_2C = 0; + ride->mechanic_status = RIDE_MECHANIC_STATUS_HEADING; + ride->var_14D |= 0x20; + ride->mechanic = mechanic->sprite_index; + mechanic->current_ride = rideIndex; + mechanic->current_ride_station = ride->inspection_station; +} + +/** + * + * rct2: 0x006B774B (forInspection = 0) + * rct2: 0x006B78C3 (forInspection = 1) + */ +rct_peep *find_closest_mechanic(int x, int y, int forInspection) +{ + unsigned int closestDistance, distance; + uint16 spriteIndex; + rct_peep *peep, *closestMechanic; + + closestDistance = -1; + FOR_ALL_STAFF(spriteIndex, peep) { + if (forInspection) { + if ((peep->state != PEEP_STATE_HEADING_TO_INSPECTION || peep->var_2C >= 4) && peep->state != PEEP_STATE_PATROLLING) + continue; + + if (!(peep->staff_orders & 2)) + continue; + } else { + if (peep->state != PEEP_STATE_PATROLLING && !(peep->staff_orders & 1)) + continue; + } + + if (map_is_location_in_park(x, y)) + if (!mechanic_is_location_in_patrol(peep, x & 0xFFE0, y & 0xFFE0)) + continue; + + if (peep->x == (sint16)0x8000) + continue; + + // Should probably be euclidean or manhattan distance, this seems a bit naive + distance = max(abs(peep->x - x), abs(peep->y - y)); + if (distance < closestDistance) { + closestDistance = distance; + closestMechanic = peep; + } + } + + return closestDistance == -1 ? NULL : closestMechanic; } rct_peep *ride_get_assigned_mechanic(rct_ride *ride) @@ -1567,118 +1153,97 @@ rct_peep *ride_get_assigned_mechanic(rct_ride *ride) return NULL; } -int ride_get_total_length(rct_ride *ride) -{ - int i, totalLength = 0; - for (i = 0; i < ride->num_stations; i++) - totalLength += ride->length[i]; - return totalLength; -} +#pragma endregion -int ride_can_have_multiple_circuits(rct_ride *ride) -{ - if (!(RCT2_GLOBAL(0x0097D4F2 + (ride->type * 8), uint16) & 0x200)) - return 0; +#pragma region Music functions - // Only allow circuit or launch modes - if ( - ride->mode != RIDE_MODE_CONTINUOUS_CIRCUIT && - ride->mode != RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE && - ride->mode != RIDE_MODE_POWERED_LAUNCH - ) { - return 0; +/** + * + * rct2: 0x006ABE85 + */ +static void ride_music_update(int rideIndex) +{ + int x, y, z; + rct_vehicle *vehicle; + rct_ride *ride = GET_RIDE(rideIndex); + + if (!(RCT2_GLOBAL(0x0097D4F2 + (ride->type * 8), uint16) & 6)) + return; + + if (ride->status != RIDE_STATUS_OPEN || !(ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC)) { + ride->music_tune_id = 255; + return; } - - // Must have no more than one vehicle and one station - if (ride->num_vehicles > 1 || ride->num_stations > 1) - return 0; - return 1; -} + if (ride->type == RIDE_TYPE_CIRCUS_SHOW) { + vehicle = &(g_sprite_list[ride->vehicles[0]].vehicle); + if (vehicle->status != VEHICLE_STATUS_DOING_CIRCUS_SHOW) { + ride->music_tune_id = 255; + return; + } + } -track_colour ride_get_track_colour(rct_ride *ride, int colourScheme) -{ - track_colour result; - result.main = ride->track_colour_main[colourScheme]; - result.additional = ride->track_colour_additional[colourScheme]; - result.supports = ride->track_colour_supports[colourScheme]; - return result; -} + // Oscillate parameters for a power cut effect when breaking down + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN)) { + if (ride->breakdown_reason_pending == BREAKDOWN_CONTROL_FAILURE) { + if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 7)) + if (ride->var_1AC != 255) + ride->var_1AC++; + } else { + if ( + (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) || + ride->breakdown_reason_pending == BREAKDOWN_BRAKES_FAILURE || + ride->breakdown_reason_pending == BREAKDOWN_CONTROL_FAILURE + ) { + if (ride->var_1AC != 255) + ride->var_1AC++; + } -vehicle_colour ride_get_vehicle_colour(rct_ride *ride, int vehicleIndex) -{ - vehicle_colour result; - result.main = ride->vehicle_colours[vehicleIndex] & 0xFF; - result.additional_1 = ride->vehicle_colours[vehicleIndex] >> 8; - result.additional_2 = ride->vehicle_colours_extended[vehicleIndex]; - return result; + if (ride->var_1AC == 255) { + ride->music_tune_id = 255; + return; + } + } + } + + // Select random tune from available tunes for a music style (of course only merry-go-rounds have more than one tune) + if (ride->music_tune_id == 255) { + uint8 *musicStyleTunes = RCT2_ADDRESS(0x009AEF28, uint8*)[ride->music]; + uint8 numTunes = *musicStyleTunes++; + ride->music_tune_id = musicStyleTunes[scenario_rand() % numTunes]; + ride->music_position = 0; + return; + } + + x = (ride->station_starts[0] & 0xFF) * 32 + 16; + y = (ride->station_starts[0] >> 8) * 32 + 16; + z = ride->station_heights[0] * 8; + + int sampleRate = 22050; + + // Alter sample rate for a power cut effect + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN)) { + sampleRate = ride->var_1AC * 70; + if (ride->breakdown_reason_pending != BREAKDOWN_CONTROL_FAILURE) + sampleRate *= -1; + sampleRate += 22050; + } + + ride->music_position = sub_6BC3AC(x, y, z, rideIndex, sampleRate, ride->music_position, &ride->music_tune_id); } /** * - * rct2: 0x006AC988 - * set the speed of the go kart type vehicle at the start to a random value or alter if peep name is an easter egg - * @param ride (esi) + * rct2: 0x006BC6D8 */ -static void ride_init_vehicle_speed(rct_ride *ride) +static void ride_play_music() { - rct_ride_type *rideEntry; - rct_vehicle *vehicle; - uint8 *unk; - int i; - - for (i = 0; i < ride->num_vehicles; i++) { - vehicle = &g_sprite_list[ride->vehicles[i]].vehicle; - vehicle->var_48 &= ~(1 << 6); - - rideEntry = GET_RIDE_ENTRY(vehicle->var_D6); - unk = (uint8*)((int)rideEntry + (vehicle->var_31 * 0x65)); - - vehicle->speed = (scenario_rand() & 16) - 8 + RCT2_GLOBAL(unk + 0x76, uint8); - - if (vehicle->var_B3) { - rct_peep *peep = &g_sprite_list[vehicle->peep].peep; - - switch (peep_get_easteregg_name_id(peep)) { - case EASTEREGG_PEEP_NAME_MICHAEL_SCHUMACHER: - vehicle->speed += 35; - break; - case EASTEREGG_PEEP_NAME_JACQUES_VILLENEUVE: - vehicle->speed += 25; - break; - case EASTEREGG_PEEP_NAME_DAMON_HILL: - vehicle->speed += 55; - break; - case EASTEREGG_PEEP_NAME_CHRIS_SAWYER: - vehicle->speed += 14; - break; - case EASTEREGG_PEEP_NAME_MR_BEAN: - vehicle->speed = 9; - break; - } - } - } -} - -rct_ride_type *ride_get_entry(rct_ride *ride) -{ - return GET_RIDE_ENTRY(ride->subtype); -} - -uint8 *get_ride_entry_indices_for_ride_type(uint8 rideType) -{ - uint8 *typeToRideEntryIndexMap = (uint8*)0x009E32F8; - uint8 *entryIndexList = typeToRideEntryIndexMap; - while (rideType > 0) { - do { - entryIndexList++; - } while (*(entryIndexList - 1) != 255); - rideType--; - } - return entryIndexList; + RCT2_CALLPROC_EBPSAFE(0x006BC6D8); } +#pragma endregion +#pragma region Measurement functions /** * rct2: 0x006B64F2 @@ -1867,4 +1432,172 @@ use_measurement: if (message != NULL) *message = STR_DATA_LOGGING_WILL_START_WHEN_NEXT_LEAVES; return NULL; } -} \ No newline at end of file +} + +#pragma endregion + +#pragma region Colour functions + +track_colour ride_get_track_colour(rct_ride *ride, int colourScheme) +{ + track_colour result; + result.main = ride->track_colour_main[colourScheme]; + result.additional = ride->track_colour_additional[colourScheme]; + result.supports = ride->track_colour_supports[colourScheme]; + return result; +} + +vehicle_colour ride_get_vehicle_colour(rct_ride *ride, int vehicleIndex) +{ + vehicle_colour result; + result.main = ride->vehicle_colours[vehicleIndex] & 0xFF; + result.additional_1 = ride->vehicle_colours[vehicleIndex] >> 8; + result.additional_2 = ride->vehicle_colours_extended[vehicleIndex]; + return result; +} + +#pragma endregion + +#pragma region Reachability + +/** + * rct2: 0x006B7A5E + **/ +void ride_check_all_reachable() +{ + rct_ride *ride; + int i; + + FOR_ALL_RIDES(i, ride) { + if (ride->connected_message_throttle != 0) + ride->connected_message_throttle--; + if (ride->status != RIDE_STATUS_OPEN || ride->connected_message_throttle != 0) + continue; + + if (RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + ride->type * 8, uint32) & 0x20000) + ride_shop_connected(ride, i); + else + ride_entrance_exit_connected(ride, i); + } +} + +/** + * rct2: 0x006B7C59 + * @return 1 if the coordinate is reachable or has no entrance, 0 otherwise + */ +static int ride_entrance_exit_is_reachable(uint16 coordinate, rct_ride* ride, int index) { + int x = ((coordinate >> 8) & 0xFF) << 5, // cx + y = (coordinate & 0xFF) << 5; // ax + uint8 station_height = ride->station_heights[index]; + int tile_idx = ((x << 8) | y) >> 5; + rct_map_element* tile = RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, rct_map_element*)[tile_idx]; + + while(1) { + uint8 element_type = tile->type & MAP_ELEMENT_TYPE_MASK; + if (element_type == MAP_ELEMENT_TYPE_ENTRANCE && station_height == tile->base_height) { + break; + } else if (tile->flags & MAP_ELEMENT_FLAG_LAST_TILE) { + return 1; + } + tile++; + } + + uint8 face_direction = tile->type & 3; + y -= RCT2_ADDRESS(0x00993CCC, sint16)[face_direction * 2]; + x -= RCT2_ADDRESS(0x00993CCE, sint16)[face_direction * 2]; + tile_idx = ((x << 8) | y) >> 5; + + return map_coord_is_connected(tile_idx, station_height, face_direction); +} + +static void ride_entrance_exit_connected(rct_ride* ride, int ride_idx) +{ + for (int i = 0; i < 4; ++i) { + uint16 station_start = ride->station_starts[i], + entrance = ride->entrances[i], + exit = ride->exits[i]; + + if (station_start == -1 ) + continue; + if (entrance != -1 && !ride_entrance_exit_is_reachable(entrance, ride, i)) { + // name of ride is parameter of the format string + RCT2_GLOBAL(0x013CE952, uint16) = ride->name; + RCT2_GLOBAL(0x013CE954, uint32) = ride->name_arguments; + news_item_add_to_queue(1, STR_ENTRANCE_NOT_CONNECTED, ride_idx); + ride->connected_message_throttle = 3; + } + + if (exit != -1 && !ride_entrance_exit_is_reachable(exit, ride, i)) { + // name of ride is parameter of the format string + RCT2_GLOBAL(0x013CE952, uint16) = ride->name; + RCT2_GLOBAL(0x013CE954, uint32) = ride->name_arguments; + news_item_add_to_queue(1, STR_EXIT_NOT_CONNECTED, ride_idx); + ride->connected_message_throttle = 3; + } + + } +} + +static void ride_shop_connected(rct_ride* ride, int ride_idx) +{ + rct_ride* ride_back = ride; + uint16 coordinate = ride->station_starts[0]; + if (coordinate == 0xFFFF) + return; + + int x = ((coordinate >> 8) & 0xFF) << 5, // cx + y = (coordinate & 0xFF) << 5; // ax + + rct_map_element* tile = RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, rct_map_element*)[coordinate]; + + for (; ; tile++){ + uint8 element_type = tile->type & MAP_ELEMENT_TYPE_MASK; + if(element_type == MAP_ELEMENT_TYPE_TRACK && tile->properties.track.ride_index == ride_idx) + break; + if(tile->flags & MAP_ELEMENT_FLAG_LAST_TILE) + return; + } + + uint16 entrance_directions = 0; + uint8 track_type = tile->properties.track.type; + ride = &g_ride_list[tile->properties.track.ride_index]; + if (RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + ride->type * 8, uint32) & 0x80000) { + entrance_directions = RCT2_ADDRESS(0x0099CA64, uint8)[track_type * 16]; + } else { + entrance_directions = RCT2_ADDRESS(0x0099BA64, uint8)[track_type * 16]; + } + + + uint8 tile_direction = tile->type & MAP_ELEMENT_DIRECTION_MASK; + entrance_directions <<= tile_direction; + entrance_directions = ((entrance_directions >> 12) | entrance_directions) & 0xF; + + // now each bit in entrance_directions stands for an entrance direction to check + if (entrance_directions == 0) + return; + + for (int count = 0; entrance_directions != 0; ++count) { + if (!(entrance_directions & 1)) { + entrance_directions >>= 1; + continue; + } + entrance_directions >>= 1; + + uint8 face_direction = count ^ 2; // flip direction north<->south, east<->west + int y2 = y - RCT2_ADDRESS(0x00993CCC, sint16)[face_direction * 2]; + int x2 = x - RCT2_ADDRESS(0x00993CCE, sint16)[face_direction * 2]; + int tile_idx = ((x2 << 8) | y2) >> 5; + + if (map_coord_is_connected(tile_idx, tile->base_height, face_direction)) + return; + } + + // name of ride is parameter of the format string + RCT2_GLOBAL(0x013CE952, uint16) = ride->name; + RCT2_GLOBAL(0x013CE954, uint32) = ride->name_arguments; + news_item_add_to_queue(1, STR_ENTRANCE_NOT_CONNECTED, ride_idx); + + ride->connected_message_throttle = 3; +} + +#pragma endregion \ No newline at end of file diff --git a/src/ride/ride.h b/src/ride/ride.h index 582b8ee118..ba0f79a059 100644 --- a/src/ride/ride.h +++ b/src/ride/ride.h @@ -276,7 +276,7 @@ enum { RIDE_LIFECYCLE_NO_RAW_STATS = 1 << 3, RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING = 1 << 4, RIDE_LIFECYCLE_ON_RIDE_PHOTO = 1 << 5, - RIDE_LIFECYCLE_6 = 1 << 6, + RIDE_LIFECYCLE_BREAKDOWN_PENDING = 1 << 6, RIDE_LIFECYCLE_BROKEN_DOWN = 1 << 7, RIDE_LIFECYCLE_DUE_INSPECTION = 1 << 8, @@ -615,5 +615,6 @@ rct_ride_type *ride_get_entry(rct_ride *ride); uint8 *get_ride_entry_indices_for_ride_type(uint8 rideType); void ride_measurements_update(); rct_ride_measurement *ride_get_measurement(int rideIndex, rct_string_id *message); +void ride_breakdown_add_news_item(int rideIndex); #endif diff --git a/src/ride/station.c b/src/ride/station.c new file mode 100644 index 0000000000..172393f331 --- /dev/null +++ b/src/ride/station.c @@ -0,0 +1,321 @@ +/***************************************************************************** + * 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 "../scenario.h" +#include "../world/sprite.h" +#include "station.h" + +static void ride_update_station_blocksection(rct_ride *ride, int stationIndex, int dl); +static void ride_update_station_bumpercar(rct_ride *ride, int stationIndex, int dl); +static void ride_update_station_race(rct_ride *ride, int stationIndex, int dl); +static void ride_race_init_vehicle_speeds(rct_ride *ride); +static void ride_invalidate_station_start(rct_ride *ride, int stationIndex, int dl); +static void sub_6AC2AF(rct_ride *ride, int stationIndex, int dl); + +/** + * + * rct2: 0x006ABFFB + */ +void ride_update_station(rct_ride *ride, int stationIndex) +{ + int dl, dh; + + if (ride->station_starts[stationIndex] == 0xFFFF) + return; + + dl = 0; + switch (ride->mode) { + case RIDE_MODE_RACE: + ride_update_station_race(ride, stationIndex, dl); + break; + case RIDE_MODE_BUMPERCAR: + ride_update_station_bumpercar(ride, stationIndex, dl); + break; + case RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED: + case RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED: + ride_update_station_blocksection(ride, stationIndex, dl); + break; + default: + if ( + (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) && + (ride->status == RIDE_STATUS_CLOSED && ride->num_riders == 0) + ) { + dh = ride->var_062[stationIndex] & 0x7F; + if (dh != 0 && dh != 0x7F && !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 7)) + ride->var_062[stationIndex]--; + } else { + dl = 1; + dh = ride->var_062[stationIndex] & 0x7F; + if (dh != 0) { + if (dh != 0x7F && !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 31)) + ride->var_062[stationIndex]--; + dl = 0; + } + } + sub_6AC2AF(ride, stationIndex, dl); + break; + } +} + +/** + * + * rct2: 0x006AC0A1 + */ +static void ride_update_station_blocksection(rct_ride *ride, int stationIndex, int dl) +{ + rct_map_element *mapElement; + + mapElement = ride_get_station_start_track_element(ride, stationIndex); + + if ((ride->status == RIDE_STATUS_CLOSED && ride->num_riders == 0) || mapElement->flags & 0x20) { + dl = 0; + if (ride->var_062[stationIndex] & (1 << 7)) { + ride->var_062[stationIndex] &= ~(1 << 7); + ride_invalidate_station_start(ride, stationIndex, dl); + return; + } + + if (mapElement->properties.track.sequence & 0x80) { + ride_invalidate_station_start(ride, stationIndex, dl); + return; + } + } else { + dl = 1; + if (!(ride->var_062[stationIndex] & (1 << 7))) { + ride->var_062[stationIndex] |= (1 << 7); + ride_invalidate_station_start(ride, stationIndex, dl); + return; + } + + if (mapElement->properties.track.sequence & 0x80) { + ride_invalidate_station_start(ride, stationIndex, dl); + return; + } + } + + sub_6AC2AF(ride, stationIndex, dl); +} + +/** + * + * rct2: 0x006AC12B + */ +static void ride_update_station_bumpercar(rct_ride *ride, int stationIndex, int dl) +{ + int i, dx, dh; + rct_vehicle *vehicle; + + if ( + ride->status == RIDE_STATUS_CLOSED || + (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) + ) { + sub_6AC2AF(ride, stationIndex, dl); + return; + } + + if (ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) { + dx = ride->var_0D0 * 32; + dl = dx & 0xFF; + dh = (dx >> 8) & 0xFF; + for (i = 0; i < ride->num_vehicles; i++) { + vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); + if (dh > vehicle->var_CE) + continue; + + if (dh < vehicle->var_CE) { + ride->lifecycle_flags &= ~VEHICLE_STATUS_WAITING_TO_DEPART; + dh = 0; + sub_6AC2AF(ride, stationIndex, dl); + return; + } + + if (dl > vehicle->var_51) + continue; + } + + dl = 1; + } else { + for (i = 0; i < ride->num_vehicles; i++) { + vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); + if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART) { + sub_6AC2AF(ride, stationIndex, dl); + return; + } + } + + ride->lifecycle_flags |= VEHICLE_STATUS_WAITING_TO_DEPART; + ride->var_14D = 12; + dl = 1; + } + sub_6AC2AF(ride, stationIndex, dl); +} + +/** + * + * rct2: 0x006AC1DF + */ +static void ride_update_station_race(rct_ride *ride, int stationIndex, int dl) +{ + int i, dh; + rct_vehicle *vehicle; + rct_peep *peep; + + if ( + ride->status == RIDE_STATUS_CLOSED || + (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) + ) { + sub_6AC2AF(ride, stationIndex, dl); + return; + } + + if (!(ride->lifecycle_flags & VEHICLE_STATUS_WAITING_TO_DEPART)) { + for (i = 0; i < ride->num_vehicles; i++) { + vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); + if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART && vehicle->status != VEHICLE_STATUS_DEPARTING) { + sub_6AC2AF(ride, stationIndex, dl); + return; + } + } + + ride_race_init_vehicle_speeds(ride); + ride->lifecycle_flags |= VEHICLE_STATUS_WAITING_TO_DEPART; + ride->var_14D = 12; + } else { + dh = ride->var_0D0; + for (i = 0; i < ride->num_vehicles; i++) { + vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); + if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART && dh <= vehicle->var_CE) { + ride->lifecycle_flags &= ~VEHICLE_STATUS_WAITING_TO_DEPART; + if (vehicle->var_B3 != 0) { + peep = &(g_sprite_list[vehicle->peep].peep); + ride->race_winner = peep->sprite_index; + ride->var_14D = 12; + } + sub_6AC2AF(ride, stationIndex, dl); + return; + } + } + } + dl = 1; + sub_6AC2AF(ride, stationIndex, dl); +} + +/** + * + * rct2: 0x006AC988 + * set the speed of the go kart type vehicle at the start to a random value or alter if peep name is an easter egg + * @param ride (esi) + */ +static void ride_race_init_vehicle_speeds(rct_ride *ride) +{ + rct_ride_type *rideEntry; + rct_vehicle *vehicle; + uint8 *unk; + int i; + + for (i = 0; i < ride->num_vehicles; i++) { + vehicle = &g_sprite_list[ride->vehicles[i]].vehicle; + vehicle->var_48 &= ~(1 << 6); + + rideEntry = GET_RIDE_ENTRY(vehicle->var_D6); + unk = (uint8*)((int)rideEntry + (vehicle->var_31 * 0x65)); + + vehicle->speed = (scenario_rand() & 16) - 8 + RCT2_GLOBAL(unk + 0x76, uint8); + + if (vehicle->var_B3) { + rct_peep *peep = &g_sprite_list[vehicle->peep].peep; + + switch (peep_get_easteregg_name_id(peep)) { + case EASTEREGG_PEEP_NAME_MICHAEL_SCHUMACHER: + vehicle->speed += 35; + break; + case EASTEREGG_PEEP_NAME_JACQUES_VILLENEUVE: + vehicle->speed += 25; + break; + case EASTEREGG_PEEP_NAME_DAMON_HILL: + vehicle->speed += 55; + break; + case EASTEREGG_PEEP_NAME_CHRIS_SAWYER: + vehicle->speed += 14; + break; + case EASTEREGG_PEEP_NAME_MR_BEAN: + vehicle->speed = 9; + break; + } + } + } +} + +/** + * + * rct2: 0x006AC2C7 + */ +static void ride_invalidate_station_start(rct_ride *ride, int stationIndex, int dl) +{ + int x, y; + rct_map_element *mapElement; + + x = (ride->station_starts[stationIndex] & 0xFF) * 32; + y = (ride->station_starts[stationIndex] >> 8) * 32; + mapElement = ride_get_station_start_track_element(ride, stationIndex); + + mapElement->properties.track.sequence &= 0x7F; + if (dl != 0) + mapElement->properties.track.sequence |= 0x80; + + // Invalidate map tile + map_invalidate_tile(x, y, mapElement->base_height * 8, mapElement->clearance_height * 8); +} + +/** + * + * rct2: 0x006AC2AF + */ +static void sub_6AC2AF(rct_ride *ride, int stationIndex, int dl) +{ + if (dl != 0) + ride->var_062[stationIndex] |= (1 << 7); + else + ride->var_062[stationIndex] &= ~(1 << 7); +} + +rct_map_element *ride_get_station_start_track_element(rct_ride *ride, int stationIndex) +{ + int x, y, z; + rct_map_element *mapElement; + + x = ride->station_starts[stationIndex] & 0xFF; + y = ride->station_starts[stationIndex] >> 8; + z = ride->station_heights[stationIndex]; + + // Get first element of the tile + mapElement = TILE_MAP_ELEMENT_POINTER(y * 256 + x); + + // Find the station track element + do { + if ((mapElement->type & MAP_ELEMENT_TYPE_MASK) == MAP_ELEMENT_TYPE_TRACK && z == mapElement->base_height) + return mapElement; + + mapElement++; + } while (!((mapElement - 1)->flags & MAP_ELEMENT_FLAG_LAST_TILE)); + + return NULL; +} \ No newline at end of file diff --git a/src/ride/station.h b/src/ride/station.h new file mode 100644 index 0000000000..17ed09e9be --- /dev/null +++ b/src/ride/station.h @@ -0,0 +1,31 @@ +/***************************************************************************** + * 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 _RIDE_STATION_H_ +#define _RIDE_STATION_H_ + +#include "../common.h" +#include "../world/map.h" +#include "ride.h" + +void ride_update_station(rct_ride *ride, int stationIndex); +rct_map_element *ride_get_station_start_track_element(rct_ride *ride, int stationIndex); + +#endif \ No newline at end of file diff --git a/src/util/util.c b/src/util/util.c index b17de76cee..3ca951960c 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -51,4 +51,15 @@ long fsize(FILE *fp) fseek(fp, originalPosition, SEEK_SET); return size; +} + +int bitscanforward(int source) +{ + int i; + + for (i = 0; i < 32; i++) + if (source & (1 << i)) + return i; + + return -1; } \ No newline at end of file diff --git a/src/util/util.h b/src/util/util.h index a5f03b67e0..bef5f0adc3 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -29,4 +29,6 @@ int mph_to_kmph(int mph); long fsize(FILE *fp); +int bitscanforward(int source); + #endif diff --git a/src/world/map.c b/src/world/map.c index 88da65fafe..7d5e6c19b7 100644 --- a/src/world/map.c +++ b/src/world/map.c @@ -474,4 +474,14 @@ int map_is_location_in_park(int x, int y) RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = 1729; return 0; +} + +/** + * + * rct2: 0x006ECB60 + * NOTE: x, y and z are in pixels, not tile units + */ +void map_invalidate_tile(int x, int y, int zLow, int zHigh) +{ + RCT2_CALLPROC_X(0x006ECB60, x, 0, y, 0, zHigh, zLow, 0); } \ No newline at end of file diff --git a/src/world/map.h b/src/world/map.h index 3bef2a673e..b562d48786 100644 --- a/src/world/map.h +++ b/src/world/map.h @@ -207,6 +207,7 @@ void map_invalidate_animations(); void sub_6A876D(); int sub_664F72(int x, int y, int z); int map_is_location_in_park(int x, int y); +void map_invalidate_tile(int x, int y, int zLow, int zHigh); void fountain_update_all(); From 19d3852f9c80391edc64b245fdbbdb591b4742e6 Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sun, 2 Nov 2014 14:03:28 +0000 Subject: [PATCH 08/11] refactor update ride more --- src/audio/audio.c | 29 ----------------------------- src/audio/audio.h | 1 - src/interface/window.c | 35 +++++++++++++++++++++++++++++++++++ src/interface/window.h | 2 ++ src/ride/ride.c | 17 ++--------------- 5 files changed, 39 insertions(+), 45 deletions(-) diff --git a/src/audio/audio.c b/src/audio/audio.c index 54bdd956d2..ea9d9fa39f 100644 --- a/src/audio/audio.c +++ b/src/audio/audio.c @@ -1836,35 +1836,6 @@ void stop_vehicle_sounds() } } -/** -* Update zoom based volume attenuation for ride music and clear music list -* rct2: 0x006BC348 -*/ -void sub_6BC348() -{ - RCT2_GLOBAL(0x009AF42C, rct_music_info*) = &RCT2_GLOBAL(0x009AF430, rct_music_info); - RCT2_GLOBAL(0x00F438A4, rct_viewport*) = (rct_viewport*)-1; - rct_window* window = RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*); - while (1) { - window--; - if (window < RCT2_ADDRESS(RCT2_ADDRESS_WINDOW_LIST, rct_window)) { - break; - } - if (window->viewport && window->viewport->flags & VIEWPORT_FLAG_SOUND_ON) { - RCT2_GLOBAL(0x00F438A4, rct_viewport*) = window->viewport; - RCT2_GLOBAL(0x00F438A8, rct_window*) = window; - RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 0; - if (window->viewport->zoom) { - RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 30; - if (window->viewport->zoom != 1) { - RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 60; - } - } - return; - } - } -} - /** * * rct2: 0x006BC3AC diff --git a/src/audio/audio.h b/src/audio/audio.h index 5821ce051f..cc91205edc 100644 --- a/src/audio/audio.h +++ b/src/audio/audio.h @@ -213,7 +213,6 @@ void audio_close(); void pause_sounds(); void unpause_sounds(); void stop_vehicle_sounds(); -void sub_6BC348(); void sub_6BC6D8(); int sub_6BC3AC(sint16 x, sint16 y, sint16 z, uint8 rideIndex, uint16 sampleRate, uint32 position, uint8 *tuneId); diff --git a/src/interface/window.c b/src/interface/window.c index 53f2602d7c..6fcfe9fbb0 100644 --- a/src/interface/window.c +++ b/src/interface/window.c @@ -1792,4 +1792,39 @@ void sub_6EA73F() window_invalidate_pressed_image_buttons(w); RCT2_CALLPROC_X(w->event_handlers[WE_RESIZE], 0, 0, 0, 0, (int)w, 0, 0); } +} + +/** + * Update zoom based volume attenuation for ride music and clear music list. + * rct2: 0x006BC348 + */ +void window_update_viewport_ride_music() +{ + rct_viewport *viewport; + rct_window *w; + + RCT2_GLOBAL(0x009AF42C, rct_music_info*) = (rct_music_info*)0x009AF430; + RCT2_GLOBAL(0x00F438A4, rct_viewport*) = (rct_viewport*)-1; + + for (w = RCT2_LAST_WINDOW; w >= g_window_list; w--) { + viewport = w->viewport; + if (viewport == NULL || !(viewport->flags & VIEWPORT_FLAG_SOUND_ON)) + continue; + + RCT2_GLOBAL(0x00F438A4, rct_viewport*) = viewport; + RCT2_GLOBAL(0x00F438A8, rct_window*) = w; + + switch (viewport->zoom) { + case 0: + RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 0; + break; + case 1: + RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 30; + break; + default: + RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 60; + break; + } + break; + } } \ No newline at end of file diff --git a/src/interface/window.h b/src/interface/window.h index 4fce868807..8b9de08ee3 100644 --- a/src/interface/window.h +++ b/src/interface/window.h @@ -463,6 +463,8 @@ void tool_cancel(); void window_close_construction_windows(); +void window_update_viewport_ride_music(); + // Open window functions void window_main_open(); void window_resize_gui(int width, int height); diff --git a/src/ride/ride.c b/src/ride/ride.c index 04a2d95666..202f274d6b 100644 --- a/src/ride/ride.c +++ b/src/ride/ride.c @@ -125,7 +125,6 @@ static int ride_get_new_breakdown_problem(rct_ride *ride); static void ride_inspection_update(rct_ride *ride); static void ride_mechanic_status_update(int rideIndex, int mechanicStatus); static void ride_music_update(int rideIndex); -static void ride_play_music(); static void ride_prepare_breakdown(int rideIndex, int breakdownReason); static void ride_shop_connected(rct_ride* ride, int ride_idx); static void ride_spiral_slide_update(rct_ride *ride); @@ -513,8 +512,6 @@ void ride_update_all() rct_ride *ride; int i; - // RCT2_CALLPROC_EBPSAFE(0x006ABE4C); return; - // Remove all rides if certain flags are set (possible scenario editor?) int *esi = (int*)0x9DCE9E; if (esi[0x1BCA] & 2) { @@ -524,14 +521,13 @@ void ride_update_all() return; } - // Something related to windows - RCT2_CALLPROC_EBPSAFE(0x006BC348); + window_update_viewport_ride_music(); // Update rides FOR_ALL_RIDES(i, ride) ride_update(i); - ride_play_music(); + sub_6BC6D8(); } /** @@ -1232,15 +1228,6 @@ static void ride_music_update(int rideIndex) ride->music_position = sub_6BC3AC(x, y, z, rideIndex, sampleRate, ride->music_position, &ride->music_tune_id); } -/** - * - * rct2: 0x006BC6D8 - */ -static void ride_play_music() -{ - RCT2_CALLPROC_EBPSAFE(0x006BC6D8); -} - #pragma endregion #pragma region Measurement functions From 3ecfee72a2815fd64ad02142cbd718ce81fdcb28 Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Mon, 3 Nov 2014 17:20:07 +0000 Subject: [PATCH 09/11] refactor ride_get_measurement --- src/ride/ride.c | 88 ++++++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 34 deletions(-) diff --git a/src/ride/ride.c b/src/ride/ride.c index 202f274d6b..349494cd15 100644 --- a/src/ride/ride.c +++ b/src/ride/ride.c @@ -1350,6 +1350,34 @@ void ride_measurements_update() } } +rct_ride_measurement *ride_get_existing_measurement(int rideIndex) +{ + int i; + rct_ride_measurement *measurement; + + for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) { + measurement = GET_RIDE_MEASUREMENT(i); + if (measurement->ride_index == rideIndex) + return measurement; + } + + return NULL; +} + +rct_ride_measurement *ride_get_free_measurement() +{ + int i; + rct_ride_measurement *measurement; + + for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) { + measurement = GET_RIDE_MEASUREMENT(i); + if (measurement->ride_index == 255) + return measurement; + } + + return NULL; +} + /** * * rct2: 0x006B66D9 @@ -1370,45 +1398,37 @@ rct_ride_measurement *ride_get_measurement(int rideIndex, rct_string_id *message } // Check if a measurement already exists for this ride - for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) { - measurement = GET_RIDE_MEASUREMENT(i); - if (measurement->ride_index == i) - goto use_measurement; - } + measurement = ride_get_existing_measurement(rideIndex); + if (measurement == NULL) { + // Find a free measurement + measurement = ride_get_free_measurement(); + if (measurement == NULL) { + // Use last recently used measurement for some other ride + lruIndex = 0; + lruTicks = 0xFFFFFFFF; + for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) { + measurement = GET_RIDE_MEASUREMENT(i); - // Find a free measurement - for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) { - measurement = GET_RIDE_MEASUREMENT(i); - if (measurement->ride_index == 255) - goto new_measurement; - } + if (measurement->last_use_tick <= lruTicks) { + lruTicks = measurement->last_use_tick; + lruIndex = i; + } + } - // Use last recently used measurement for some other ride - lruIndex = 0; - lruTicks = 0xFFFFFFFF; - for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) { - measurement = GET_RIDE_MEASUREMENT(i); - - if (measurement->last_use_tick <= lruTicks) { - lruTicks = measurement->last_use_tick; - lruIndex = i; + i = lruIndex; + measurement = GET_RIDE_MEASUREMENT(i); + ride->measurement_index = 255; } + + measurement->ride_index = rideIndex; + ride->measurement_index = i; + measurement->flags = 0; + if (RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + (ride->type * 8), uint32) & 0x80) + measurement->flags |= RIDE_MEASUREMENT_FLAG_G_FORCES; + measurement->num_items = 0; + measurement->current_item = 0; } - i = lruIndex; - measurement = GET_RIDE_MEASUREMENT(i); - ride->measurement_index = 255; - -new_measurement: - measurement->ride_index = rideIndex; - ride->measurement_index = i; - measurement->flags = 0; - if (RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + (ride->type * 8), uint32) & 0x80) - measurement->flags |= RIDE_MEASUREMENT_FLAG_G_FORCES; - measurement->num_items = 0; - measurement->current_item = 0; - -use_measurement: measurement->last_use_tick = RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, uint32); if (measurement->flags & 1) { if (message != NULL) *message = 0; From 073a747660404649353024426f34a0302b1c0b5c Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Mon, 3 Nov 2014 21:53:22 +0000 Subject: [PATCH 10/11] fix ride update, financial statistics --- src/ride/ride.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ride/ride.c b/src/ride/ride.c index 349494cd15..8a9c720cea 100644 --- a/src/ride/ride.c +++ b/src/ride/ride.c @@ -564,13 +564,14 @@ static void ride_update(int rideIndex) ride->var_128 = ride->var_126; ride->var_126 = ride->var_124; ride->var_124 = ride->var_120; + ride->var_120 = 0; ride->var_14D |= 1; ride->income_per_hour = ride_calculate_income_per_hour(ride); ride->var_14D |= 2; if (ride->upkeep_cost != (money16)0xFFFF) - ride->upkeep_cost = (money16)ride->income_per_hour - (ride->upkeep_cost * 16); + ride->profit = (money16)ride->income_per_hour - (ride->upkeep_cost * 16); } // Ride specific updates From 015e85d2630da8720bcb934979bfec7eea19bf32 Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Tue, 4 Nov 2014 01:07:54 +0000 Subject: [PATCH 11/11] refactor station update and fix bugs --- src/ride/ride.c | 1 + src/ride/ride.h | 11 ++- src/ride/station.c | 200 +++++++++++++++++++++------------------------ src/ride/vehicle.h | 5 +- 4 files changed, 108 insertions(+), 109 deletions(-) diff --git a/src/ride/ride.c b/src/ride/ride.c index 8a9c720cea..62287777d0 100644 --- a/src/ride/ride.c +++ b/src/ride/ride.c @@ -711,6 +711,7 @@ static void ride_inspection_update(rct_ride *ride) // Inspect the first station that has an exit ride->lifecycle_flags |= RIDE_LIFECYCLE_DUE_INSPECTION; + ride->mechanic_status = RIDE_MECHANIC_STATUS_CALLING; ride->inspection_station = 0; for (i = 0; i < 4; i++) { if (ride->exits[i] != 0xFFFF) { diff --git a/src/ride/ride.h b/src/ride/ride.h index ba0f79a059..f87a90d903 100644 --- a/src/ride/ride.h +++ b/src/ride/ride.h @@ -97,7 +97,7 @@ typedef struct { uint16 station_starts[4]; // 0x052 uint8 station_heights[4]; // 0x05A uint8 pad_05E[0x4]; - uint8 var_062[4]; + uint8 station_depart[4]; // 0x062 uint8 pad_066[0x4]; uint16 entrances[4]; // 0x06A uint16 exits[4]; // 0x072 @@ -114,7 +114,11 @@ typedef struct { uint8 var_0CD; uint8 min_waiting_time; // 0x0CE uint8 max_waiting_time; // 0x0CF - uint8 var_0D0; + union { + uint8 var_0D0; + uint8 time_limit; // 0x0D0 + uint8 num_laps; // 0x0D0 + }; uint8 pad_0D1[0x3]; uint8 measurement_index; // 0x0D4 uint8 var_0D5; @@ -573,6 +577,9 @@ enum { #define MAX_RIDE_MEASUREMENTS 8 #define RIDE_RELIABILITY_UNDEFINED 0xFFFF +#define STATION_DEPART_FLAG (1 << 7) +#define STATION_DEPART_MASK (~STATION_DEPART_FLAG) + // rct2: 0x009ACFA4 rct_ride_type **gRideTypeList; diff --git a/src/ride/station.c b/src/ride/station.c index 172393f331..38b7b5f792 100644 --- a/src/ride/station.c +++ b/src/ride/station.c @@ -23,12 +23,12 @@ #include "../world/sprite.h" #include "station.h" -static void ride_update_station_blocksection(rct_ride *ride, int stationIndex, int dl); -static void ride_update_station_bumpercar(rct_ride *ride, int stationIndex, int dl); -static void ride_update_station_race(rct_ride *ride, int stationIndex, int dl); +static void ride_update_station_blocksection(rct_ride *ride, int stationIndex); +static void ride_update_station_bumpercar(rct_ride *ride, int stationIndex); +static void ride_update_station_normal(rct_ride *ride, int stationIndex); +static void ride_update_station_race(rct_ride *ride, int stationIndex); static void ride_race_init_vehicle_speeds(rct_ride *ride); static void ride_invalidate_station_start(rct_ride *ride, int stationIndex, int dl); -static void sub_6AC2AF(rct_ride *ride, int stationIndex, int dl); /** * @@ -36,41 +36,22 @@ static void sub_6AC2AF(rct_ride *ride, int stationIndex, int dl); */ void ride_update_station(rct_ride *ride, int stationIndex) { - int dl, dh; - if (ride->station_starts[stationIndex] == 0xFFFF) return; - dl = 0; switch (ride->mode) { case RIDE_MODE_RACE: - ride_update_station_race(ride, stationIndex, dl); + ride_update_station_race(ride, stationIndex); break; case RIDE_MODE_BUMPERCAR: - ride_update_station_bumpercar(ride, stationIndex, dl); + ride_update_station_bumpercar(ride, stationIndex); break; case RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED: case RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED: - ride_update_station_blocksection(ride, stationIndex, dl); + ride_update_station_blocksection(ride, stationIndex); break; default: - if ( - (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) && - (ride->status == RIDE_STATUS_CLOSED && ride->num_riders == 0) - ) { - dh = ride->var_062[stationIndex] & 0x7F; - if (dh != 0 && dh != 0x7F && !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 7)) - ride->var_062[stationIndex]--; - } else { - dl = 1; - dh = ride->var_062[stationIndex] & 0x7F; - if (dh != 0) { - if (dh != 0x7F && !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 31)) - ride->var_062[stationIndex]--; - dl = 0; - } - } - sub_6AC2AF(ride, stationIndex, dl); + ride_update_station_normal(ride, stationIndex); break; } } @@ -79,102 +60,114 @@ void ride_update_station(rct_ride *ride, int stationIndex) * * rct2: 0x006AC0A1 */ -static void ride_update_station_blocksection(rct_ride *ride, int stationIndex, int dl) +static void ride_update_station_blocksection(rct_ride *ride, int stationIndex) { rct_map_element *mapElement; mapElement = ride_get_station_start_track_element(ride, stationIndex); if ((ride->status == RIDE_STATUS_CLOSED && ride->num_riders == 0) || mapElement->flags & 0x20) { - dl = 0; - if (ride->var_062[stationIndex] & (1 << 7)) { - ride->var_062[stationIndex] &= ~(1 << 7); - ride_invalidate_station_start(ride, stationIndex, dl); - return; - } + ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG; - if (mapElement->properties.track.sequence & 0x80) { - ride_invalidate_station_start(ride, stationIndex, dl); - return; - } + if ((ride->station_depart[stationIndex] & STATION_DEPART_FLAG) || (mapElement->properties.track.sequence & 0x80)) + ride_invalidate_station_start(ride, stationIndex, 0); } else { - dl = 1; - if (!(ride->var_062[stationIndex] & (1 << 7))) { - ride->var_062[stationIndex] |= (1 << 7); - ride_invalidate_station_start(ride, stationIndex, dl); - return; - } - - if (mapElement->properties.track.sequence & 0x80) { - ride_invalidate_station_start(ride, stationIndex, dl); - return; + if (!(ride->station_depart[stationIndex] & STATION_DEPART_FLAG)) { + ride->station_depart[stationIndex] |= STATION_DEPART_FLAG; + ride_invalidate_station_start(ride, stationIndex, 1); + } else if (mapElement->properties.track.sequence & 0x80) { + ride_invalidate_station_start(ride, stationIndex, 1); } } - - sub_6AC2AF(ride, stationIndex, dl); } /** * * rct2: 0x006AC12B */ -static void ride_update_station_bumpercar(rct_ride *ride, int stationIndex, int dl) +static void ride_update_station_bumpercar(rct_ride *ride, int stationIndex) { - int i, dx, dh; + int i, dx, dl, dh; rct_vehicle *vehicle; if ( ride->status == RIDE_STATUS_CLOSED || (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) ) { - sub_6AC2AF(ride, stationIndex, dl); + ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG; return; } if (ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) { - dx = ride->var_0D0 * 32; + dx = ride->time_limit * 32; dl = dx & 0xFF; dh = (dx >> 8) & 0xFF; for (i = 0; i < ride->num_vehicles; i++) { vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); - if (dh > vehicle->var_CE) + if (vehicle->var_CE < dh || (vehicle->var_CE < dh && vehicle->var_51 > dl)) continue; - if (dh < vehicle->var_CE) { - ride->lifecycle_flags &= ~VEHICLE_STATUS_WAITING_TO_DEPART; - dh = 0; - sub_6AC2AF(ride, stationIndex, dl); - return; - } - - if (dl > vehicle->var_51) - continue; + // End match + ride->lifecycle_flags &= ~RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING; + ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG; + return; } - dl = 1; + // Continue match + ride->station_depart[stationIndex] |= STATION_DEPART_FLAG; } else { + // Check if all vehicles are ready to go for (i = 0; i < ride->num_vehicles; i++) { vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART) { - sub_6AC2AF(ride, stationIndex, dl); + ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG; return; } } - ride->lifecycle_flags |= VEHICLE_STATUS_WAITING_TO_DEPART; - ride->var_14D = 12; - dl = 1; + // Begin the match + ride->lifecycle_flags |= RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING; + ride->station_depart[stationIndex] |= STATION_DEPART_FLAG; + ride->var_14D |= 12; + } +} + +/** + * + * rct2: 0x006AC02C + */ +static void ride_update_station_normal(rct_ride *ride, int stationIndex) +{ + int time; + + time = ride->station_depart[stationIndex] & STATION_DEPART_MASK; + if ( + (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) && + (ride->status == RIDE_STATUS_CLOSED && ride->num_riders == 0) + ) { + if (time != 0 && time != 127 && !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 7)) + time--; + + ride->station_depart[stationIndex] = time; + } else { + if (time == 0) { + ride->station_depart[stationIndex] |= STATION_DEPART_FLAG; + } else { + if (time != 127 && !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 31)) + time--; + + ride->station_depart[stationIndex] = time; + } } - sub_6AC2AF(ride, stationIndex, dl); } /** * * rct2: 0x006AC1DF */ -static void ride_update_station_race(rct_ride *ride, int stationIndex, int dl) +static void ride_update_station_race(rct_ride *ride, int stationIndex) { - int i, dh; + int i, numLaps; rct_vehicle *vehicle; rct_peep *peep; @@ -182,40 +175,47 @@ static void ride_update_station_race(rct_ride *ride, int stationIndex, int dl) ride->status == RIDE_STATUS_CLOSED || (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) ) { - sub_6AC2AF(ride, stationIndex, dl); + ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG; return; } - if (!(ride->lifecycle_flags & VEHICLE_STATUS_WAITING_TO_DEPART)) { + if (ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) { + numLaps = ride->num_laps; + for (i = 0; i < ride->num_vehicles; i++) { + vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); + if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART && vehicle->num_laps >= numLaps) { + // Found a winner + if (vehicle->var_B3 != 0) { + peep = &(g_sprite_list[vehicle->peep].peep); + ride->race_winner = peep->sprite_index; + ride->var_14D |= 12; + } + + // Race is over + ride->lifecycle_flags &= ~RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING; + ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG; + return; + } + } + + // Continue racing + ride->station_depart[stationIndex] |= STATION_DEPART_FLAG; + } else { + // Check if all vehicles are ready to go for (i = 0; i < ride->num_vehicles; i++) { vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART && vehicle->status != VEHICLE_STATUS_DEPARTING) { - sub_6AC2AF(ride, stationIndex, dl); + ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG; return; } } + // Begin the race ride_race_init_vehicle_speeds(ride); - ride->lifecycle_flags |= VEHICLE_STATUS_WAITING_TO_DEPART; - ride->var_14D = 12; - } else { - dh = ride->var_0D0; - for (i = 0; i < ride->num_vehicles; i++) { - vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle); - if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART && dh <= vehicle->var_CE) { - ride->lifecycle_flags &= ~VEHICLE_STATUS_WAITING_TO_DEPART; - if (vehicle->var_B3 != 0) { - peep = &(g_sprite_list[vehicle->peep].peep); - ride->race_winner = peep->sprite_index; - ride->var_14D = 12; - } - sub_6AC2AF(ride, stationIndex, dl); - return; - } - } + ride->lifecycle_flags |= RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING; + ride->station_depart[stationIndex] |= STATION_DEPART_FLAG; + ride->var_14D |= 12; } - dl = 1; - sub_6AC2AF(ride, stationIndex, dl); } /** @@ -285,18 +285,6 @@ static void ride_invalidate_station_start(rct_ride *ride, int stationIndex, int map_invalidate_tile(x, y, mapElement->base_height * 8, mapElement->clearance_height * 8); } -/** - * - * rct2: 0x006AC2AF - */ -static void sub_6AC2AF(rct_ride *ride, int stationIndex, int dl) -{ - if (dl != 0) - ride->var_062[stationIndex] |= (1 << 7); - else - ride->var_062[stationIndex] &= ~(1 << 7); -} - rct_map_element *ride_get_station_start_track_element(rct_ride *ride, int stationIndex) { int x, y, z; diff --git a/src/ride/vehicle.h b/src/ride/vehicle.h index 4f7f0db47c..9e7b0c656b 100644 --- a/src/ride/vehicle.h +++ b/src/ride/vehicle.h @@ -76,7 +76,10 @@ typedef struct { uint8 pad_C3[0x09]; uint8 var_CC; uint8 var_CD; - uint8 var_CE; + union { + uint8 var_CE; + uint8 num_laps; // 0xCE + }; uint8 pad_CF[0x07]; uint8 var_D6; } rct_vehicle;