diff --git a/projects/openrct2.vcxproj b/projects/openrct2.vcxproj
index a7a7e5578d..baa3d25704 100644
--- a/projects/openrct2.vcxproj
+++ b/projects/openrct2.vcxproj
@@ -89,6 +89,7 @@
+
@@ -190,6 +191,7 @@
+
diff --git a/projects/openrct2.vcxproj.filters b/projects/openrct2.vcxproj.filters
index c25f074b1b..3cf9846b55 100644
--- a/projects/openrct2.vcxproj.filters
+++ b/projects/openrct2.vcxproj.filters
@@ -381,7 +381,6 @@
-
Source\Drawing
@@ -434,6 +433,10 @@
Source\Windows
+
+
+ Source\Ride
+
@@ -631,5 +634,8 @@
Source\World
+
+ Source\Ride
+
\ No newline at end of file
diff --git a/src/addresses.h b/src/addresses.h
index 5abb64b0b9..fcd74c5c49 100644
--- a/src/addresses.h
+++ b/src/addresses.h
@@ -493,7 +493,7 @@ static void RCT2_CALLPROC_EBPSAFE(int address)
/* Returns the flags register
*
*Flags register is as follows:
- *0bSZ0A_0P0C_0000_00000
+ *0bSZ0A_0P0C_0000_0000
*S = Signed flag
*Z = Zero flag
*C = Carry flag
diff --git a/src/audio/audio.c b/src/audio/audio.c
index 4a0f5fd2de..ea9d9fa39f 100644
--- a/src/audio/audio.c
+++ b/src/audio/audio.c
@@ -1836,35 +1836,6 @@ void stop_vehicle_sounds()
}
}
-/**
-* Update zoom based volume attenuation for ride music and clear music list
-* rct2: 0x006BC348
-*/
-void sub_6BC348()
-{
- RCT2_GLOBAL(0x009AF42C, rct_music_info*) = &RCT2_GLOBAL(0x009AF430, rct_music_info);
- RCT2_GLOBAL(0x00F438A4, rct_viewport*) = (rct_viewport*)-1;
- rct_window* window = RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*);
- while (1) {
- window--;
- if (window < RCT2_ADDRESS(RCT2_ADDRESS_WINDOW_LIST, rct_window)) {
- break;
- }
- if (window->viewport && window->viewport->flags & VIEWPORT_FLAG_SOUND_ON) {
- RCT2_GLOBAL(0x00F438A4, rct_viewport*) = window->viewport;
- RCT2_GLOBAL(0x00F438A8, rct_window*) = window;
- RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 0;
- if (window->viewport->zoom) {
- RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 30;
- if (window->viewport->zoom != 1) {
- RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 60;
- }
- }
- return;
- }
- }
-}
-
/**
*
* rct2: 0x006BC3AC
@@ -1874,17 +1845,32 @@ void sub_6BC348()
* @param x (ax)
* @param y (cx)
* @param z (dx)
- * @param bx
- * @param ebp
- * @param di
- * @returns ebp register
+ * @param sampleRate (di)
+ * @param rideIndex (bl)
+ * @param position (ebp)
+ * @param tuneId (bh)
+ * @returns new position (ebp)
*/
-int sub_6BC3AC(sint16 x, sint16 y, sint16 z, uint16 bx, uint32 ebp, uint16 di)
+int sub_6BC3AC(sint16 x, sint16 y, sint16 z, uint8 rideIndex, uint16 sampleRate, uint32 position, uint8 *tuneId)
{
- uint8 bl = LOBYTE(bx);
- uint8 bh = HIBYTE(bx);
+ {
+ int a_eax, a_ebx, a_ecx, a_edx, a_esi, a_edi, a_ebp;
+
+ a_eax = x;
+ a_ebx = (*tuneId << 8) | rideIndex;
+ a_ecx = y;
+ a_edx = z;
+ a_edi = sampleRate;
+ a_ebp = position;
+ RCT2_CALLFUNC_X(0x006BC3AC, &a_eax, &a_ebx, &a_ecx, &a_edx, &a_esi, &a_edi, &a_ebp);
+
+ *tuneId = (a_ebx >> 8) & 0xFF;
+ return a_ebp;
+ }
+
+ // TODO fix / bh needs returning too!
if(!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR) && !RCT2_GLOBAL(0x009AF59C, uint8) && RCT2_GLOBAL(0x00F438A4, rct_viewport*) != (rct_viewport*)-1) {
- RCT2_GLOBAL(0x009AF47C, uint16) = di;
+ RCT2_GLOBAL(0x009AF47C, uint16) = sampleRate;
sint16 v11;
sint16 v12;
switch (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint32)) {
@@ -1988,12 +1974,12 @@ int sub_6BC3AC(sint16 x, sint16 y, sint16 z, uint16 bx, uint32 ebp, uint16 di)
rct_music_info2* music_info2 = &RCT2_GLOBAL(0x009AF46C, rct_music_info2);
int channel = 0;
uint32 a1;
- while (music_info2->id != bl && music_info2->var_1 != bh) {
+ while (music_info2->id != rideIndex && music_info2->var_1 != *tuneId) {
music_info2++;
channel++;
if (channel >= 2) {
- rct_music_info3* music_info3 = &RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[bh];
- a1 = ebp + music_info3->var_4;
+ rct_music_info3* music_info3 = &RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[*tuneId];
+ a1 = position + music_info3->var_4;
goto label51;
}
}
@@ -2007,11 +1993,11 @@ int sub_6BC3AC(sint16 x, sint16 y, sint16 z, uint16 bx, uint32 ebp, uint16 di)
a1 = sub_401B46(channel);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
label51:
- if (a1 < RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[bh].var_0) {
+ if (a1 < RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[*tuneId].var_0) {
rct_music_info* music_info = RCT2_GLOBAL(0x009AF42C, rct_music_info*);
if (music_info < (rct_music_info*)0x009AF46C/*music_info list end*/) {
- music_info->id = bl;
- music_info->var_1 = bh;
+ music_info->id = rideIndex;
+ music_info->var_1 = *tuneId;
music_info->offset = a1;
music_info->volume = v32;
music_info->pan = panx;
@@ -2022,16 +2008,16 @@ int sub_6BC3AC(sint16 x, sint16 y, sint16 z, uint16 bx, uint32 ebp, uint16 di)
} else {
uint32 eax;
label58:
- eax = ebp;
- ebp = bh;
- rct_music_info3* music_info3 = &RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[bh];
+ eax = position;
+ position = *tuneId;
+ rct_music_info3* music_info3 = &RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[*tuneId];
eax += music_info3->var_4;
if (eax < music_info3->var_0) {
- ebp = eax;
+ position = eax;
}
}
}
- return ebp;
+ return position;
}
/**
diff --git a/src/audio/audio.h b/src/audio/audio.h
index 44e52fbaf6..cc91205edc 100644
--- a/src/audio/audio.h
+++ b/src/audio/audio.h
@@ -213,8 +213,8 @@ void audio_close();
void pause_sounds();
void unpause_sounds();
void stop_vehicle_sounds();
-void sub_6BC348();
void sub_6BC6D8();
+int sub_6BC3AC(sint16 x, sint16 y, sint16 z, uint8 rideIndex, uint16 sampleRate, uint32 position, uint8 *tuneId);
// 0x009AF59C probably does the same job
// once it's confirmed and calls in pause_sounds() are reversed, it can be used instead of this
diff --git a/src/interface/window.c b/src/interface/window.c
index 53f2602d7c..6fcfe9fbb0 100644
--- a/src/interface/window.c
+++ b/src/interface/window.c
@@ -1792,4 +1792,39 @@ void sub_6EA73F()
window_invalidate_pressed_image_buttons(w);
RCT2_CALLPROC_X(w->event_handlers[WE_RESIZE], 0, 0, 0, 0, (int)w, 0, 0);
}
+}
+
+/**
+ * Update zoom based volume attenuation for ride music and clear music list.
+ * rct2: 0x006BC348
+ */
+void window_update_viewport_ride_music()
+{
+ rct_viewport *viewport;
+ rct_window *w;
+
+ RCT2_GLOBAL(0x009AF42C, rct_music_info*) = (rct_music_info*)0x009AF430;
+ RCT2_GLOBAL(0x00F438A4, rct_viewport*) = (rct_viewport*)-1;
+
+ for (w = RCT2_LAST_WINDOW; w >= g_window_list; w--) {
+ viewport = w->viewport;
+ if (viewport == NULL || !(viewport->flags & VIEWPORT_FLAG_SOUND_ON))
+ continue;
+
+ RCT2_GLOBAL(0x00F438A4, rct_viewport*) = viewport;
+ RCT2_GLOBAL(0x00F438A8, rct_window*) = w;
+
+ switch (viewport->zoom) {
+ case 0:
+ RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 0;
+ break;
+ case 1:
+ RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 30;
+ break;
+ default:
+ RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 60;
+ break;
+ }
+ break;
+ }
}
\ No newline at end of file
diff --git a/src/interface/window.h b/src/interface/window.h
index 27ab7d56b5..2e504e8aa2 100644
--- a/src/interface/window.h
+++ b/src/interface/window.h
@@ -463,6 +463,8 @@ void tool_cancel();
void window_close_construction_windows();
+void window_update_viewport_ride_music();
+
// Open window functions
void window_main_open();
void window_resize_gui(int width, int height);
diff --git a/src/peep/peep.c b/src/peep/peep.c
index 7412b278f5..59c60138cc 100644
--- a/src/peep/peep.c
+++ b/src/peep/peep.c
@@ -558,11 +558,11 @@ void get_arguments_from_action(rct_peep* peep, uint32 *argument_1, uint32* argum
*argument_2 = 0;
break;
case PEEP_STATE_ANSWERING:
- if (peep->pad_2C == 0){
+ if (peep->var_2C == 0){
*argument_1 = STR_WALKING;
*argument_2 = 0;
}
- else if (peep->pad_2C == 1){
+ else if (peep->var_2C == 1){
*argument_1 = STR_ANSWERING_RADIO_CALL;
*argument_2 = 0;
}
diff --git a/src/peep/peep.h b/src/peep/peep.h
index e9e665e9bb..24f2cac38c 100644
--- a/src/peep/peep.h
+++ b/src/peep/peep.h
@@ -304,7 +304,7 @@ typedef struct {
typedef struct {
uint8 sprite_identifier; // 0x00
- uint8 pad_01;
+ uint8 var_01;
uint16 var_02; // 0x02
uint16 next; // 0x04
uint16 previous; // 0x06
@@ -329,7 +329,7 @@ typedef struct {
uint16 next_z; // 0x28
uint8 var_2A;
uint8 state; // 0x2B
- uint8 pad_2C;
+ uint8 var_2C;
uint8 sprite_type; // 0x2D
uint8 type; // 0x2E
union{
@@ -363,7 +363,7 @@ typedef struct {
uint8 photo4_ride_ref; // 0x5E
uint8 pad_5F[0x09]; // 0x5C
uint8 current_ride; // 0x68
- uint8 pad_69;
+ uint8 current_ride_station; // 0x69
uint8 current_train; // 0x6A
uint8 current_car; // 0x6B
uint8 current_seat; // 0x6C
diff --git a/src/peep/staff.c b/src/peep/staff.c
index 4bbcf64275..bd176a3781 100644
--- a/src/peep/staff.c
+++ b/src/peep/staff.c
@@ -290,3 +290,34 @@ 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)
+{
+ // 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;
+
+ return staff_is_location_in_patrol_area(mechanic, x, y);
+}
\ No newline at end of file
diff --git a/src/peep/staff.h b/src/peep/staff.h
index 72e9f0c965..758159489c 100644
--- a/src/peep/staff.h
+++ b/src/peep/staff.h
@@ -22,6 +22,7 @@
#define _STAFF_H_
#include "../common.h"
+#include "peep.h"
#define STAFF_MAX_COUNT 0xC8
#define STAFF_TYPE_COUNT 0x04
@@ -45,5 +46,6 @@ void game_command_hire_new_staff_member(int* eax, int* ebx, int* ecx, int* edx,
void update_staff_colour(uint8 staff_type, uint16 color);
uint16 hire_new_staff_member(uint8 staff_type);
void sub_6C0C3F();
+int mechanic_is_location_in_patrol(rct_peep *mechanic, int x, int y);
#endif
\ No newline at end of file
diff --git a/src/ride/ride.c b/src/ride/ride.c
index 9eaca0a549..62287777d0 100644
--- a/src/ride/ride.c
+++ b/src/ride/ride.c
@@ -20,17 +20,21 @@
#include
#include "../addresses.h"
+#include "../audio/audio.h"
#include "../game.h"
#include "../interface/window.h"
+#include "../localisation/date.h"
#include "../localisation/localisation.h"
#include "../management/news_item.h"
#include "../peep/peep.h"
#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
@@ -103,9 +107,47 @@ const uint8 gRideClassifications[255] = {
#pragma endregion
+static const int RideInspectionInterval[] = {
+ 10, 20, 30, 45, 60, 120, 0, 0
+};
+
rct_ride_type **gRideTypeList = RCT2_ADDRESS(0x009ACFA4, rct_ride_type*);
rct_ride* g_ride_list = RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride);
+// 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_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()
{
rct_ride *ride;
@@ -135,44 +177,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
*/
@@ -201,157 +205,51 @@ void ride_update_favourited_stat()
window_invalidate_by_class(WC_RIDE_LIST);
}
+money32 get_shop_item_cost(int shopItem)
+{
+ return shopItem < 32 ?
+ RCT2_GLOBAL(0x00982164 + (shopItem * 8), uint16) :
+ RCT2_GLOBAL(0x00982144 + (shopItem * 8), uint16);
+}
+
/**
*
- * rct2: 0x006ABE4C
+ * rct2: 0x006AC3AB
*/
-void ride_update_all()
+money32 ride_calculate_income_per_hour(rct_ride *ride)
{
- RCT2_CALLPROC_EBPSAFE(0x006ABE4C);
-}
+ rct_ride_type *entry;
+ money32 incomePerHour, priceMinusCost;
+ int shopItem;
-/**
- * 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];
+ entry = GET_RIDE_ENTRY(ride->subtype);
+ incomePerHour =
+ ride->var_124 +
+ ride->var_126 +
+ ride->var_128 +
+ ride->var_12A +
+ ride->var_12C +
+ ride->var_12E +
+ ride->age +
+ ride->running_cost +
+ ride->var_134 +
+ ride->var_136;
+ incomePerHour *= 12;
+ priceMinusCost = ride->price;
- 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++;
+ shopItem = entry->shop_item;
+ if (shopItem != 255) {
+ priceMinusCost -= get_shop_item_cost(shopItem);
+
+ shopItem = entry->shop_item_secondary;
+ if (shopItem != 255) {
+ priceMinusCost -= get_shop_item_cost(shopItem);
+ priceMinusCost /= 2;
+ }
}
- 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);
- }
+ incomePerHour *= priceMinusCost;
+ return incomePerHour;
}
/**
@@ -432,6 +330,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;
@@ -482,50 +498,638 @@ 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;
+ // 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;
}
+
+ window_update_viewport_ride_music();
+
+ // Update rides
+ FOR_ALL_RIDES(i, ride)
+ ride_update(i);
+
+ sub_6BC6D8();
+}
+
+/**
+ *
+ * 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_120 = 0;
+ ride->var_14D |= 1;
+
+ ride->income_per_hour = ride_calculate_income_per_hour(ride);
+ ride->var_14D |= 2;
+
+ if (ride->upkeep_cost != (money16)0xFFFF)
+ ride->profit = (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->mechanic_status = RIDE_MECHANIC_STATUS_CALLING;
+ 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)
@@ -547,118 +1151,88 @@ 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;
-
- // 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;
-}
-
-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 region Music functions
/**
*
- * 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: 0x006ABE85
*/
-void ride_init_vehicle_speed(rct_ride *ride)
+static void ride_music_update(int rideIndex)
{
- rct_ride_type *rideEntry;
+ int x, y, z;
rct_vehicle *vehicle;
- uint8 *unk;
- int i;
+ rct_ride *ride = GET_RIDE(rideIndex);
- for (i = 0; i < ride->num_vehicles; i++) {
- vehicle = &g_sprite_list[ride->vehicles[i]].vehicle;
- vehicle->var_48 &= ~(1 << 6);
+ if (!(RCT2_GLOBAL(0x0097D4F2 + (ride->type * 8), uint16) & 6))
+ return;
- rideEntry = GET_RIDE_ENTRY(vehicle->var_D6);
- unk = (uint8*)((int)rideEntry + (vehicle->var_31 * 0x65));
+ if (ride->status != RIDE_STATUS_OPEN || !(ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC)) {
+ ride->music_tune_id = 255;
+ return;
+ }
- vehicle->speed = (scenario_rand() & 16) - 8 + RCT2_GLOBAL(unk + 0x76, uint8);
+ 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;
+ }
+ }
- if (vehicle->var_B3) {
- rct_peep *peep = &g_sprite_list[vehicle->peep].peep;
+ // 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++;
+ }
- 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;
+ if (ride->var_1AC == 255) {
+ ride->music_tune_id = 255;
+ return;
}
}
}
-}
-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--;
+ // 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;
}
- return entryIndexList;
+
+ 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);
}
+#pragma endregion
+#pragma region Measurement functions
/**
* rct2: 0x006B64F2
@@ -778,6 +1352,34 @@ void ride_measurements_update()
}
}
+rct_ride_measurement *ride_get_existing_measurement(int rideIndex)
+{
+ int i;
+ rct_ride_measurement *measurement;
+
+ for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) {
+ measurement = GET_RIDE_MEASUREMENT(i);
+ if (measurement->ride_index == rideIndex)
+ return measurement;
+ }
+
+ return NULL;
+}
+
+rct_ride_measurement *ride_get_free_measurement()
+{
+ int i;
+ rct_ride_measurement *measurement;
+
+ for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) {
+ measurement = GET_RIDE_MEASUREMENT(i);
+ if (measurement->ride_index == 255)
+ return measurement;
+ }
+
+ return NULL;
+}
+
/**
*
* rct2: 0x006B66D9
@@ -798,45 +1400,37 @@ rct_ride_measurement *ride_get_measurement(int rideIndex, rct_string_id *message
}
// Check if a measurement already exists for this ride
- for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) {
- measurement = GET_RIDE_MEASUREMENT(i);
- if (measurement->ride_index == i)
- goto use_measurement;
- }
+ measurement = ride_get_existing_measurement(rideIndex);
+ if (measurement == NULL) {
+ // Find a free measurement
+ measurement = ride_get_free_measurement();
+ if (measurement == NULL) {
+ // Use last recently used measurement for some other ride
+ lruIndex = 0;
+ lruTicks = 0xFFFFFFFF;
+ for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) {
+ measurement = GET_RIDE_MEASUREMENT(i);
- // Find a free measurement
- for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) {
- measurement = GET_RIDE_MEASUREMENT(i);
- if (measurement->ride_index == 255)
- goto new_measurement;
- }
+ if (measurement->last_use_tick <= lruTicks) {
+ lruTicks = measurement->last_use_tick;
+ lruIndex = i;
+ }
+ }
- // Use last recently used measurement for some other ride
- lruIndex = 0;
- lruTicks = 0xFFFFFFFF;
- for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) {
- measurement = GET_RIDE_MEASUREMENT(i);
-
- if (measurement->last_use_tick <= lruTicks) {
- lruTicks = measurement->last_use_tick;
- lruIndex = i;
+ i = lruIndex;
+ measurement = GET_RIDE_MEASUREMENT(i);
+ ride->measurement_index = 255;
}
+
+ measurement->ride_index = rideIndex;
+ ride->measurement_index = i;
+ measurement->flags = 0;
+ if (RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + (ride->type * 8), uint32) & 0x80)
+ measurement->flags |= RIDE_MEASUREMENT_FLAG_G_FORCES;
+ measurement->num_items = 0;
+ measurement->current_item = 0;
}
- i = lruIndex;
- measurement = GET_RIDE_MEASUREMENT(i);
- ride->measurement_index = 255;
-
-new_measurement:
- measurement->ride_index = rideIndex;
- ride->measurement_index = i;
- measurement->flags = 0;
- if (RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + (ride->type * 8), uint32) & 0x80)
- measurement->flags |= RIDE_MEASUREMENT_FLAG_G_FORCES;
- measurement->num_items = 0;
- measurement->current_item = 0;
-
-use_measurement:
measurement->last_use_tick = RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, uint32);
if (measurement->flags & 1) {
if (message != NULL) *message = 0;
@@ -847,4 +1441,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 fe255e6920..f87a90d903 100644
--- a/src/ride/ride.h
+++ b/src/ride/ride.h
@@ -97,7 +97,7 @@ typedef struct {
uint16 station_starts[4]; // 0x052
uint8 station_heights[4]; // 0x05A
uint8 pad_05E[0x4];
- uint8 var_062[4];
+ uint8 station_depart[4]; // 0x062
uint8 pad_066[0x4];
uint16 entrances[4]; // 0x06A
uint16 exits[4]; // 0x072
@@ -114,7 +114,11 @@ typedef struct {
uint8 var_0CD;
uint8 min_waiting_time; // 0x0CE
uint8 max_waiting_time; // 0x0CF
- uint8 var_0D0;
+ union {
+ uint8 var_0D0;
+ uint8 time_limit; // 0x0D0
+ uint8 num_laps; // 0x0D0
+ };
uint8 pad_0D1[0x3];
uint8 measurement_index; // 0x0D4
uint8 var_0D5;
@@ -178,20 +182,26 @@ typedef struct {
uint16 var_158;
uint8 pad_15A;
uint8 num_riders; // 0x15B
- uint8 var_15C;
+ uint8 music_tune_id; // 0x15C
uint8 var_15D;
- uint16 maze_tiles; // 0x15E
+ union {
+ uint16 slide_peep; // 0x15E
+ uint16 maze_tiles; // 0x15E
+ };
uint8 pad_160[0x16];
uint8 var_176;
uint8 pad_177[0x9];
sint16 build_date; // 0x180
money16 upkeep_cost; // 0x182
uint16 race_winner; // 0x184
- uint8 pad_186[0x06];
- uint8 var_18C;
+ uint8 pad_186[0x02];
+ uint32 music_position; // 0x188
+ uint8 breakdown_reason_pending; // 0x18C
uint8 mechanic_status; // 0x18D
uint16 mechanic; // 0x18E
- uint8 pad_190[0x03];
+ uint8 inspection_station; // 0x190
+ uint8 broken_vehicle; // 0x191
+ uint8 broken_car; // 0x192
uint8 breakdown_reason; // 0x193
money16 price_secondary; // 0x194
uint16 var_196;
@@ -200,9 +210,18 @@ typedef struct {
uint8 var_199;
uint8 inspection_interval; // 0x19A
uint8 last_inspection; // 0x19B
- uint8 pad_19C[0x8];
+ uint8 var_19C;
+ uint8 var_19D;
+ uint8 var_19E;
+ uint8 var_19F;
+ uint8 var_1A0;
+ uint8 var_1A1;
+ uint8 var_1A2;
+ uint8 var_1A3;
uint32 var_1A4;
- uint8 pad_1A8[6];
+ uint8 pad_1A8[4];
+ uint8 var_1AC;
+ uint8 var_1AD;
uint8 var_1AE;
uint8 connected_message_throttle; // 0x1AF
money32 income_per_hour; // 0x1B0
@@ -261,8 +280,9 @@ 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_BREAKDOWN_PENDING = 1 << 6,
RIDE_LIFECYCLE_BROKEN_DOWN = 1 << 7,
+ RIDE_LIFECYCLE_DUE_INSPECTION = 1 << 8,
RIDE_LIFECYCLE_CRASHED = 1 << 10,
RIDE_LIFECYCLE_11 = 1 << 11,
@@ -480,9 +500,11 @@ enum {
};
enum {
- RIDE_MECHANIC_STATUS_CALLING = 1,
- RIDE_MECHANIC_STATUS_HEADING = 2,
- RIDE_MECHANIC_STATUS_FIXING = 3,
+ RIDE_MECHANIC_STATUS_UNDEFINED,
+ RIDE_MECHANIC_STATUS_CALLING,
+ RIDE_MECHANIC_STATUS_HEADING,
+ RIDE_MECHANIC_STATUS_FIXING,
+ RIDE_MECHANIC_STATUS_4
};
enum {
@@ -522,6 +544,16 @@ enum {
RIDE_ENTRANCE_STYLE_SPACE
};
+enum {
+ RIDE_INSPECTION_EVERY_10_MINUTES,
+ RIDE_INSPECTION_EVERY_20_MINUTES,
+ RIDE_INSPECTION_EVERY_30_MINUTES,
+ RIDE_INSPECTION_EVERY_45_MINUTES,
+ RIDE_INSPECTION_EVERY_HOUR,
+ RIDE_INSPECTION_EVERY_2_HOURS,
+ RIDE_INSPECTION_NEVER
+};
+
typedef struct {
uint8 main;
uint8 additional;
@@ -545,6 +577,9 @@ enum {
#define MAX_RIDE_MEASUREMENTS 8
#define RIDE_RELIABILITY_UNDEFINED 0xFFFF
+#define STATION_DEPART_FLAG (1 << 7)
+#define STATION_DEPART_MASK (~STATION_DEPART_FLAG)
+
// rct2: 0x009ACFA4
rct_ride_type **gRideTypeList;
@@ -587,5 +622,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/ride_data.c b/src/ride/ride_data.c
index b1ae1d8b85..d6645eef1a 100644
--- a/src/ride/ride_data.c
+++ b/src/ride/ride_data.c
@@ -771,4 +771,98 @@ const uint8 RideAvailableModes[] = {
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 58 Mine Ride
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 59 LIM Launched Roller Coaster
RIDE_MODE_POWERED_LAUNCH_35, RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED, 0xFF // 60 (none)
+};
+
+const uint8 RideAvailableBreakdowns[] = {
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 00 Spiral Roller coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 01 Stand Up Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 02 Suspended Swinging
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 03 Inverted
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 04 Steel Mini Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 05 Mini Railroad
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_DOORS_STUCK_CLOSED) | (1 << BREAKDOWN_DOORS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 06 Monorail
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 07 Mini Suspended Coaster
+ (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 08 Bumper Boats
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 09 Wooden Wild Mine/Mouse
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 0A Steeplechase/Motorbike/Soap Box Derby
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 0B Car Ride
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 0C Launched Freefall
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 0D Bobsleigh Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 0E Observation Tower
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 0F Looping Roller Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 10 Dinghy Slide
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 11 Mine Train Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 12 Chairlift
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 13 Corkscrew Roller Coaster
+ 0, // 14 Maze
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 15 Spiral Slide
+ (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 16 Go Karts
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_CONTROL_FAILURE), // 17 Log Flume
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_CONTROL_FAILURE), // 18 River Rapids
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 19 Bumper Cars
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 1A Pirate Ship
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 1B Swinging Inverter Ship
+ 0, // 1C Food Stall
+ 0, // 1D (none)
+ 0, // 1E Drink Stall
+ 0, // 1F (none)
+ 0, // 20 Shop (all types)
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_CONTROL_FAILURE), // 21 Merry Go Round
+ 0, // 22 Balloon Stall (maybe)
+ 0, // 23 Information Kiosk
+ 0, // 24 Bathroom
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 25 Ferris Wheel
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 26 Motion Simulator
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 27 3D Cinema
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 28 Gravitron
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 29 Space Rings
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 2A Reverse Freefall Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_DOORS_STUCK_CLOSED) | (1 << BREAKDOWN_DOORS_STUCK_OPEN), // 2B Elevator
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 2C Vertical Drop Roller Coaster
+ 0, // 2D ATM
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 2E Twist
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 2F Haunted House
+ 0, // 30 First Aid
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 31 Circus Show
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 32 Ghost Train
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 33 Twister Roller Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 34 Wooden Roller Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 35 Side-Friction Roller Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 36 Wild Mouse
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 37 Multi Dimension Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 38 (none)
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 39 Flying Roller Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 3A (none)
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 3B Virginia Reel
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_CONTROL_FAILURE), // 3C Splash Boats
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 3D Mini Helicopters
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 3E Lay-down Roller Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_DOORS_STUCK_CLOSED) | (1 << BREAKDOWN_DOORS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 3F Suspended Monorail
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 40 (none)
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 41 Reverser Roller Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 42 Heartline Twister Roller Coaster
+ 0, // 43 Mini Golf
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 44 Giga Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 45 Roto-Drop
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 46 Flying Saucers
+ 0, // 47 Crooked House
+ (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 48 Monorail Cycles
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 49 Compact Inverted Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 4A Water Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 4B Air Powered Vertical Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 4C Inverted Hairpin Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 4D Magic Carpet
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 4E Submarine Ride
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 4F River Rafts
+ 0, // 50 (none)
+ (1 << BREAKDOWN_SAFETY_CUT_OUT), // 51 Enterprise
+ 0, // 52 (none)
+ 0, // 53 (none)
+ 0, // 54 (none)
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 55 (none)
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 56 Inverted Impulse Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 57 Mini Roller Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION), // 58 Mine Ride
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE), // 59 LIM Launched Roller Coaster
+ (1 << BREAKDOWN_SAFETY_CUT_OUT) | (1 << BREAKDOWN_RESTRAINTS_STUCK_CLOSED) | (1 << BREAKDOWN_RESTRAINTS_STUCK_OPEN) | (1 << BREAKDOWN_VEHICLE_MALFUNCTION) | (1 << BREAKDOWN_BRAKES_FAILURE) // 60 (none)
};
\ No newline at end of file
diff --git a/src/ride/ride_data.h b/src/ride/ride_data.h
index d6b1617b04..d45438480d 100644
--- a/src/ride/ride_data.h
+++ b/src/ride/ride_data.h
@@ -41,5 +41,6 @@ extern const uint8 rideUnknownData3[0x60];
extern const rct_ride_name_convention RideNameConvention[96];
extern const uint8 RideAvailableModes[];
+extern const uint8 RideAvailableBreakdowns[];
#endif
\ No newline at end of file
diff --git a/src/ride/station.c b/src/ride/station.c
new file mode 100644
index 0000000000..38b7b5f792
--- /dev/null
+++ b/src/ride/station.c
@@ -0,0 +1,309 @@
+/*****************************************************************************
+ * 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);
+static void ride_update_station_bumpercar(rct_ride *ride, int stationIndex);
+static void ride_update_station_normal(rct_ride *ride, int stationIndex);
+static void ride_update_station_race(rct_ride *ride, int stationIndex);
+static void ride_race_init_vehicle_speeds(rct_ride *ride);
+static void ride_invalidate_station_start(rct_ride *ride, int stationIndex, int dl);
+
+/**
+ *
+ * rct2: 0x006ABFFB
+ */
+void ride_update_station(rct_ride *ride, int stationIndex)
+{
+ if (ride->station_starts[stationIndex] == 0xFFFF)
+ return;
+
+ switch (ride->mode) {
+ case RIDE_MODE_RACE:
+ ride_update_station_race(ride, stationIndex);
+ break;
+ case RIDE_MODE_BUMPERCAR:
+ ride_update_station_bumpercar(ride, stationIndex);
+ break;
+ case RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED:
+ case RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED:
+ ride_update_station_blocksection(ride, stationIndex);
+ break;
+ default:
+ ride_update_station_normal(ride, stationIndex);
+ break;
+ }
+}
+
+/**
+ *
+ * rct2: 0x006AC0A1
+ */
+static void ride_update_station_blocksection(rct_ride *ride, int stationIndex)
+{
+ rct_map_element *mapElement;
+
+ mapElement = ride_get_station_start_track_element(ride, stationIndex);
+
+ if ((ride->status == RIDE_STATUS_CLOSED && ride->num_riders == 0) || mapElement->flags & 0x20) {
+ ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG;
+
+ if ((ride->station_depart[stationIndex] & STATION_DEPART_FLAG) || (mapElement->properties.track.sequence & 0x80))
+ ride_invalidate_station_start(ride, stationIndex, 0);
+ } else {
+ if (!(ride->station_depart[stationIndex] & STATION_DEPART_FLAG)) {
+ ride->station_depart[stationIndex] |= STATION_DEPART_FLAG;
+ ride_invalidate_station_start(ride, stationIndex, 1);
+ } else if (mapElement->properties.track.sequence & 0x80) {
+ ride_invalidate_station_start(ride, stationIndex, 1);
+ }
+ }
+}
+
+/**
+ *
+ * rct2: 0x006AC12B
+ */
+static void ride_update_station_bumpercar(rct_ride *ride, int stationIndex)
+{
+ int i, dx, dl, dh;
+ rct_vehicle *vehicle;
+
+ if (
+ ride->status == RIDE_STATUS_CLOSED ||
+ (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED))
+ ) {
+ ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG;
+ return;
+ }
+
+ if (ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) {
+ dx = ride->time_limit * 32;
+ dl = dx & 0xFF;
+ dh = (dx >> 8) & 0xFF;
+ for (i = 0; i < ride->num_vehicles; i++) {
+ vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle);
+ if (vehicle->var_CE < dh || (vehicle->var_CE < dh && vehicle->var_51 > dl))
+ continue;
+
+ // End match
+ ride->lifecycle_flags &= ~RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING;
+ ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG;
+ return;
+ }
+
+ // Continue match
+ ride->station_depart[stationIndex] |= STATION_DEPART_FLAG;
+ } else {
+ // Check if all vehicles are ready to go
+ for (i = 0; i < ride->num_vehicles; i++) {
+ vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle);
+ if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART) {
+ ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG;
+ return;
+ }
+ }
+
+ // Begin the match
+ ride->lifecycle_flags |= RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING;
+ ride->station_depart[stationIndex] |= STATION_DEPART_FLAG;
+ ride->var_14D |= 12;
+ }
+}
+
+/**
+ *
+ * rct2: 0x006AC02C
+ */
+static void ride_update_station_normal(rct_ride *ride, int stationIndex)
+{
+ int time;
+
+ time = ride->station_depart[stationIndex] & STATION_DEPART_MASK;
+ if (
+ (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) &&
+ (ride->status == RIDE_STATUS_CLOSED && ride->num_riders == 0)
+ ) {
+ if (time != 0 && time != 127 && !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 7))
+ time--;
+
+ ride->station_depart[stationIndex] = time;
+ } else {
+ if (time == 0) {
+ ride->station_depart[stationIndex] |= STATION_DEPART_FLAG;
+ } else {
+ if (time != 127 && !(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 31))
+ time--;
+
+ ride->station_depart[stationIndex] = time;
+ }
+ }
+}
+
+/**
+ *
+ * rct2: 0x006AC1DF
+ */
+static void ride_update_station_race(rct_ride *ride, int stationIndex)
+{
+ int i, numLaps;
+ rct_vehicle *vehicle;
+ rct_peep *peep;
+
+ if (
+ ride->status == RIDE_STATUS_CLOSED ||
+ (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED))
+ ) {
+ ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG;
+ return;
+ }
+
+ if (ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) {
+ numLaps = ride->num_laps;
+ for (i = 0; i < ride->num_vehicles; i++) {
+ vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle);
+ if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART && vehicle->num_laps >= numLaps) {
+ // Found a winner
+ if (vehicle->var_B3 != 0) {
+ peep = &(g_sprite_list[vehicle->peep].peep);
+ ride->race_winner = peep->sprite_index;
+ ride->var_14D |= 12;
+ }
+
+ // Race is over
+ ride->lifecycle_flags &= ~RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING;
+ ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG;
+ return;
+ }
+ }
+
+ // Continue racing
+ ride->station_depart[stationIndex] |= STATION_DEPART_FLAG;
+ } else {
+ // Check if all vehicles are ready to go
+ for (i = 0; i < ride->num_vehicles; i++) {
+ vehicle = &(g_sprite_list[ride->vehicles[i]].vehicle);
+ if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART && vehicle->status != VEHICLE_STATUS_DEPARTING) {
+ ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG;
+ return;
+ }
+ }
+
+ // Begin the race
+ ride_race_init_vehicle_speeds(ride);
+ ride->lifecycle_flags |= RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING;
+ ride->station_depart[stationIndex] |= STATION_DEPART_FLAG;
+ ride->var_14D |= 12;
+ }
+}
+
+/**
+ *
+ * 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);
+}
+
+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/ride/vehicle.h b/src/ride/vehicle.h
index 4f7f0db47c..9e7b0c656b 100644
--- a/src/ride/vehicle.h
+++ b/src/ride/vehicle.h
@@ -76,7 +76,10 @@ typedef struct {
uint8 pad_C3[0x09];
uint8 var_CC;
uint8 var_CD;
- uint8 var_CE;
+ union {
+ uint8 var_CE;
+ uint8 num_laps; // 0xCE
+ };
uint8 pad_CF[0x07];
uint8 var_D6;
} rct_vehicle;
diff --git a/src/scenario.c b/src/scenario.c
index 1a81cbe709..829f69c32e 100644
--- a/src/scenario.c
+++ b/src/scenario.c
@@ -642,7 +642,7 @@ void scenario_update()
*
* rct2: 0x006E37D2
*/
-int scenario_rand()
+unsigned int scenario_rand()
{
int eax = RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_SRAND_0, uint32);
RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_SRAND_0, uint32) += ror32(RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_SRAND_1, uint32) ^ 0x1234567F, 7);
diff --git a/src/scenario.h b/src/scenario.h
index e969e8b34d..221a673a22 100644
--- a/src/scenario.h
+++ b/src/scenario.h
@@ -407,6 +407,6 @@ int scenario_load(const char *path);
int scenario_load_and_play(const rct_scenario_basic *scenario);
int scenario_load_and_play_from_path(const char *path);
void scenario_update();
-int scenario_rand();
+unsigned int scenario_rand();
#endif
diff --git a/src/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/windows/guest.c b/src/windows/guest.c
index 4baa96add5..842a936510 100644
--- a/src/windows/guest.c
+++ b/src/windows/guest.c
@@ -627,7 +627,7 @@ void window_guest_overview_mouse_up(){
RCT2_CALLPROC_X(0x0069E9D3, 0x8000, 0, peep->y, peep->z, (int)peep, 0, 0);
RCT2_CALLPROC_X(0x0069A409, 0, 0, 0, 0, (int)peep, 0, 0);
peep->state = 9;
- peep->pad_2C = 0;
+ peep->var_2C = 0;
RCT2_CALLPROC_X(0x0069A42F, 0, 0, 0, 0, (int)peep, 0, 0);
break;
case WIDX_RENAME:
@@ -1204,7 +1204,7 @@ void window_guest_overview_tool_down(){
int dest_z = ((uint8*)edx)[2] * 8 + 16;
- if (sub_664F72(tile_x, tile_y, dest_z)){
+ if (!sub_664F72(tile_x, tile_y, dest_z)){
window_error_open(0x785,-1);
return;
}
diff --git a/src/windows/staff.c b/src/windows/staff.c
index a22ad15bab..c6a8e2141f 100644
--- a/src/windows/staff.c
+++ b/src/windows/staff.c
@@ -1124,7 +1124,7 @@ void window_staff_overview_tool_down(){
int dest_z = ((uint8*)edx)[2] * 8 + 16;
- if (sub_664F72(tile_x, tile_y, dest_z)){
+ if (!sub_664F72(tile_x, tile_y, dest_z)){
window_error_open(0x785, -1);
return;
}
diff --git a/src/world/map.c b/src/world/map.c
index 11b22e7763..b1da00915b 100644
--- a/src/world/map.c
+++ b/src/world/map.c
@@ -437,27 +437,56 @@ static void sub_6A87BB(int x, int y)
RCT2_CALLPROC_X(0x006A87BB, x, 0, y, 0, 0, 0, 0);
}
-/* rct2: 0x664F72 */
-int sub_664F72(int x, int y, int z){
- if (x > 0x1FFF || y > 0x1FFF){
- RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = 0x6C1;
+/**
+ *
+ * rct2: 0x00664F72
+ */
+int sub_664F72(int x, int y, int z)
+{
+ rct_map_element *mapElement;
+
+ if (x < (256 * 32) && y < (256 * 32)) {
+ mapElement = map_get_surface_element_at(x / 32, y / 32);
+ if (mapElement->properties.surface.ownership & 0x20)
+ return 1;
+
+ if (mapElement->properties.surface.ownership & 0x10) {
+ z /= 8;
+ if (z < mapElement->base_height || z - 2 > mapElement->base_height)
+ return 1;
+ }
+ }
+
+ RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = 1729;
+ return 0;
+}
+
+/**
+ *
+ * rct2: 0x00664F2C
+ */
+int map_is_location_in_park(int x, int y)
+{
+ rct_map_element *mapElement;
+
+ if (x < (256 * 32) && y < (256 * 32)) {
+ mapElement = map_get_surface_element_at(x / 32, y / 32);
+ if (mapElement->properties.surface.ownership & 0x20)
return 1;
}
- rct_map_element* map_element = map_get_surface_element_at(x / 32, y / 32);
- if (map_element->properties.surface.ownership & 0x20) return 0;
- if (!(map_element->properties.surface.ownership & 0x10)){
- RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = 0x6C1;
- return 1;
- }
+ RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = 1729;
+ return 0;
+}
- z >>= 3;
- if ((z & 0xFF) < map_element->base_height)return 0;
- z = (z & 0xFF) - 2;
- if (z > map_element->base_height)return 0;
-
- RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = 0x6C1;
- return 1;
+/**
+ *
+ * rct2: 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);
}
/**
diff --git a/src/world/map.h b/src/world/map.h
index 113d69d2d8..4ff0115588 100644
--- a/src/world/map.h
+++ b/src/world/map.h
@@ -206,6 +206,8 @@ int map_coord_is_connected(uint16 coordinate, uint8 height, uint8 face_direction
void map_invalidate_animations();
void sub_6A876D();
int sub_664F72(int x, int y, int z);
+int map_is_location_in_park(int x, int y);
+void map_invalidate_tile(int x, int y, int zLow, int zHigh);
void fountain_update_all();
diff --git a/src/world/park.c b/src/world/park.c
index 1cf7dfb678..9130a3fb64 100644
--- a/src/world/park.c
+++ b/src/world/park.c
@@ -508,7 +508,7 @@ static rct_peep *park_generate_new_guest_due_to_campaign(int campaign)
static void park_generate_new_guests()
{
// Generate a new guest for some probability
- if ((scenario_rand() & 0xFFFF) < _guestGenerationProbability) {
+ if ((int)(scenario_rand() & 0xFFFF) < _guestGenerationProbability) {
int difficultGeneration = (RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_DIFFICULT_GUEST_GENERATION) != 0;
if (!difficultGeneration || _suggestedGuestMaximum + 150 >= RCT2_GLOBAL(RCT2_ADDRESS_GUESTS_IN_PARK, uint16))
park_generate_new_guest();
@@ -519,7 +519,7 @@ static void park_generate_new_guests()
for (campaign = 0; campaign < ADVERTISING_CAMPAIGN_COUNT; campaign++) {
if (RCT2_ADDRESS(0x01358102, uint8)[campaign] != 0) {
// Random chance of guest generation
- if ((scenario_rand() & 0xFFFF) < marketing_get_campaign_guest_generation_probability(campaign))
+ if ((int)(scenario_rand() & 0xFFFF) < marketing_get_campaign_guest_generation_probability(campaign))
park_generate_new_guest_due_to_campaign(campaign);
}
}