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();