From cf59455d6914de47b095ef974eb0f9b8d76056cc Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sun, 2 Nov 2014 03:37:56 +0000 Subject: [PATCH] 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();