diff --git a/OpenRCT2.xcodeproj/project.pbxproj b/OpenRCT2.xcodeproj/project.pbxproj index 9312acd27b4e..55b21a689295 100644 --- a/OpenRCT2.xcodeproj/project.pbxproj +++ b/OpenRCT2.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ D43532601C34730200BA219B /* libpng.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D435325E1C3472E500BA219B /* libpng.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; D46105CE1C38828D00DB1EE3 /* scenario_sources.c in Sources */ = {isa = PBXBuildFile; fileRef = D46105CD1C38828D00DB1EE3 /* scenario_sources.c */; }; D4ABAB061C2F812B0080CAD9 /* news_options.c in Sources */ = {isa = PBXBuildFile; fileRef = D4ABAB051C2F812B0080CAD9 /* news_options.c */; }; + D4B85B5B1C41C7F3005C568A /* cable_lift.c in Sources */ = {isa = PBXBuildFile; fileRef = D4B85B591C41C7F3005C568A /* cable_lift.c */; }; D4B8C2A81C41EADF00B006AC /* argparse.c in Sources */ = {isa = PBXBuildFile; fileRef = D4B8C2A61C41EADF00B006AC /* argparse.c */; }; D4D4DF141C34697B0048BE43 /* image_io.c in Sources */ = {isa = PBXBuildFile; fileRef = D4D4DF121C34697B0048BE43 /* image_io.c */; }; D4EC47DF1C26342F0024B507 /* addresses.c in Sources */ = {isa = PBXBuildFile; fileRef = D4EC46D61C26342F0024B507 /* addresses.c */; }; @@ -221,6 +222,8 @@ D4895D321C23EFDD000CD788 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = distribution/osx/Info.plist; sourceTree = SOURCE_ROOT; }; D497D0781C20FD52002BF46A /* OpenRCT2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OpenRCT2.app; sourceTree = BUILT_PRODUCTS_DIR; }; D4ABAB051C2F812B0080CAD9 /* news_options.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = news_options.c; sourceTree = ""; }; + D4B85B591C41C7F3005C568A /* cable_lift.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cable_lift.c; sourceTree = ""; }; + D4B85B5A1C41C7F3005C568A /* cable_lift.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cable_lift.h; sourceTree = ""; }; D4B8C2A61C41EADF00B006AC /* argparse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = argparse.c; sourceTree = ""; }; D4B8C2A71C41EADF00B006AC /* argparse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = argparse.h; sourceTree = ""; }; D4D4DF121C34697B0048BE43 /* image_io.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = image_io.c; path = src/image_io.c; sourceTree = ""; }; @@ -856,6 +859,8 @@ D4EC475C1C26342F0024B507 /* ride */ = { isa = PBXGroup; children = ( + D4B85B591C41C7F3005C568A /* cable_lift.c */, + D4B85B5A1C41C7F3005C568A /* cable_lift.h */, D4EC475D1C26342F0024B507 /* ride.c */, D4EC475E1C26342F0024B507 /* ride.h */, D4EC475F1C26342F0024B507 /* ride_data.c */, @@ -1425,6 +1430,7 @@ D4EC48111C26342F0024B507 /* network.cpp in Sources */, D4EC47FF1C26342F0024B507 /* widget.c in Sources */, D4EC48691C26342F0024B507 /* title_options.c in Sources */, + D4B85B5B1C41C7F3005C568A /* cable_lift.c in Sources */, D4EC48471C26342F0024B507 /* map_tooltip.c in Sources */, D4EC483F1C26342F0024B507 /* guest.c in Sources */, D4EC487A1C26342F0024B507 /* map_helpers.c in Sources */, diff --git a/openrct2.vcxproj b/openrct2.vcxproj index df7f253bfc8f..c390fa4819a0 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -85,6 +85,7 @@ + @@ -247,6 +248,7 @@ + @@ -370,4 +372,4 @@ - \ No newline at end of file + diff --git a/openrct2.vcxproj.filters b/openrct2.vcxproj.filters index 95890bc7c5b0..72b54f2aeba3 100644 --- a/openrct2.vcxproj.filters +++ b/openrct2.vcxproj.filters @@ -543,8 +543,11 @@ Source + + Source\Ride + - Source + Source\Drawing Source\Windows @@ -830,6 +833,12 @@ Source\Interface + + Source\Ride + + + Source\Core + Source\Drawing @@ -852,4 +861,4 @@ Source\argparse - \ No newline at end of file + diff --git a/src/audio/mixer.cpp b/src/audio/mixer.cpp index 8df91e49c95d..7f23802a26b9 100644 --- a/src/audio/mixer.cpp +++ b/src/audio/mixer.cpp @@ -806,6 +806,7 @@ void* Mixer_Play_Effect(size_t id, int loop, int volume, float pan, double rate, return 0; } if (id >= Util::CountOf(gMixer.css1sources)) { + log_error("Tried to play an invalid sound id. %i", id); return 0; } gMixer.Lock(); diff --git a/src/hook.c b/src/hook.c index 66b33e3e7331..ebed9b189c9c 100644 --- a/src/hook.c +++ b/src/hook.c @@ -87,7 +87,7 @@ void hookfunc(int address, int newaddress, int stacksize, int registerargs[], in data[i++] = 0x89; //mov eax, esp data[i++] = 0xE0; data[i++] = 0x83; //sub eax, (0xC + numargs*4) & 0xF - data[i++] = 0xE8; + data[i++] = 0xE8; data[i++] = (0xC + numrargs * 4) & 0xF; data[i++] = 0x83; //and eax, 0xC data[i++] = 0xE0; @@ -95,7 +95,7 @@ void hookfunc(int address, int newaddress, int stacksize, int registerargs[], in data[i++] = 0xA3; //mov [0x9ABDA8], eax data[i++] = 0xA8; data[i++] = 0xBD; - data[i++] = 0x9A; + data[i++] = 0x9A; data[i++] = 0x00; data[i++] = 0x58; //pop eax data[i++] = 0x2B; //sub esp, [0x9ABDA8] diff --git a/src/object.c b/src/object.c index 3e1058c46f29..bfd160ccb4fe 100644 --- a/src/object.c +++ b/src/object.c @@ -501,14 +501,14 @@ static bool object_type_ride_load(void *objectEntry, uint32 entryIndex) if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_FLAT) { int al = 1; - if (vehicleEntry->var_14 & 2) { + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_SWINGING) { al = 13; - if ((vehicleEntry->var_14 & 0x820) != 0x820) { + if ((vehicleEntry->flags_b & (VEHICLE_ENTRY_FLAG_B_5 | VEHICLE_ENTRY_FLAG_B_11)) != (VEHICLE_ENTRY_FLAG_B_5 | VEHICLE_ENTRY_FLAG_B_11)) { al = 7; - if (!(vehicleEntry->var_14 & 0x20)) { - if (!(vehicleEntry->var_14 & 0x800)) { + if (!(vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_5)) { + if (!(vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_11)) { al = 5; - if (vehicleEntry->var_14 & 0x200) { + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_9) { al = 3; } } @@ -519,18 +519,18 @@ static bool object_type_ride_load(void *objectEntry, uint32 entryIndex) // 0x6DE90B al = 0x20; - if (!(vehicleEntry->var_12 & 0x4000)) { + if (!(vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_14)) { al = 1; - if (vehicleEntry->var_14 & 0x80) { + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_7) { if (vehicleEntry->var_11 != 6) { al = 2; - if (!(vehicleEntry->var_12 & 0x80)) { + if (!(vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_7)) { al = 4; } } } } - if (vehicleEntry->var_12 & 0x1000) { + if (vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_12) { al = vehicleEntry->special_frames; } vehicleEntry->var_02 = al; @@ -543,7 +543,7 @@ static bool object_type_ride_load(void *objectEntry, uint32 entryIndex) if (vehicleEntry->car_visual != VEHICLE_VISUAL_RIVER_RAPIDS) { int b = vehicleEntry->var_16 * 32; - if (vehicleEntry->var_12 & 0x800) b /= 2; + if (vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_11) b /= 2; if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_15) b /= 8; image_index += b; @@ -552,7 +552,7 @@ static bool object_type_ride_load(void *objectEntry, uint32 entryIndex) if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_GENTLE_SLOPES) { vehicleEntry->var_20 = image_index; b = vehicleEntry->var_16 * 72; - if (vehicleEntry->var_12 & 0x4000) + if (vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_14) b = vehicleEntry->var_16 * 16; image_index += b; @@ -564,12 +564,14 @@ static bool object_type_ride_load(void *objectEntry, uint32 entryIndex) b = vehicleEntry->var_16 * 80; image_index += b; } + // Verticle if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_VERTICAL_SLOPES) { vehicleEntry->var_28 = image_index; b = vehicleEntry->var_16 * 116; image_index += b; } + // Unknown if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_DIAGONAL_SLOPES) { vehicleEntry->var_2C = image_index; @@ -596,12 +598,14 @@ static bool object_type_ride_load(void *objectEntry, uint32 entryIndex) b = vehicleEntry->var_16 * 128; image_index += b; } + // Unknown if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_DIAGONAL_GENTLE_SLOPE_BANKED_TRANSITIONS) { vehicleEntry->var_3C = image_index; b = vehicleEntry->var_16 * 16; image_index += b; } + // Unknown if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_GENTLE_SLOPE_BANKED_TRANSITIONS) { vehicleEntry->var_40 = image_index; @@ -626,6 +630,7 @@ static bool object_type_ride_load(void *objectEntry, uint32 entryIndex) b = vehicleEntry->var_16 * 80; image_index += b; } + // Unknown if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_RESTRAINT_ANIMATION) { vehicleEntry->var_1C = image_index; @@ -649,9 +654,9 @@ static bool object_type_ride_load(void *objectEntry, uint32 entryIndex) cur_vehicle_images_offset = image_index + vehicleEntry->no_seating_rows * vehicleEntry->no_vehicle_images; // 0x6DEB0D - if (!(vehicleEntry->var_12 & 0x400)) { + if (!(vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_10)) { int num_images = cur_vehicle_images_offset - vehicleEntry->base_image_id; - if (vehicleEntry->var_12 & 0x2000) { + if (vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_13) { num_images *= 2; } @@ -734,7 +739,7 @@ static void object_type_ride_unload(void *objectEntry) rideVehicleEntry->no_vehicle_images = 0; rideVehicleEntry->var_16 = 0; - if (!(rideVehicleEntry->var_12 & 0x400)) { + if (!(rideVehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_10)) { rideVehicleEntry->sprite_width = 0; rideVehicleEntry->sprite_height_negative = 0; rideVehicleEntry->sprite_height_positive = 0; diff --git a/src/peep/peep.c b/src/peep/peep.c index cdead601f99c..9a114fc845d4 100644 --- a/src/peep/peep.c +++ b/src/peep/peep.c @@ -37,6 +37,7 @@ #include "../world/footpath.h" #include "../management/marketing.h" #include "../game.h" +#include "../ride/track.h" #include "peep.h" #include "staff.h" @@ -1657,8 +1658,8 @@ static void peep_go_to_ride_entrance(rct_peep* peep, rct_ride* ride){ uint8 shift_multiplier = 21; rct_ride_type* ride_type = GET_RIDE_ENTRY(ride->subtype); - if (ride_type->vehicles[ride_type->default_vehicle].var_12 & (1 << 3) || - ride_type->vehicles[ride_type->default_vehicle].var_14 & 0x5000){ + if (ride_type->vehicles[ride_type->default_vehicle].flags_a & VEHICLE_ENTRY_FLAG_A_3 || + ride_type->vehicles[ride_type->default_vehicle].flags_b & (VEHICLE_ENTRY_FLAG_B_12 | VEHICLE_ENTRY_FLAG_B_14)){ shift_multiplier = 32; } @@ -1736,7 +1737,7 @@ static void peep_update_ride_sub_state_0(rct_peep* peep){ } } else{ - chosen_train = ride->var_066[peep->current_ride_station]; + chosen_train = ride->train_at_station[peep->current_ride_station]; } if (chosen_train == 0xFF){ return; @@ -1862,8 +1863,8 @@ void peep_update_ride_sub_state_1(rct_peep* peep){ { uint8 vehicle = ride_entry->default_vehicle; - if (ride_entry->vehicles[vehicle].var_12 & (1 << 3) || - ride_entry->vehicles[vehicle].var_14 & ((1 << 14) | (1<<12))) + if (ride_entry->vehicles[vehicle].flags_a & VEHICLE_ENTRY_FLAG_A_3 || + ride_entry->vehicles[vehicle].flags_b & (VEHICLE_ENTRY_FLAG_B_12 | VEHICLE_ENTRY_FLAG_B_14)) RCT2_GLOBAL(0xF1AECA, uint16) = 0x1C; else RCT2_GLOBAL(0xF1AECA, uint16) = 0x10; @@ -1964,7 +1965,7 @@ void peep_update_ride_sub_state_1(rct_peep* peep){ ride_entry = GET_RIDE_ENTRY(vehicle->ride_subtype); rct_ride_type_vehicle* vehicle_type = &ride_entry->vehicles[vehicle->vehicle_type]; - if (vehicle_type->var_14 & (1 << 10)){ + if (vehicle_type->flags_b & VEHICLE_ENTRY_FLAG_B_10){ sint16 x, y, z; x = ride->entrances[peep->current_ride_station] & 0xFF; y = ride->entrances[peep->current_ride_station] >> 8; @@ -2021,7 +2022,7 @@ void peep_update_ride_sub_state_1(rct_peep* peep){ return; } - if (vehicle_type->var_14 & (1 << 15)){ + if (vehicle_type->flags_b & VEHICLE_ENTRY_FLAG_B_15){ peep->destination_x = vehicle->x; peep->destination_y = vehicle->y; peep->destination_tolerence = 15; @@ -2074,8 +2075,8 @@ static void peep_go_to_ride_exit(rct_peep* peep, rct_ride* ride, sint16 x, sint1 rct_ride_type* ride_type = GET_RIDE_ENTRY(ride->subtype); rct_ride_type_vehicle* vehicle_entry = &ride_type->vehicles[ride_type->default_vehicle]; - if (vehicle_entry->var_12 & (1 << 3) || - vehicle_entry->var_14 & 0x5000){ + if (vehicle_entry->flags_a & VEHICLE_ENTRY_FLAG_A_3 || + vehicle_entry->flags_b & (VEHICLE_ENTRY_FLAG_B_12 | VEHICLE_ENTRY_FLAG_B_14)){ shift_multiplier = 32; } @@ -2226,7 +2227,7 @@ static void peep_update_ride_sub_state_2(rct_peep* peep){ rct_ride_type* ride_entry = GET_RIDE_ENTRY(vehicle->ride_subtype); - if (ride_entry->vehicles[0].var_12 & (1 << 3)){ + if (ride_entry->vehicles[0].flags_a & VEHICLE_ENTRY_FLAG_A_3){ vehicle->var_D5 &= ~(1 << 5); @@ -2272,8 +2273,7 @@ static void peep_update_ride_sub_state_2(rct_peep* peep){ rct_vehicle *currentTrain = GET_VEHICLE(ride->vehicles[peep->current_train]); if (ride->status == RIDE_STATUS_OPEN && ++peep->var_AC != 0 && - !(vehicle->update_flags & VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART) - ) { + !(currentTrain->update_flags & VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART)){ return; } @@ -2378,7 +2378,7 @@ void peep_update_ride_sub_state_7(rct_peep* peep){ rct_ride_type* ride_entry = GET_RIDE_ENTRY(vehicle->ride_subtype); rct_ride_type_vehicle* vehicle_entry = &ride_entry->vehicles[vehicle->vehicle_type]; - if (!(vehicle_entry->var_14 & (1 << 10))){ + if (!(vehicle_entry->flags_b & VEHICLE_ENTRY_FLAG_B_10)){ sint16 x, y, z; x = ride->exits[peep->current_ride_station] & 0xFF; y = ride->exits[peep->current_ride_station] >> 8; @@ -2393,7 +2393,7 @@ void peep_update_ride_sub_state_7(rct_peep* peep){ for (; vehicle->is_child; vehicle = GET_VEHICLE(vehicle->prev_vehicle_on_ride)){ uint16 trackType = vehicle->track_type >> 2; - if (trackType == 0 || trackType > 3) + if (trackType == TRACK_ELEM_FLAT || trackType > TRACK_ELEM_MIDDLE_STATION) continue; rct_map_element* inner_map = map_get_first_element_at(vehicle->track_x / 32, vehicle->track_y / 32); @@ -2413,12 +2413,12 @@ void peep_update_ride_sub_state_7(rct_peep* peep){ vehicle_entry = &ride_entry->vehicles[ride_entry->default_vehicle]; uint8 shift_multiplier = 12; - if (vehicle_entry->var_14 & (1 << 14)){ + if (vehicle_entry->flags_b & VEHICLE_ENTRY_FLAG_B_14){ shift_multiplier = 9; } uint8 direction = exit_direction; - if (vehicle_entry->var_14 & ((1 << 14) | (1 << 12))){ + if (vehicle_entry->flags_b & (VEHICLE_ENTRY_FLAG_B_12 | VEHICLE_ENTRY_FLAG_B_14)){ direction = ((vehicle->sprite_direction + 3) / 8) + 1; direction &= 3; @@ -2559,7 +2559,7 @@ static void peep_update_ride_prepare_for_state_9(rct_peep* peep){ rct_ride_type* ride_type = GET_RIDE_ENTRY(ride->subtype); rct_ride_type_vehicle* vehicle_entry = &ride_type->vehicles[ride_type->default_vehicle]; - if (vehicle_entry->var_14 & 0x5000){ + if (vehicle_entry->flags_b & (VEHICLE_ENTRY_FLAG_B_12 | VEHICLE_ENTRY_FLAG_B_14)){ shift_multiplier = 32; } @@ -2775,7 +2775,7 @@ static void peep_update_ride_sub_state_13(rct_peep* peep){ rct_ride_type* ride_type = GET_RIDE_ENTRY(ride->subtype); rct_ride_type_vehicle* vehicle_entry = &ride_type->vehicles[ride_type->default_vehicle]; - if (vehicle_entry->var_14 & 0x5000){ + if (vehicle_entry->flags_b & (VEHICLE_ENTRY_FLAG_B_12 | VEHICLE_ENTRY_FLAG_B_14)){ shift_multiplier = 32; } diff --git a/src/peep/peep.h b/src/peep/peep.h index c1e90f71fad3..8ee4f1674c52 100644 --- a/src/peep/peep.h +++ b/src/peep/peep.h @@ -414,7 +414,7 @@ typedef struct { uint16 next_y; // 0x26 uint8 next_z; // 0x28 uint8 next_var_29; // 0x29 - uint8 outside_of_park; // 0x2A + uint8 outside_of_park; // 0x2A uint8 state; // 0x2B uint8 sub_state; // 0x2C uint8 sprite_type; // 0x2D diff --git a/src/rct1.c b/src/rct1.c index 203aa18465d2..8ae952df682c 100644 --- a/src/rct1.c +++ b/src/rct1.c @@ -712,7 +712,7 @@ static void rct1_fix_z() for (int i = 0; i < 4; i++) { ride->station_heights[i] /= 2; } - ride->var_116 /= 2; + ride->start_drop_height /= 2; ride->highest_drop_height = 1; if (ride->var_11F != 255) { ride->var_11F /= 2; diff --git a/src/ride/cable_lift.c b/src/ride/cable_lift.c new file mode 100644 index 000000000000..c1b0444256e9 --- /dev/null +++ b/src/ride/cable_lift.c @@ -0,0 +1,485 @@ +#include "../world/sprite.h" +#include "cable_lift.h" +#include "ride.h" +#include "track.h" + +static void cable_lift_update_moving_to_end_of_station(rct_vehicle *vehicle); +static void cable_lift_update_waiting_to_depart(rct_vehicle *vehicle); +static void cable_lift_update_departing(rct_vehicle *vehicle); +static void cable_lift_update_travelling(rct_vehicle *vehicle); +static void cable_lift_update_arriving(rct_vehicle *vehicle); + +rct_vehicle *cable_lift_segment_create(int rideIndex, int x, int y, int z, int direction, uint16 var_44, sint32 remaining_distance, bool head) +{ + rct_ride *ride = GET_RIDE(rideIndex); + rct_vehicle *current = &(create_sprite(1)->vehicle); + current->sprite_identifier = SPRITE_IDENTIFIER_VEHICLE; + current->ride = rideIndex; + current->ride_subtype = 0xFF; + if (head) { + move_sprite_to_list((rct_sprite*)current, SPRITE_LINKEDLIST_OFFSET_VEHICLE); + ride->cable_lift = current->sprite_index; + } + current->is_child = head ? 0 : 1; + current->var_44 = var_44; + current->remaining_distance = remaining_distance; + current->sprite_width = 10; + current->sprite_height_negative = 10; + current->sprite_height_positive = 10; + current->friction = 100; + current->num_seats = 0; + current->speed = 20; + current->powered_acceleration = 80; + current->velocity = 0; + current->acceleration = 0; + current->var_4A = 0; + current->swinging_car_var_0 = 0; + current->var_4E = 0; + current->restraints_position = 0; + current->var_BA = 0; + current->var_B6 = 0; + current->var_B8 = 0; + current->sound1_id = 0xFF; + current->sound2_id = 0xFF; + current->var_C4 = 0; + current->var_C5 = 0; + current->var_C8 = 0; + current->var_CA = 0; + current->scream_sound_id = 0xFF; + current->var_1F = 0; + current->var_20 = 0; + for (int j = 0; j < 32; j++) { + current->peep[j] = SPRITE_INDEX_NULL; + } + current->var_CD = 0; + current->sprite_direction = direction << 3; + current->track_x = x; + current->track_y = y; + + z = z * 8; + current->track_z = z; + z += RCT2_GLOBAL(0x0097D21A + (ride->type * 8), sint8); + + sprite_move(16, 16, z, (rct_sprite*)current); + current->track_type = (TRACK_ELEM_CABLE_LIFT_HILL << 2) | (current->sprite_direction >> 3); + current->track_progress = 164; + current->update_flags = VEHICLE_UPDATE_FLAG_1; + current->status = VEHICLE_STATUS_MOVING_TO_END_OF_STATION; + current->sub_state = 0; + current->num_peeps = 0; + current->next_free_seat = 0; + return current; +} + +void cable_lift_update(rct_vehicle *vehicle) +{ + switch (vehicle->status) { + case VEHICLE_STATUS_MOVING_TO_END_OF_STATION: + cable_lift_update_moving_to_end_of_station(vehicle); + break; + case VEHICLE_STATUS_WAITING_FOR_PASSENGERS: + // Stays in this state until a train puts it into next state + break; + case VEHICLE_STATUS_WAITING_TO_DEPART: + cable_lift_update_waiting_to_depart(vehicle); + break; + case VEHICLE_STATUS_DEPARTING: + cable_lift_update_departing(vehicle); + break; + case VEHICLE_STATUS_TRAVELLING: + cable_lift_update_travelling(vehicle); + break; + case VEHICLE_STATUS_ARRIVING: + cable_lift_update_arriving(vehicle); + break; + } +} + +/** + * + * rct2: 0x006DF8A4 + */ +static void cable_lift_update_moving_to_end_of_station(rct_vehicle *vehicle) +{ + if (vehicle->velocity >= -439800) + vehicle->acceleration = -2932; + + if (vehicle->velocity < -439800) { + vehicle->velocity -= vehicle->velocity / 16; + vehicle->acceleration = 0; + } + + if (!(cable_lift_update_track_motion(vehicle) & (1 << 0))) + return; + + vehicle->velocity = 0; + vehicle->acceleration = 0; + vehicle->status = VEHICLE_STATUS_WAITING_FOR_PASSENGERS; +} + +/** + * + * rct2: 0x006DF8F1 + */ +static void cable_lift_update_waiting_to_depart(rct_vehicle *vehicle) +{ + if (vehicle->velocity >= -58640) + vehicle->acceleration = -14660; + + if (vehicle->velocity < -58640) { + vehicle->velocity -= vehicle->velocity / 16; + vehicle->acceleration = 0; + } + + cable_lift_update_track_motion(vehicle); + + // Next check to see if the second part of the cable lift + // is at the front of the passenger vehicle to simulate the + // cable being attached underneath the train. + rct_vehicle* passengerVehicle = GET_VEHICLE(vehicle->var_C0); + rct_vehicle* cableLiftSecondPart = GET_VEHICLE(vehicle->prev_vehicle_on_ride); + + sint16 dist_x = abs(passengerVehicle->x - cableLiftSecondPart->x); + sint16 dist_y = abs(passengerVehicle->y - cableLiftSecondPart->y); + + if (dist_x + dist_y > 2) + return; + + vehicle->velocity = 0; + vehicle->acceleration = 0; + vehicle->status = VEHICLE_STATUS_DEPARTING; + vehicle->sub_state = 0; +} + +/** + * + * rct2: 0x006DF97A + */ +static void cable_lift_update_departing(rct_vehicle *vehicle) +{ + vehicle->sub_state++; + if (vehicle->sub_state < 16) + return; + + rct_vehicle* passengerVehicle = GET_VEHICLE(vehicle->var_C0); + vehicle->status = VEHICLE_STATUS_TRAVELLING; + passengerVehicle->status = VEHICLE_STATUS_TRAVELLING_CABLE_LIFT; +} + +/** + * + * rct2: 0x006DF99C + */ +static void cable_lift_update_travelling(rct_vehicle *vehicle) +{ + rct_vehicle* passengerVehicle = GET_VEHICLE(vehicle->var_C0); + + vehicle->velocity = min(passengerVehicle->velocity, 439800); + vehicle->acceleration = 0; + if (passengerVehicle->update_flags & VEHICLE_UPDATE_FLAG_BROKEN_TRAIN) + return; + + if (!(cable_lift_update_track_motion(vehicle) & (1 << 1))) + return; + + vehicle->velocity = 0; + vehicle->acceleration = 0; + vehicle->status = VEHICLE_STATUS_ARRIVING; + vehicle->sub_state = 0; +} + +/** + * + * rct2: 0x006DF9F0 + */ +static void cable_lift_update_arriving(rct_vehicle *vehicle) +{ + vehicle->sub_state++; + if (vehicle->sub_state >= 64) + vehicle->status = VEHICLE_STATUS_MOVING_TO_END_OF_STATION; +} + +bool sub_6DF01A_loop(rct_vehicle* vehicle) { + rct_ride* ride = GET_RIDE(vehicle->ride); + rct_xyz16 *unk_F64E20 = RCT2_ADDRESS(0x00F64E20, rct_xyz16); + + for (; vehicle->remaining_distance >= 13962; RCT2_GLOBAL(0x00F64E10, uint32)++) { + uint8 trackType = vehicle->track_type >> 2; + if (trackType == TRACK_ELEM_CABLE_LIFT_HILL && + vehicle->track_progress == 160) { + RCT2_GLOBAL(0x00F64E18, uint32) |= (1 << 1); + } + + uint16 trackProgress = vehicle->track_progress + 1; + + const rct_vehicle_info *moveInfo = vehicle_get_move_info(vehicle->var_CD, vehicle->track_type, 0); + uint16 trackTotalProgress = *((uint16*)((int)moveInfo - 2)); + if (trackProgress >= trackTotalProgress) { + RCT2_GLOBAL(0x00F64E36, uint8) = gTrackDefinitions[trackType].vangle_end; + RCT2_GLOBAL(0x00F64E37, uint8) = gTrackDefinitions[trackType].bank_end; + rct_map_element* trackElement = + map_get_track_element_at_of_type_seq( + vehicle->track_x, + vehicle->track_y, + vehicle->track_z / 8, + trackType, + 0); + + rct_xy_element input; + rct_xy_element output; + int outputZ; + int outputDirection; + + input.x = vehicle->track_x; + input.y = vehicle->track_y; + input.element = trackElement; + + if (!track_block_get_next(&input, &output, &outputZ, &outputDirection)) + return false; + + if (gTrackDefinitions[output.element->properties.track.type].vangle_start != RCT2_GLOBAL(0x00F64E36, uint8) || + gTrackDefinitions[output.element->properties.track.type].bank_start != RCT2_GLOBAL(0x00F64E37, uint8)) + return false; + + vehicle->track_x = output.x; + vehicle->track_y = output.y; + vehicle->track_z = outputZ; + vehicle->track_direction = outputDirection; + vehicle->track_type |= output.element->properties.track.type << 2; + trackProgress = 0; + } + + vehicle->track_progress = trackProgress; + moveInfo = vehicle_get_move_info(vehicle->var_CD, vehicle->track_type, trackProgress); + rct_xyz16 unk = { + .x = moveInfo->x, + .y = moveInfo->y, + .z = moveInfo->z + }; + + unk.x += vehicle->track_x; + unk.y += vehicle->track_y; + unk.z += vehicle->track_z; + + uint8 bx = 0; + unk.z += RCT2_GLOBAL(0x0097D21A + (ride->type * 8), sint8); + if (unk.x != unk_F64E20->x) + bx |= (1 << 0); + if (unk.y != unk_F64E20->y) + bx |= (1 << 1); + if (unk.z != unk_F64E20->z) + bx |= (1 << 2); + + vehicle->remaining_distance -= RCT2_ADDRESS(0x009A2930, sint32)[bx]; + unk_F64E20->x = unk.x; + unk_F64E20->y = unk.y; + unk_F64E20->z = unk.z; + + vehicle->sprite_direction = moveInfo->direction; + vehicle->var_20 = moveInfo->var_08; + vehicle->var_1F = moveInfo->var_07; + + if (vehicle->remaining_distance >= 13962) { + vehicle->acceleration += RCT2_ADDRESS(0x009A2970, sint32)[vehicle->var_1F]; + } + } + return true; +} + +bool sub_6DF21B_loop(rct_vehicle* vehicle) { + rct_ride* ride = GET_RIDE(vehicle->ride); + rct_xyz16 *unk_F64E20 = RCT2_ADDRESS(0x00F64E20, rct_xyz16); + + for (; vehicle->remaining_distance < 0; RCT2_GLOBAL(0x00F64E10, uint32)++) { + uint16 trackProgress = vehicle->track_progress - 1; + const rct_vehicle_info *moveInfo; + + if ((sint16)trackProgress == -1) { + uint8 trackType = vehicle->track_type >> 2; + RCT2_GLOBAL(0x00F64E36, uint8) = gTrackDefinitions[trackType].vangle_start; + RCT2_GLOBAL(0x00F64E37, uint8) = gTrackDefinitions[trackType].bank_start; + rct_map_element* trackElement = + map_get_track_element_at_of_type_seq( + vehicle->track_x, + vehicle->track_y, + vehicle->track_z / 8, + trackType, + 0); + + rct_xy_element input; + + input.x = vehicle->track_x; + input.y = vehicle->track_y; + input.element = trackElement; + track_begin_end output; + + if (!track_block_get_previous(input.x, input.y, input.element, &output)) + return false; + + if (gTrackDefinitions[output.begin_element->properties.track.type].vangle_end != RCT2_GLOBAL(0x00F64E36, uint8) || + gTrackDefinitions[output.begin_element->properties.track.type].bank_end != RCT2_GLOBAL(0x00F64E37, uint8)) + return false; + + vehicle->track_x = output.begin_x; + vehicle->track_y = output.begin_y; + vehicle->track_z = output.begin_z; + vehicle->track_direction = output.begin_direction; + vehicle->track_type |= output.begin_element->properties.track.type << 2; + + if (output.begin_element->properties.track.type == TRACK_ELEM_END_STATION) { + RCT2_GLOBAL(0x00F64E18, uint32) = (1 << 0); + } + + moveInfo = vehicle_get_move_info(vehicle->var_CD, vehicle->track_type, 0); + uint16 trackTotalProgress = *((uint16*)((int)moveInfo - 2)); + trackProgress = trackTotalProgress - 1; + } + vehicle->track_progress = trackProgress; + + moveInfo = vehicle_get_move_info(vehicle->var_CD, vehicle->track_type, trackProgress); + rct_xyz16 unk = { + .x = moveInfo->x, + .y = moveInfo->y, + .z = moveInfo->z + }; + + unk.x += vehicle->track_x; + unk.y += vehicle->track_y; + unk.z += vehicle->track_z; + + uint8 bx = 0; + unk.z += RCT2_GLOBAL(0x0097D21A + (ride->type * 8), sint8); + if (unk.x != unk_F64E20->x) + bx |= (1 << 0); + if (unk.y != unk_F64E20->y) + bx |= (1 << 1); + if (unk.z != unk_F64E20->z) + bx |= (1 << 2); + + vehicle->remaining_distance += RCT2_ADDRESS(0x009A2930, sint32)[bx]; + unk_F64E20->x = unk.x; + unk_F64E20->y = unk.y; + unk_F64E20->z = unk.z; + + vehicle->sprite_direction = moveInfo->direction; + vehicle->var_20 = moveInfo->var_08; + vehicle->var_1F = moveInfo->var_07; + + if (vehicle->remaining_distance < 0) { + vehicle->acceleration += RCT2_ADDRESS(0x009A2970, sint32)[vehicle->var_1F]; + } + } + return true; +} + +/** + * + * rct2: 0x006DEF56 + */ +int cable_lift_update_track_motion(rct_vehicle *cableLift) +{ + rct_ride_type* rideEntry = GET_RIDE_ENTRY(cableLift->ride_subtype); + rct_ride_type_vehicle* vehicleEntry = &rideEntry->vehicles[cableLift->vehicle_type]; + rct_ride* ride = GET_RIDE(cableLift->ride); + + RCT2_GLOBAL(0x00F64E2C, uint8) = 0; + RCT2_GLOBAL(0x00F64E04, rct_vehicle*) = cableLift; + RCT2_GLOBAL(0x00F64E18, uint32) = 0; + RCT2_GLOBAL(0x00F64E1C, uint32) = (uint32)-1; + + cableLift->velocity += cableLift->acceleration; + RCT2_GLOBAL(0x00F64E08, sint32) = cableLift->velocity; + RCT2_GLOBAL(0x00F64E0C, sint32) = (cableLift->velocity / 1024) * 42; + + rct_vehicle* frontVehicle = cableLift; + if (cableLift->velocity < 0) { + frontVehicle = vehicle_get_tail(cableLift); + } + + RCT2_GLOBAL(0x00F64E00, rct_vehicle*) = frontVehicle; + + for (rct_vehicle* vehicle = frontVehicle;;) { + vehicle->acceleration = RCT2_ADDRESS(0x009A2970, sint32)[vehicle->var_1F]; + RCT2_GLOBAL(0x00F64E10, uint32) = 1; + vehicle->remaining_distance += RCT2_GLOBAL(0x00F64E0C, sint32); + + if (vehicle->remaining_distance < 0 || vehicle->remaining_distance >= 13962) { + rct_xyz16 *unk_F64E20 = RCT2_ADDRESS(0x00F64E20, rct_xyz16); + unk_F64E20->x = vehicle->x; + unk_F64E20->y = vehicle->y; + unk_F64E20->z = vehicle->z; + invalidate_sprite_2((rct_sprite*)vehicle); + + while (true) { + if (vehicle->remaining_distance < 0) { + if (sub_6DF21B_loop(vehicle) == true) { + break; + } + else { + RCT2_GLOBAL(0x00F64E18, uint32) |= (1 << 5); + RCT2_GLOBAL(0x00F64E0C, uint32) -= vehicle->remaining_distance - 13962; + vehicle->remaining_distance = 13962; + vehicle->acceleration += RCT2_ADDRESS(0x009A2970, uint32)[vehicle->var_1F]; + RCT2_GLOBAL(0x00F64E10, uint32)++; + continue; + } + } + else { + if (sub_6DF01A_loop(vehicle) == true) { + break; + } + else { + RCT2_GLOBAL(0x00F64E18, uint32) |= (1 << 5); + RCT2_GLOBAL(0x00F64E0C, uint32) -= vehicle->remaining_distance + 1; + vehicle->remaining_distance = -1; + vehicle->acceleration += RCT2_ADDRESS(0x009A2970, uint32)[vehicle->var_1F]; + RCT2_GLOBAL(0x00F64E10, uint32)++; + } + } + } + sprite_move( + unk_F64E20->x, + unk_F64E20->y, + unk_F64E20->z, + (rct_sprite*)vehicle); + + invalidate_sprite_2((rct_sprite*)vehicle); + } + vehicle->acceleration /= RCT2_GLOBAL(0x00F64E10, uint32); + if (RCT2_GLOBAL(0x00F64E08, sint32) >= 0) { + if (vehicle->next_vehicle_on_train == 0xFFFF) + break; + vehicle = GET_VEHICLE(vehicle->next_vehicle_on_train); + } + else { + if (vehicle == cableLift) + break; + vehicle = GET_VEHICLE(vehicle->prev_vehicle_on_ride); + } + } + + uint32 vehicleCount = 0; + uint16 frictionTotal = 0; + sint32 var2CTotal = 0; + + for (uint16 spriteId = cableLift->sprite_index; spriteId != 0xFFFF;) { + rct_vehicle* vehicle = GET_VEHICLE(spriteId); + vehicleCount++; + frictionTotal += vehicle->friction; + var2CTotal += vehicle->acceleration; + spriteId = vehicle->next_vehicle_on_train; + } + + sint32 ecx = (var2CTotal / vehicleCount) >> 9; + ecx -= cableLift->velocity >> 12; + + sint32 edx = cableLift->velocity >> 8; + edx *= edx; + if (cableLift->velocity < 0) + edx = -edx; + edx >>= 4; + ecx -= edx / frictionTotal; + + cableLift->acceleration = ecx; + return RCT2_GLOBAL(0x00F64E18, uint32); +} diff --git a/src/ride/cable_lift.h b/src/ride/cable_lift.h new file mode 100644 index 000000000000..95b32757c4f2 --- /dev/null +++ b/src/ride/cable_lift.h @@ -0,0 +1,11 @@ +#ifndef _CABLE_LIFT_H_ +#define _CABLE_LIFT_H_ + +#include "../common.h" +#include "vehicle.h" + +rct_vehicle *cable_lift_segment_create(int rideIndex, int x, int y, int z, int direction, uint16 var_44, sint32 remaining_distance, bool head); +void cable_lift_update(rct_vehicle *vehicle); +int cable_lift_update_track_motion(rct_vehicle *cableLift); + +#endif diff --git a/src/ride/ride.c b/src/ride/ride.c index acc9743a29f3..28d04e22ff53 100644 --- a/src/ride/ride.c +++ b/src/ride/ride.c @@ -45,6 +45,7 @@ #include "../world/map.h" #include "../world/map_animation.h" #include "../world/sprite.h" +#include "cable_lift.h" #include "ride.h" #include "ride_data.h" #include "track.h" @@ -929,7 +930,7 @@ static void ride_remove_vehicles(rct_ride *ride) } for (i = 0; i < 4; i++) - ride->var_066[i] = 255; + ride->train_at_station[i] = 255; } } @@ -2693,7 +2694,12 @@ void ride_measurement_update(rct_ride_measurement *measurement) } uint8 trackType = (vehicle->track_type >> 2) & 0xFF; - if (trackType == 216 || trackType == 123 || trackType == 9 || trackType == 63 || trackType == 147 || trackType == 155) + if (trackType == 216 || + trackType == TRACK_ELEM_CABLE_LIFT_HILL || + trackType == TRACK_ELEM_25_DEG_UP_TO_FLAT || + trackType == TRACK_ELEM_60_DEG_UP_TO_FLAT || + trackType == TRACK_ELEM_DIAG_25_DEG_UP_TO_FLAT || + trackType == TRACK_ELEM_DIAG_60_DEG_UP_TO_FLAT) if (vehicle->velocity == 0) return; @@ -4279,7 +4285,7 @@ void loc_6DDF9C(rct_ride *ride, rct_map_element *mapElement) for (int i = 0; i < ride->num_vehicles; i++) { train = GET_VEHICLE(ride->vehicles[i]); if (i == 0) { - sub_6DAB4C(train, NULL); + vehicle_update_track_motion(train, NULL); vehicle_unset_var_48_b1(train); continue; } @@ -4289,9 +4295,9 @@ void loc_6DDF9C(rct_ride *ride, rct_map_element *mapElement) car = train; while (true) { car->velocity = 0; - car->var_2C = 0; + car->acceleration = 0; car->var_4A = 0; - car->var_24 += 13962; + car->remaining_distance += 13962; uint16 spriteIndex = car->next_vehicle_on_train; if (spriteIndex == SPRITE_INDEX_NULL) { @@ -4299,7 +4305,7 @@ void loc_6DDF9C(rct_ride *ride, rct_map_element *mapElement) } car = GET_VEHICLE(spriteIndex); } - } while (sub_6DAB4C(train, NULL) & 0x400); + } while (vehicle_update_track_motion(train, NULL) & 0x400); mapElement->flags |= (1 << 5); car = train; @@ -4480,10 +4486,10 @@ bool ride_create_cable_lift(int rideIndex, bool isApplying) uint16 var_44 = edx & 0xFFFF; edx = rol32(edx, 10) >> 1; ebx -= edx; - uint32 var_24 = ebx; + sint32 remaining_distance = ebx; ebx -= edx; - rct_vehicle *current = cable_lift_segment_create(rideIndex, x, y, z, direction, var_44, var_24, i == 0); + rct_vehicle *current = cable_lift_segment_create(rideIndex, x, y, z, direction, var_44, remaining_distance, i == 0); current->next_vehicle_on_train = SPRITE_INDEX_NULL; if (i == 0) { head = current; @@ -4498,7 +4504,7 @@ bool ride_create_cable_lift(int rideIndex, bool isApplying) tail->next_vehicle_on_ride = head->sprite_index; ride->lifecycle_flags |= RIDE_LIFECYCLE_CABLE_LIFT; - sub_6DEF56(head); + cable_lift_update_track_motion(head); return true; } @@ -5373,7 +5379,7 @@ money32 ride_create(int type, int subType, int flags, int *outRideIndex, int *ou ride->station_starts[i] = 0xFFFF; ride->entrances[i] = 0xFFFF; ride->exits[i] = 0xFFFF; - ride->var_066[i] = 255; + ride->train_at_station[i] = 255; ride->queue_time[i] = 0; } @@ -5405,7 +5411,7 @@ money32 ride_create(int type, int subType, int flags, int *outRideIndex, int *ou RCT2_GLOBAL(0x0097CF40 + 5 + (ride->type * 8), uint8) ) / 4; - ride->lift_hill_speed = RCT2_ADDRESS(0x0097D7C9, uint8)[ride->type * 4]; + ride->lift_hill_speed = RideLiftData[ride->type].minimum_speed; ride->measurement_index = 255; ride->excitement = (ride_rating)-1; @@ -5978,53 +5984,182 @@ bool ride_type_has_flag(int rideType, int flag) return (RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + (rideType * 8), uint32) & flag) != 0; } -/** - * The next six functions are helpers to access ride data at the offset 10E & - * 110. We believe it stores three distinct values in the following format: +/* + * The next eight functions are helpers to access ride data at the offset 10E & + * 110. Known as the turn counts. There are 3 different types (default, banked, sloped) + * and there are 4 counts as follows: + * + * 1 element turns: low 5 bits + * 2 element turns: bits 6-8 + * 3 element turns: bits 9-11 + * 4 element or more turns: bits 12-15 * - * unknown1: bits 9-11 - * unknown2: bits 6-8 - * unknown3: low 5 bits + * 4 plus elements only possible on sloped type. Falls back to 3 element + * if by some miracle you manage 4 element none sloped. */ -int get_var_10E_unk_1(rct_ride* ride) { - return (ride->var_10E >> 8) & 0x7; -} +void increment_turn_count_1_element(rct_ride* ride, uint8 type){ + uint16* turn_count; + switch (type){ + case 0: + turn_count = &ride->turn_count_default; + break; + case 1: + turn_count = &ride->turn_count_banked; + break; + case 2: + turn_count = &ride->turn_count_sloped; + break; + default: + return; + } + uint16 value = (*turn_count & TURN_MASK_1_ELEMENT) + 1; + *turn_count &= ~TURN_MASK_1_ELEMENT; -int get_var_10E_unk_2(rct_ride* ride) { - return (ride->var_10E >> 5) & 0x7; + if (value > TURN_MASK_1_ELEMENT) + value = TURN_MASK_1_ELEMENT; + *turn_count |= value; } -int get_var_10E_unk_3(rct_ride* ride) { - return ride->var_10E & 0x1F; -} +void increment_turn_count_2_elements(rct_ride* ride, uint8 type){ + uint16* turn_count; + switch (type){ + case 0: + turn_count = &ride->turn_count_default; + break; + case 1: + turn_count = &ride->turn_count_banked; + break; + case 2: + turn_count = &ride->turn_count_sloped; + break; + default: + return; + } + uint16 value = (*turn_count & TURN_MASK_2_ELEMENTS) + 0x20; + *turn_count &= ~TURN_MASK_2_ELEMENTS; -int get_var_110_unk_1(rct_ride* ride) { - return (ride->var_110 >> 8) & 0x7; + if (value > TURN_MASK_2_ELEMENTS) + value = TURN_MASK_2_ELEMENTS; + *turn_count |= value; } -int get_var_110_unk_2(rct_ride* ride) { - return (ride->var_110 >> 5) & 0x7; +void increment_turn_count_3_elements(rct_ride* ride, uint8 type){ + uint16* turn_count; + switch (type){ + case 0: + turn_count = &ride->turn_count_default; + break; + case 1: + turn_count = &ride->turn_count_banked; + break; + case 2: + turn_count = &ride->turn_count_sloped; + break; + default: + return; + } + uint16 value = (*turn_count & TURN_MASK_3_ELEMENTS) + 0x100; + *turn_count &= ~TURN_MASK_3_ELEMENTS; + + if (value > TURN_MASK_3_ELEMENTS) + value = TURN_MASK_3_ELEMENTS; + *turn_count |= value; } -int get_var_110_unk_3(rct_ride* ride) { - return ride->var_110 & 0x1F; +void increment_turn_count_4_plus_elements(rct_ride* ride, uint8 type){ + uint16* turn_count; + switch (type){ + case 0: + case 1: + // Just incase fallback to 3 element turn + increment_turn_count_3_elements(ride, type); + return; + case 2: + turn_count = &ride->turn_count_sloped; + break; + default: + return; + } + uint16 value = (*turn_count & TURN_MASK_4_PLUS_ELEMENTS) + 0x800; + *turn_count &= ~TURN_MASK_4_PLUS_ELEMENTS; + + if (value > TURN_MASK_4_PLUS_ELEMENTS) + value = TURN_MASK_4_PLUS_ELEMENTS; + *turn_count |= value; } -int get_var_112_unk_1(rct_ride* ride) { - return (ride->var_112 >> 11) & 0x3F; +int get_turn_count_1_element(rct_ride* ride, uint8 type) { + uint16* turn_count; + switch (type){ + case 0: + turn_count = &ride->turn_count_default; + break; + case 1: + turn_count = &ride->turn_count_banked; + break; + case 2: + turn_count = &ride->turn_count_sloped; + break; + default: + return 0; + } + + return (*turn_count) & TURN_MASK_1_ELEMENT; } -int get_var_112_unk_2(rct_ride* ride) { - return (ride->var_112 >> 8) & 7; +int get_turn_count_2_elements(rct_ride* ride, uint8 type) { + uint16* turn_count; + switch (type){ + case 0: + turn_count = &ride->turn_count_default; + break; + case 1: + turn_count = &ride->turn_count_banked; + break; + case 2: + turn_count = &ride->turn_count_sloped; + break; + default: + return 0; + } + + return ((*turn_count) & TURN_MASK_2_ELEMENTS) >> 5; } -int get_var_112_unk_3(rct_ride* ride) { - return (ride->var_112 >> 5) & 7; +int get_turn_count_3_elements(rct_ride* ride, uint8 type) { + uint16* turn_count; + switch (type){ + case 0: + turn_count = &ride->turn_count_default; + break; + case 1: + turn_count = &ride->turn_count_banked; + break; + case 2: + turn_count = &ride->turn_count_sloped; + break; + default: + return 0; + } + + return ((*turn_count) & TURN_MASK_3_ELEMENTS) >> 8; } -int get_var_112_unk_4(rct_ride* ride) { - return ride->var_112 & 0x1F; +int get_turn_count_4_plus_elements(rct_ride* ride, uint8 type) { + uint16* turn_count; + switch (type){ + case 0: + case 1: + return 0; + case 2: + turn_count = &ride->turn_count_sloped; + break; + default: + return 0; + } + + return ((*turn_count) & TURN_MASK_4_PLUS_ELEMENTS) >> 11; } bool ride_has_spinning_tunnel(rct_ride *ride) { @@ -6183,7 +6318,7 @@ void set_vehicle_type_image_max_sizes(rct_ride_type_vehicle* vehicle_type, int n // Moved from object paint - if (vehicle_type->var_12 & 0x2000){ + if (vehicle_type->flags_a & VEHICLE_ENTRY_FLAG_A_13){ bl += 16; } @@ -6704,7 +6839,7 @@ void ride_update_max_vehicles(int rideIndex) int totalFriction = 0; for (int i = 0; i < numCars; i++) { vehicleEntry = &rideEntry->vehicles[trainLayout[i]]; - trainLength += vehicleEntry->var_04; + trainLength += vehicleEntry->spacing; totalFriction += vehicleEntry->car_friction; } @@ -6736,7 +6871,7 @@ void ride_update_max_vehicles(int rideIndex) trainLength = 0; for (int i = 0; i < newCarsPerTrain; i++) { vehicleEntry = &rideEntry->vehicles[trainLayout[i]]; - trainLength += vehicleEntry->var_04; + trainLength += vehicleEntry->spacing; } int totalLength = trainLength / 2; @@ -6762,7 +6897,7 @@ void ride_update_max_vehicles(int rideIndex) int totalSpacing = 0; for (int i = 0; i < newCarsPerTrain; i++) { vehicleEntry = &rideEntry->vehicles[trainLayout[i]]; - totalSpacing += vehicleEntry->var_04; + totalSpacing += vehicleEntry->spacing; } totalSpacing >>= 13; @@ -7294,16 +7429,12 @@ void game_command_remove_ride_entrance_or_exit(int *eax, int *ebx, int *ecx, int * * rct2: 0x006B752C */ -void ride_crash(int rideIndex, int vehicleIndex) +void ride_crash(uint8 rideIndex, uint8 vehicleIndex) { rct_ride *ride; rct_vehicle *vehicle; rct_window *w; - // TODO Remove these when hook is no longer used - rideIndex &= 0xFF; - vehicleIndex &= 0xFF; - ride = GET_RIDE(rideIndex); vehicle = GET_VEHICLE(ride->vehicles[vehicleIndex]); diff --git a/src/ride/ride.h b/src/ride/ride.h index a9f5c6b30d47..5b207ddcf03b 100644 --- a/src/ride/ride.h +++ b/src/ride/ride.h @@ -75,28 +75,33 @@ typedef struct { * size: unknown */ typedef struct { - rct_string_id name; // 0x000 - rct_string_id description; // 0x002 - uint32 images_offset; // 0x004 - uint32 flags; // 0x008 - uint8 ride_type[3]; // 0x00C - uint8 min_cars_in_train; // 0x00F - uint8 max_cars_in_train; // 0x010 - uint8 cars_per_flat_ride; // 0x011 - uint8 zero_cars; // 0x012 - uint8 tab_vehicle; // 0x013 - uint8 default_vehicle; // 0x014 - uint8 front_vehicle; // 0x015 - uint8 second_vehicle; // 0x016 - uint8 rear_vehicle; // 0x017 - uint8 third_vehicle; // 0x018 + rct_string_id name; // 0x000 + rct_string_id description; // 0x002 + uint32 images_offset; // 0x004 + uint32 flags; // 0x008 + uint8 ride_type[3]; // 0x00C + uint8 min_cars_in_train; // 0x00F + uint8 max_cars_in_train; // 0x010 + uint8 cars_per_flat_ride; // 0x011 + // Number of cars that can't hold passengers + uint8 zero_cars; // 0x012 + // The index to the vehicle type displayed in + // the vehicle tab. + uint8 tab_vehicle; // 0x013 + uint8 default_vehicle; // 0x014 + // Convert from first - fourth vehicle to + // vehicle structure + uint8 front_vehicle; // 0x015 + uint8 second_vehicle; // 0x016 + uint8 rear_vehicle; // 0x017 + uint8 third_vehicle; // 0x018 uint8 pad_019; - rct_ride_type_vehicle vehicles[4]; // 0x01A + rct_ride_type_vehicle vehicles[4]; // 0x1A vehicle_colour_preset_list *vehicle_preset_list; // 0x1AE - sint8 excitement_multipler; // 0x1B2 - sint8 intensity_multipler; // 0x1B3 - sint8 nausea_multipler; // 0x1B4 - uint8 max_height; // 0x1B5 + sint8 excitement_multipler; // 0x1B2 + sint8 intensity_multipler; // 0x1B3 + sint8 nausea_multipler; // 0x1B4 + uint8 max_height; // 0x1B5 union { uint64 enabledTrackPieces; // 0x1B6 struct { @@ -138,7 +143,9 @@ typedef struct { uint8 station_heights[4]; // 0x05A uint8 station_length[4]; // 0x05E uint8 station_depart[4]; // 0x062 - uint8 var_066[4]; + // ride->vehicle index for current train waiting for passengers + // at station + uint8 train_at_station[4]; // 0x066 uint16 entrances[4]; // 0x06A uint16 exits[4]; // 0x072 uint16 last_peep_in_queue[4]; // 0x07A @@ -177,16 +184,24 @@ typedef struct { // (in RCT2, display_speed = (max_speed * 9) >> 18) sint32 max_speed; // 0x0D8 sint32 average_speed; // 0x0DC - uint8 pad_0E0[4]; + uint8 current_test_segment; // 0x0E0 + uint8 var_0E1; + uint8 pad_0E2[0x2]; sint32 length[4]; // 0x0E4 uint16 time[4]; // 0x0F4 fixed16_2dp max_positive_vertical_g; // 0x0FC fixed16_2dp max_negative_vertical_g; // 0x0FE fixed16_2dp max_lateral_g; // 0x100 - uint8 pad_102[0xC]; - uint16 var_10E; - uint16 var_110; - uint16 var_112; + fixed16_2dp previous_vertical_g;// 0x102 + fixed16_2dp previous_lateral_g; // 0x104 + uint8 pad_106[0x2]; + uint32 testing_flags; // 0x108 + // x y map location + uint16 var_10C; + // Next 3 variables are related (XXXX XYYY ZZZa aaaa) + uint16 turn_count_default; // 0x10E X = current turn count + uint16 turn_count_banked; // 0x110 + uint16 turn_count_sloped; // 0x112 X = number turns > 3 elements union { uint8 inversions; // 0x114 (???X XXXX) uint8 holes; // 0x114 (???X XXXX) @@ -194,12 +209,14 @@ typedef struct { // It reaches the maximum value of 7 at about 50% undercover and doesn't increase beyond that. uint8 undercover_portion; // 0x114 (XXX?-????) }; - uint8 drops; // 0x115 (??XX XXXX) - uint8 var_116; + // Y is number of powered lifts, X is drops + uint8 drops; // 0x115 (YYXX XXXX) + uint8 start_drop_height; // 0x116 uint8 highest_drop_height; // 0x117 sint32 sheltered_length; // 0x118 - uint8 pad_11C[0x2]; - uint8 num_sheltered_sections; // 0x11E + uint16 var_11C; + uint8 num_sheltered_sections; // 0x11E (?abY YYYY) + // z related to var_10C uint8 var_11F; // Customer counter in the current 960 game tick (about 30 seconds) interval uint16 cur_num_customers; // 0x120 @@ -297,7 +314,7 @@ typedef struct { uint32 lifecycle_flags; // 0x1D0 uint8 vehicle_colours_extended[32]; // 0x1D4 uint16 total_air_time; // 0x1F4 - uint8 pad_1F6; + uint8 var_1F6; uint8 num_circuits; // 0x1F7 sint16 cable_lift_x; // 0x1F8 sint16 cable_lift_y; // 0x1FA @@ -376,9 +393,11 @@ enum { RIDE_ENTRY_FLAG_NO_INVERSIONS = 1 << 1, // 0x2 RIDE_ENTRY_FLAG_NO_BANKED_TRACK = 1 << 2, // 0x4 RIDE_ENTRY_FLAG_3 = 1 << 3, // 0x8 - RIDE_ENTRY_FLAG_4 = 1 << 4, // 0x10 - RIDE_ENTRY_FLAG_5 = 1 << 5, // 0x20 - RIDE_ENTRY_FLAG_6 = 1 << 6, // 0x40 + RIDE_ENTRY_FLAG_ALTERNATIVE_SWING_MODE_1 = 1 << 4, // 0x10 + // Twist type rotation ride + RIDE_ENTRY_FLAG_ALTERNATIVE_ROTATION_MODE_1 = 1 << 5, // 0x20 + // Lifting arm rotation ride (enterprise) + RIDE_ENTRY_FLAG_ALTERNATIVE_ROTATION_MODE_2 = 1 << 6, // 0x40 RIDE_ENTRY_FLAG_7 = 1 << 7, // 0x80 RIDE_ENTRY_FLAG_8 = 1 << 8, // 0x100 RIDE_ENTRY_FLAG_9 = 1 << 9, // 0x200 @@ -392,7 +411,8 @@ enum { RIDE_ENTRY_DISABLE_FIRST_TWO_OPERATING_MODES = 1 << 17, // 0x20000 RIDE_ENTRY_FLAG_18 = 1 << 18, // 0x40000 RIDE_ENTRY_FLAG_19 = 1 << 19, // 0x80000 - RIDE_ENTRY_FLAG_20 = 1 << 20, // 0x100000 + // Must be set with swing mode 1 as well. + RIDE_ENTRY_FLAG_ALTERNATIVE_SWING_MODE_2 = 1 << 20, // 0x100000 RIDE_ENTRY_FLAG_21 = 1 << 21, // 0x200000 RIDE_ENTRY_FLAG_22 = 1 << 22, // 0x400000 RIDE_ENTRY_FLAG_23 = 1 << 23, // 0x800000 @@ -406,6 +426,17 @@ enum { RIDE_ENTRY_FLAG_31 = 1 << 31, // 0x80000000 }; +enum{ + RIDE_TESTING_SHELTERED = (1 << 0), + RIDE_TESTING_TURN_LEFT = (1 << 1), + RIDE_TESTING_TURN_RIGHT = (1 << 2), + RIDE_TESTING_TURN_BANKED = (1 << 3), + RIDE_TESTING_TURN_SLOPED = (1 << 4), + RIDE_TESTING_DROP_DOWN = (1 << 5), + RIDE_TESTING_POWERED_LIFT = (1 << 6), + RIDE_TESTING_DROP_UP = (1 << 7), +}; + enum { RIDE_TYPE_NULL = 255, RIDE_TYPE_SPIRAL_ROLLER_COASTER = 0, @@ -518,7 +549,7 @@ enum { RIDE_MODE_ROTATING_LIFT, RIDE_MODE_STATION_TO_STATION, RIDE_MODE_SINGLE_RIDE_PER_ADMISSION, - RIDE_MODE_UNLIMITED_RIDES_PER_ADMISSION, + RIDE_MODE_UNLIMITED_RIDES_PER_ADMISSION = 10, RIDE_MODE_MAZE, RIDE_MODE_RACE, RIDE_MODE_BUMPERCAR, @@ -528,7 +559,7 @@ enum { RIDE_MODE_FORWARD_ROTATION, RIDE_MODE_BACKWARD_ROTATION, RIDE_MODE_FILM_AVENGING_AVIATORS, - RIDE_MODE_3D_FILM_MOUSE_TAILS, + RIDE_MODE_3D_FILM_MOUSE_TAILS = 20, RIDE_MODE_SPACE_RINGS, RIDE_MODE_BEGINNERS, RIDE_MODE_LIM_POWERED_LAUNCH, @@ -538,7 +569,7 @@ enum { RIDE_MODE_INTENSE, RIDE_MODE_BERSERK, RIDE_MODE_HAUNTED_HOUSE, - RIDE_MODE_CIRCUS_SHOW, + RIDE_MODE_CIRCUS_SHOW = 30, RIDE_MODE_DOWNWARD_LAUNCH, RIDE_MODE_CROOKED_HOUSE, RIDE_MODE_FREEFALL_DROP, @@ -813,6 +844,12 @@ enum { #define STATION_DEPART_FLAG (1 << 7) #define STATION_DEPART_MASK (~STATION_DEPART_FLAG) +#define CURRENT_TURN_COUNT_MASK 0xF800 +#define TURN_MASK_1_ELEMENT 0x001F +#define TURN_MASK_2_ELEMENTS 0x00E0 +#define TURN_MASK_3_ELEMENTS 0x0700 +#define TURN_MASK_4_PLUS_ELEMENTS 0xF800 + // rct2: 0x009ACFA4 extern rct_ride_type **gRideTypeList; @@ -934,6 +971,7 @@ void game_command_callback_ride_remove_track_piece(int eax, int ebx, int ecx, in void game_command_demolish_ride(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp); void game_command_set_ride_appearance(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp); void game_command_set_ride_price(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp); + void ride_clear_for_construction(int rideIndex); void set_vehicle_type_image_max_sizes(rct_ride_type_vehicle* vehicle_type, int num_images); void invalidate_test_results(int rideIndex); @@ -941,16 +979,14 @@ void invalidate_test_results(int rideIndex); void ride_select_next_section(); void ride_select_previous_section(); -int get_var_10E_unk_1(rct_ride* ride); -int get_var_10E_unk_2(rct_ride* ride); -int get_var_10E_unk_3(rct_ride* ride); -int get_var_110_unk_1(rct_ride* ride); -int get_var_110_unk_2(rct_ride* ride); -int get_var_110_unk_3(rct_ride* ride); -int get_var_112_unk_1(rct_ride* ride); -int get_var_112_unk_2(rct_ride* ride); -int get_var_112_unk_3(rct_ride* ride); -int get_var_112_unk_4(rct_ride* ride); +void increment_turn_count_1_element(rct_ride* ride, uint8 type); +void increment_turn_count_2_elements(rct_ride* ride, uint8 type); +void increment_turn_count_3_elements(rct_ride* ride, uint8 type); +void increment_turn_count_4_plus_elements(rct_ride* ride, uint8 type); +int get_turn_count_1_element(rct_ride* ride, uint8 type); +int get_turn_count_2_elements(rct_ride* ride, uint8 type); +int get_turn_count_3_elements(rct_ride* ride, uint8 type); +int get_turn_count_4_plus_elements(rct_ride* ride, uint8 type); uint8 ride_get_helix_sections(rct_ride *ride); bool ride_has_spinning_tunnel(rct_ride *ride); @@ -999,7 +1035,7 @@ void game_command_place_ride_entrance_or_exit(int *eax, int *ebx, int *ecx, int void game_command_remove_ride_entrance_or_exit(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp); void sub_6CB945(int rideIndex); -void ride_crash(int rideIndex, int vehicleIndex); +void ride_crash(uint8 rideIndex, uint8 vehicleIndex); bool ride_type_is_intamin(int rideType); void sub_6C94D8(); diff --git a/src/ride/ride_data.c b/src/ride/ride_data.c index e195d2524fd1..2a20c03721f5 100644 --- a/src/ride/ride_data.c +++ b/src/ride/ride_data.c @@ -11,6 +11,7 @@ #include "../localisation/localisation.h" #include "ride.h" #include "ride_data.h" +#include "../audio/audio.h" const bool hasRunningTrack[0x60] = { true, // 0 Spiral Roller coaster @@ -986,100 +987,6 @@ const rct_ride_entrance_definition RideEntranceDefinitions[RIDE_ENTRANCE_STYLE_C { 0, 0, 2, STR_ENTRANCE_NONE, 0x00000000, 0, } // RIDE_ENTRANCE_STYLE_NONE }; -// Data read from 0x0097D7C9 4 bytes at a time -const uint8 RideLiftHillAdjustments[0x60] = { - 7, // Spiral Roller coaster - 4, // Stand Up Coaster - 4, // Suspended Swinging - 5, // Inverted - 4, // Steel Mini Coaster - 5, // Mini Railroad - 5, // Monorail - 4, // Mini Suspended Coaster - 5, // Boat Ride - 4, // Wooden Wild Mine/Mouse - 4, // Steeplechase/Motorbike/Soap Box Derby - 5, // Car Ride - 5, // Launched Freefall - 4, // Bobsleigh Coaster - 5, // Observation Tower - 4, // Looping Roller Coaster - 4, // Dinghy Slide - 4, // Mine Train Coaster - 5, // Chairlift - 4, // Corkscrew Roller Coaster - 5, // Maze - 5, // Spiral Slide - 5, // Go Karts - 5, // Log Flume - 5, // River Rapids - 5, // Bumper Cars - 5, // Pirate Ship - 5, // Swinging Inverter Ship - 5, // Food Stall - 5, // (none) - 5, // Drink Stall - 5, // (none) - 5, // Shop (all types) - 5, // Merry Go Round - 5, // Balloon Stall (maybe) - 5, // Information Kiosk - 5, // Bathroom - 5, // Ferris Wheel - 5, // Motion Simulator - 5, // 3D Cinema - 5, // Topspin - 5, // Space Rings - 5, // Reverse Freefall Coaster - 5, // Elevator - 4, // Vertical Drop Roller Coaster - 5, // ATM - 5, // Twist - 5, // Haunted House - 5, // First Aid - 5, // Circus Show - 5, // Ghost Train - 5, // Twister Roller Coaster - 5, // Wooden Roller Coaster - 3, // Side-Friction Roller Coaster - 4, // Wild Mouse - 4, // Multi Dimension Coaster - 4, // (none) - 4, // Flying Roller Coaster - 4, // (none) - 3, // Virginia Reel - 5, // Splash Boats - 5, // Mini Helicopters - 4, // Lay-down Roller Coaster - 5, // Suspended Monorail - 4, // (none) - 3, // Reverser Roller Coaster - 4, // Heartline Twister Roller Coaster - 5, // Mini Golf - 5, // Giga Coaster - 5, // Roto-Drop - 5, // Flying Saucers - 5, // Crooked House - 5, // Monorail Cycles - 4, // Compact Inverted Coaster - 4, // Water Coaster - 5, // Air Powered Vertical Coaster - 4, // Inverted Hairpin Coaster - 5, // Magic Carpet - 5, // Submarine Ride - 5, // River Rafts - 5, // (none) - 5, // Enterprise - 5, // (none) - 5, // (none) - 5, // (none) - 4, // (none) - 4, // Inverted Impulse Coaster - 4, // Mini Roller Coaster - 5, // Mine Ride - 4 // LIM Launched Roller Coaster -}; - // rct2: 0x0097D4F2 const rct_ride_data_4 RideData4[91] = { { RIDE_TYPE_FLAG4_ALLOW_MUSIC | RIDE_TYPE_FLAG4_PEEP_CHECK_GFORCES | RIDE_TYPE_FLAG4_HAS_ENTRANCE_EXIT | RIDE_TYPE_FLAG4_HAS_AIR_TIME | RIDE_TYPE_FLAG4_ALLOW_MULTIPLE_CIRCUITS | RIDE_TYPE_FLAG4_11 | RIDE_TYPE_FLAG4_13, MUSIC_STYLE_ROCK_STYLE_3, 0, 0, 0, 20, 20 }, // RIDE_TYPE_SPIRAL_ROLLER_COASTER @@ -1353,7 +1260,7 @@ const rct_ride_type_vehicle CableLiftVehicle = { .rotation_frame_mask = 31, .var_02 = 0, .var_03 = 0, - .var_04 = 0, + .spacing = 0, .car_friction = 0, .tab_height = 0, .num_seats = 0, @@ -1362,8 +1269,8 @@ const rct_ride_type_vehicle CableLiftVehicle = { .sprite_height_negative = 0, .sprite_height_positive = 0, .var_11 = 0, - .var_12 = 0, - .var_14 = 0, + .flags_a = 0, + .flags_b = 0, .var_16 = 1, .base_image_id = 29110, .var_1C = 0, @@ -1383,7 +1290,9 @@ const rct_ride_type_vehicle CableLiftVehicle = { .no_seating_rows = 0, .spinning_inertia = 0, .spinning_friction = 255, - .pad_57 = { 0,0,0 }, + .var_57 = 0, + .var_58 = 0, + .sound_range = 0, .var_5A = 0, .powered_acceleration = 0, .powered_max_speed = 0, @@ -1393,3 +1302,110 @@ const rct_ride_type_vehicle CableLiftVehicle = { .special_frames = 0, .peep_loading_positions = NULL }; + +/* rct2: 0x009A0AA0 */ +const uint16 RideFilmLength[3] = { + 5000, // MOUSE_TAILS + 6000, // STORM_CHASERS + 7000 // SPACE_RAIDERS +}; + +/* rct2: 0x009A0AC4 */ +const uint16 RideCrookedHouseLength[1] = { + 600 +}; + +/* rct2: 0x0097D7C8, 0x0097D7C9, 0x0097D7CA */ +const rct_ride_lift_data RideLiftData[] = { + { SOUND_LIFT_3, 7, 7 }, // Spiral Roller coaster + { SOUND_LIFT_1, 4, 6 }, // Stand Up Coaster + { SOUND_LIFT_1, 4, 6 }, // Suspended Swinging + { SOUND_LIFT_7, 5, 7 }, // Inverted + { SOUND_LIFT_3, 4, 6 }, // Steel Mini Coaster + { 255, 5, 5 }, // Mini Railroad + { 255, 5, 5 }, // Monorail + { SOUND_LIFT_3, 4, 5 }, // Mini Suspended Coaster + { 255, 5, 5 }, // Boat Ride + { SOUND_LIFT_1, 4, 5 }, // Wooden Wild Mine/Mouse + { SOUND_LIFT_1, 4, 5 }, // Steeplechase/Motorbike/Soap Box D + { 255, 5, 5 }, // Car Ride + { 255, 5, 5 }, // Launched Freefall + { SOUND_LIFT_3, 4, 5 }, // Bobsleigh Coaster + { 255, 5, 5 }, // Observation Tower + { SOUND_LIFT_1, 4, 6 }, // Looping Roller Coaster + { SOUND_LIFT_3, 4, 5 }, // Dinghy Slide + { SOUND_LIFT_4, 4, 6 }, // Mine Train Coaster + { 255, 5, 5 }, // Chairlift + { SOUND_LIFT_4, 4, 6 }, // Corkscrew Roller Coaster + { 255, 5, 5 }, // Maze + { 255, 5, 5 }, // Spiral Slide + { 255, 5, 5 }, // Go Karts + { 255, 5, 5 }, // Log Flume + { 255, 5, 5 }, // River Rapids + { 255, 5, 5 }, // Bumper Cars + { 255, 5, 5 }, // Pirate Ship + { 255, 5, 5 }, // Swinging Inverter Ship + { 255, 5, 5 }, // Food Stall + { 255, 5, 5 }, // (none) + { 255, 5, 5 }, // Drink Stall + { 255, 5, 5 }, // (none) + { 255, 5, 5 }, // Shop (all types) + { 255, 5, 5 }, // Merry Go Round + { 255, 5, 5 }, // Balloon Stall (maybe) + { 255, 5, 5 }, // Information Kiosk + { 255, 5, 5 }, // Bathroom + { 255, 5, 5 }, // Ferris Wheel + { 255, 5, 5 }, // Motion Simulator + { 255, 5, 5 }, // 3D Cinema + { 255, 5, 5 }, // Topspin + { 255, 5, 5 }, // Space Rings + { 255, 5, 5 }, // Reverse Freefall Coaster + { 255, 5, 5 }, // Elevator + { SOUND_LIFT_7, 4, 5 }, // Vertical Drop Roller Coaster + { 255, 5, 5 }, // ATM + { 255, 5, 5 }, // Twist + { 255, 5, 5 }, // Haunted House + { 255, 5, 5 }, // First Aid + { 255, 5, 5 }, // Circus Show + { 255, 5, 5 }, // Ghost Train + { SOUND_LIFT_7, 5, 8 }, // Twister Roller Coaster + { SOUND_LIFT_5, 5, 7 }, // Wooden Roller Coaster + { SOUND_LIFT_5, 3, 4 }, // Side-Friction Roller Coaster + { SOUND_LIFT_6, 4, 6 }, // Wild Mouse + { SOUND_LIFT_3, 4, 6 }, // Multi Dimension Coaster + { SOUND_LIFT_3, 4, 6 }, // (none) + { SOUND_LIFT_7, 4, 6 }, // Flying Roller Coaster + { SOUND_LIFT_7, 4, 6 }, // (none) + { SOUND_LIFT_1, 3, 4 }, // Virginia Reel + { 255, 5, 5 }, // Splash Boats + { 255, 5, 5 }, // Mini Helicopters + { SOUND_LIFT_1, 4, 6 }, // Lay-down Roller Coaster + { 255, 5, 5 }, // Suspended Monorail + { SOUND_LIFT_1, 4, 6 }, // (none) + { SOUND_LIFT_1, 3, 4 }, // Reverser Roller Coaster + { SOUND_LIFT_1, 4, 6 }, // Heartline Twister Roller Coaster + { 255, 5, 5 }, // Mini Golf + { SOUND_LIFT_1, 5, 8 }, // Giga Coaster + { 255, 5, 5 }, // Roto-Drop + { 255, 5, 5 }, // Flying Saucers + { 255, 5, 5 }, // Crooked House + { 255, 5, 5 }, // Monorail Cycles + { SOUND_LIFT_3, 4, 6 }, // Compact Inverted Coaster + { SOUND_LIFT_1, 4, 6 }, // Water Coaster + { 255, 5, 5 }, // Air Powered Vertical Coaster + { SOUND_LIFT_6, 4, 6 }, // Inverted Hairpin Coaster + { 255, 5, 5 }, // Magic Carpet + { 255, 5, 5 }, // Submarine Ride + { 255, 5, 5 }, // River Rafts + { 255, 5, 5 }, // (none) + { 255, 5, 5 }, // Enterprise + { 255, 5, 5 }, // (none) + { 255, 5, 5 }, // (none) + { 255, 5, 5 }, // (none) + { SOUND_LIFT_4, 4, 7 }, // (none) + { SOUND_LIFT_1, 4, 7 }, // Inverted Impulse Coaster + { SOUND_LIFT_1, 4, 6 }, // Mini Roller Coaster + { 255, 5, 5 }, // Mine Ride + { SOUND_LIFT_6, 4, 6 }, // (none) + { 255, 4, 6 } // LIM Launched Roller Coaster +}; diff --git a/src/ride/ride_data.h b/src/ride/ride_data.h index cf037b891ad3..505d40a061f3 100644 --- a/src/ride/ride_data.h +++ b/src/ride/ride_data.h @@ -54,6 +54,12 @@ typedef struct { uint8 pad[4]; } rct_ride_data_4; +typedef struct { + uint8 sound_id; + uint8 minimum_speed; + uint8 maximum_speed; +} rct_ride_lift_data; + enum { RIDE_TYPE_FLAG4_0 = (1 << 0), RIDE_TYPE_FLAG4_MUSIC_ON_DEFAULT = (1 << 1), @@ -88,7 +94,7 @@ extern const uint8 AllRideModesAvailable[]; extern const uint8 RideAvailableBreakdowns[]; extern const rct_ride_entrance_definition RideEntranceDefinitions[RIDE_ENTRANCE_STYLE_COUNT]; -extern const uint8 RideLiftHillAdjustments[0x60]; +extern const rct_ride_lift_data RideLiftData[91]; extern const rct_ride_data_4 RideData4[91]; @@ -98,4 +104,7 @@ extern const uint32 ShopItemImage[SHOP_ITEM_COUNT]; extern const rct_ride_type_vehicle CableLiftVehicle; +extern const uint16 RideFilmLength[3]; +extern const uint16 RideCrookedHouseLength[1]; + #endif diff --git a/src/ride/ride_ratings.c b/src/ride/ride_ratings.c index 707a126de54e..a2262d654906 100644 --- a/src/ride/ride_ratings.c +++ b/src/ride/ride_ratings.c @@ -843,12 +843,9 @@ static void ride_ratings_apply_intensity_penalty(rating_tuple *ratings) */ static void set_unreliability_factor(rct_ride *ride) { - // The higher the number, the lower the breakdown - // possibility. Range is [3, 7]. values are here: - // https://gist.github.com/kevinburke/123977c4884ccadbec70. Consider - // inlining this per ride - uint8 lift_speed_adjustment = RideLiftHillAdjustments[ride->type]; - ride->unreliability_factor += (ride->lift_hill_speed - lift_speed_adjustment) * 2; + // The bigger the difference in lift speed and minimum the higher the unreliability + uint8 lift_speed_adjustment = RideLiftData[ride->type].minimum_speed; + ride->unreliability_factor += (ride->lift_hill_speed - lift_speed_adjustment) * 2; } static uint32 get_proximity_score_helper_1(uint16 x, uint16 max, uint32 multiplier) @@ -930,24 +927,25 @@ static int sub_65E72D(rct_ride *ride) return (dh << 8) | numShelteredEighths; } -static rating_tuple get_var_10E_rating(rct_ride* ride) { - int var_10E_unk_1 = get_var_10E_unk_1(ride); - int var_10E_unk_2 = get_var_10E_unk_2(ride); - int var_10E_unk_3 = get_var_10E_unk_3(ride); +static rating_tuple get_flat_turns_rating(rct_ride* ride) { + rating_tuple rating; - int excitement = (var_10E_unk_1 * 0x28000) >> 16; - excitement += (var_10E_unk_2 * 0x30000) >> 16; - excitement += (var_10E_unk_3 * 63421) >> 16; + int no_3_plus_turns = get_turn_count_3_elements(ride, 0); + int no_2_turns = get_turn_count_2_elements(ride, 0); + int no_1_turns = get_turn_count_1_element(ride, 0); - int intensity = (var_10E_unk_1 * 81920) >> 16; - intensity += (var_10E_unk_2 * 49152) >> 16; - intensity += (var_10E_unk_3 * 21140) >> 16; + rating.excitement = (no_3_plus_turns * 0x28000) >> 16; + rating.excitement += (no_2_turns * 0x30000) >> 16; + rating.excitement += (no_1_turns * 63421) >> 16; - int nausea = (var_10E_unk_1 * 0x50000) >> 16; - nausea += (var_10E_unk_2 * 0x32000) >> 16; - nausea += (var_10E_unk_3 * 42281) >> 16; + rating.intensity = (no_3_plus_turns * 81920) >> 16; + rating.intensity += (no_2_turns * 49152) >> 16; + rating.intensity += (no_1_turns * 21140) >> 16; + + rating.nausea = (no_3_plus_turns * 0x50000) >> 16; + rating.nausea += (no_2_turns * 0x32000) >> 16; + rating.nausea += (no_1_turns * 42281) >> 16; - rating_tuple rating = { excitement, intensity, nausea }; return rating; } @@ -955,24 +953,25 @@ static rating_tuple get_var_10E_rating(rct_ride* ride) { * * rct2: 0x0065DF72 */ -static rating_tuple get_var_110_rating(rct_ride* ride) { - int var_110_unk_1 = get_var_110_unk_1(ride); - int var_110_unk_2 = get_var_110_unk_2(ride); - int var_110_unk_3 = get_var_110_unk_3(ride); +static rating_tuple get_banked_turns_rating(rct_ride* ride) { + rating_tuple rating; - int excitement = (var_110_unk_1 * 0x3C000) >> 16; - excitement += (var_110_unk_2 * 0x3C000) >> 16; - excitement += (var_110_unk_3 * 73992) >> 16; + int no_3_plus_turns = get_turn_count_3_elements(ride, 1); + int no_2_turns = get_turn_count_2_elements(ride, 1); + int no_1_turns = get_turn_count_1_element(ride, 1); - int intensity = (var_110_unk_1 * 0x14000) >> 16; - intensity += (var_110_unk_2 * 49152) >> 16; - intensity += (var_110_unk_3 * 21140) >> 16; + rating.excitement = (no_3_plus_turns * 0x3C000) >> 16; + rating.excitement += (no_2_turns * 0x3C000) >> 16; + rating.excitement += (no_1_turns * 73992) >> 16; - int nausea = (var_110_unk_1 * 0x50000) >> 16; - nausea += (var_110_unk_2 * 0x32000) >> 16; - nausea += (var_110_unk_3 * 48623) >> 16; + rating.intensity = (no_3_plus_turns * 0x14000) >> 16; + rating.intensity += (no_2_turns * 49152) >> 16; + rating.intensity += (no_1_turns * 21140) >> 16; + + rating.nausea = (no_3_plus_turns * 0x50000) >> 16; + rating.nausea += (no_2_turns * 0x32000) >> 16; + rating.nausea += (no_1_turns * 48623) >> 16; - rating_tuple rating = { excitement, intensity, nausea }; return rating; } @@ -980,30 +979,21 @@ static rating_tuple get_var_110_rating(rct_ride* ride) { * * rct2: 0x0065E047 */ -static rating_tuple get_var_112_rating(rct_ride *ride) { - int al; - - al = get_var_112_unk_1(ride); - al = min(al, 4); - int excitement = (al * 0x78000) >> 16; +static rating_tuple get_sloped_turns_rating(rct_ride* ride) { + rating_tuple rating; - al = get_var_112_unk_1(ride); - al = min(al, 8); - int nausea = (al * 0x78000) >> 16; + int no_4_plus_turns = get_turn_count_4_plus_elements(ride, 2); + int no_3_turns = get_turn_count_3_elements(ride, 2); + int no_2_turns = get_turn_count_2_elements(ride, 2); + int no_1_turns = get_turn_count_1_element(ride, 2); - al = get_var_112_unk_2(ride); - al = min(al, 6); - excitement += (al * 273066) >> 16; + rating.excitement = (min(no_4_plus_turns, 4) * 0x78000) >> 16; + rating.excitement += (min(no_3_turns, 6) * 273066) >> 16; + rating.excitement += (min(no_2_turns, 6) * 0x3AAAA) >> 16; + rating.excitement += (min(no_1_turns, 7) * 187245) >> 16; + rating.intensity = 0; + rating.nausea = (min(no_4_plus_turns, 8) * 0x78000) >> 16; - al = get_var_112_unk_3(ride); - al = min(al, 6); - excitement += (al * 0x3AAAA) >> 16; - - al = get_var_112_unk_4(ride); - al = min(al, 7); - excitement += (al * 187245) >> 16; - - rating_tuple rating = { excitement, 0, nausea }; return rating; } @@ -1012,11 +1002,12 @@ static rating_tuple get_var_112_rating(rct_ride *ride) { * rct2: 0x0065E0F2 */ static rating_tuple get_inversions_ratings(uint8 inversions) { - int excitement = (min(inversions, 6) * 0x1AAAAA) >> 16; - int intensity = (inversions * 0x320000) >> 16; - int nausea = (inversions * 0x15AAAA) >> 16; + rating_tuple rating; + + rating.excitement = (min(inversions, 6) * 0x1AAAAA) >> 16; + rating.intensity = (inversions * 0x320000) >> 16; + rating.nausea = (inversions * 0x15AAAA) >> 16; - rating_tuple rating = { excitement, intensity, nausea }; return rating; } @@ -1079,17 +1070,17 @@ static rating_tuple sub_65DDD1(rct_ride *ride) intensity += special_track_element_rating.intensity; nausea += special_track_element_rating.nausea; - rating_tuple var_10E_rating = get_var_10E_rating(ride); + rating_tuple var_10E_rating = get_flat_turns_rating(ride); excitement += var_10E_rating.excitement; intensity += var_10E_rating.intensity; nausea += var_10E_rating.nausea; - rating_tuple var_110_rating = get_var_110_rating(ride); + rating_tuple var_110_rating = get_banked_turns_rating(ride); excitement += var_110_rating.excitement; intensity += var_110_rating.intensity; nausea += var_110_rating.nausea; - rating_tuple var_112_rating = get_var_112_rating(ride); + rating_tuple var_112_rating = get_sloped_turns_rating(ride); excitement += var_112_rating.excitement; intensity += var_112_rating.intensity; nausea += var_112_rating.nausea; diff --git a/src/ride/station.c b/src/ride/station.c index 91f25949fbc5..0db623df5ea3 100644 --- a/src/ride/station.c +++ b/src/ride/station.c @@ -105,7 +105,7 @@ static void ride_update_station_bumpercar(rct_ride *ride, int stationIndex) 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)) + if (vehicle->var_CE < dh || (vehicle->var_CE < dh && vehicle->sub_state > dl)) continue; // End match diff --git a/src/ride/track.c b/src/ride/track.c index 1079794daf68..69fe5d59f33c 100644 --- a/src/ride/track.c +++ b/src/ride/track.c @@ -2385,33 +2385,16 @@ int track_delete() int track_is_connected_by_shape(rct_map_element *a, rct_map_element *b) { int trackType, aBank, aAngle, bBank, bAngle; - rct_ride *ride; - ride = GET_RIDE(a->properties.track.ride_index); trackType = a->properties.track.type; aBank = gTrackDefinitions[trackType].bank_end; aAngle = gTrackDefinitions[trackType].vangle_end; - if (RideData4[ride->type].flags & RIDE_TYPE_FLAG4_3) { - if (a->properties.track.colour & 4) { - if (aBank == TRACK_BANK_NONE) - aBank = TRACK_BANK_UPSIDE_DOWN; - else if (aBank == TRACK_BANK_UPSIDE_DOWN) - aBank = TRACK_BANK_NONE; - } - } + aBank = track_get_actual_bank(a, aBank); - ride = GET_RIDE(b->properties.track.ride_index); trackType = b->properties.track.type; bBank = gTrackDefinitions[trackType].bank_start; bAngle = gTrackDefinitions[trackType].vangle_start; - if (RideData4[ride->type].flags & RIDE_TYPE_FLAG4_3) { - if (b->properties.track.colour & 4) { - if (bBank == TRACK_BANK_NONE) - bBank = TRACK_BANK_UPSIDE_DOWN; - else if (bBank == TRACK_BANK_UPSIDE_DOWN) - bBank = TRACK_BANK_NONE; - } - } + bBank = track_get_actual_bank(b, bBank); return aBank == bBank && aAngle == bAngle; } @@ -4939,6 +4922,30 @@ bool track_element_is_lift_hill(rct_map_element *trackElement) return trackElement->type & 0x80; } +/** + * Checks if a track element is recognised as the beginning of a block. + * A beginning of a block can be the end of a station, the end of a lift hill, + * or a block brake. + */ +bool track_element_is_block_start(rct_map_element *trackElement) +{ + switch (trackElement->properties.track.type) { + case TRACK_ELEM_END_STATION: + case TRACK_ELEM_CABLE_LIFT_HILL: + case 216: + return true; + case TRACK_ELEM_25_DEG_UP_TO_FLAT: + case TRACK_ELEM_60_DEG_UP_TO_FLAT: + case TRACK_ELEM_DIAG_25_DEG_UP_TO_FLAT: + case TRACK_ELEM_DIAG_60_DEG_UP_TO_FLAT: + if (track_element_is_lift_hill(trackElement)) { + return true; + } + break; + } + return false; +} + bool track_element_is_cable_lift(rct_map_element *trackElement) { return trackElement->properties.track.colour & TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT; } @@ -4950,3 +4957,74 @@ void track_element_set_cable_lift(rct_map_element *trackElement) { void track_element_clear_cable_lift(rct_map_element *trackElement) { trackElement->properties.track.colour &= ~TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT; } + +int track_get_actual_bank(rct_map_element *mapElement, int bank) +{ + rct_ride *ride = GET_RIDE(mapElement->properties.track.ride_index); + int trackColour = mapElement->properties.track.colour; + return track_get_actual_bank_2(ride->type, trackColour, bank); +} + +int track_get_actual_bank_2(int rideType, int trackColour, int bank) +{ + if (RideData4[rideType].flags & RIDE_TYPE_FLAG4_3) { + if (trackColour & 4) { + if (bank == TRACK_BANK_NONE) { + bank = TRACK_BANK_UPSIDE_DOWN; + } else if (bank == TRACK_BANK_UPSIDE_DOWN) { + bank = TRACK_BANK_NONE; + } + } + } + return bank; +} + +int track_get_actual_bank_3(rct_vehicle *vehicle, rct_map_element *mapElement) +{ + uint8 colourThingToXor = (vehicle->update_flags >> 9) & 0xFF; + int trackType = mapElement->properties.track.type; + int rideType = GET_RIDE(mapElement->properties.track.ride_index)->type; + int trackColour = mapElement->properties.track.colour ^ colourThingToXor; + int bankStart = gTrackDefinitions[trackType].bank_start; + return track_get_actual_bank_2(rideType, trackColour, bankStart); +} + +bool track_element_is_station(rct_map_element *trackElement) +{ + switch (trackElement->properties.track.type) { + case TRACK_ELEM_END_STATION: + case TRACK_ELEM_BEGIN_STATION: + case TRACK_ELEM_MIDDLE_STATION: + return true; + default: + return false; + } +} + +bool track_element_is_covered(int trackElementType) +{ + switch (trackElementType) { + case TRACK_ELEM_FLAT_COVERED: + case TRACK_ELEM_25_DEG_UP_COVERED: + case TRACK_ELEM_60_DEG_UP_COVERED: + case TRACK_ELEM_FLAT_TO_25_DEG_UP_COVERED: + case TRACK_ELEM_25_DEG_UP_TO_60_DEG_UP_COVERED: + case TRACK_ELEM_60_DEG_UP_TO_25_DEG_UP_COVERED: + case TRACK_ELEM_25_DEG_UP_TO_FLAT_COVERED: + case TRACK_ELEM_25_DEG_DOWN_COVERED: + case TRACK_ELEM_60_DEG_DOWN_COVERED: + case TRACK_ELEM_FLAT_TO_25_DEG_DOWN_COVERED: + case TRACK_ELEM_25_DEG_DOWN_TO_60_DEG_DOWN_COVERED: + case TRACK_ELEM_60_DEG_DOWN_TO_25_DEG_DOWN_COVERED: + case TRACK_ELEM_25_DEG_DOWN_TO_FLAT_COVERED: + case TRACK_ELEM_LEFT_QUARTER_TURN_5_TILES_COVERED: + case TRACK_ELEM_RIGHT_QUARTER_TURN_5_TILES_COVERED: + case TRACK_ELEM_S_BEND_LEFT_COVERED: + case TRACK_ELEM_S_BEND_RIGHT_COVERED: + case TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES_COVERED: + case TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES_COVERED: + return true; + default: + return false; + } +} diff --git a/src/ride/track.h b/src/ride/track.h index 569076766605..21e9421fbbee 100644 --- a/src/ride/track.h +++ b/src/ride/track.h @@ -300,6 +300,17 @@ enum { TRACK_CORKSCREW_DOWN = 224 }; +enum { + TRACK_ELEM_FLAG_TURN_LEFT = (1 << 1), + TRACK_ELEM_FLAG_TURN_RIGHT = (1 << 2), + TRACK_ELEM_FLAG_TURN_BANKED = (1 << 3), + TRACK_ELEM_FLAG_TURN_SLOPED = (1 << 4), + TRACK_ELEM_FLAG_DOWN = (1 << 5), + TRACK_ELEM_FLAG_UP = (1 << 6), + TRACK_ELEM_FLAG_INVERSION = (1 << 7), + TRACK_ELEM_FLAG_HELIX = (1 << 11), +}; + enum { TRACK_ELEM_FLAT, TRACK_ELEM_END_STATION, @@ -311,7 +322,7 @@ enum { TRACK_ELEM_25_DEG_UP_TO_60_DEG_UP, TRACK_ELEM_60_DEG_UP_TO_25_DEG_UP, TRACK_ELEM_25_DEG_UP_TO_FLAT, - TRACK_ELEM_25_DEG_DOWN, + TRACK_ELEM_25_DEG_DOWN = 10, TRACK_ELEM_60_DEG_DOWN, TRACK_ELEM_FLAT_TO_25_DEG_DOWN, TRACK_ELEM_25_DEG_DOWN_TO_60_DEG_DOWN, @@ -321,7 +332,7 @@ enum { TRACK_ELEM_RIGHT_QUARTER_TURN_5_TILES, TRACK_ELEM_FLAT_TO_LEFT_BANK, TRACK_ELEM_FLAT_TO_RIGHT_BANK, - TRACK_ELEM_LEFT_BANK_TO_FLAT, + TRACK_ELEM_LEFT_BANK_TO_FLAT = 20, TRACK_ELEM_RIGHT_BANK_TO_FLAT, TRACK_ELEM_BANKED_LEFT_QUARTER_TURN_5_TILES, TRACK_ELEM_BANKED_RIGHT_QUARTER_TURN_5_TILES, @@ -331,7 +342,7 @@ enum { TRACK_ELEM_25_DEG_UP_TO_RIGHT_BANK, TRACK_ELEM_LEFT_BANK_TO_25_DEG_DOWN, TRACK_ELEM_RIGHT_BANK_TO_25_DEG_DOWN, - TRACK_ELEM_25_DEG_DOWN_TO_LEFT_BANK, + TRACK_ELEM_25_DEG_DOWN_TO_LEFT_BANK = 30, TRACK_ELEM_25_DEG_DOWN_TO_RIGHT_BANK, TRACK_ELEM_LEFT_BANK, TRACK_ELEM_RIGHT_BANK, @@ -341,7 +352,7 @@ enum { TRACK_ELEM_RIGHT_QUARTER_TURN_5_TILES_25_DEG_DOWN, TRACK_ELEM_S_BEND_LEFT, TRACK_ELEM_S_BEND_RIGHT, - TRACK_ELEM_LEFT_VERTICAL_LOOP, + TRACK_ELEM_LEFT_VERTICAL_LOOP = 40, TRACK_ELEM_RIGHT_VERTICAL_LOOP, TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES, TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES, @@ -351,7 +362,7 @@ enum { TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES_25_DEG_UP, TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES_25_DEG_DOWN, TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES_25_DEG_DOWN, - TRACK_ELEM_LEFT_QUARTER_TURN_1_TILE, + TRACK_ELEM_LEFT_QUARTER_TURN_1_TILE = 50, TRACK_ELEM_RIGHT_QUARTER_TURN_1_TILE, TRACK_ELEM_LEFT_TWIST_DOWN_TO_UP, TRACK_ELEM_RIGHT_TWIST_DOWN_TO_UP, @@ -361,7 +372,7 @@ enum { TRACK_ELEM_HALF_LOOP_DOWN, TRACK_ELEM_LEFT_CORKSCREW_UP, TRACK_ELEM_RIGHT_CORKSCREW_UP, - TRACK_ELEM_LEFT_CORKSCREW_DOWN, + TRACK_ELEM_LEFT_CORKSCREW_DOWN = 60, TRACK_ELEM_RIGHT_CORKSCREW_DOWN, TRACK_ELEM_FLAT_TO_60_DEG_UP, TRACK_ELEM_60_DEG_UP_TO_FLAT, @@ -371,7 +382,7 @@ enum { TRACK_ELEM_TOWER_SECTION, TRACK_ELEM_FLAT_COVERED, TRACK_ELEM_25_DEG_UP_COVERED, - TRACK_ELEM_60_DEG_UP_COVERED, + TRACK_ELEM_60_DEG_UP_COVERED = 70, TRACK_ELEM_FLAT_TO_25_DEG_UP_COVERED, TRACK_ELEM_25_DEG_UP_TO_60_DEG_UP_COVERED, TRACK_ELEM_60_DEG_UP_TO_25_DEG_UP_COVERED, @@ -381,7 +392,7 @@ enum { TRACK_ELEM_FLAT_TO_25_DEG_DOWN_COVERED, TRACK_ELEM_25_DEG_DOWN_TO_60_DEG_DOWN_COVERED, TRACK_ELEM_60_DEG_DOWN_TO_25_DEG_DOWN_COVERED, - TRACK_ELEM_25_DEG_DOWN_TO_FLAT_COVERED, + TRACK_ELEM_25_DEG_DOWN_TO_FLAT_COVERED = 80, TRACK_ELEM_LEFT_QUARTER_TURN_5_TILES_COVERED, TRACK_ELEM_RIGHT_QUARTER_TURN_5_TILES_COVERED, TRACK_ELEM_S_BEND_LEFT_COVERED, @@ -391,7 +402,7 @@ enum { TRACK_ELEM_LEFT_HALF_BANKED_HELIX_UP_SMALL, TRACK_ELEM_RIGHT_HALF_BANKED_HELIX_UP_SMALL, TRACK_ELEM_LEFT_HALF_BANKED_HELIX_DOWN_SMALL, - TRACK_ELEM_RIGHT_HALF_BANKED_HELIX_DOWN_SMALL, + TRACK_ELEM_RIGHT_HALF_BANKED_HELIX_DOWN_SMALL = 90, TRACK_ELEM_LEFT_HALF_BANKED_HELIX_UP_LARGE, TRACK_ELEM_RIGHT_HALF_BANKED_HELIX_UP_LARGE, TRACK_ELEM_LEFT_HALF_BANKED_HELIX_DOWN_LARGE, @@ -401,7 +412,7 @@ enum { TRACK_ELEM_LEFT_QUARTER_TURN_1_TILE_60_DEG_DOWN, TRACK_ELEM_RIGHT_QUARTER_TURN_1_TILE_60_DEG_DOWN, TRACK_ELEM_BRAKES, - TRACK_ELEM_ROTATION_CONTROL_TOGGLE, + TRACK_ELEM_ROTATION_CONTROL_TOGGLE = 100, TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP, TRACK_ELEM_LEFT_QUARTER_BANKED_HELIX_LARGE_UP, TRACK_ELEM_RIGHT_QUARTER_BANKED_HELIX_LARGE_UP, @@ -411,7 +422,7 @@ enum { TRACK_ELEM_RIGHT_QUARTER_HELIX_LARGE_UP, TRACK_ELEM_LEFT_QUARTER_HELIX_LARGE_DOWN, TRACK_ELEM_RIGHT_QUARTER_HELIX_LARGE_DOWN, - TRACK_ELEM_25_DEG_UP_LEFT_BANKED, + TRACK_ELEM_25_DEG_UP_LEFT_BANKED = 110, TRACK_ELEM_25_DEG_UP_RIGHT_BANKED, TRACK_ELEM_WATERFALL, TRACK_ELEM_RAPIDS, @@ -421,7 +432,7 @@ enum { TRACK_ELEM_WATER_SPLASH, TRACK_ELEM_FLAT_TO_60_DEG_UP_LONG_BASE, TRACK_ELEM_60_DEG_UP_TO_FLAT_LONG_BASE, - TRACK_ELEM_WHIRLPOOL, + TRACK_ELEM_WHIRLPOOL = 120, TRACK_ELEM_FLAT_TO_60_DEG_DOWN_LONG_BASE, TRACK_ELEM_60_DEG_UP_TO_FLAT_LONG_BASE_122, TRACK_ELEM_CABLE_LIFT_HILL, @@ -431,7 +442,7 @@ enum { TRACK_ELEM_90_DEG_DOWN, TRACK_ELEM_60_DEG_UP_TO_90_DEG_UP, TRACK_ELEM_90_DEG_DOWN_TO_60_DEG_DOWN, - TRACK_ELEM_90_DEG_UP_TO_60_DEG_UP, + TRACK_ELEM_90_DEG_UP_TO_60_DEG_UP = 130, TRACK_ELEM_60_DEG_DOWN_TO_90_DEG_DOWN, TRACK_ELEM_BRAKE_FOR_DROP, TRACK_ELEM_LEFT_EIGHTH_TO_DIAG, @@ -441,7 +452,7 @@ enum { TRACK_ELEM_LEFT_EIGHTH_BANK_TO_DIAG, TRACK_ELEM_RIGHT_EIGHTH_BANK_TO_DIAG, TRACK_ELEM_LEFT_EIGHTH_BANK_TO_ORTHOGONAL, - TRACK_ELEM_RIGHT_EIGHTH_BANK_TO_ORTHOGONAL, + TRACK_ELEM_RIGHT_EIGHTH_BANK_TO_ORTHOGONAL = 140, TRACK_ELEM_DIAG_FLAT, TRACK_ELEM_DIAG_25_DEG_UP, TRACK_ELEM_DIAG_60_DEG_UP, @@ -451,7 +462,7 @@ enum { TRACK_ELEM_DIAG_25_DEG_UP_TO_FLAT, TRACK_ELEM_DIAG_25_DEG_DOWN, TRACK_ELEM_DIAG_60_DEG_DOWN, - TRACK_ELEM_DIAG_FLAT_TO_25_DEG_DOWN, + TRACK_ELEM_DIAG_FLAT_TO_25_DEG_DOWN = 150, TRACK_ELEM_DIAG_25_DEG_DOWN_TO_60_DEG_DOWN, TRACK_ELEM_DIAG_60_DEG_DOWN_TO_25_DEG_DOWN, TRACK_ELEM_DIAG_25_DEG_DOWN_TO_FLAT, @@ -461,7 +472,7 @@ enum { TRACK_ELEM_DIAG_60_DEG_DOWN_TO_FLAT, TRACK_ELEM_DIAG_FLAT_TO_LEFT_BANK, TRACK_ELEM_DIAG_FLAT_TO_RIGHT_BANK, - TRACK_ELEM_DIAG_LEFT_BANK_TO_FLAT, + TRACK_ELEM_DIAG_LEFT_BANK_TO_FLAT = 160, TRACK_ELEM_DIAG_RIGHT_BANK_TO_FLAT, TRACK_ELEM_DIAG_LEFT_BANK_TO_25_DEG_UP, TRACK_ELEM_DIAG_RIGHT_BANK_TO_25_DEG_UP, @@ -471,7 +482,7 @@ enum { TRACK_ELEM_DIAG_RIGHT_BANK_TO_25_DEG_DOWN, TRACK_ELEM_DIAG_25_DEG_DOWN_TO_LEFT_BANK, TRACK_ELEM_DIAG_25_DEG_DOWN_TO_RIGHT_BANK, - TRACK_ELEM_DIAG_LEFT_BANK, + TRACK_ELEM_DIAG_LEFT_BANK = 170, TRACK_ELEM_DIAG_RIGHT_BANK, TRACK_ELEM_LOG_FLUME_REVERSER, TRACK_ELEM_SPINNING_TUNNEL, @@ -481,13 +492,81 @@ enum { TRACK_ELEM_RIGHT_BARREL_ROLL_DOWN_TO_UP, TRACK_ELEM_LEFT_BANK_TO_LEFT_QUARTER_TURN_3_TILES_25_DEG_UP, TRACK_ELEM_RIGHT_BANK_TO_RIGHT_QUARTER_TURN_3_TILES_25_DEG_UP, - TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES_25_DEG_DOWN_TO_LEFT_BANK, + TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES_25_DEG_DOWN_TO_LEFT_BANK = 180, TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES_25_DEG_DOWN_TO_RIGHT_BANK, TRACK_ELEM_POWERED_LIFT, TRACK_ELEM_LEFT_LARGE_HALF_LOOP_UP, TRACK_ELEM_RIGHT_LARGE_HALF_LOOP_UP, TRACK_ELEM_RIGHT_LARGE_HALF_LOOP_DOWN, - TRACK_ELEM_LEFT_LARGE_HALF_LOOP_DOWN + TRACK_ELEM_LEFT_LARGE_HALF_LOOP_DOWN, + TRACK_ELEM_LEFT_FLYER_TWIST_UP, + TRACK_ELEM_RIGHT_FLYER_TWIST_UP, + TRACK_ELEM_LEFT_FLYER_TWIST_DOWN, + TRACK_ELEM_RIGHT_FLYER_TWIST_DOWN, + TRACK_ELEM_FLYER_HALF_LOOP_UP, + TRACK_ELEM_FLYER_HALF_LOOP_DOWN, + TRACK_ELEM_LEFT_FLYER_CORKSCREW_UP, + TRACK_ELEM_RIGHT_FLYER_CORKSCREW_UP, + TRACK_ELEM_LEFT_FLYER_CORKSCREW_DOWN, + TRACK_ELEM_RIGHT_FLYER_CORKSCREW_DOWN, + TRACK_ELEM_HEARTLINE_TRANSFER_UP, + TRACK_ELEM_HEARTLINE_TRANSFER_DOWN, + TRACK_ELEM_LEFT_HEARTLINE_ROLL, + TRACK_ELEM_RIGHT_HEARTLINE_ROLL, + TRACK_ELEM_MINI_GOLF_HOLE_A, + TRACK_ELEM_MINI_GOLF_HOLE_B, + TRACK_ELEM_MINI_GOLF_HOLE_C, + TRACK_ELEM_MINI_GOLF_HOLE_D, + TRACK_ELEM_MINI_GOLF_HOLE_E, + TRACK_ELEM_MULTIDIM_INVERTED_FLAT_TO_90_DEG_QUARTER_LOOP_DOWN, + TRACK_ELEM_90_DEG_TO_INVERTED_FLAT_QUARTER_LOOP_UP, + TRACK_ELEM_INVERTED_FLAT_TO_90_DEG_QUARTER_LOOP_DOWN, + TRACK_ELEM_LEFT_CURVED_LIFT_HILL, + TRACK_ELEM_RIGHT_CURVED_LIFT_HILL, + TRACK_ELEM_LEFT_REVERSER, + TRACK_ELEM_RIGHT_REVERSER, + TRACK_ELEM_AIR_THRUST_TOP_CAP, + TRACK_ELEM_AIR_THRUST_VERTICAL_DOWN, + TRACK_ELEM_AIR_THRUST_VERTICAL_DOWN_TO_LEVEL, + TRACK_ELEM_BLOCK_BRAKES, + TRACK_ELEM_LEFT_BANKED_QUARTER_TURN_3_TILE_25_DEG_UP, + TRACK_ELEM_RIGHT_BANKED_QUARTER_TURN_3_TILE_25_DEG_UP, + TRACK_ELEM_LEFT_BANKED_QUARTER_TURN_3_TILE_25_DEG_DOWN, + TRACK_ELEM_RIGHT_BANKED_QUARTER_TURN_3_TILE_25_DEG_DOWN, + TRACK_ELEM_LEFT_BANKED_QUARTER_TURN_5_TILE_25_DEG_UP, + TRACK_ELEM_RIGHT_BANKED_QUARTER_TURN_5_TILE_25_DEG_UP, + TRACK_ELEM_LEFT_BANKED_QUARTER_TURN_5_TILE_25_DEG_DOWN, + TRACK_ELEM_RIGHT_BANKED_QUARTER_TURN_5_TILE_25_DEG_DOWN, + TRACK_ELEM_25_DEG_UP_TO_LEFT_BANKED_25_DEG_UP, + TRACK_ELEM_25_DEG_UP_TO_RIGHT_BANKED_25_DEG_UP, + TRACK_ELEM_LEFT_BANKED_25_DEG_UP_TO_25_DEG_UP, + TRACK_ELEM_RIGHT_BANKED_25_DEG_UP_TO_25_DEG_UP, + TRACK_ELEM_25_DEG_DOWN_TO_LEFT_BANKED_25_DEG_DOWN, + TRACK_ELEM_25_DEG_DOWN_TO_RIGHT_BANKED_25_DEG_DOWN, + TRACK_ELEM_LEFT_BANKED_25_DEG_DOWN_TO_25_DEG_DOWN, + TRACK_ELEM_RIGHT_BANKED_25_DEG_DOWN_TO_25_DEG_DOWN, + TRACK_ELEM_LEFT_BANKED_FLAT_TO_LEFT_BANKED_25_DEG_UP, + TRACK_ELEM_RIGHT_BANKED_FLAT_TO_RIGHT_BANKED_25_DEG_UP, + TRACK_ELEM_LEFT_BANKED_25_DEG_UP_TO_LEFT_BANKED_FLAT, + TRACK_ELEM_RIGHT_BANKED_25_DEG_UP_TO_RIGHT_BANKED_FLAT, + TRACK_ELEM_LEFT_BANKED_FLAT_TO_LEFT_BANKED_25_DEG_DOWN, + TRACK_ELEM_RIGHT_BANKED_FLAT_TO_RIGHT_BANKED_25_DEG_DOWN, + TRACK_ELEM_LEFT_BANKED_25_DEG_DOWN_TO_LEFT_BANKED_FLAT, + TRACK_ELEM_RIGHT_BANKED_25_DEG_DOWN_TO_RIGHT_BANKED_FLAT, + TRACK_ELEM_FLAT_TO_LEFT_BANKED_25_DEG_UP, + TRACK_ELEM_FLAT_TO_RIGHT_BANKED_25_DEG_UP, + TRACK_ELEM_LEFT_BANKED_25_DEG_UP_TO_FLAT, + TRACK_ELEM_RIGHT_BANKED_25_DEG_UP_TO_FLAT, + TRACK_ELEM_FLAT_TO_LEFT_BANKED_25_DEG_DOWN, + TRACK_ELEM_FLAT_TO_RIGHT_BANKED_25_DEG_DOWN, + TRACK_ELEM_LEFT_BANKED_25_DEG_DOWN_TO_FLAT, + TRACK_ELEM_RIGHT_BANKED_25_DEG_DOWN_TO_FLAT, + TRACK_ELEM_LEFT_QUARTER_TURN_1_TILE_90_DEG_UP, + TRACK_ELEM_RIGHT_QUARTER_TURN_1_TILE_90_DEG_UP, + TRACK_ELEM_LEFT_QUARTER_TURN_1_TILE_90_DEG_DOWN, + TRACK_ELEM_RIGHT_QUARTER_TURN_1_TILE_90_DEG_DOWN, + TRACK_ELEM_MULTIDIM_90_DEG_UP_TO_INVERTED_FLAT_QUARTER_LOOP, + TRACK_ELEM_MULTIDIM_FLAT_TO_90_DEG_DOWN_QUARTER_LOOP, }; enum { @@ -547,9 +626,17 @@ void track_get_back(rct_xy_element *input, rct_xy_element *output); void track_get_front(rct_xy_element *input, rct_xy_element *output); bool track_element_is_lift_hill(rct_map_element *trackElement); +bool track_element_is_block_start(rct_map_element *trackElement); bool track_element_is_cable_lift(rct_map_element *trackElement); void track_element_set_cable_lift(rct_map_element *trackElement); void track_element_clear_cable_lift(rct_map_element *trackElement); +int track_get_actual_bank(rct_map_element *mapElement, int bank); +int track_get_actual_bank_2(int rideType, int trackColour, int bank); +int track_get_actual_bank_3(rct_vehicle *vehicle, rct_map_element *mapElement); + +bool track_element_is_station(rct_map_element *trackElement); +bool track_element_is_covered(int trackElementType); + #endif diff --git a/src/ride/track_paint.c b/src/ride/track_paint.c index 649bf1c41d89..ac548815baed 100644 --- a/src/ride/track_paint.c +++ b/src/ride/track_paint.c @@ -82,7 +82,7 @@ static rct_xy16 loc_7667AC[] = { /** * - * rct2: 0x0142811C + * rct2: 0x0142811C * Can be calculated as Rounddown(34*sin(x)+0.5) * where x is in 7.5 deg segments. */ @@ -178,9 +178,9 @@ static void top_spin_paint_vehicle(sint8 al, sint8 cl, uint8 rideIndex, uint8 di uint32 seatImageId; - if (vehicle != NULL && vehicle->var_B5 >= 64) { + if (vehicle != NULL && vehicle->restraints_position >= 64) { // Open Restraints - image_id = (vehicle->var_B5 - 64) >> 6; + image_id = (vehicle->restraints_position - 64) >> 6; image_id += direction * 3; image_id += rideEntry->vehicles[0].base_image_id; image_id += 64; @@ -1028,7 +1028,7 @@ static void shop_paint_setup(uint8 rideIndex, uint8 trackSequence, uint8 directi RCT2_GLOBAL(0x009DEA52, uint16) = 2; RCT2_GLOBAL(0x009DEA54, uint16) = 2; RCT2_GLOBAL(0x009DEA56, sint16) = height16; - sub_98199C(0, 45, imageId, 0, height, 28, 28, rotation); + sub_98199C(0, 45, imageId, 0, height, 28, 28, rotation); } else { RCT2_GLOBAL(0x009DEA52, uint16) = 2; RCT2_GLOBAL(0x009DEA54, uint16) = 2; @@ -1099,7 +1099,7 @@ static void facility_paint_setup(uint8 rideIndex, uint8 trackSequence, uint8 dir RCT2_GLOBAL(0x009DEA52, uint16) = direction == 3 ? 28 : 2; RCT2_GLOBAL(0x009DEA54, uint16) = direction == 0 ? 28 : 2; RCT2_GLOBAL(0x009DEA56, sint16) = height16; - sub_98199C(0, 29, imageId, 0, height, lengthY, lengthX, rotation); + sub_98199C(0, 29, imageId, 0, height, lengthY, lengthX, rotation); } else { // Door image or base RCT2_GLOBAL(0x009DEA52, uint16) = direction == 3 ? 28 : 2; diff --git a/src/ride/vehicle.c b/src/ride/vehicle.c index 4f66083e0b1b..afe238067cc3 100644 --- a/src/ride/vehicle.c +++ b/src/ride/vehicle.c @@ -19,24 +19,153 @@ *****************************************************************************/ #include "../addresses.h" +#include "../game.h" #include "../audio/audio.h" #include "../audio/mixer.h" #include "../config.h" #include "../hook.h" #include "../interface/viewport.h" #include "../openrct2.h" +#include "../scenario.h" +#include "../world/map_animation.h" #include "../world/sprite.h" +#include "../world/scenery.h" +#include "cable_lift.h" +#include "track.h" #include "ride.h" #include "ride_data.h" #include "track.h" +#include "track_data.h" #include "vehicle.h" static void vehicle_update(rct_vehicle *vehicle); +static void vehicle_update_showing_film(rct_vehicle *vehicle); +static void vehicle_update_doing_circus_show(rct_vehicle *vehicle); +static void vehicle_update_moving_to_end_of_station(rct_vehicle *vehicle); +static void vehicle_update_waiting_for_passengers(rct_vehicle* vehicle); +static void vehicle_update_waiting_to_depart(rct_vehicle* vehicle); +static void vehicle_update_departing(rct_vehicle* vehicle); +static void vehicle_finish_departing(rct_vehicle* vehicle); +static void vehicle_update_travelling(rct_vehicle* vehicle); +static void vehicle_update_ferris_wheel_rotating(rct_vehicle* vehicle); +static void vehicle_update_rotating(rct_vehicle* vehicle); +static void vehicle_update_space_rings_operating(rct_vehicle* vehicle); +static void vehicle_update_haunted_house_operating(rct_vehicle* vehicle); +static void vehicle_update_crooked_house_operating(rct_vehicle* vehicle); +static void vehicle_update_bumpcar_mode(rct_vehicle* vehicle); +static void vehicle_update_swinging(rct_vehicle* vehicle); +static void vehicle_update_simulator_operating(rct_vehicle* vehicle); +static void vehicle_update_top_spin_operating(rct_vehicle* vehicle); +static void vehicle_update_crash(rct_vehicle *vehicle); +static void vehicle_update_travelling_boat(rct_vehicle* vehicle); +static void vehicle_update_motion_boat_hire(rct_vehicle *vehicle); +static void sub_6DA280(rct_vehicle *vehicle); +static bool vehicle_is_boat_on_water(rct_vehicle *vehicle, int x, int y); +static void vehicle_update_arriving(rct_vehicle* vehicle); +static void vehicle_update_unloading_passengers(rct_vehicle* vehicle); +static void vehicle_update_waiting_for_cable_lift(rct_vehicle *vehicle); +static void vehicle_update_travelling_cable_lift(rct_vehicle* vehicle); +static void vehicle_update_crash_setup(rct_vehicle* vehicle); +static void vehicle_update_collision_setup(rct_vehicle* vehicle); +static int vehicle_update_motion_bumper_car(rct_vehicle* vehicle); +static bool vehicle_update_bumper_car_collision(rct_vehicle *vehicle, sint16 x, sint16 y, uint16 *spriteId); +static void sub_6D63D4(rct_vehicle *vehicle); +static bool vehicle_update_motion_collision_detection(rct_vehicle *vehicle, sint16 x, sint16 y, sint16 z, uint16 *otherVehicleIndex); +static void vehicle_update_sound(rct_vehicle *vehicle); +static int vehicle_update_scream_sound(rct_vehicle *vehicle); + +static void vehicle_kill_all_passengers(rct_vehicle* vehicle); +static bool vehicle_can_depart_synchronised(rct_vehicle *vehicle); + +#define NO_SCREAM 254 + +rct_xyz16 *unk_F64E20 = (rct_xyz16*)0x00F64E20; + +const uint8 byte_9A3A14[] = { SOUND_SCREAM_8, SOUND_SCREAM_1 }; +const uint8 byte_9A3A16[] = { SOUND_SCREAM_1, SOUND_SCREAM_6 }; +const uint8 byte_9A3A18[] = { + SOUND_SCREAM_3, SOUND_SCREAM_1, SOUND_SCREAM_5, SOUND_SCREAM_6, + SOUND_SCREAM_7, SOUND_SCREAM_2, SOUND_SCREAM_4 +}; + +const rct_vehicle_info *vehicle_get_move_info(int cd, int typeAndDirection, int offset) +{ + const rct_vehicle_info **infoListList = RCT2_ADDRESS(0x008B8F30, const rct_vehicle_info**)[cd]; + const rct_vehicle_info *infoList = infoListList[typeAndDirection]; + return &infoList[offset]; +} + +const uint8 DoorOpenSoundIds[] = { + SOUND_DOOR_OPEN, + SOUND_62 +}; + +const uint8 DoorCloseSoundIds[] = { + SOUND_DOOR_CLOSE, + SOUND_62 +}; + +static const struct { sint8 x, y, z; } SteamParticleOffsets[] = { + { -11, 0, 22 }, + { -10, 4, 22 }, + { -8, 8, 22 }, + { -4, 10, 22 }, + { 0, 11, 22 }, + { 4, 10, 22 }, + { 8, 8, 22 }, + { 10, 4, 22 }, + { 11, 0, 22 }, + { 10, -4, 22 }, + { 8, -8, 22 }, + { 4, -10, 22 }, + { 0, -11, 22 }, + { -4, -10, 22 }, + { -8, -8, 22 }, + { -10, -4, 22 }, + { -9, 0, 27 }, + { -8, 4, 27 }, + { -6, 6, 27 }, + { -4, 8, 27 }, + { 0, 9, 27 }, + { 4, 8, 27 }, + { 6, 6, 27 }, + { 8, 4, 27 }, + { 9, 0, 27 }, + { 8, -4, 27 }, + { 6, -6, 27 }, + { 4, -8, 27 }, + { 0, -9, 27 }, + { -4, -8, 27 }, + { -6, -6, 27 }, + { -8, -4, 27 }, + { -13, 0, 18 }, + { -12, 4, 17 }, + { -9, 9, 17 }, + { -4, 8, 17 }, + { 0, 13, 18 }, + { 4, 8, 17 }, + { 6, 6, 17 }, + { 8, 4, 17 }, + { 13, 0, 18 }, + { 8, -4, 17 }, + { 6, -6, 17 }, + { 4, -8, 17 }, + { 0, -13, 18 }, + { -4, -8, 17 }, + { -6, -6, 17 }, + { -8, -4, 17 } +}; + +void vehicle_invalidate(rct_vehicle *vehicle) +{ + invalidate_sprite_2((rct_sprite*)vehicle); +} + /** -* -* rct2: 0x006BB9FF -*/ + * + * rct2: 0x006BB9FF + */ void vehicle_update_sound_params(rct_vehicle* vehicle) { if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR) && (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TRACK_DESIGNER) || RCT2_GLOBAL(0x0141F570, uint8) == 6)) { @@ -131,9 +260,9 @@ void vehicle_update_sound_params(rct_vehicle* vehicle) } /** -* -* rct2: 0x006BC2F3 -*/ + * + * rct2: 0x006BC2F3 + */ int sub_6BC2F3(rct_vehicle* vehicle) { int result = 0; @@ -159,9 +288,9 @@ int sub_6BC2F3(rct_vehicle* vehicle) } /** -* -* rct2: 0x006BBC6B -*/ + * + * rct2: 0x006BBC6B + */ void vehicle_sounds_update() { if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_SOUND_DEVICE, uint32) != -1 && !gGameSoundsOff && gConfigSound.sound && !gOpenRCT2Headless) { @@ -451,188 +580,7731 @@ void vehicle_update_all() /** * - * rct2: 0x006D77F2 + * rct2: 0x006D6956 + * @returns 0 when all closed */ -static void vehicle_update(rct_vehicle *vehicle) -{ - RCT2_CALLPROC_X(0x006D77F2, 0, 0, 0, 0, (int)vehicle, 0, 0); +static int vehicle_close_restraints(rct_vehicle* vehicle){ + rct_ride* ride = GET_RIDE(vehicle->ride); + int ebp = 0; + uint16 vehicle_id = vehicle->sprite_index; + + do { + vehicle = GET_VEHICLE(vehicle_id); + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_BROKEN_CAR && + vehicle->restraints_position != 0 && + ( + ride->breakdown_reason_pending == BREAKDOWN_RESTRAINTS_STUCK_OPEN || + ride->breakdown_reason_pending == BREAKDOWN_DOORS_STUCK_OPEN) + ){ + + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)){ + + ride->lifecycle_flags |= RIDE_LIFECYCLE_BROKEN_DOWN; + + ride_breakdown_add_news_item(vehicle->ride); + + ride->window_invalidate_flags |= + RIDE_INVALIDATE_RIDE_MAIN | + RIDE_INVALIDATE_RIDE_LIST | + RIDE_INVALIDATE_RIDE_MAINTENANCE; + + ride->mechanic_status = RIDE_MECHANIC_STATUS_CALLING; + + rct_vehicle* broken_vehicle = GET_VEHICLE(ride->vehicles[ride->broken_vehicle]); + ride->inspection_station = broken_vehicle->current_station; + + ride->breakdown_reason = ride->breakdown_reason_pending; + } + } + else{ + if (vehicle->restraints_position - 20 < 0){ + vehicle->restraints_position = 0; + continue; + } + vehicle->restraints_position -= 20; + } + vehicle_invalidate(vehicle); + ebp++; + } while ((vehicle_id = vehicle->next_vehicle_on_train) != 0xFFFF); + + return ebp; } /** * - * rct2: 0x006D73D0 - * ax: verticalG - * dx: lateralG - * esi: vehicle + * rct2: 0x006D6A2C + * @returns 0 when all open */ -void vehicle_get_g_forces(rct_vehicle *vehicle, int *verticalG, int *lateralG) -{ - int eax, ebx, ecx, edx, esi, edi, ebp; +static int vehicle_open_restraints(rct_vehicle* vehicle){ + int ebp = 0; + uint16 vehicle_id = vehicle->sprite_index; - esi = (int)vehicle; - RCT2_CALLFUNC_X(0x006D73D0, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + do { + vehicle = GET_VEHICLE(vehicle_id); + + vehicle->swinging_car_var_0 = 0; + vehicle->var_4E = 0; + vehicle->var_4A = 0; + + rct_ride* ride = GET_RIDE(vehicle->ride); + rct_ride_type* rideEntry = GET_RIDE_ENTRY(vehicle->ride_subtype); + rct_ride_type_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type]; + + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_SPINNING) { + if (abs(vehicle->var_B6) <= 700 && + !(vehicle->var_BA & 0x30) && + ( + !(vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_14) || + !(vehicle->var_BA & 0xF8)) + ){ + vehicle->var_B6 = 0; + } + else { + ebp++; + + if (abs(vehicle->var_B6) < 600) { + vehicle->var_B6 = 600; + } + sint16 value = vehicle->var_B6 / 256; + vehicle->var_BA += value; + vehicle->var_B6 -= value; + + vehicle_invalidate(vehicle); + continue; + } + } + if (vehicleEntry->var_11 == 6 && + vehicle->var_C5 != 0){ + + if (vehicle->var_C8 + 0x3333 < 0xFFFF){ + vehicle->var_C8 = vehicle->var_C8 + 0x3333 - 0xFFFF; + vehicle->var_C5++; + vehicle->var_C5 &= 7; + vehicle_invalidate(vehicle); + } + else{ + vehicle->var_C8 += 0x3333; + } + ebp++; + continue; + } + + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_BROKEN_CAR && + vehicle->restraints_position != 0xFF && + ( + ride->breakdown_reason_pending == BREAKDOWN_RESTRAINTS_STUCK_CLOSED || + ride->breakdown_reason_pending == BREAKDOWN_DOORS_STUCK_CLOSED) + ){ + + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)){ + + ride->lifecycle_flags |= RIDE_LIFECYCLE_BROKEN_DOWN; - if (verticalG != NULL) *verticalG = (sint16)(eax & 0xFFFF); - if (lateralG != NULL) *lateralG = (sint16)(edx & 0xFFFF); + ride_breakdown_add_news_item(vehicle->ride); + + ride->window_invalidate_flags |= + RIDE_INVALIDATE_RIDE_MAIN | + RIDE_INVALIDATE_RIDE_LIST | + RIDE_INVALIDATE_RIDE_MAINTENANCE; + + ride->mechanic_status = RIDE_MECHANIC_STATUS_CALLING; + + rct_vehicle* broken_vehicle = GET_VEHICLE(ride->vehicles[ride->broken_vehicle]); + ride->inspection_station = broken_vehicle->current_station; + + ride->breakdown_reason = ride->breakdown_reason_pending; + } + } + else{ + if (vehicle->restraints_position + 20 > 0xFF){ + vehicle->restraints_position = 255; + continue; + } + vehicle->restraints_position += 20; + } + vehicle_invalidate(vehicle); + ebp++; + } while ((vehicle_id = vehicle->next_vehicle_on_train) != 0xFFFF); + + return ebp; } -void vehicle_set_map_toolbar(rct_vehicle *vehicle) +/** + * + * rct2: 0x006D6D1F + */ +static void vehicle_update_measurements(rct_vehicle *vehicle) { rct_ride *ride; - int vehicleIndex; ride = GET_RIDE(vehicle->ride); - while (vehicle->is_child) { - vehicle = GET_VEHICLE(vehicle->prev_vehicle_on_ride); + if (vehicle->status == VEHICLE_STATUS_TRAVELLING_BOAT){ + ride->lifecycle_flags |= RIDE_LIFECYCLE_TESTED; + ride->lifecycle_flags |= RIDE_LIFECYCLE_NO_RAW_STATS; + ride->lifecycle_flags &= ~RIDE_LIFECYCLE_TEST_IN_PROGRESS; + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_TESTING; + window_invalidate_by_number(WC_RIDE, vehicle->ride); + return; } - for (vehicleIndex = 0; vehicleIndex < 32; vehicleIndex++) - if (ride->vehicles[vehicleIndex] == vehicle->sprite_index) - break; + uint8 entrance = ride->var_1F6; + if (ride->entrances[entrance] != 0xFFFF){ + uint8 test_segment = ride->current_test_segment; - RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 0, uint16) = 2215; - RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 2, uint16) = 1165; - RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 4, uint16) = ride->name; - RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 6, uint32) = ride->name_arguments; - RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 10, uint16) = RideNameConvention[ride->type].vehicle_name + 2; - RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 12, uint16) = vehicleIndex + 1; + ride->var_0E1++; + if (ride->var_0E1 >= 32)ride->var_0E1 = 0; - int arg0, arg1; - ride_get_status(vehicle->ride, &arg0, &arg1); - RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 14, uint16) = (uint16)arg0; - RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 16, uint32) = (uint16)arg1; -} + sint32 velocity = abs(vehicle->velocity); + if (velocity > ride->max_speed){ + ride->max_speed = velocity; + } -rct_vehicle *vehicle_get_head(rct_vehicle *vehicle) -{ - rct_vehicle *prevVehicle; + if (ride->var_0E1 == 0 && velocity > 0x8000){ + ride->average_speed += velocity; + ride->time[test_segment]++; + } - for (;;) { - prevVehicle = GET_VEHICLE(vehicle->prev_vehicle_on_ride); - if (prevVehicle->next_vehicle_on_train == SPRITE_INDEX_NULL) + sint32 distance = abs(((vehicle->velocity + vehicle->acceleration) >> 10) * 42); + if (vehicle->var_CE == 0){ + ride->length[test_segment] += distance; + } + + if (RCT2_ADDRESS(RCT2_ADDRESS_RIDE_FLAGS, uint32)[ride->type * 2] & RIDE_TYPE_FLAG_HAS_G_FORCES){ + int vertical_g, lateral_g; + vehicle_get_g_forces(vehicle, &vertical_g, &lateral_g); + + vertical_g += ride->previous_vertical_g; + lateral_g += ride->previous_lateral_g; + vertical_g >>= 1; + lateral_g >>= 1; + + ride->previous_vertical_g = vertical_g; + ride->previous_lateral_g = lateral_g; + + if (vertical_g <= 0){ + ride->total_air_time++; + } + + if (vertical_g > ride->max_positive_vertical_g) + ride->max_positive_vertical_g = vertical_g; + + if (vertical_g < ride->max_negative_vertical_g) + ride->max_negative_vertical_g = vertical_g; + + lateral_g = abs(lateral_g); + + if (lateral_g > ride->max_lateral_g) + ride->max_lateral_g = lateral_g; + } + } + + uint16 map_location = (vehicle->track_x / 32) | ((vehicle->track_y / 32) << 8); + if (vehicle->track_z / 8 != ride->var_11F || map_location != ride->var_10C){ + ride->var_11F = vehicle->track_z / 8; + ride->var_10C = map_location; + + if (ride->entrances[ride->var_1F6] == 0xFFFF) + return; + + uint16 track_elem_type = vehicle->track_type / 4; + if (track_elem_type == TRACK_ELEM_POWERED_LIFT || (vehicle->update_flags & VEHICLE_UPDATE_FLAG_0)){ + if (!(ride->testing_flags & RIDE_TESTING_POWERED_LIFT)){ + ride->testing_flags |= RIDE_TESTING_POWERED_LIFT; + if (ride->drops + 64 < 0xFF){ + ride->drops += 64; + } + } + } + else{ + ride->testing_flags &= ~RIDE_TESTING_POWERED_LIFT; + } + + if (ride->type == RIDE_TYPE_WATER_COASTER){ + if (track_elem_type >= TRACK_ELEM_FLAT_COVERED && track_elem_type <= TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES_COVERED){ + ride->special_track_elements |= RIDE_ELEMENT_TUNNEL_SPLASH_OR_RAPIDS; + } + } + + switch (track_elem_type) + { + case TRACK_ELEM_RAPIDS: + case TRACK_ELEM_SPINNING_TUNNEL: + ride->special_track_elements |= RIDE_ELEMENT_TUNNEL_SPLASH_OR_RAPIDS; + break; + case TRACK_ELEM_WATERFALL: + case TRACK_ELEM_LOG_FLUME_REVERSER: + ride->special_track_elements |= RIDE_ELEMENT_REVERSER_OR_WATERFALL; + break; + case TRACK_ELEM_WHIRLPOOL: + ride->special_track_elements |= RIDE_ELEMENT_WHIRLPOOL; break; + case TRACK_ELEM_WATER_SPLASH: + if (vehicle->velocity >= 0xB0000){ + ride->special_track_elements |= RIDE_ELEMENT_TUNNEL_SPLASH_OR_RAPIDS; + } + } + + // ax + uint16 track_flags = RCT2_ADDRESS(0x0099423C, uint16)[track_elem_type]; + + uint32 testing_flags = ride->testing_flags; + if (testing_flags & RIDE_TESTING_TURN_LEFT && + track_flags & TRACK_ELEM_FLAG_TURN_LEFT){ + // 0x800 as this is masked to CURRENT_TURN_COUNT_MASK + ride->turn_count_default += 0x800; + } + else if (testing_flags & RIDE_TESTING_TURN_RIGHT && + track_flags & TRACK_ELEM_FLAG_TURN_RIGHT){ + // 0x800 as this is masked to CURRENT_TURN_COUNT_MASK + ride->turn_count_default += 0x800; + } + else if (testing_flags & RIDE_TESTING_TURN_RIGHT || + testing_flags & RIDE_TESTING_TURN_LEFT){ + + ride->testing_flags &= ~( + RIDE_TESTING_TURN_LEFT | + RIDE_TESTING_TURN_RIGHT | + RIDE_TESTING_TURN_BANKED | + RIDE_TESTING_TURN_SLOPED); + + uint8 turn_type = 1; + if (!(testing_flags & RIDE_TESTING_TURN_BANKED)){ + turn_type = 2; + if (!(testing_flags & RIDE_TESTING_TURN_SLOPED)){ + turn_type = 0; + } + } + switch (ride->turn_count_default >> 11){ + case 0: + increment_turn_count_1_element(ride, turn_type); + break; + case 1: + increment_turn_count_2_elements(ride, turn_type); + break; + case 2: + increment_turn_count_3_elements(ride, turn_type); + break; + default: + increment_turn_count_4_plus_elements(ride, turn_type); + break; + } + } + else { + if (track_flags & TRACK_ELEM_FLAG_TURN_LEFT){ + ride->testing_flags |= RIDE_TESTING_TURN_LEFT; + ride->turn_count_default &= ~CURRENT_TURN_COUNT_MASK; + + if (track_flags & TRACK_ELEM_FLAG_TURN_BANKED){ + ride->testing_flags |= RIDE_TESTING_TURN_BANKED; + } + if (track_flags & TRACK_ELEM_FLAG_TURN_SLOPED){ + ride->testing_flags |= RIDE_TESTING_TURN_SLOPED; + } + } + + if (track_flags & TRACK_ELEM_FLAG_TURN_RIGHT){ + ride->testing_flags |= RIDE_TESTING_TURN_RIGHT; + ride->turn_count_default &= ~CURRENT_TURN_COUNT_MASK; + + if (track_flags & TRACK_ELEM_FLAG_TURN_BANKED){ + ride->testing_flags |= RIDE_TESTING_TURN_BANKED; + } + if (track_flags & TRACK_ELEM_FLAG_TURN_SLOPED){ + ride->testing_flags |= RIDE_TESTING_TURN_SLOPED; + } + } + } + + if (testing_flags & RIDE_TESTING_DROP_DOWN){ + if (vehicle->velocity < 0 || !(track_flags & TRACK_ELEM_FLAG_DOWN)){ + ride->testing_flags &= ~RIDE_TESTING_DROP_DOWN; + + sint16 z = vehicle->z / 8 - ride->start_drop_height; + if (z < 0){ + z = abs(z); + if (z > ride->highest_drop_height){ + ride->highest_drop_height = (uint8)z; + } + } + } + } + else if (track_flags & TRACK_ELEM_FLAG_DOWN && vehicle->velocity >= 0){ + ride->testing_flags &= ~RIDE_TESTING_DROP_UP; + ride->testing_flags |= RIDE_TESTING_DROP_DOWN; + + uint8 drops = ride->drops & 0x3F; + if (drops != 0x3F) + drops++; + ride->drops &= ~0x3F; + ride->drops |= drops; + + ride->start_drop_height = vehicle->z / 8; + testing_flags &= ~RIDE_TESTING_DROP_UP; + } + + if (testing_flags & RIDE_TESTING_DROP_UP){ + if (vehicle->velocity > 0 || !(track_flags & TRACK_ELEM_FLAG_UP)){ + ride->testing_flags &= ~RIDE_TESTING_DROP_UP; + + sint16 z = vehicle->z / 8 - ride->start_drop_height; + if (z < 0){ + z = abs(z); + if (z > ride->highest_drop_height){ + ride->highest_drop_height = (uint8)z; + } + } + } + } + else if (track_flags & TRACK_ELEM_FLAG_UP && vehicle->velocity <= 0){ + ride->testing_flags &= ~RIDE_TESTING_DROP_DOWN; + ride->testing_flags |= RIDE_TESTING_DROP_UP; + + uint8 drops = ride->drops & 0x3F; + if (drops != 0x3F) + drops++; + ride->drops &= ~0x3F; + ride->drops |= drops; + + ride->start_drop_height = vehicle->z / 8; + } + + if (track_flags & TRACK_ELEM_FLAG_INVERSION){ + uint8 inversions = ride->inversions & 0x1F; + if (inversions != 0x1F) + inversions++; + + ride->inversions &= ~0x1F; + ride->inversions |= inversions; + } + + if (track_flags & TRACK_ELEM_FLAG_HELIX){ + uint8 helixes = ride_get_helix_sections(ride); + if (helixes != 0x1F) + helixes++; + + ride->special_track_elements &= ~0x1F; + ride->special_track_elements |= helixes; + } - vehicle = prevVehicle; } - return vehicle; -} + if (ride->entrances[ride->var_1F6] == 0xFFFF) + return; -rct_vehicle *vehicle_get_tail(rct_vehicle *vehicle) -{ - uint16 spriteIndex; + sint16 x, y; + x = vehicle->x; + y = vehicle->y; - while ((spriteIndex = vehicle->next_vehicle_on_train) != SPRITE_INDEX_NULL) { - vehicle = GET_VEHICLE(spriteIndex); + if (x == SPRITE_LOCATION_NULL){ + ride->testing_flags &= ~RIDE_TESTING_SHELTERED; + return; } - return vehicle; -} -int vehicle_is_used_in_pairs(rct_vehicle *vehicle) -{ - return vehicle->num_seats & VEHICLE_SEAT_PAIR_FLAG; -} + rct_map_element* map_element = map_get_surface_element_at(x / 32, y / 32); + if (map_element->base_height * 8 <= vehicle->z){ -/** - * - * rct2: 0x006DEF56 - */ -void sub_6DEF56(rct_vehicle *cableLift) -{ - RCT2_CALLPROC_X(0x006DEF56, 0, 0, 0, 0, (int)cableLift, 0, 0); + bool cover_found = false; + do{ + if (map_element_get_type(map_element) == MAP_ELEMENT_TYPE_SCENERY_MULTIPLE) { + cover_found = true; + break; + } + + if (map_element_get_type(map_element) == MAP_ELEMENT_TYPE_PATH) { + cover_found = true; + break; + } + + if (map_element_get_type(map_element) != MAP_ELEMENT_TYPE_SCENERY) + continue; + + rct_scenery_entry* scenery = g_smallSceneryEntries[map_element->properties.scenery.type]; + if (scenery->small_scenery.flags & SMALL_SCENERY_FLAG_FULL_TILE) { + cover_found = true; + break; + } + } while (!map_element_is_last_for_tile(map_element++)); + + if (cover_found == false) { + ride->testing_flags &= ~RIDE_TESTING_SHELTERED; + return; + } + } + + if (!(ride->testing_flags & RIDE_TESTING_SHELTERED)){ + ride->testing_flags |= RIDE_TESTING_SHELTERED; + + uint8 num_sheltered_sections = ride->num_sheltered_sections & 0x1F; + if (num_sheltered_sections != 0x1F) + num_sheltered_sections++; + ride->num_sheltered_sections &= ~0x1F; + ride->num_sheltered_sections |= num_sheltered_sections; + + if (vehicle->var_1F != 0){ + ride->num_sheltered_sections |= (1 << 5); + } + + if (vehicle->var_20 != 0){ + ride->num_sheltered_sections |= (1 << 6); + } + } + + sint32 distance = ((vehicle->velocity + vehicle->acceleration) >> 10) * 42; + if (distance < 0)return; + + ride->sheltered_length += distance; } -rct_vehicle *cable_lift_segment_create(int rideIndex, int x, int y, int z, int direction, uint16 var_44, uint32 var_24, bool head) +static uint16 sub_6D7AC0(int currentSoundId, int currentVolume, int targetSoundId, int targetVolume) { - rct_ride *ride = GET_RIDE(rideIndex); - rct_vehicle *current = &(create_sprite(1)->vehicle); - current->sprite_identifier = SPRITE_IDENTIFIER_VEHICLE; - current->ride = rideIndex; - current->ride_subtype = 0xFF; - if (head) { - move_sprite_to_list((rct_sprite*)current, SPRITE_LINKEDLIST_OFFSET_VEHICLE); - ride->cable_lift = current->sprite_index; - } - current->is_child = head ? 0 : 1; - current->var_44 = var_44; - current->var_24 = var_24; - current->sprite_width = 10; - current->sprite_height_negative = 10; - current->sprite_height_positive = 10; - current->friction = 100; - current->num_seats = 0; - current->speed = 20; - current->acceleration = 80; - current->velocity = 0; - current->var_2C = 0; - current->var_4A = 0; - current->var_4C = 0; - current->var_4E = 0; - current->var_B5 = 0; - current->var_BA = 0; - current->var_B6 = 0; - current->var_B8 = 0; - current->sound1_id = 0xFF; - current->sound2_id = 0xFF; - current->var_C4 = 0; - current->var_C5 = 0; - current->var_C8 = 0; - current->scream_sound_id = 0xFF; - current->var_1F = 0; - current->var_20 = 0; - for (int j = 0; j < 32; j++) { - current->peep[j] = SPRITE_INDEX_NULL; - } - current->var_CD = 0; - current->sprite_direction = direction << 3; - current->track_x = x; - current->track_y = y; - - z = z * 8; - current->track_z = z; - z += RCT2_GLOBAL(0x0097D21A + (ride->type * 8), uint8); - - sprite_move(16, 16, z, (rct_sprite*)current); - current->track_type = (TRACK_ELEM_CABLE_LIFT_HILL << 2) | (current->sprite_direction >> 3); - current->var_34 = 164; - current->update_flags = VEHICLE_UPDATE_FLAG_1; - current->status = VEHICLE_STATUS_MOVING_TO_END_OF_STATION; - current->var_51 = 0; - current->num_peeps = 0; - current->next_free_seat = 0; - return current; + if (currentSoundId != 255) { + if (currentSoundId == targetSoundId) { + currentVolume = min(currentVolume + 15, targetVolume); + return (currentVolume << 8) | currentSoundId; + } else { + currentVolume -= 9; + if (currentVolume >= 80) + return (currentVolume << 8) | currentSoundId; + } + } + + // Begin sound at quarter volume + currentSoundId = targetSoundId; + currentVolume = targetVolume == 255 ? 255 : targetVolume / 4; + + return (currentVolume << 8) | currentSoundId; } /** * - * rct2: 0x006DD365 + * rct2: 0x006D77F2 */ -bool sub_6DD365(rct_vehicle *vehicle) +static void vehicle_update(rct_vehicle *vehicle) { - registers regs; - regs.esi = (int)vehicle; + rct_ride *ride; + rct_ride_type *rideEntry; + + // The cable lift uses the ride type of NULL + if (vehicle->ride_subtype == RIDE_TYPE_NULL) { + cable_lift_update(vehicle); + return; + } + + rideEntry = GET_RIDE_ENTRY(vehicle->ride_subtype); + + rct_ride_type_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type]; + + ride = GET_RIDE(vehicle->ride); + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_TESTING) + vehicle_update_measurements(vehicle); + + RCT2_GLOBAL(0x00F64E34, uint8) = 255; + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN)) { + RCT2_GLOBAL(0x00F64E34, uint8) = ride->breakdown_reason_pending; + if ((vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_3) && ride->breakdown_reason_pending == BREAKDOWN_SAFETY_CUT_OUT) { + if (!(vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_13) || + ( + vehicle->var_1F == 2 && + vehicle->velocity <= 0x20000 + ) + ) { + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_7; + } + } + } - return RCT2_CALLFUNC_Y(0x006DD365, ®s) & 0x100; + switch (vehicle->status) { + case VEHICLE_STATUS_MOVING_TO_END_OF_STATION: + vehicle_update_moving_to_end_of_station(vehicle); + break; + case VEHICLE_STATUS_WAITING_FOR_PASSENGERS: + vehicle_update_waiting_for_passengers(vehicle); + break; + case VEHICLE_STATUS_WAITING_TO_DEPART: + vehicle_update_waiting_to_depart(vehicle); + break; + case VEHICLE_STATUS_CRASHING: + case VEHICLE_STATUS_CRASHED: + vehicle_update_crash(vehicle); + break; + case VEHICLE_STATUS_TRAVELLING_BUMPER_CARS: + vehicle_update_bumpcar_mode(vehicle); + break; + case VEHICLE_STATUS_SWINGING: + vehicle_update_swinging(vehicle); + break; + case VEHICLE_STATUS_SIMULATOR_OPERATING: + vehicle_update_simulator_operating(vehicle); + break; + case VEHICLE_STATUS_TOP_SPIN_OPERATING: + vehicle_update_top_spin_operating(vehicle); + break; + case VEHICLE_STATUS_FERRIS_WHEEL_ROTATING: + vehicle_update_ferris_wheel_rotating(vehicle); + break; + case VEHICLE_STATUS_SPACE_RINGS_OPERATING: + vehicle_update_space_rings_operating(vehicle); + break; + case VEHICLE_STATUS_HAUNTED_HOUSE_OPERATING: + vehicle_update_haunted_house_operating(vehicle); + break; + case VEHICLE_STATUS_CROOKED_HOUSE_OPERATING: + vehicle_update_crooked_house_operating(vehicle); + break; + case VEHICLE_STATUS_ROTATING: + vehicle_update_rotating(vehicle); + break; + case VEHICLE_STATUS_DEPARTING: + vehicle_update_departing(vehicle); + break; + case VEHICLE_STATUS_TRAVELLING: + vehicle_update_travelling(vehicle); + break; + case VEHICLE_STATUS_TRAVELLING_CABLE_LIFT: + vehicle_update_travelling_cable_lift(vehicle); + break; + case VEHICLE_STATUS_TRAVELLING_BOAT: + vehicle_update_travelling_boat(vehicle); + break; + case VEHICLE_STATUS_ARRIVING: + vehicle_update_arriving(vehicle); + break; + case VEHICLE_STATUS_UNLOADING_PASSENGERS: + vehicle_update_unloading_passengers(vehicle); + break; + case VEHICLE_STATUS_WAITING_FOR_CABLE_LIFT: + vehicle_update_waiting_for_cable_lift(vehicle); + break; + case VEHICLE_STATUS_SHOWING_FILM: + vehicle_update_showing_film(vehicle); + break; + case VEHICLE_STATUS_DOING_CIRCUS_SHOW: + vehicle_update_doing_circus_show(vehicle); + } + + vehicle_update_sound(vehicle); } + + /** * - * rct2: 0x006DAB4C + * rct2: 0x006D7BCC */ -int sub_6DAB4C(rct_vehicle *vehicle, int *outStation) -{ - registers regs; - regs.esi = (int)vehicle; +static void vehicle_update_moving_to_end_of_station(rct_vehicle *vehicle){ + rct_ride* ride = GET_RIDE(vehicle->ride); + int eax, ebx; - RCT2_CALLFUNC_Y(0x006DAB4C, ®s); + switch (ride->mode){ + case RIDE_MODE_UPWARD_LAUNCH: + case RIDE_MODE_ROTATING_LIFT: + case RIDE_MODE_DOWNWARD_LAUNCH: + case RIDE_MODE_FREEFALL_DROP: + if (vehicle->velocity >= -131940){ + vehicle->acceleration = -3298; + } + if (vehicle->velocity < -131940){ + vehicle->velocity = vehicle->velocity / 16; + vehicle->acceleration = 0; + } - if (outStation != NULL) *outStation = regs.ebx; - return regs.eax; -} + eax = vehicle_update_track_motion(vehicle, &ebx); + if (!(eax&(1 << 5))) + break; + //Fall through to next case + case RIDE_MODE_BUMPERCAR: + case RIDE_MODE_SWING: + case RIDE_MODE_ROTATION: + case RIDE_MODE_FORWARD_ROTATION: + case RIDE_MODE_BACKWARD_ROTATION: + case RIDE_MODE_FILM_AVENGING_AVIATORS: + case RIDE_MODE_FILM_THRILL_RIDERS: + case RIDE_MODE_BEGINNERS: + case RIDE_MODE_INTENSE: + case RIDE_MODE_BERSERK: + case RIDE_MODE_3D_FILM_MOUSE_TAILS: + case RIDE_MODE_3D_FILM_STORM_CHASERS: + case RIDE_MODE_3D_FILM_SPACE_RAIDERS: + case RIDE_MODE_SPACE_RINGS: + case RIDE_MODE_HAUNTED_HOUSE: + case RIDE_MODE_CROOKED_HOUSE: + case RIDE_MODE_CIRCUS_SHOW: + vehicle->current_station = 0; + vehicle->velocity = 0; + vehicle->acceleration = 0; + vehicle->status = VEHICLE_STATUS_WAITING_FOR_PASSENGERS; + vehicle->sub_state = 0; + vehicle_invalidate_window(vehicle); + break; + default: + { + rct_ride_type* rideEntry = GET_RIDE_ENTRY(vehicle->ride_subtype); + rct_ride_type_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type]; -rct_ride_type_vehicle *vehicle_get_vehicle_entry(rct_vehicle *vehicle) -{ - rct_ride_type *rideEntry = GET_RIDE_ENTRY(vehicle->ride_subtype); + if (!(vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_3)){ + if (vehicle->velocity <= 131940){ + vehicle->acceleration = 3298; + } + } + if (vehicle->velocity > 131940){ + vehicle->velocity = vehicle->velocity / 16; + vehicle->acceleration = 0; + } + + int station; + eax = vehicle_update_track_motion(vehicle, &station); + + if (eax & (1 << 1)){ + vehicle->velocity = 0; + vehicle->acceleration = 0; + vehicle->sub_state++; + + if (ride->mode == RIDE_MODE_RACE && + vehicle->sub_state >= 40){ + vehicle->status = VEHICLE_STATUS_WAITING_FOR_PASSENGERS; + vehicle->sub_state = 0; + vehicle_invalidate_window(vehicle); + break; + } + } + else{ + if (vehicle->velocity > 98955){ + vehicle->sub_state = 0; + } + } + + if (!(eax & (1 << 0))) + break; + + vehicle->current_station = station; + vehicle->velocity = 0; + vehicle->acceleration = 0; + vehicle->status = VEHICLE_STATUS_WAITING_FOR_PASSENGERS; + vehicle->sub_state = 0; + vehicle_invalidate_window(vehicle); + break; + } + } +} + +/** + * + * rct2: 0x006D7FB4 + */ +static void train_ready_to_depart(rct_vehicle* vehicle, uint8 num_peeps_on_train, uint8 num_used_seats){ + + if (num_peeps_on_train != num_used_seats) + return; + + rct_ride* ride = GET_RIDE(vehicle->ride); + + if (ride->status == RIDE_STATUS_OPEN && + !(ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) && + !(vehicle->update_flags & VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART)){ + return; + } + + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)){ + if (ride->status != RIDE_STATUS_CLOSED || + ride->num_riders != 0){ + ride->train_at_station[vehicle->current_station] = 0xFF; + vehicle->sub_state = 2; + return; + } + } + + if (ride->mode == RIDE_MODE_FORWARD_ROTATION || + ride->mode == RIDE_MODE_BACKWARD_ROTATION){ + + uint8 peep = ((-vehicle->var_1F) / 8) & 0xF; + if (vehicle->peep[peep] != 0xFFFF){ + ride->train_at_station[vehicle->current_station] = 0xFF; + vehicle->status = VEHICLE_STATUS_UNLOADING_PASSENGERS; + vehicle->sub_state = 2; + vehicle_invalidate_window(vehicle); + return; + } + + if (vehicle->num_peeps == 0) + return; + + ride->train_at_station[vehicle->current_station] = 0xFF; + vehicle->sub_state = 2; + return; + } + + if (num_peeps_on_train == 0) + return; + + ride->train_at_station[vehicle->current_station] = 0xFF; + vehicle->status = VEHICLE_STATUS_UNLOADING_PASSENGERS; + vehicle->sub_state = 2; + vehicle_invalidate_window(vehicle); +} + +/** + * + * rct2: 0x006D7DA1 + */ +static void vehicle_update_waiting_for_passengers(rct_vehicle* vehicle){ + vehicle->velocity = 0; + + rct_ride* ride = GET_RIDE(vehicle->ride); + + if (vehicle->sub_state == 0){ + if (vehicle_open_restraints(vehicle)) + return; + + if (ride->entrances[vehicle->current_station] == 0xFFFF){ + ride->train_at_station[vehicle->current_station] = 0xFF; + vehicle->sub_state = 2; + return; + } + + uint8 train_index = 0; + while (ride->vehicles[train_index] != vehicle->sprite_index)train_index++; + + if (ride->train_at_station[vehicle->current_station] != 0xFF) + return; + + ride->train_at_station[vehicle->current_station] = train_index; + vehicle->sub_state = 1; + vehicle->var_C0 = 0; + + vehicle_invalidate(vehicle); + return; + } + else if (vehicle->sub_state == 1){ + if (vehicle->var_C0 != 0xFFFF) + vehicle->var_C0++; + + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART; + + // 0xF64E31, 0xF64E32, 0xF64E33 + uint8 num_peeps_on_train = 0, num_used_seats_on_train = 0, num_seats_on_train = 0; + + for (uint16 sprite_id = vehicle->sprite_index; sprite_id != 0xFFFF;){ + rct_vehicle* train_vehicle = GET_VEHICLE(sprite_id); + + num_peeps_on_train += train_vehicle->num_peeps; + num_used_seats_on_train += train_vehicle->next_free_seat; + num_seats_on_train += train_vehicle->num_seats; + + sprite_id = train_vehicle->next_vehicle_on_train; + } + + num_seats_on_train &= 0x7F; + + if (!(RCT2_ADDRESS(RCT2_ADDRESS_RIDE_FLAGS, uint32)[ride->type * 2] & RIDE_TYPE_FLAG_NO_TEST_MODE)){ + if (vehicle->var_C0 < 20){ + train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); + return; + } + } + else{ + if (num_peeps_on_train == 0){ + train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); + return; + } + } + + if (RCT2_ADDRESS(RCT2_ADDRESS_RIDE_FLAGS, uint32)[ride->type * 2] & RIDE_TYPE_FLAG_HAS_LOAD_OPTIONS){ + if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH){ + if (ride->min_waiting_time * 32 > vehicle->var_C0){ + train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); + return; + } + } + if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MAXIMUM_LENGTH){ + if (ride->max_waiting_time * 32 < vehicle->var_C0){ + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART; + train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); + return; + } + } + } + + if (ride->depart_flags & RIDE_DEPART_LEAVE_WHEN_ANOTHER_ARRIVES){ + + for (int i = 0; i < 32; ++i){ + uint16 train_id = ride->vehicles[i]; + if (train_id == 0xFFFF) + continue; + + if (train_id == vehicle->sprite_index) + continue; + + rct_vehicle* train = GET_VEHICLE(train_id); + + if (train->status == VEHICLE_STATUS_UNLOADING_PASSENGERS || + train->status == VEHICLE_STATUS_MOVING_TO_END_OF_STATION){ + if (train->current_station == vehicle->current_station){ + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART; + train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); + return; + } + } + } + } + + if (RCT2_ADDRESS(RCT2_ADDRESS_RIDE_FLAGS, uint32)[ride->type * 2] & RIDE_TYPE_FLAG_HAS_LOAD_OPTIONS && + ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD){ + + if (num_peeps_on_train == num_seats_on_train){ + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART; + train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); + return; + } + + uint8 load = ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD_MASK; + if (load == 3){ + train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); + return; + } + + uint8 three_quater_seats = (3 * num_seats_on_train) / 4; + if (three_quater_seats != 0 && num_peeps_on_train >= three_quater_seats){ + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART; + train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); + return; + } + + if (load == 2){ + train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); + return; + } + + if (num_seats_on_train / 2 != 0 && num_peeps_on_train >= num_seats_on_train / 2){ + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART; + train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); + return; + } + + if (load == 1){ + train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); + return; + } + + if (num_seats_on_train / 4 != 0 && num_peeps_on_train >= num_seats_on_train / 4){ + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART; + train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); + return; + } + + if (load == 0){ + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART; + train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); + return; + } + + if (num_peeps_on_train != 0){ + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART; + } + train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); + return; + } + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART; + train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); + return; + } + + if (vehicle_close_restraints(vehicle)) + return; + + vehicle->velocity = 0; + vehicle->status = VEHICLE_STATUS_WAITING_TO_DEPART; + vehicle->sub_state = 0; + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_WAIT_ON_ADJACENT; + + if (ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS){ + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_WAIT_ON_ADJACENT; + } + + vehicle_invalidate_window(vehicle); +} + +/** + * + * rct2: 0x006D91BF + */ +static void vehicle_update_bumpcar_mode(rct_vehicle* vehicle) { + rct_ride* ride = GET_RIDE(vehicle->ride); + rct_ride_type* rideEntry = GET_RIDE_ENTRY(vehicle->ride_subtype); + rct_ride_type_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type]; + + if (vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_7 && vehicle->var_C5 != 1) { + vehicle->var_C5 = 1; + vehicle_invalidate(vehicle); + } + + vehicle_update_motion_bumper_car(vehicle); + + // Update the length of time vehicle has been in bumper mode + if (vehicle->sub_state++ == 0xFF) { + vehicle->var_CE++; + } + + if (ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) + return; + + vehicle->var_C5 = 0; + vehicle_invalidate(vehicle); + vehicle->velocity = 0; + vehicle->acceleration = 0; + vehicle->status = VEHICLE_STATUS_UNLOADING_PASSENGERS; + vehicle->sub_state = 0; + vehicle_invalidate_window(vehicle); +} + +/** + * + * rct2: 0x006D808BE + */ +static void vehicle_update_waiting_to_depart(rct_vehicle* vehicle) { + rct_ride* ride = GET_RIDE(vehicle->ride); + bool shouldBreak = false; + if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) { + switch (ride->breakdown_reason_pending) { + case BREAKDOWN_RESTRAINTS_STUCK_CLOSED: + case BREAKDOWN_RESTRAINTS_STUCK_OPEN: + case BREAKDOWN_DOORS_STUCK_CLOSED: + case BREAKDOWN_DOORS_STUCK_OPEN: + break; + default: + shouldBreak = true; + break; + } + } + + bool skipCheck = false; + if (shouldBreak == true || ride->status != RIDE_STATUS_OPEN) { + if (ride->mode == RIDE_MODE_FORWARD_ROTATION || + ride->mode == RIDE_MODE_BACKWARD_ROTATION) { + uint8 bl = ((-vehicle->var_1F) >> 3) & 0xF; + if (vehicle->peep[bl] == 0xFFFF) { + if (vehicle->num_peeps == 0) { + skipCheck = true; + } + } + else { + if (ride->exits[vehicle->current_station] != 0xFFFF) { + vehicle->status = VEHICLE_STATUS_UNLOADING_PASSENGERS; + vehicle->sub_state = 0; + vehicle_invalidate_window(vehicle); + return; + } + } + } + else { + uint16 spriteId = vehicle->sprite_index; + for (rct_vehicle* curVehicle; spriteId != 0xFFFF; spriteId = curVehicle->next_vehicle_on_train) { + curVehicle = GET_VEHICLE(spriteId); + + if (curVehicle->num_peeps != 0) { + if (ride->exits[vehicle->current_station] != 0xFFFF) { + vehicle->status = VEHICLE_STATUS_UNLOADING_PASSENGERS; + vehicle->sub_state = 0; + vehicle_invalidate_window(vehicle); + return; + } + break; + } + } + } + } + + if (skipCheck == false) { + if (!(ride->station_depart[vehicle->current_station] & STATION_DEPART_FLAG)) + return; + } + + if (RCT2_ADDRESS(RCT2_ADDRESS_RIDE_FLAGS, uint32)[ride->type * 2] + & RIDE_TYPE_FLAG_CAN_SYNCHRONISE_ADJACENT_STATIONS) { + if (ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS) { + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_WAIT_ON_ADJACENT) { + if (vehicle_can_depart_synchronised(vehicle)) { + return; + } + } + } + } + + vehicle->status = VEHICLE_STATUS_DEPARTING; + vehicle->sub_state = 0; + + if (ride->lifecycle_flags & RIDE_LIFECYCLE_CABLE_LIFT) { + RCT2_GLOBAL(0x00F441D2, uint8) = vehicle->ride; + rct_xy_element track; + int z; + int direction; + + if (track_block_get_next_from_zero( + vehicle->track_x, + vehicle->track_y, + vehicle->track_z, + vehicle->ride, + (uint8)(vehicle->track_direction & 0x3), + &track, + &z, + &direction)) { + + if (track_element_is_cable_lift(track.element)) { + vehicle->status = VEHICLE_STATUS_WAITING_FOR_CABLE_LIFT; + } + } + } + + switch (ride->mode) { + case RIDE_MODE_BUMPERCAR: + vehicle->status = VEHICLE_STATUS_TRAVELLING_BUMPER_CARS; + vehicle_invalidate_window(vehicle); + // Bumper mode uses sub_state / var_CE to tell how long + // the vehicle has been ridden. + vehicle->sub_state = 0; + vehicle->var_CE = 0; + vehicle_update_bumpcar_mode(vehicle); + break; + case RIDE_MODE_SWING: + vehicle->status = VEHICLE_STATUS_SWINGING; + vehicle->sub_state = 0; + vehicle_invalidate_window(vehicle); + vehicle->var_CE = 0; + vehicle->current_time = -1; + vehicle_update_swinging(vehicle); + break; + case RIDE_MODE_ROTATION: + vehicle->status = VEHICLE_STATUS_ROTATING; + vehicle->sub_state = 0; + vehicle_invalidate_window(vehicle); + vehicle->var_CE = 0; + vehicle->current_time = -1; + vehicle_update_rotating(vehicle); + break; + case RIDE_MODE_FILM_AVENGING_AVIATORS: + case RIDE_MODE_FILM_THRILL_RIDERS: + vehicle->status = VEHICLE_STATUS_SIMULATOR_OPERATING; + vehicle->sub_state = 0; + if (ride->mode == RIDE_MODE_FILM_THRILL_RIDERS) + vehicle->sub_state = 1; + vehicle_invalidate_window(vehicle); + vehicle->current_time = -1; + vehicle_update_simulator_operating(vehicle); + break; + case RIDE_MODE_BEGINNERS: + case RIDE_MODE_INTENSE: + case RIDE_MODE_BERSERK: + vehicle->status = VEHICLE_STATUS_TOP_SPIN_OPERATING; + vehicle_invalidate_window(vehicle); + + switch (ride->mode) { + case RIDE_MODE_BEGINNERS: + vehicle->sub_state = 0; + break; + case RIDE_MODE_INTENSE: + vehicle->sub_state = 1; + break; + case RIDE_MODE_BERSERK: + vehicle->sub_state = 2; + break; + } + vehicle->current_time = -1; + vehicle->var_1F = 0; + vehicle->var_20 = 0; + vehicle_update_top_spin_operating(vehicle); + break; + case RIDE_MODE_FORWARD_ROTATION: + case RIDE_MODE_BACKWARD_ROTATION: + vehicle->status = VEHICLE_STATUS_FERRIS_WHEEL_ROTATING; + vehicle->sub_state = vehicle->var_1F; + vehicle_invalidate_window(vehicle); + vehicle->var_CE = 0; + vehicle->ferris_wheel_var_0 = 8; + vehicle->ferris_wheel_var_1 = 8; + vehicle_update_ferris_wheel_rotating(vehicle); + break; + case RIDE_MODE_3D_FILM_MOUSE_TAILS: + case RIDE_MODE_3D_FILM_STORM_CHASERS: + case RIDE_MODE_3D_FILM_SPACE_RAIDERS: + vehicle->status = VEHICLE_STATUS_SHOWING_FILM; + vehicle_invalidate_window(vehicle); + switch (ride->mode) { + case RIDE_MODE_3D_FILM_MOUSE_TAILS: + vehicle->sub_state = 0; + break; + case RIDE_MODE_3D_FILM_STORM_CHASERS: + vehicle->sub_state = 1; + break; + case RIDE_MODE_3D_FILM_SPACE_RAIDERS: + vehicle->sub_state = 2; + break; + } + vehicle->current_time = -1; + vehicle_update_showing_film(vehicle); + break; + case RIDE_MODE_CIRCUS_SHOW: + vehicle->status = VEHICLE_STATUS_DOING_CIRCUS_SHOW; + vehicle->sub_state = 0; + vehicle_invalidate_window(vehicle); + vehicle->current_time = -1; + vehicle_update_doing_circus_show(vehicle); + break; + case RIDE_MODE_SPACE_RINGS: + vehicle->status = VEHICLE_STATUS_SPACE_RINGS_OPERATING; + vehicle->sub_state = 0; + vehicle_invalidate_window(vehicle); + vehicle->var_1F = 0; + vehicle->current_time = -1; + vehicle_update_space_rings_operating(vehicle); + break; + case RIDE_MODE_HAUNTED_HOUSE: + vehicle->status = VEHICLE_STATUS_HAUNTED_HOUSE_OPERATING; + vehicle->sub_state = 0; + vehicle_invalidate_window(vehicle); + vehicle->var_1F = 0; + vehicle->current_time = -1; + vehicle_update_haunted_house_operating(vehicle); + break; + case RIDE_MODE_CROOKED_HOUSE: + vehicle->status = VEHICLE_STATUS_CROOKED_HOUSE_OPERATING; + vehicle->sub_state = 0; + vehicle_invalidate_window(vehicle); + vehicle->var_1F = 0; + vehicle->current_time = -1; + vehicle_update_crooked_house_operating(vehicle); + break; + default: + vehicle->sub_state = 0; + vehicle_invalidate_window(vehicle); + vehicle->var_CE = 0; + break; + } +} + +typedef struct { + uint8 ride_id; + uint8 station_id; + uint16 vehicle_id; +} rct_synchrnoised_vehicle; + +// 8 synchrnoised vehicle info +rct_synchrnoised_vehicle *_synchrnoisedVehicles = (rct_synchrnoised_vehicle*)0x00F64E4C; + +#define _lastSynchrnoisedVehicle RCT2_GLOBAL(0x00F64E48, rct_synchrnoised_vehicle*) +#define MaxSynchrnoisedVehicle ((rct_synchrnoised_vehicle*)0x00F64E6C) + +/** + * Checks if a map position contains a synchrnoised ride station and adds the vehicle + * to synchrnoise to the vehicle synchronisation list. + * rct2: 0x006DE1A4 + */ +static bool try_add_synchronised_station(int x, int y, int z) +{ + bool foundMapElement = false; + rct_map_element *mapElement = map_get_first_element_at(x >> 5, y >> 5); + if (mapElement != NULL) { + do { + if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_TRACK) continue; + if (z != mapElement->base_height && + z != mapElement->base_height - 2 && + z != mapElement->base_height + 2 + ) { + continue; + } + + foundMapElement = true; + break; + } while (!map_element_is_last_for_tile(mapElement++)); + } + if (!foundMapElement) { + return false; + } + + int rideIndex = mapElement->properties.track.ride_index; + rct_ride *ride = GET_RIDE(rideIndex); + if (!(ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS)) { + return false; + } + + int stationIndex = map_get_station(mapElement); + + rct_synchrnoised_vehicle *sv = _lastSynchrnoisedVehicle; + sv->ride_id = rideIndex; + sv->station_id = stationIndex; + sv->vehicle_id = SPRITE_INDEX_NULL; + _lastSynchrnoisedVehicle++; + + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) { + return false; + } + + for (int i = 0; i < ride->num_vehicles; i++) { + uint16 spriteIndex = ride->vehicles[i]; + if (spriteIndex == SPRITE_INDEX_NULL) continue; + + rct_vehicle *vehicle = GET_VEHICLE(spriteIndex); + if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART) continue; + if (vehicle->sub_state != 0) continue; + if (!(vehicle->update_flags & VEHICLE_UPDATE_FLAG_WAIT_ON_ADJACENT)) continue; + if (vehicle->current_station != stationIndex) continue; + + sv->vehicle_id = spriteIndex; + return true; + } + return false; +} + +/** + * Checks whether a vehicle can depart a station when set to synchrnoise with adjacent stations. + * rct2: 0x006DE287 + * @param vehicle The vehicle waiting to depart. + * @returns true if the vehicle can depart (all adjacent trains are ready or broken down), otherwise false. + */ +static bool vehicle_can_depart_synchronised(rct_vehicle *vehicle) +{ + rct_ride *ride = GET_RIDE(vehicle->ride); + int station = vehicle->current_station; + uint16 xy = ride->station_starts[station]; + int x = (xy & 0xFF) * 32; + int y = (xy >> 8) * 32; + int z = ride->station_heights[station]; + + rct_map_element *mapElement = map_get_track_element_at(x, y, z); + int direction = (mapElement->type + 1) & 3; + _lastSynchrnoisedVehicle = _synchrnoisedVehicles; + + while (_lastSynchrnoisedVehicle < MaxSynchrnoisedVehicle) { + x += TileDirectionDelta[direction].x; + y += TileDirectionDelta[direction].y; + if (!try_add_synchronised_station(x, y, z)) { + break; + } + } + + while (_lastSynchrnoisedVehicle < MaxSynchrnoisedVehicle) { + x += TileDirectionDelta[direction].x; + y += TileDirectionDelta[direction].y; + if (!try_add_synchronised_station(x, y, z)) { + break; + } + } + + if (_lastSynchrnoisedVehicle == _synchrnoisedVehicles) { + // No adjacent stations, allow depart + return true; + } + + for (rct_synchrnoised_vehicle *sv = _synchrnoisedVehicles; sv < _lastSynchrnoisedVehicle; sv++) { + if (ride_is_block_sectioned(ride)) { + if (!(ride->station_depart[sv->station_id] & 0x80)) { + sv = _synchrnoisedVehicles; + uint8 rideId = 0xFF; + for (; sv < _lastSynchrnoisedVehicle; sv++) { + if (rideId == 0xFF) { + rideId = sv->ride_id; + } + if (rideId != sv->ride_id) { + return true; + } + } + + ride = GET_RIDE(rideId); + for (int i = 0; i < ride->num_vehicles; i++) { + rct_vehicle *v = GET_VEHICLE(ride->vehicles[i]); + if (v->status != VEHICLE_STATUS_WAITING_TO_DEPART && v->velocity != 0) { + return true; + } + } + + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_WAIT_ON_ADJACENT; + return false; + } + } + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)) { + if (ride->status != RIDE_STATUS_CLOSED) { + if (sv->vehicle_id == SPRITE_INDEX_NULL) { + if (_lastSynchrnoisedVehicle > &_synchrnoisedVehicles[1]) { + return true; + } + uint8 someRideIndex = _synchrnoisedVehicles[0].ride_id; + // uint8 currentStation = _synchrnoisedVehicles[0].station_id + if (someRideIndex != vehicle->ride) { + return true; + } + + ride = GET_RIDE(someRideIndex); + int numAdjacentTrainsAtStation = 0; + int numTravelingTrains = 0; + int currentStation = vehicle->current_station; + for (int i = 0; i < ride->num_vehicles; i++) { + uint16 spriteIndex = ride->vehicles[i]; + if (spriteIndex != SPRITE_INDEX_NULL) { + rct_vehicle *otherVehicle = GET_VEHICLE(spriteIndex); + if (otherVehicle->status != VEHICLE_STATUS_TRAVELLING) { + if (currentStation == otherVehicle->current_station) { + if (otherVehicle->status == VEHICLE_STATUS_WAITING_TO_DEPART || + otherVehicle->status == VEHICLE_STATUS_MOVING_TO_END_OF_STATION + ) { + numAdjacentTrainsAtStation++; + } + } + } else { + numTravelingTrains++; + } + } + } + + int totalTrains = numAdjacentTrainsAtStation + numTravelingTrains; + if (totalTrains != ride->num_vehicles || numTravelingTrains >= ride->num_vehicles / 2) { + return true; + } else { + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_WAIT_ON_ADJACENT; + return false; + } + } + } + } + } + + for (rct_synchrnoised_vehicle *sv = _synchrnoisedVehicles; sv < _lastSynchrnoisedVehicle; sv++) { + if (sv->vehicle_id != SPRITE_INDEX_NULL) { + rct_vehicle *v = GET_VEHICLE(sv->vehicle_id); + v->update_flags &= ~VEHICLE_UPDATE_FLAG_WAIT_ON_ADJACENT; + } + } + + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_WAIT_ON_ADJACENT; + return false; +} + +/** + * + * rct2: 0x006D9EB0 + */ +void vehicle_peep_easteregg_here_we_are(rct_vehicle* vehicle) { + uint16 spriteId = vehicle->sprite_index; + do { + vehicle = GET_VEHICLE(spriteId); + for (int i = 0; i < vehicle->num_peeps; ++i) { + rct_peep* peep = GET_PEEP(vehicle->peep[i]); + if (peep->flags & PEEP_FLAGS_HERE_WE_ARE) { + peep_insert_new_thought(peep, PEEP_THOUGHT_HERE_WE_ARE, peep->current_ride); + } + } + } while ((spriteId = vehicle->next_vehicle_on_train) != 0xFFFF); +} + +/** + * Performed when vehicle has completed a full circuit + * rct2: 0x006D7338 + */ +void vehicle_update_test_finish(rct_vehicle* vehicle) { + rct_ride* ride = GET_RIDE(vehicle->ride); + ride->lifecycle_flags &= ~RIDE_LIFECYCLE_TEST_IN_PROGRESS; + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_TESTING; + ride->lifecycle_flags |= RIDE_LIFECYCLE_TESTED; + + for (int i = ride->num_stations - 1; i >= 1; i--) { + if (ride->time[i - 1] != 0) + continue; + + uint16 oldTime = ride->time[i - 1]; + ride->time[i - 1] = ride->time[i]; + ride->time[i] = oldTime; + + sint32 oldLength = ride->length[i - 1]; + ride->length[i - 1] = ride->length[i]; + ride->length[i] = oldLength; + } + + uint32 totalTime = 0; + for (uint8 i = 0; i < ride->num_stations; ++i) { + totalTime += ride->time[i]; + } + + totalTime = max(totalTime, 1); + ride->average_speed = ride->average_speed / totalTime; + + window_invalidate_by_number(WC_RIDE, vehicle->ride); +} + +/** + * + * rct2: 0x006D6BE7 + */ +void vehicle_test_reset(rct_vehicle* vehicle) { + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_TESTING; + + rct_ride* ride = GET_RIDE(vehicle->ride); + ride->lifecycle_flags |= RIDE_LIFECYCLE_TEST_IN_PROGRESS; + ride->lifecycle_flags &= ~RIDE_LIFECYCLE_NO_RAW_STATS; + ride->max_speed = 0; + ride->average_speed = 0; + ride->current_test_segment = 0; + ride->var_0E1 = 0; + ride->max_positive_vertical_g = FIXED_2DP(1, 0); + ride->max_negative_vertical_g = FIXED_2DP(1, 0); + ride->max_lateral_g = 0; + ride->previous_vertical_g = 0; + ride->previous_lateral_g = 0; + ride->testing_flags = 0; + ride->var_10C = 0xFFFF; + ride->var_11F = 0xFF; + ride->turn_count_default = 0; + ride->turn_count_banked = 0; + ride->turn_count_sloped = 0; + ride->inversions = 0; + ride->drops = 0; + ride->sheltered_length = 0; + ride->var_11C = 0; + ride->num_sheltered_sections = 0; + ride->highest_drop_height = 0; + ride->special_track_elements = 0; + memset(&ride->length, 0, 4 * 4); + memset(&ride->time, 0, 4 * 2); + ride->var_1F6 = vehicle->current_station; + window_invalidate_by_number(WC_RIDE, vehicle->ride); +} + +static bool vehicle_next_tower_element_is_top(rct_vehicle* vehicle) { + rct_map_element* mapElement = map_get_track_element_at_of_type( + vehicle->track_x, + vehicle->track_y, + vehicle->track_z / 8, + vehicle->track_type >> 2); + + if (mapElement->flags & MAP_ELEMENT_FLAG_LAST_TILE) { + return true; + } + + if (mapElement->clearance_height == (mapElement + 1)->base_height) { + if ((mapElement + 1)->properties.track.type == TRACK_ELEM_TOWER_SECTION) { + return false; + } + } + + if ((mapElement + 1)->flags & MAP_ELEMENT_FLAG_LAST_TILE) { + return true; + } + + if (mapElement->clearance_height != (mapElement + 2)->base_height) { + return true; + } + + if ((mapElement + 2)->properties.track.type == TRACK_ELEM_TOWER_SECTION) { + return false; + } + return true; +} + +/** + * + * rct2: 0x006D986C + */ +static void vehicle_update_travelling_boat_hire_setup(rct_vehicle* vehicle) { + vehicle->var_34 = vehicle->sprite_direction; + vehicle->track_x = vehicle->x & 0xFFE0; + vehicle->track_y = vehicle->y & 0xFFE0; + + rct_xy8 location = { + .x = (vehicle->track_x + RCT2_ADDRESS(0x00993CCC, sint16)[2 * (vehicle->sprite_direction >> 3)]) / 32, + .y = (vehicle->track_y + RCT2_ADDRESS(0x00993CCE, sint16)[2 * (vehicle->sprite_direction >> 3)]) / 32 + }; + + vehicle->boat_location = location; + vehicle->var_35 = 0; + vehicle->status = VEHICLE_STATUS_TRAVELLING_BOAT; + vehicle_invalidate_window(vehicle); + vehicle->sub_state = 0; + vehicle->remaining_distance += 27924; + + vehicle_update_travelling_boat(vehicle); +} + +/** + * + * rct2: 0x006D982F + */ +static void vehicle_update_departing_boat_hire(rct_vehicle* vehicle) { + vehicle->lost_time_out = 0; + rct_ride* ride = GET_RIDE(vehicle->ride); + + ride->station_depart[vehicle->current_station] &= STATION_DEPART_FLAG; + uint8 waitingTime = max(ride->min_waiting_time, 3); + waitingTime = min(waitingTime, 127); + ride->station_depart[vehicle->current_station] |= waitingTime; + vehicle_update_travelling_boat_hire_setup(vehicle); +} + +/** + * + * rct2: 0x006D845B + */ +static void vehicle_update_departing(rct_vehicle* vehicle) { + rct_ride* ride = GET_RIDE(vehicle->ride); + rct_ride_type* rideEntry = GET_RIDE_ENTRY(vehicle->ride_subtype); + + if (vehicle->sub_state == 0) { + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_BROKEN_TRAIN) { + if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) + return; + + ride->lifecycle_flags |= RIDE_LIFECYCLE_BROKEN_DOWN; + ride_breakdown_add_news_item(vehicle->ride); + + ride->window_invalidate_flags |= + RIDE_INVALIDATE_RIDE_MAIN | + RIDE_INVALIDATE_RIDE_LIST | + RIDE_INVALIDATE_RIDE_MAINTENANCE; + ride->mechanic_status = RIDE_MECHANIC_STATUS_CALLING; + ride->inspection_station = vehicle->current_station; + ride->breakdown_reason = ride->breakdown_reason_pending; + vehicle->velocity = 0; + return; + } + + vehicle->sub_state = 1; + vehicle_peep_easteregg_here_we_are(vehicle); + + if (rideEntry->flags & RIDE_ENTRY_FLAG_3) { + uint8 soundId = (rideEntry->vehicles[0].sound_range == 4) ? + SOUND_TRAM : + SOUND_TRAIN_CHUGGING; + + audio_play_sound_at_location( + soundId, + vehicle->x, + vehicle->y, + vehicle->z); + } + + if (ride->mode == RIDE_MODE_UPWARD_LAUNCH || + (ride->mode == RIDE_MODE_DOWNWARD_LAUNCH && vehicle->var_CE > 1)) { + + audio_play_sound_at_location( + SOUND_RIDE_LAUNCH_2, + vehicle->x, + vehicle->y, + vehicle->z + ); + } + + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)) { + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_TESTING) { + if (ride->current_test_segment + 1 < ride->num_stations) { + ride->current_test_segment++; + ride->var_1F6 = vehicle->current_station; + } + else { + vehicle_update_test_finish(vehicle); + } + } + else if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TEST_IN_PROGRESS)) { + vehicle_test_reset(vehicle); + } + } + } + + rct_ride_type_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type]; + + switch (ride->mode) { + case RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE: + if (vehicle->velocity >= -131940) + vehicle->acceleration = -3298; + break; + case RIDE_MODE_POWERED_LAUNCH_PASSTROUGH: + case RIDE_MODE_POWERED_LAUNCH: + case RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED: + case RIDE_MODE_LIM_POWERED_LAUNCH: + case RIDE_MODE_UPWARD_LAUNCH: + if (ride->type == RIDE_TYPE_AIR_POWERED_VERTICAL_COASTER) { + if ((ride->launch_speed << 16) > vehicle->velocity) { + vehicle->acceleration = ride->launch_speed << 13; + } + break; + } + + if ((ride->launch_speed << 16) > vehicle->velocity) + vehicle->acceleration = ride->launch_speed << 12; + break; + case RIDE_MODE_DOWNWARD_LAUNCH: + if (vehicle->var_CE >= 1) { + if ((14 << 16) > vehicle->velocity) + vehicle->acceleration = 14 << 12; + break; + } + // Fall through + case RIDE_MODE_CONTINUOUS_CIRCUIT: + case RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED: + case RIDE_MODE_ROTATING_LIFT: + case RIDE_MODE_FREEFALL_DROP: + case RIDE_MODE_BOAT_HIRE: + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_3) + break; + + if (vehicle->velocity <= 131940) + vehicle->acceleration = 3298; + break; + } + + uint32 flags = vehicle_update_track_motion(vehicle, NULL); + + if (flags & (1 << 8)) { + if (ride->mode == RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE) { + vehicle->velocity = -vehicle->velocity; + vehicle_finish_departing(vehicle); + return; + } + } + + if (flags & ((1 << 5) | (1 << 12))) { + if (ride->mode == RIDE_MODE_BOAT_HIRE) { + vehicle_update_departing_boat_hire(vehicle); + return; + } + else if (ride->mode == RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE) { + vehicle->velocity = -vehicle->velocity; + vehicle_finish_departing(vehicle); + return; + } + else if (ride->mode == RIDE_MODE_SHUTTLE) { + vehicle->update_flags ^= VEHICLE_UPDATE_FLAG_3; + vehicle->velocity = 0; + } + } + + if (flags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_ON_LIFT_HILL) { + vehicle->var_B8 |= (1 << 1); + if (ride->mode != RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE) { + sint32 speed = ride->lift_hill_speed * 31079; + if (vehicle->velocity <= speed) { + vehicle->acceleration = 15539; + if (vehicle->velocity != 0) { + if (RCT2_GLOBAL(0x00F64E34, uint8) == BREAKDOWN_SAFETY_CUT_OUT) { + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_7; + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_1; + } + } + else + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_1; + } + } + else { + sint32 speed = ride->lift_hill_speed * -31079; + if (vehicle->velocity >= speed) { + vehicle->acceleration = -15539; + if (vehicle->velocity != 0) { + if (RCT2_GLOBAL(0x00F64E34, uint8) == BREAKDOWN_SAFETY_CUT_OUT) { + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_7; + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_1; + } + } + else + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_1; + } + } + } + + if (ride->mode == RIDE_MODE_FREEFALL_DROP) { + vehicle->var_C5++; + }else{ + bool shouldLaunch = true; + if (ride->mode == RIDE_MODE_DOWNWARD_LAUNCH) { + if (vehicle->var_CE < 1) + shouldLaunch = false; + } + + if (shouldLaunch) { + if (!(flags & (1 << 3)) || (RCT2_GLOBAL(0x00F64E1C, uint8) != vehicle->current_station)) { + vehicle_finish_departing(vehicle); + return; + } + + if (!(flags & (1 << 5))) + return; + if (ride->mode == RIDE_MODE_BOAT_HIRE || + ride->mode == RIDE_MODE_ROTATING_LIFT || + ride->mode == RIDE_MODE_SHUTTLE) + return; + + if (ride->lifecycle_flags & RIDE_LIFECYCLE_SIX_FLAGS_DEPRECATED) + return; + + vehicle_update_crash_setup(vehicle); + return; + } + } + + if (vehicle_next_tower_element_is_top(vehicle) == false) { + if (ride->mode == RIDE_MODE_FREEFALL_DROP) + vehicle_invalidate(vehicle); + return; + } + + vehicle_finish_departing(vehicle); +} + +/** + * Part of vehicle_update_departing + * Called after finishing departing sequence to enter + * traveling state. + * Vertical rides class the lift to the top of the tower + * as the departing sequence. After this point station + * boosters do not affect the ride. + * rct2: 0x006D8858 + */ +static void vehicle_finish_departing(rct_vehicle* vehicle) { + rct_ride* ride = GET_RIDE(vehicle->ride); + + if (ride->mode == RIDE_MODE_DOWNWARD_LAUNCH ) { + if (vehicle->var_CE >= 1 && (14 << 16) > vehicle->velocity) + return; + + audio_play_sound_at_location( + SOUND_RIDE_LAUNCH_1, + vehicle->x, + vehicle->y, + vehicle->z); + } + + if (ride->mode == RIDE_MODE_UPWARD_LAUNCH) { + if ((ride->launch_speed << 16) > vehicle->velocity) + return; + + audio_play_sound_at_location( + SOUND_RIDE_LAUNCH_1, + vehicle->x, + vehicle->y, + vehicle->z); + } + + if (ride->mode != RIDE_MODE_RACE && + ride->mode != RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED && + ride->mode != RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED) { + + ride->station_depart[vehicle->current_station] &= STATION_DEPART_FLAG; + uint8 waitingTime = 3; + if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH) { + waitingTime = max(ride->min_waiting_time, 3); + waitingTime = min(waitingTime, 127); + } + + ride->station_depart[vehicle->current_station] |= waitingTime; + } + + vehicle->status = VEHICLE_STATUS_TRAVELLING; + vehicle_invalidate_window(vehicle); + vehicle->lost_time_out = 0; + + vehicle->sub_state = 1; + if (vehicle->velocity < 0) + vehicle->sub_state = 0; +} + +/** + * + * rct2: 0x006DE5CB + */ +static void vehicle_check_if_missing(rct_vehicle* vehicle) { + rct_ride* ride = GET_RIDE(vehicle->ride); + + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) + return; + + if (ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED || + ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED) + return; + + if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_27)) + return; + + vehicle->lost_time_out++; + if (ride->lifecycle_flags & RIDE_LIFECYCLE_11) + return; + + uint16 limit = ride->type == RIDE_TYPE_BOAT_RIDE ? 15360 : 9600; + + if (vehicle->lost_time_out <= limit) + return; + + ride->lifecycle_flags |= RIDE_LIFECYCLE_11; + + RCT2_GLOBAL(0x0013CE952, rct_string_id) + = RCT2_ADDRESS(0x0097C98E, rct_string_id)[ride->type * 4] + 6; + + uint8 vehicleIndex = 0; + for (; vehicleIndex < ride->num_vehicles; ++vehicleIndex) + if (ride->vehicles[vehicleIndex] == vehicle->sprite_index) break; + + vehicleIndex++; + RCT2_GLOBAL(0x0013CE954, uint16) = vehicleIndex; + RCT2_GLOBAL(0x0013CE956, rct_string_id) = ride->name; + RCT2_GLOBAL(0x0013CE958, uint32) = ride->name_arguments; + RCT2_GLOBAL(0x0013CE95C, rct_string_id) = + RCT2_ADDRESS(0x0097C98E, rct_string_id)[ride->type * 4 + 2]; + + news_item_add_to_queue(NEWS_ITEM_RIDE, 2218, vehicle->ride); +} + +/** + * Setup function for a vehicle colliding with + * another vehicle. + * + * rct2: 0x006DA059 + */ +static void vehicle_update_collision_setup(rct_vehicle* vehicle) { + vehicle->status = VEHICLE_STATUS_CRASHED; + vehicle_invalidate_window(vehicle); + + rct_ride* ride = GET_RIDE(vehicle->ride); + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) { + rct_vehicle* frontVehicle = vehicle; + while (frontVehicle->is_child != 0)frontVehicle = GET_VEHICLE(frontVehicle->prev_vehicle_on_ride); + + uint8 trainIndex = 0; + while (ride->vehicles[trainIndex] != frontVehicle->sprite_index) + trainIndex++; + + ride_crash(vehicle->ride, trainIndex); + + if (ride->status != RIDE_STATUS_CLOSED) { + ride_set_status(vehicle->ride, RIDE_STATUS_CLOSED); + } + } + + ride->lifecycle_flags |= RIDE_LIFECYCLE_CRASHED; + ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; + vehicle_kill_all_passengers(vehicle); + + rct_vehicle* lastVehicle = vehicle; + uint16 spriteId = vehicle->sprite_index; + for (rct_vehicle* train; spriteId != 0xFFFF; spriteId = train->next_vehicle_on_train) { + train = GET_VEHICLE(spriteId); + lastVehicle = train; + + train->sub_state = 2; + + audio_play_sound_at_location( + SOUND_CRASH, + train->x, + train->y, + train->z + ); + + sprite_misc_3_create( + train->x, + train->y, + train->z + ); + + for (int i = 0; i < 10; i++) { + crashed_vehicle_particle_create( + train->colours, + train->x, + train->y, + train->z + ); + } + + train->var_0C |= (1 << 7); + train->var_C8 = scenario_rand(); + train->var_CA = scenario_rand(); + + train->var_C5 = train->var_CA & 0x7; + train->sprite_width = 13; + train->sprite_height_negative = 45; + train->sprite_height_positive = 5; + + sprite_move(train->x, train->y, train->z, (rct_sprite*)train); + invalidate_sprite_2((rct_sprite*)train); + + train->var_4E = 0; + } + + (GET_VEHICLE(vehicle->prev_vehicle_on_ride))->next_vehicle_on_ride = lastVehicle->next_vehicle_on_ride; + (GET_VEHICLE(lastVehicle->next_vehicle_on_ride))->prev_vehicle_on_ride = vehicle->prev_vehicle_on_ride; + vehicle->velocity = 0; +} + +/** + * + * rct2: 0x006D9EFE + */ +static void vehicle_update_crash_setup(rct_vehicle* vehicle) { + vehicle->status = VEHICLE_STATUS_CRASHING; + vehicle_invalidate_window(vehicle); + + int num_peeps = vehicle_get_total_num_peeps(vehicle); + if (num_peeps != 0) { + audio_play_sound_at_location( + SOUND_HAUNTED_HOUSE_SCREAM_2, + vehicle->x, + vehicle->y, + vehicle->z + ); + } + + int edx = vehicle->velocity >> 10; + + rct_vehicle* lastVehicle = vehicle; + uint16 spriteId = vehicle->sprite_index; + for (rct_vehicle* trainVehicle; spriteId != 0xFFFF; spriteId = trainVehicle->next_vehicle_on_train) { + trainVehicle = GET_VEHICLE(spriteId); + lastVehicle = trainVehicle; + + trainVehicle->sub_state = 0; + int x = RCT2_ADDRESS(0x009A3AC4, sint16)[trainVehicle->sprite_direction * 2]; + int y = RCT2_ADDRESS(0x009A3AC6, sint16)[trainVehicle->sprite_direction * 2]; + + int ecx = RCT2_ADDRESS(0x009A37E4, uint32)[trainVehicle->var_1F] >> 15; + x *= ecx; + y *= ecx; + x >>= 16; + y >>= 16; + ecx = RCT2_ADDRESS(0x009A38D4, uint32)[trainVehicle->var_1F] >> 23; + x *= edx; + y *= edx; + ecx *= edx; + x >>= 8; + y >>= 8; + ecx >>= 8; + + trainVehicle->var_B6 = x; + trainVehicle->var_C0 = y; + trainVehicle->var_4E = ecx; + trainVehicle->var_B6 += (scenario_rand() & 0xF) - 8; + trainVehicle->var_C0 += (scenario_rand() & 0xF) - 8; + trainVehicle->var_4E += (scenario_rand() & 0xF) - 8; + + trainVehicle->track_x = 0; + trainVehicle->track_y = 0; + trainVehicle->track_z = 0; + } + + (GET_VEHICLE(vehicle->prev_vehicle_on_ride))->next_vehicle_on_ride = lastVehicle->next_vehicle_on_ride; + (GET_VEHICLE(lastVehicle->next_vehicle_on_ride))->prev_vehicle_on_ride = vehicle->prev_vehicle_on_ride; + vehicle->velocity = 0; +} + +/** + * + * rct2: 0x006D8937 + */ +static void vehicle_update_travelling(rct_vehicle* vehicle) { + vehicle_check_if_missing(vehicle); + + rct_ride* ride = GET_RIDE(vehicle->ride); + if (RCT2_GLOBAL(0x00F64E34, uint8) == 0 && ride->mode == RIDE_MODE_ROTATING_LIFT) + return; + + if (vehicle->sub_state == 2) { + vehicle->velocity = 0; + vehicle->acceleration = 0; + vehicle->var_C0--; + if (vehicle->var_C0 == 0) + vehicle->sub_state = 0; + } + + if (ride->mode == RIDE_MODE_FREEFALL_DROP && + vehicle->var_C5 != 0) { + + vehicle->var_C5++; + vehicle->velocity = 0; + vehicle->acceleration = 0; + vehicle_invalidate(vehicle); + return; + } + + uint32 flags = vehicle_update_track_motion(vehicle, NULL); + + bool skipCheck = false; + if (flags & ((1 << 8) | (1 << 9)) && + ride->mode == RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE && + vehicle->sub_state == 0) { + vehicle->sub_state = 1; + vehicle->velocity = 0; + skipCheck = true; + } + + if (!skipCheck) { + if (flags & (1 << 6)) { + vehicle_update_crash_setup(vehicle); + return; + } + + if (flags & (1 << 7)) { + vehicle_update_collision_setup(vehicle); + return; + } + + if (flags & ((1 << 5) | (1 << 12))) { + if (ride->mode == RIDE_MODE_ROTATING_LIFT) { + if (vehicle->sub_state <= 1) { + vehicle->status = VEHICLE_STATUS_ARRIVING; + vehicle_invalidate_window(vehicle); + vehicle->sub_state = 1; + vehicle->var_C0 = 0; + return; + } + } + else if (ride->mode == RIDE_MODE_BOAT_HIRE) { + vehicle_update_travelling_boat_hire_setup(vehicle); + return; + } + else if (ride->mode == RIDE_MODE_SHUTTLE) { + vehicle->update_flags ^= VEHICLE_UPDATE_FLAG_3; + vehicle->velocity = 0; + } + else { + if (ride->lifecycle_flags & RIDE_LIFECYCLE_SIX_FLAGS_DEPRECATED) { + if (vehicle->sub_state != 0) { + vehicle->update_flags ^= VEHICLE_UPDATE_FLAG_3; + vehicle->velocity = 0; + } + else { + vehicle->sub_state = 1; + vehicle->velocity = 0; + } + } + else { + if (vehicle->sub_state != 0) { + vehicle_update_crash_setup(vehicle); + return; + } + vehicle->sub_state = 1; + vehicle->velocity = 0; + } + } + } + } + + if (ride->mode == RIDE_MODE_ROTATING_LIFT && + vehicle->sub_state <= 1) { + + if (vehicle->sub_state == 0) { + if (vehicle->velocity >= -131940) + vehicle->acceleration = -3298; + vehicle->velocity = max(vehicle->velocity, -131940); + } + else { + if (vehicle_next_tower_element_is_top(vehicle) == true) { + vehicle->velocity = 0; + vehicle->sub_state = 2; + vehicle->var_C0 = 150; + } + else { + if (vehicle->velocity <= 131940) + vehicle->acceleration = 3298; + } + } + } + + if (flags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_ON_LIFT_HILL) { + if (ride->mode == RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE) { + if (vehicle->sub_state == 0) { + if (vehicle->velocity != 0) + vehicle->var_B8 |= (1 << 1); + + if (!(vehicle->update_flags & VEHICLE_UPDATE_FLAG_12)) { + if (vehicle->velocity >= ride->lift_hill_speed * -31079) { + vehicle->acceleration = -15539; + + if (RCT2_GLOBAL(0x00F64E34, uint8) == 0) { + vehicle->var_B8 &= ~(1 << 1); + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_7; + } + } + } + } + } + else { + vehicle->var_B8 |= (1 << 1); + if (vehicle->velocity <= ride->lift_hill_speed * 31079) { + vehicle->acceleration = 15539; + if (vehicle->velocity != 0) { + if (RCT2_GLOBAL(0x00F64E34, uint8) == 0) { + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_7; + vehicle->var_B8 &= ~(1 << 1); + } + } + else { + vehicle->var_B8 &= ~(1 << 1); + } + } + } + } + + if (!(flags & (1 << 3))) + return; + + if (ride->mode == RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE && + vehicle->velocity >= 0 && + !(vehicle->update_flags & VEHICLE_UPDATE_FLAG_12)) { + return; + } + + if (ride->mode == RIDE_MODE_POWERED_LAUNCH_PASSTROUGH && + vehicle->velocity < 0) + return; + + vehicle->status = VEHICLE_STATUS_ARRIVING; + vehicle->current_station = RCT2_GLOBAL(0x00F64E1C, uint8); + vehicle_invalidate_window(vehicle); + vehicle->var_C0 = 0; + + vehicle->sub_state = 0; + if (vehicle->velocity < 0) + vehicle->sub_state = 1; +} + +/** + * + * rct2: 0x006D8C36 + */ +static void vehicle_update_arriving(rct_vehicle* vehicle) { + RCT2_GLOBAL(0x00F64E35, uint8) = 1; + rct_ride* ride = GET_RIDE(vehicle->ride); + + switch (ride->mode) { + case RIDE_MODE_SWING: + case RIDE_MODE_ROTATION: + case RIDE_MODE_FORWARD_ROTATION: + case RIDE_MODE_BACKWARD_ROTATION: + case RIDE_MODE_FILM_AVENGING_AVIATORS: + case RIDE_MODE_FILM_THRILL_RIDERS: + case RIDE_MODE_BEGINNERS: + case RIDE_MODE_INTENSE: + case RIDE_MODE_BERSERK: + case RIDE_MODE_3D_FILM_MOUSE_TAILS: + case RIDE_MODE_3D_FILM_STORM_CHASERS: + case RIDE_MODE_3D_FILM_SPACE_RAIDERS: + case RIDE_MODE_CIRCUS_SHOW: + case RIDE_MODE_SPACE_RINGS: + case RIDE_MODE_HAUNTED_HOUSE: + case RIDE_MODE_CROOKED_HOUSE: + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_12; + vehicle->velocity = 0; + vehicle->acceleration = 0; + vehicle->status = VEHICLE_STATUS_UNLOADING_PASSENGERS; + vehicle->sub_state = 0; + vehicle_invalidate_window(vehicle); + return; + } + + if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN && + ride->breakdown_reason_pending == BREAKDOWN_BRAKES_FAILURE && + ride->inspection_station == vehicle->current_station && + ride->mechanic_status != RIDE_MECHANIC_STATUS_4) + RCT2_GLOBAL(0x00F64E35, uint8) = 0; + + rct_ride_type* rideEntry = GET_RIDE_ENTRY(vehicle->ride_subtype); + rct_ride_type_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type]; + + if (vehicle->sub_state == 0) { + if (ride->mode == RIDE_MODE_RACE && + ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) { + goto loc_6D8E36; + } + + if (vehicle->velocity <= 131940) { + vehicle->acceleration = 3298; + goto loc_6D8E36; + } + + sint32 velocity_diff = vehicle->velocity; + if (velocity_diff >= 1572864) + velocity_diff /= 8; + else + velocity_diff /= 16; + + if (RCT2_GLOBAL(0x00F64E35, uint8) == 0) { + goto loc_6D8E36; + } + + if (ride->num_circuits != 1) { + if (vehicle->num_laps + 1 < ride->num_circuits) { + goto loc_6D8E36; + } + } + vehicle->velocity -= velocity_diff; + vehicle->acceleration = 0; + } + else { + if (!(vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_3) && + vehicle->velocity >= -131940) { + vehicle->acceleration = -3298; + } + + if (vehicle->velocity >= -131940) { + goto loc_6D8E36; + } + + sint32 velocity_diff = vehicle->velocity; + if (velocity_diff < -1572864) + velocity_diff /= 8; + else + velocity_diff /= 16; + + if (RCT2_GLOBAL(0x00F64E35, uint8) == 0) { + goto loc_6D8E36; + } + + if (vehicle->num_laps + 1 < ride->num_circuits) { + goto loc_6D8E36; + } + + if (vehicle->num_laps + 1 != ride->num_circuits) { + vehicle->velocity -= velocity_diff; + vehicle->acceleration = 0; + goto loc_6D8E36; + } + + if (RideData4[ride->type].flags & RIDE_TYPE_FLAG4_ALLOW_MULTIPLE_CIRCUITS && + ride->mode != RIDE_MODE_SHUTTLE && + ride->mode != RIDE_MODE_POWERED_LAUNCH) { + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_12; + } + else{ + vehicle->velocity -= velocity_diff; + vehicle->acceleration = 0; + } + } + + uint32 flags; + loc_6D8E36: + flags = vehicle_update_track_motion(vehicle, NULL); + if (flags & (1 << 7) && + RCT2_GLOBAL(0x00F64E35, uint8) == 0) { + vehicle_update_collision_setup(vehicle); + return; + } + + if (flags & (1 << 0) && + RCT2_GLOBAL(0x00F64E35, uint8) == 0) { + vehicle->status = VEHICLE_STATUS_DEPARTING; + vehicle->sub_state = 1; + vehicle_invalidate_window(vehicle); + return; + } + + if (!(flags & ((1 << 0) | (1 << 1) | (1 << 5)))) { + if (vehicle->velocity > 98955) + vehicle->var_C0 = 0; + return; + } + + vehicle->var_C0++; + if (flags & (1 << 1) && + vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_14 && + vehicle->var_C0 < 40){ + return; + } + + rct_map_element* mapElement = map_get_track_element_at( + vehicle->track_x, + vehicle->track_y, + vehicle->track_z / 8 + ); + + vehicle->current_station = map_get_station(mapElement); + vehicle->num_laps++; + + if (vehicle->sub_state != 0) { + if (vehicle->num_laps < ride->num_circuits) { + vehicle->status = VEHICLE_STATUS_DEPARTING; + vehicle->sub_state = 1; + vehicle_invalidate_window(vehicle); + return; + } + + if (vehicle->num_laps == ride->num_circuits && + vehicle->update_flags & VEHICLE_UPDATE_FLAG_12) { + vehicle->status = VEHICLE_STATUS_DEPARTING; + vehicle->sub_state = 1; + vehicle_invalidate_window(vehicle); + return; + } + } + + if (ride->num_circuits != 1 && + vehicle->num_laps < ride->num_circuits) { + vehicle->status = VEHICLE_STATUS_DEPARTING; + vehicle->sub_state = 1; + vehicle_invalidate_window(vehicle); + return; + } + + if ((ride->mode == RIDE_MODE_UPWARD_LAUNCH || + ride->mode == RIDE_MODE_DOWNWARD_LAUNCH) && + vehicle->var_CE < 2) { + audio_play_sound_at_location( + SOUND_RIDE_LAUNCH_2, + vehicle->x, + vehicle->y, + vehicle->z); + vehicle->velocity = 0; + vehicle->acceleration = 0; + vehicle->status = VEHICLE_STATUS_DEPARTING; + vehicle->sub_state = 1; + vehicle_invalidate_window(vehicle); + return; + } + + if (ride->mode == RIDE_MODE_RACE && + ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) { + vehicle->status = VEHICLE_STATUS_DEPARTING; + vehicle->sub_state = 1; + vehicle_invalidate_window(vehicle); + return; + } + + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_12; + vehicle->velocity = 0; + vehicle->acceleration = 0; + vehicle->status = VEHICLE_STATUS_UNLOADING_PASSENGERS; + vehicle->sub_state = 0; + vehicle_invalidate_window(vehicle); +} + +/** + * + * rct2: 0x006D9002 + */ +static void vehicle_update_unloading_passengers(rct_vehicle* vehicle) { + if (vehicle->sub_state == 0) { + if (!vehicle_open_restraints(vehicle)) { + vehicle->sub_state = 1; + } + } + + rct_ride* ride = GET_RIDE(vehicle->ride); + if (ride->mode == RIDE_MODE_FORWARD_ROTATION || + ride->mode == RIDE_MODE_BACKWARD_ROTATION) { + uint8 seat = ((-vehicle->var_1F) >> 3) & 0xF; + if (vehicle->restraints_position == 255 && + (vehicle->peep[seat * 2] != 0xFFFF)) { + vehicle->next_free_seat -= 2; + + rct_peep* peep = GET_PEEP(vehicle->peep[seat * 2]); + vehicle->peep[seat * 2] = 0xFFFF; + + peep_decrement_num_riders(peep); + peep->sub_state = 7; + peep->state = PEEP_STATE_LEAVING_RIDE; + peep_window_state_update(peep); + + peep = GET_PEEP(vehicle->peep[seat * 2 + 1]); + vehicle->peep[seat * 2 + 1] = 0xFFFF; + + peep_decrement_num_riders(peep); + peep->sub_state = 7; + peep->state = PEEP_STATE_LEAVING_RIDE; + peep_window_state_update(peep); + } + } + else { + if (ride->exits[vehicle->current_station] == 0xFFFF) { + if (vehicle->sub_state != 1) + return; + + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) && + vehicle->update_flags & VEHICLE_UPDATE_FLAG_TESTING && + ride->current_test_segment + 1 >= ride->num_stations) { + vehicle_update_test_finish(vehicle); + } + vehicle->status = VEHICLE_STATUS_MOVING_TO_END_OF_STATION; + vehicle->sub_state = 0; + vehicle_invalidate_window(vehicle); + return; + } + + uint16 spriteId = vehicle->sprite_index; + for (rct_vehicle* train; spriteId != 0xFFFF; spriteId = train->next_vehicle_on_train) { + train = GET_VEHICLE(spriteId); + if (train->restraints_position != 255) + continue; + + if (train->next_free_seat == 0) + continue; + + train->next_free_seat = 0; + for (uint8 peepIndex = 0; peepIndex < train->num_peeps; peepIndex++) { + rct_peep* peep = GET_PEEP(train->peep[peepIndex]); + peep_decrement_num_riders(peep); + peep->sub_state = 7; + peep->state = PEEP_STATE_LEAVING_RIDE; + peep_window_state_update(peep); + } + } + } + + if (vehicle->sub_state != 1) + return; + + uint16 spriteId = vehicle->sprite_index; + for (rct_vehicle* train; spriteId != 0xFFFF; spriteId = train->next_vehicle_on_train) { + train = GET_VEHICLE(spriteId); + if (train->num_peeps != train->next_free_seat) + return; + } + + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) && + vehicle->update_flags & VEHICLE_UPDATE_FLAG_TESTING && + ride->current_test_segment + 1 >= ride->num_stations) { + vehicle_update_test_finish(vehicle); + } + vehicle->status = VEHICLE_STATUS_MOVING_TO_END_OF_STATION; + vehicle->sub_state = 0; + vehicle_invalidate_window(vehicle); +} + +/** + * + * rct2: 0x006D9CE9 + */ +static void vehicle_update_waiting_for_cable_lift(rct_vehicle *vehicle) +{ + rct_ride* ride = GET_RIDE(vehicle->ride); + + rct_vehicle* cableLift = GET_VEHICLE(ride->cable_lift); + + if (cableLift->status != VEHICLE_STATUS_WAITING_FOR_PASSENGERS) + return; + + cableLift->status = VEHICLE_STATUS_WAITING_TO_DEPART; + cableLift->var_C0 = vehicle->sprite_index; +} + +/** + * + * rct2: 0x006D9D21 + */ +static void vehicle_update_travelling_cable_lift(rct_vehicle* vehicle) { + rct_ride* ride = GET_RIDE(vehicle->ride); + + if (vehicle->sub_state == 0) { + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_BROKEN_TRAIN) { + if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) + return; + + ride->lifecycle_flags |= RIDE_LIFECYCLE_BROKEN_DOWN; + ride_breakdown_add_news_item(vehicle->ride); + ride->window_invalidate_flags |= + RIDE_INVALIDATE_RIDE_MAIN | + RIDE_INVALIDATE_RIDE_LIST | + RIDE_INVALIDATE_RIDE_MAINTENANCE; + + ride->mechanic_status = RIDE_MECHANIC_STATUS_CALLING; + ride->inspection_station = vehicle->current_station; + ride->breakdown_reason = ride->breakdown_reason_pending; + vehicle->velocity = 0; + return; + } + + vehicle->sub_state = 1; + vehicle_peep_easteregg_here_we_are(vehicle); + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)) { + if (!(vehicle->update_flags & VEHICLE_UPDATE_FLAG_TESTING)) { + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TEST_IN_PROGRESS)) { + vehicle_test_reset(vehicle); + } + else { + uint8 currentSegment = ride->current_test_segment + 1; + if (currentSegment >= ride->num_stations) { + vehicle_update_test_finish(vehicle); + } + else { + ride->current_test_segment = currentSegment; + ride->var_1F6 = vehicle->current_station; + } + } + } + } + } + + rct_ride_type* rideEntry = GET_RIDE_ENTRY(vehicle->ride_subtype); + rct_ride_type_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type]; + + if (vehicle->velocity <= 439800) { + vehicle->acceleration = 4398; + } + int flags = vehicle_update_track_motion(vehicle, NULL); + + if (flags & (1 << 11)) { + vehicle->status = VEHICLE_STATUS_TRAVELLING; + vehicle_invalidate_window(vehicle); + vehicle->sub_state = 1; + vehicle->lost_time_out = 0; + return; + } + + if (vehicle->sub_state == 2) + return; + + if (flags & (1 << 3) && vehicle->current_station == RCT2_GLOBAL(0x00F64E1C, uint8)) + return; + + vehicle->sub_state = 2; + + if (ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED || + ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED) + return; + + // This is slightly different to the vanilla function + ride->station_depart[vehicle->current_station] &= STATION_DEPART_FLAG; + uint8 waitingTime = 3; + if (ride->depart_flags & RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH) { + waitingTime = max(ride->min_waiting_time, 3); + waitingTime = min(waitingTime, 127); + } + + ride->station_depart[vehicle->current_station] |= waitingTime; +} + +/** + * + * rct2: 0x006D9820 + */ +static void vehicle_update_travelling_boat(rct_vehicle* vehicle) +{ + vehicle_check_if_missing(vehicle); + vehicle_update_motion_boat_hire(vehicle); +} + +static void loc_6DA9F9(rct_vehicle *vehicle, int x, int y, int bx, int dx) +{ + vehicle->remaining_distance = 0; + if (!vehicle_update_motion_collision_detection(vehicle, x, y, vehicle->z, NULL)) { + vehicle->track_x = bx; + vehicle->track_y = dx; + + rct_map_element *mapElement = map_get_track_element_at( + vehicle->track_x, + vehicle->track_y, + vehicle->track_z >> 3 + ); + + rct_ride *ride = GET_RIDE(vehicle->ride); + vehicle->track_type = + (mapElement->properties.track.type << 2) | + (ride->boat_hire_return_direction & 3); + + vehicle->track_progress = 0; + vehicle->status = VEHICLE_STATUS_TRAVELLING; + unk_F64E20->x = x; + unk_F64E20->y = y; + } +} + +/** + * + * rct2: 0x006DA717 + */ +static void vehicle_update_motion_boat_hire(rct_vehicle *vehicle) +{ + // RCT2_CALLPROC_X(0x006DA717, 0, 0, 0, 0, (int)vehicle, 0, 0); + RCT2_GLOBAL(0x00F64E18, uint32) = 0; + vehicle->velocity += vehicle->acceleration; + RCT2_GLOBAL(0x00F64E08, sint32) = vehicle->velocity; + RCT2_GLOBAL(0x00F64E0C, sint32) = (vehicle->velocity >> 10) * 42; + + rct_ride_type_vehicle *vehicleEntry = vehicle_get_vehicle_entry(vehicle); + if (vehicleEntry->flags_b & (VEHICLE_ENTRY_FLAG_B_7 | VEHICLE_ENTRY_FLAG_B_8)) { + sub_6D63D4(vehicle); + } + + RCT2_GLOBAL(0x00F64E10, uint32) = 1; + vehicle->acceleration = 0; + vehicle->remaining_distance += RCT2_GLOBAL(0x00F64E0C, sint32); + if (vehicle->remaining_distance >= 0x368A) { + vehicle->var_B8 &= ~(1 << 1); + unk_F64E20->x = vehicle->x; + unk_F64E20->y = vehicle->y; + unk_F64E20->z = vehicle->z; + vehicle_invalidate(vehicle); + + for (;;) { + // loc_6DA7A5 + vehicle->var_35++; + int x = (vehicle->boat_location.x * 32) + 16; + int y = (vehicle->boat_location.y * 32) + 16; + int z; + uint8 bl; + + x -= vehicle->x; + if (x >= 0) { + y -= vehicle->y; + if (y < 0) { + // loc_6DA81A: + y = -y; + bl = 24; + if (y <= x * 4) { + bl = 16; + if (x <= y * 4) { + bl = 20; + } + } + } else { + bl = 8; + if (y <= x * 4) { + bl = 16; + if (x <= y * 4) { + bl = 12; + } + } + } + } else { + y -= vehicle->y; + if (y < 0) { + // loc_6DA83D: + x = -x; + y = -y; + bl = 24; + if (y <= x * 4) { + bl = 0; + if (x <= y * 4) { + bl = 28; + } + } + } else { + x = -x; + bl = 8; + if (y <= x * 4) { + bl = 0; + if (x <= y * 4) { + bl = 4; + } + } + } + } + + // loc_6DA861: + vehicle->var_34 = bl; + x += y; + if (x <= 12) { + sub_6DA280(vehicle); + } + + if (!(vehicle->var_35 & (1 << 0))) { + uint8 spriteDirection = vehicle->sprite_direction; + if (spriteDirection != vehicle->var_34) { + uint8 dl = (vehicle->var_34 + 16 - spriteDirection) & 0x1E; + if (dl >= 16) { + spriteDirection += 2; + if (dl > 24) { + vehicle->var_35--; + } + } else { + spriteDirection -= 2; + if (dl < 8) { + vehicle->var_35--; + } + } + + vehicle->sprite_direction = spriteDirection & 0x1E; + } + } + + int edi = (vehicle->sprite_direction | (vehicle->var_35 & 1)) & 0x1F; + x = vehicle->x + RCT2_ADDRESS(0x009A36C4, sint16)[edi * 4]; + y = vehicle->y + RCT2_ADDRESS(0x009A36C6, sint16)[edi * 4]; + z = vehicle->z; + if (vehicle_update_motion_collision_detection(vehicle, x, y, z, NULL)) { + vehicle->remaining_distance = 0; + if (vehicle->sprite_direction == vehicle->var_34) { + vehicle->sprite_direction ^= (1 << 4); + sub_6DA280(vehicle); + vehicle->sprite_direction ^= (1 << 4); + } + break; + } + + int bx = floor2(x, 32); + int dx = floor2(y, 32); + if (bx != vehicle->track_x || dx != vehicle->track_y) { + if (vehicle_is_boat_on_water(vehicle, x, y)) { + // loc_6DA939: + rct_ride *ride = GET_RIDE(vehicle->ride); + + bool do_loc_6DAA97 = false; + if (vehicle->sub_state != 1) { + do_loc_6DAA97 = true; + } else { + uint16 xy = (((dx >> 5) << 8) | (bx >> 5)); + if (xy != ride->boat_hire_return_position) { + do_loc_6DAA97 = true; + } + } + + // loc_6DAA97: + if (do_loc_6DAA97) { + vehicle->remaining_distance = 0; + if (vehicle->sprite_direction == vehicle->var_34) { + sub_6DA280(vehicle); + } + break; + } + + if (!(ride->boat_hire_return_direction & 1)) { + uint16 bp = y & 0x1F; + if (bp == 16) { + loc_6DA9F9(vehicle, x, y, bx, dx); + break; + } + if (bp <= 16) { + x = unk_F64E20->x; + y = unk_F64E20->y + 1; + } else { + x = unk_F64E20->x; + y = unk_F64E20->y - 1; + } + } else { + // loc_6DA9A2: + uint16 bp = x & 0x1F; + if (bp == 16) { + loc_6DA9F9(vehicle, x, y, bx, dx); + break; + } + if (bp <= 16) { + x = unk_F64E20->x + 1; + y = unk_F64E20->y; + } else { + x = unk_F64E20->x - 1; + y = unk_F64E20->y; + } + } + + // loc_6DA9D1: + vehicle->remaining_distance = 0; + if (!vehicle_update_motion_collision_detection(vehicle, x, y, vehicle->z, NULL)) { + unk_F64E20->x = x; + unk_F64E20->y = y; + } + break; + } + vehicle->track_x = bx; + vehicle->track_y = dx; + } + + vehicle->remaining_distance -= RCT2_ADDRESS(0x009A36C8, uint32)[edi * 2]; + unk_F64E20->x = x; + unk_F64E20->y = y; + if (vehicle->remaining_distance < 0x368A) { + break; + } + RCT2_GLOBAL(0x00F64E10, uint32)++; + } + + sprite_move( + unk_F64E20->x, + unk_F64E20->y, + unk_F64E20->z, + (rct_sprite*)vehicle + ); + vehicle_invalidate(vehicle); + } + +// loc_6DAAC9: + { + int edx = vehicle->velocity >> 8; + edx = (edx * edx); + if (vehicle->velocity < 0) { + edx = -edx; + } + edx >>= 5; + int eax = ((vehicle->velocity >> 1) + edx) / vehicle->friction; + int ecx = -eax; + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_3) { + eax = vehicle->speed << 14; + int ebx = (vehicle->speed * vehicle->friction) >> 2; + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_3) { + eax = -eax; + } + eax -= vehicle->velocity; + edx = vehicle->powered_acceleration * 2; + ecx += (eax * edx) / ebx; + } + vehicle->acceleration = ecx; + } + // eax = RCT2_GLOBAL(0x00F64E18, uint32); + // ebx = RCT2_GLOBAL(0x00F64E1C, uint32); +} + + /** + * + * rct2: 0x006DA280 + */ +static void sub_6DA280(rct_vehicle *vehicle) +{ + rct_ride* ride = GET_RIDE(vehicle->ride); + + rct_xy8 location = { + .x = (vehicle->x + RCT2_ADDRESS(0x00993CCC, sint16)[2 * (ride->boat_hire_return_direction & 3)]) / 32, + .y = (vehicle->y + RCT2_ADDRESS(0x00993CCE, sint16)[2 * (ride->boat_hire_return_direction & 3)]) / 32 + }; + + if (*((uint16*)&location) == ride->boat_hire_return_position) { + vehicle->sub_state = 1; + vehicle->boat_location = location; + return; + } + + vehicle->sub_state = 0; + uint8 curDirection = ((vehicle->sprite_direction + 19) >> 3) & 3; + uint8 randDirection = scenario_rand() & 3; + + rct_ride_type* rideEntry = GET_RIDE_ENTRY(vehicle->ride_subtype); + if (scenario_rand() & 1 && (!(rideEntry->flags & RIDE_ENTRY_FLAG_7) || vehicle->lost_time_out > 1920)) { + location = *((rct_xy8*)&ride->boat_hire_return_position); + rct_xy16 destLocation = { + .x = location.x * 32 - RCT2_ADDRESS(0x00993CCC, sint16)[2 * (ride->boat_hire_return_direction & 3)] + 16, + .y = location.y * 32 - RCT2_ADDRESS(0x00993CCE, sint16)[2 * (ride->boat_hire_return_direction & 3)] + 16 + }; + + destLocation.x -= vehicle->x; + destLocation.y -= vehicle->y; + + if (abs(destLocation.x) <= abs(destLocation.y)) { + randDirection = destLocation.y < 0 ? 3 : 1; + } else { + randDirection = destLocation.x < 0 ? 0 : 2; + } + } + + static const sint8 rotations[] = { 0, 1, -1, 2 }; + for (int i = 0; i < 4; i++) { + if (randDirection + rotations[i] == curDirection) { + continue; + } + + sint16 x = vehicle->track_x + RCT2_ADDRESS(0x00993CCC, sint16)[2 * (randDirection + rotations[i] & 3)]; + sint16 y = vehicle->track_y + RCT2_ADDRESS(0x00993CCE, sint16)[2 * (randDirection + rotations[i] & 3)]; + + if (vehicle_is_boat_on_water(vehicle, x, y)) { + continue; + } + vehicle->boat_location.x = x / 32; + vehicle->boat_location.y = y / 32; + return; + } + + sint16 x = vehicle->track_x + RCT2_ADDRESS(0x00993CCC, sint16)[2 * (curDirection & 3)]; + sint16 y = vehicle->track_y + RCT2_ADDRESS(0x00993CCE, sint16)[2 * (curDirection & 3)]; + vehicle->boat_location.x = x / 32; + vehicle->boat_location.y = y / 32; +} + + /** + * + * rct2: 0x006DA22A + */ +static bool vehicle_is_boat_on_water(rct_vehicle *vehicle, int x, int y) +{ + int z = vehicle->track_z >> 3; + rct_map_element *mapElement = map_get_first_element_at(x >> 5, y >> 5); + do { + if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_SURFACE) { + int waterZ = (mapElement->properties.surface.terrain & 0x1F) * 2; + if (z != waterZ) { + return true; + } + } else { + if (z > mapElement->base_height - 2 && z < mapElement->clearance_height + 2) { + return true; + } + } + } while (!map_element_is_last_for_tile(mapElement++)); + return false; +} + + /** + * + * rct2: 0x006D9249 + */ +static void vehicle_update_swinging(rct_vehicle* vehicle) { + rct_ride* ride = GET_RIDE(vehicle->ride); + rct_ride_type* rideEntry = GET_RIDE_ENTRY(vehicle->ride_subtype); + + // SubState for this ride means swinging state + // 0 == first swing + // 3 == full swing + uint8 swingState = vehicle->sub_state; + if (rideEntry->flags & RIDE_ENTRY_FLAG_ALTERNATIVE_SWING_MODE_1) { + swingState += 4; + if (rideEntry->flags & RIDE_ENTRY_FLAG_ALTERNATIVE_SWING_MODE_2) + swingState += 4; + } + uint8* edi = RCT2_ADDRESS(0x0099F9D0, uint8*)[swingState]; + uint8 al = edi[(uint16)(vehicle->current_time + 1)]; + + // 0x80 indicates that a complete swing has been + // completed and the next swing can start + if (al != 0x80) { + vehicle->current_time++; + if (al == vehicle->var_1F) + return; + // Used to know which sprite to draw + vehicle->var_1F = al; + vehicle_invalidate(vehicle); + return; + } + + vehicle->current_time = -1; + vehicle->var_CE++; + if (ride->status != RIDE_STATUS_CLOSED) { + // It takes 3 swings to get into full swing + // ride->rotations already takes this into account + if (vehicle->var_CE + 3 < ride->rotations) { + // Go to the next swing state until we + // are at full swing. + if (vehicle->sub_state != 3) { + vehicle->sub_state++; + } + vehicle_update_swinging(vehicle); + return; + } + } + + // To get to this part of the code the + // swing has to be in slowing down phase + if (vehicle->sub_state == 0) { + vehicle->status = VEHICLE_STATUS_ARRIVING; + vehicle_invalidate_window(vehicle); + vehicle->sub_state = 0; + vehicle->var_C0 = 0; + return; + } + // Go towards first swing state + vehicle->sub_state--; + vehicle_update_swinging(vehicle); +} + +/** + * + * rct2: 0x006D9413 + */ +static void vehicle_update_ferris_wheel_rotating(rct_vehicle* vehicle) { + if (RCT2_GLOBAL(0x00F64E34, uint8) == 0) + return; + + rct_ride* ride = GET_RIDE(vehicle->ride); + if ((vehicle->ferris_wheel_var_1 -= 1) != 0) + return; + + sint8 ferris_wheel_var_0 = vehicle->ferris_wheel_var_0; + + if (ferris_wheel_var_0 == 3) { + vehicle->ferris_wheel_var_0 = ferris_wheel_var_0; + vehicle->ferris_wheel_var_1 = ferris_wheel_var_0; + } + else if (ferris_wheel_var_0 < 3) { + if (ferris_wheel_var_0 != -8) + ferris_wheel_var_0--; + vehicle->ferris_wheel_var_0 = ferris_wheel_var_0; + vehicle->ferris_wheel_var_1 = -ferris_wheel_var_0; + } + else { + ferris_wheel_var_0--; + vehicle->ferris_wheel_var_0 = ferris_wheel_var_0; + vehicle->ferris_wheel_var_1 = ferris_wheel_var_0; + } + + uint8 rotation = vehicle->var_1F; + if (ride->mode & RIDE_MODE_FORWARD_ROTATION) + rotation++; + else + rotation--; + + rotation &= 0x7F; + vehicle->var_1F = rotation; + + if (rotation == vehicle->sub_state) + vehicle->var_CE++; + + vehicle_invalidate(vehicle); + + uint8 subState = vehicle->sub_state; + if (ride->mode & RIDE_MODE_FORWARD_ROTATION) + subState++; + else + subState--; + subState &= 0x7F; + + if (subState == vehicle->var_1F) { + bool shouldStop = true; + if (ride->status != RIDE_STATUS_CLOSED) { + if (vehicle->var_CE <= ride->rotations) + shouldStop = false; + } + + if (shouldStop) { + ferris_wheel_var_0 = vehicle->ferris_wheel_var_0; + vehicle->ferris_wheel_var_0 = -abs(ferris_wheel_var_0); + } + } + + if (vehicle->ferris_wheel_var_0 != -8) + return; + + subState = vehicle->sub_state; + if (ride->mode & RIDE_MODE_FORWARD_ROTATION) + subState += 8; + else + subState -= 8; + subState &= 0x7F; + + if (subState != vehicle->var_1F) + return; + + vehicle->status = VEHICLE_STATUS_ARRIVING; + vehicle_invalidate_window(vehicle); + vehicle->sub_state = 0; + vehicle->var_C0 = 0; +} + +/** + * + * rct2: 0x006D94F2 + */ +static void vehicle_update_simulator_operating(rct_vehicle* vehicle) { + if (RCT2_GLOBAL(0x00F64E34, uint8) == 0) + return; + + uint8* edi = RCT2_ADDRESS(0x009A042C, uint8*)[vehicle->sub_state]; + + uint8 al = edi[(uint16)(vehicle->current_time + 1)]; + if (al != 0xFF) { + vehicle->current_time++; + if (al == vehicle->var_1F) + return; + vehicle->var_1F = al; + vehicle_invalidate(vehicle); + return; + } + + vehicle->status = VEHICLE_STATUS_ARRIVING; + vehicle_invalidate_window(vehicle); + vehicle->sub_state = 0; + vehicle->var_C0 = 0; +} + +/** + * + * rct2: 0x006D92FF + */ +static void vehicle_update_rotating(rct_vehicle* vehicle) { + if (RCT2_GLOBAL(0x00F64E34, uint8) == 0) + return; + + rct_ride* ride = GET_RIDE(vehicle->ride); + rct_ride_type* rideEntry = GET_RIDE_ENTRY(vehicle->ride_subtype); + + uint8* edi; + if (rideEntry->flags & RIDE_ENTRY_FLAG_ALTERNATIVE_ROTATION_MODE_1) { + edi = RCT2_ADDRESS(0x0099F0F4, uint8*)[vehicle->sub_state]; + } + else if (rideEntry->flags & RIDE_ENTRY_FLAG_ALTERNATIVE_ROTATION_MODE_2) { + edi = RCT2_ADDRESS(0x009A2428, uint8*)[vehicle->sub_state]; + } + else { + edi = RCT2_ADDRESS(0x0099EB1C, uint8*)[vehicle->sub_state]; + } + + sint32 var_4C = (sint16)vehicle->current_time; + if (RCT2_GLOBAL(0x00F64E34, uint8) == BREAKDOWN_CONTROL_FAILURE) { + var_4C += (ride->var_1AC >> 6) + 1; + } + var_4C++; + + uint8 al = edi[(uint32)var_4C]; + if (al != 0xFF) { + vehicle->current_time = (uint16)var_4C; + if (al == vehicle->var_1F) + return; + vehicle->var_1F = al; + vehicle_invalidate(vehicle); + return; + } + + vehicle->current_time = -1; + vehicle->var_CE++; + if (RCT2_GLOBAL(0x00F64E34, uint8) != BREAKDOWN_CONTROL_FAILURE) { + bool shouldStop = true; + if (ride->status != RIDE_STATUS_CLOSED) { + al = vehicle->var_CE + 1; + if (ride->type == RIDE_TYPE_ENTERPRISE) + al += 9; + + if (al < ride->rotations) + shouldStop = false; + } + + if (shouldStop == true) { + if (vehicle->sub_state == 2) { + vehicle->status = VEHICLE_STATUS_ARRIVING; + vehicle_invalidate_window(vehicle); + vehicle->sub_state = 0; + vehicle->var_C0 = 0; + return; + } + vehicle->sub_state++; + vehicle_update_rotating(vehicle); + return; + } + } + + if (ride->type == RIDE_TYPE_ENTERPRISE && + vehicle->sub_state == 2) { + vehicle->status = VEHICLE_STATUS_ARRIVING; + vehicle_invalidate_window(vehicle); + vehicle->sub_state = 0; + vehicle->var_C0 = 0; + return; + } + + vehicle->sub_state = 1; + vehicle_update_rotating(vehicle); +} + +/** + * + * rct2: 0x006D97CB + */ +static void vehicle_update_space_rings_operating(rct_vehicle* vehicle) { + if (RCT2_GLOBAL(0x00F64E34, uint8) == 0) + return; + + uint8* edi = RCT2_ADDRESS(0x009A0ACC, uint8*)[vehicle->sub_state]; + + uint8 al = edi[(uint16)(vehicle->current_time + 1)]; + if (al != 0xFF) { + vehicle->current_time++; + if (al == vehicle->var_1F) + return; + vehicle->var_1F = al; + vehicle_invalidate(vehicle); + return; + } + + vehicle->status = VEHICLE_STATUS_ARRIVING; + vehicle_invalidate_window(vehicle); + vehicle->sub_state = 0; + vehicle->var_C0 = 0; +} + +/** + * + * rct2: 0x006D9641 + */ +static void vehicle_update_haunted_house_operating(rct_vehicle* vehicle) { + if (RCT2_GLOBAL(0x00F64E34, uint8) == 0) + return; + + if (vehicle->var_1F != 0) { + if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 1) { + vehicle->var_1F++; + vehicle_invalidate(vehicle); + + if (vehicle->var_1F == 19) + vehicle->var_1F = 0; + } + } + + uint16* edi = RCT2_ADDRESS(0x009A0ABC, uint16*)[vehicle->sub_state]; + + uint16 ax = *edi; + if ((uint16)(vehicle->current_time + 1) > ax) { + vehicle->status = VEHICLE_STATUS_ARRIVING; + vehicle_invalidate_window(vehicle); + vehicle->sub_state = 0; + vehicle->var_C0 = 0; + return; + } + + vehicle->current_time++; + switch (vehicle->current_time) { + case 45: + audio_play_sound_at_location( + SOUND_HAUNTED_HOUSE_SCARE, + vehicle->x, + vehicle->y, + vehicle->z); + break; + case 75: + vehicle->var_1F = 1; + vehicle_invalidate(vehicle); + break; + case 400: + audio_play_sound_at_location( + SOUND_HAUNTED_HOUSE_SCREAM_1, + vehicle->x, + vehicle->y, + vehicle->z); + break; + case 745: + audio_play_sound_at_location( + SOUND_HAUNTED_HOUSE_SCARE, + vehicle->x, + vehicle->y, + vehicle->z); + break; + case 775: + vehicle->var_1F = 1; + vehicle_invalidate(vehicle); + break; + case 1100: + audio_play_sound_at_location( + SOUND_HAUNTED_HOUSE_SCREAM_2, + vehicle->x, + vehicle->y, + vehicle->z); + break; + } +} + +/** + * + * rct2: 0x006d9781 + */ +static void vehicle_update_crooked_house_operating(rct_vehicle* vehicle) { + if (RCT2_GLOBAL(0x00F64E34, uint8) == 0) + return; + + if ((uint16)(vehicle->current_time + 1) > RideCrookedHouseLength[vehicle->sub_state]) { + vehicle->status = VEHICLE_STATUS_ARRIVING; + vehicle_invalidate_window(vehicle); + vehicle->sub_state = 0; + vehicle->var_C0 = 0; + return; + } + + vehicle->current_time++; +} + +/** + * + * rct2: 0x006D9547 + */ +static void vehicle_update_top_spin_operating(rct_vehicle* vehicle) { + if (RCT2_GLOBAL(0x00F64E34, uint8) == 0) + return; + + uint8* edi = RCT2_ADDRESS(0x009A12E0, uint8*)[vehicle->sub_state]; + + uint8 al = edi[(vehicle->current_time + 1) * 2]; + if (al != 0xFF) { + vehicle->current_time = vehicle->current_time + 1; + if (al != vehicle->var_1F) { + vehicle->var_1F = al; + vehicle_invalidate(vehicle); + } + al = edi[vehicle->current_time * 2 + 1]; + if (al != vehicle->var_20) { + vehicle->var_20 = al; + vehicle_invalidate(vehicle); + } + return; + } + + vehicle->status = VEHICLE_STATUS_ARRIVING; + vehicle_invalidate_window(vehicle); + vehicle->sub_state = 0; + vehicle->var_C0 = 0; +} + +/** + * + * rct2: 0x006D95AD + */ +static void vehicle_update_showing_film(rct_vehicle *vehicle) +{ + int currentTime, totalTime; + + if (RCT2_GLOBAL(0x00F64E34, uint8) == 0) + return; + + totalTime = RideFilmLength[vehicle->sub_state]; + currentTime = vehicle->current_time + 1; + if (currentTime <= totalTime) { + vehicle->current_time = currentTime; + } + else { + vehicle->status = VEHICLE_STATUS_ARRIVING; + vehicle_invalidate_window(vehicle); + vehicle->sub_state = 0; + vehicle->var_C0 = 0; + } +} + +/** + * + * rct2: 0x006D95F7 + */ +static void vehicle_update_doing_circus_show(rct_vehicle *vehicle) +{ + int currentTime, totalTime; + + if (RCT2_GLOBAL(0x00F64E34, uint8) == 0) + return; + + totalTime = *(RCT2_ADDRESS(0x009A0AB4, uint16*)[vehicle->sub_state]); + currentTime = vehicle->current_time + 1; + if (currentTime <= totalTime) { + vehicle->current_time = currentTime; + } else { + vehicle->status = VEHICLE_STATUS_ARRIVING; + vehicle_invalidate_window(vehicle); + vehicle->sub_state = 0; + vehicle->var_C0 = 0; + } +} + +/** + * + * rct2: 0x0068B8BD + * @returns the map element that the vehicle will collide with or NULL if no collisions. + */ +static rct_map_element* vehicle_check_collision(sint16 x, sint16 y, sint16 z) { + rct_map_element* mapElement = map_get_first_element_at(x / 32, y / 32); + if (mapElement == NULL) + // Can't return null as that implies no collision, + // but should still cause a crash when dereferenced. + return (rct_map_element *) -1; + + uint8 bl; + if ((x & 0x1F) >= 16) { + bl = 1; + if ((y & 0x1F) < 16) + bl = 2; + } + else { + bl = 4; + if ((y & 0x1F) >= 16) + bl = 8; + } + + do { + if (z / 8 < mapElement->base_height) + continue; + + if (z / 8 >= mapElement->clearance_height) + continue; + + if (mapElement->flags & bl) + return mapElement; + } while (!map_element_is_last_for_tile(mapElement++)); + + return NULL; +} + +/** + * + * rct2: 0x006DE6C6 + */ +static void vehicle_kill_all_passengers(rct_vehicle* vehicle) { + uint16 numFatalities = 0; + + uint16 spriteId = vehicle->sprite_index; + for (rct_vehicle* curVehicle; spriteId != 0xFFFF; spriteId = curVehicle->next_vehicle_on_train) { + curVehicle = GET_VEHICLE(spriteId); + numFatalities += curVehicle->num_peeps; + } + + rct_ride* ride = GET_RIDE(vehicle->ride); + RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint16) = numFatalities; + + uint8 crashType = numFatalities == 0 ? + RIDE_CRASH_TYPE_NO_FATALITIES : + RIDE_CRASH_TYPE_FATALITIES; + + if (crashType >= ride->last_crash_type) + ride->last_crash_type = crashType; + + if (numFatalities != 0) { + if (!(RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_SIX_FLAGS_DEPRECATED)) { + RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, uint16) = ride->name; + RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 4, uint32) = ride->name_arguments; + + news_item_add_to_queue(NEWS_ITEM_RIDE, 2219, vehicle->ride); + } + + if (RCT2_GLOBAL(0x135882E, uint16) < 500) { + RCT2_GLOBAL(0x135882E, uint16) += 200; + } + } + + spriteId = vehicle->sprite_index; + for (rct_vehicle* curVehicle; spriteId != 0xFFFF; spriteId = curVehicle->next_vehicle_on_train) { + curVehicle = GET_VEHICLE(spriteId); + + if (curVehicle->num_peeps != curVehicle->next_free_seat) + continue; + + if (curVehicle->num_peeps == 0) + continue; + + for (uint8 i = 0; i < curVehicle->num_peeps; i++) { + rct_peep* peep = GET_PEEP(curVehicle->peep[i]); + if (peep->outside_of_park == 0) { + RCT2_GLOBAL(RCT2_ADDRESS_GUESTS_IN_PARK, uint16)--; + RCT2_GLOBAL(RCT2_ADDRESS_BTM_TOOLBAR_DIRTY_FLAGS, uint16) |= + BTM_TB_DIRTY_FLAG_PEEP_COUNT; + } + ride->num_riders--; + peep_sprite_remove(peep); + } + + curVehicle->num_peeps = 0; + curVehicle->next_free_seat = 0; + } +} + +static void vehicle_crash_on_land(rct_vehicle* vehicle) { + vehicle->status = VEHICLE_STATUS_CRASHED; + vehicle_invalidate_window(vehicle); + + rct_ride* ride = GET_RIDE(vehicle->ride); + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) { + + rct_vehicle* frontVehicle = vehicle; + while (frontVehicle->is_child != 0)frontVehicle = GET_VEHICLE(frontVehicle->prev_vehicle_on_ride); + + uint8 trainIndex = 0; + while (ride->vehicles[trainIndex] != frontVehicle->sprite_index) + trainIndex++; + + ride_crash(vehicle->ride, trainIndex); + + if (ride->status != RIDE_STATUS_CLOSED) { + ride_set_status(vehicle->ride, RIDE_STATUS_CLOSED); + } + } + ride->lifecycle_flags |= RIDE_LIFECYCLE_CRASHED; + ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; + + if (vehicle->is_child == 0) { + vehicle_kill_all_passengers(vehicle); + } + + vehicle->sub_state = 2; + audio_play_sound_at_location(SOUND_CRASH, vehicle->x, vehicle->y, vehicle->z); + + sprite_misc_3_create(vehicle->x, vehicle->y, vehicle->z); + sprite_misc_5_create(vehicle->x, vehicle->y, vehicle->z); + + uint8 numParticles = min(vehicle->sprite_width, 7); + + while (numParticles-- != 0) + crashed_vehicle_particle_create(vehicle->colours, vehicle->x, vehicle->y, vehicle->z); + + vehicle->var_0C |= (1 << 7); + vehicle->var_C5 = 0; + vehicle->var_C8 = 0; + vehicle->sprite_width = 13; + vehicle->sprite_height_negative = 45; + vehicle->sprite_height_positive = 5; + + sprite_move(vehicle->x, vehicle->y, vehicle->z, (rct_sprite*)vehicle); + vehicle_invalidate(vehicle); + + vehicle->var_4E = 0; +} + +static void vehicle_crash_on_water(rct_vehicle* vehicle) { + vehicle->status = VEHICLE_STATUS_CRASHED; + vehicle_invalidate_window(vehicle); + + rct_ride* ride = GET_RIDE(vehicle->ride); + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) { + + rct_vehicle* frontVehicle = vehicle; + while (frontVehicle->is_child != 0)frontVehicle = GET_VEHICLE(frontVehicle->prev_vehicle_on_ride); + + uint8 trainIndex = 0; + while (ride->vehicles[trainIndex] != frontVehicle->sprite_index) + trainIndex++; + + ride_crash(vehicle->ride, trainIndex); + + if (ride->status != RIDE_STATUS_CLOSED) { + ride_set_status(vehicle->ride, RIDE_STATUS_CLOSED); + } + } + ride->lifecycle_flags |= RIDE_LIFECYCLE_CRASHED; + ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; + + if (vehicle->is_child == 0) { + vehicle_kill_all_passengers(vehicle); + } + + vehicle->sub_state = 2; + audio_play_sound_at_location(SOUND_WATER_1, vehicle->x, vehicle->y, vehicle->z); + + crash_splash_create(vehicle->x, vehicle->y, vehicle->z); + crash_splash_create(vehicle->x - 8, vehicle->y - 9, vehicle->z); + crash_splash_create(vehicle->x + 11, vehicle->y - 9, vehicle->z); + crash_splash_create(vehicle->x + 11, vehicle->y + 8, vehicle->z); + crash_splash_create(vehicle->x - 4, vehicle->y + 8, vehicle->z); + + for (int i = 0; i < 10; ++i) + crashed_vehicle_particle_create(vehicle->colours, vehicle->x - 4, vehicle->y + 8, vehicle->z); + + vehicle->var_0C |= (1 << 7); + vehicle->var_C5 = 0; + vehicle->var_C8 = 0; + vehicle->sprite_width = 13; + vehicle->sprite_height_negative = 45; + vehicle->sprite_height_positive = 5; + + sprite_move(vehicle->x, vehicle->y, vehicle->z, (rct_sprite*)vehicle); + vehicle_invalidate(vehicle); + + vehicle->var_4E = 0xFFFF; +} + +/** + * + * rct2: 0x006D98CA + */ +static void vehicle_update_crash(rct_vehicle *vehicle){ + uint16 spriteId = vehicle->sprite_index; + rct_vehicle* curVehicle; + do { + curVehicle = GET_VEHICLE(spriteId); + if (curVehicle->sub_state > 1) { + if (curVehicle->var_4E <= 96) { + curVehicle->var_4E++; + if ((scenario_rand() & 0xFFFF) <= 0x1555) { + sprite_misc_3_create( + curVehicle->x + (scenario_rand() & 2 - 1), + curVehicle->y + (scenario_rand() & 2 - 1), + curVehicle->z + ); + } + } + if (curVehicle->var_C8 + 7281 > 0xFFFF) { + curVehicle->var_C5++; + if (curVehicle->var_C5 >= 8) + curVehicle->var_C5 = 0; + invalidate_sprite_2((rct_sprite*)curVehicle); + } + curVehicle->var_C8 += 7281; + continue; + } + + rct_map_element* collideElement = vehicle_check_collision(curVehicle->x, curVehicle->y, curVehicle->z); + if (collideElement == NULL) { + curVehicle->sub_state = 1; + } + else if (curVehicle->sub_state == 1){ + vehicle_crash_on_land(curVehicle); + continue; + } + + int z = map_element_height(curVehicle->x, curVehicle->y); + sint16 waterHeight = (z >> 16) & 0xFFFF; + z = (sint16)(z & 0xFFFF); + sint16 zDiff; + if (waterHeight != 0) { + zDiff = curVehicle->z - waterHeight; + if (zDiff <= 0 && zDiff >= -20) { + vehicle_crash_on_water(curVehicle); + continue; + } + } + + zDiff = curVehicle->z - z; + if ((zDiff <= 0 && zDiff >= -20) || curVehicle->z < 16){ + vehicle_crash_on_land(curVehicle); + continue; + } + + invalidate_sprite_2((rct_sprite*)curVehicle); + + rct_xyz16 curPosition = { + .x = curVehicle->x, + .y = curVehicle->y, + .z = curVehicle->z + }; + + curPosition.x += (sint8)(curVehicle->var_B6 >> 8); + curPosition.y += (sint8)(curVehicle->var_C0 >> 8); + curPosition.z += (sint8)(curVehicle->var_4E >> 8); + curVehicle->track_x += (sint16)(curVehicle->var_B6 << 8); + curVehicle->track_y += (sint16)(curVehicle->var_C0 << 8); + curVehicle->track_z += (sint16)(curVehicle->var_4E << 8); + + if (curPosition.x > 0x1FFF || + curPosition.y > 0x1FFF) { + vehicle_crash_on_land(curVehicle); + continue; + } + + sprite_move(curPosition.x, curPosition.y, curPosition.z, (rct_sprite*)curVehicle); + invalidate_sprite_2((rct_sprite*)curVehicle); + + if (curVehicle->sub_state == 1) { + curVehicle->var_4E += 0xFFEC; + } + } while ((spriteId = curVehicle->next_vehicle_on_train) != 0xFFFF); +} +/** + * + * rct2: 0x006D7888 + */ +static void vehicle_update_sound(rct_vehicle *vehicle) +{ + rct_ride *ride; + rct_ride_type *rideEntry; + // bl should be set before hand + uint8 bl = 255, dl = 255; + // bh screamVolume should be set before hand + uint8 screamId, screamVolume = 255; + uint16 soundIdVolume; + + ride = GET_RIDE(vehicle->ride); + rideEntry = GET_RIDE_ENTRY(vehicle->ride_subtype); + + rct_ride_type_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type]; + + int ecx = abs(vehicle->velocity) - 0x10000; + if (ecx >= 0) { + dl = vehicleEntry->var_57; + ecx >>= 15; + bl = min(208 + ecx & 0xFF, 255); + } + + switch (vehicleEntry->sound_range) { + case 3: + screamId = vehicle->scream_sound_id; + if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 0x7F)) { + if (vehicle->velocity < 0x40000 || vehicle->scream_sound_id != 255) + goto loc_6D7A97; + + if ((scenario_rand() & 0xFFFF) <= 0x5555) { + vehicle->scream_sound_id = SOUND_TRAIN_WHISTLE; + screamVolume = 255; + break; + } + } + if (screamId == NO_SCREAM) screamId = 255; + screamVolume = 255; + break; + + case 4: + screamId = vehicle->scream_sound_id; + if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 0x7F)) { + if (vehicle->velocity < 0x40000 || vehicle->scream_sound_id != 255) + goto loc_6D7A97; + + if ((scenario_rand() & 0xFFFF) <= 0x5555) { + vehicle->scream_sound_id = SOUND_TRAM; + screamVolume = 255; + break; + } + } + if (screamId == NO_SCREAM) screamId = 255; + screamVolume = 255; + break; + + default: + if ((vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_4)) { + screamId = vehicle_update_scream_sound(vehicle); + if (screamId == NO_SCREAM) + screamId = 255; + if (screamId == 255) + goto loc_6D7A97; + break; + } + + loc_6D7A97: + vehicle->scream_sound_id = 255; + assert(ride->type < countof(RideLiftData)); + // Get lift hill sound + screamId = RideLiftData[ride->type].sound_id; + screamVolume = 243; + if (!(vehicle->var_B8 & 2)) + screamId = 255; + } + + // Friction sound + soundIdVolume = sub_6D7AC0(vehicle->sound1_id, vehicle->sound1_volume, dl, bl); + vehicle->sound1_id = soundIdVolume & 0xFF; + vehicle->sound1_volume = (soundIdVolume >> 8) & 0xFF; + + // Scream sound + soundIdVolume = sub_6D7AC0(vehicle->sound2_id, vehicle->sound2_volume, screamId, screamVolume); + vehicle->sound2_id = soundIdVolume & 0xFF; + vehicle->sound2_volume = (soundIdVolume >> 8) & 0xFF; + + { + int ebx = RCT2_ADDRESS(0x009A3684, sint16)[vehicle->sprite_direction]; + int eax = ((vehicle->velocity >> 14) * ebx) >> 14; + eax = clamp(-127, eax, 127); + + vehicle->var_BF = eax & 0xFF; + } +} + +/** + * + * rct2: 0x006D796B + */ +static int vehicle_update_scream_sound(rct_vehicle *vehicle) +{ + uint32 r; + uint16 spriteIndex; + rct_ride_type *rideEntry; + rct_vehicle *vehicle2; + + rideEntry = GET_RIDE_ENTRY(vehicle->ride_subtype); + + rct_ride_type_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type]; + + int totalNumPeeps = vehicle_get_total_num_peeps(vehicle); + if (totalNumPeeps == 0) + return 255; + + if (vehicle->velocity < 0) { + if (vehicle->velocity > -0x2C000) + return 255; + + spriteIndex = vehicle->sprite_index; + do { + vehicle2 = &(g_sprite_list[spriteIndex].vehicle); + if (vehicle2->var_1F < 1) + continue; + if (vehicle2->var_1F <= 4) + goto produceScream; + if (vehicle2->var_1F < 9) + continue; + if (vehicle2->var_1F <= 15) + goto produceScream; + } while ((spriteIndex = vehicle2->next_vehicle_on_train) != SPRITE_INDEX_NULL); + return 255; + } + + if (vehicle->velocity < 0x2C000) + return 255; + + spriteIndex = vehicle->sprite_index; + do { + vehicle2 = &(g_sprite_list[spriteIndex].vehicle); + if (vehicle2->var_1F < 5) + continue; + if (vehicle2->var_1F <= 8) + goto produceScream; + if (vehicle2->var_1F < 17) + continue; + if (vehicle2->var_1F <= 23) + goto produceScream; + } while ((spriteIndex = vehicle2->next_vehicle_on_train) != SPRITE_INDEX_NULL); + return 255; + +produceScream: + if (vehicle->scream_sound_id == 255) { + r = scenario_rand(); + if (totalNumPeeps >= (int)(r % 16)) { + switch (vehicleEntry->sound_range) { + case 0: + vehicle->scream_sound_id = byte_9A3A14[r % 2]; + break; + case 1: + vehicle->scream_sound_id = byte_9A3A18[r % 7]; + break; + case 2: + vehicle->scream_sound_id = byte_9A3A16[r % 2]; + break; + default: + vehicle->scream_sound_id = NO_SCREAM; + break; + } + } else { + vehicle->scream_sound_id = NO_SCREAM; + } + } + return vehicle->scream_sound_id; +} + +/** + * + * rct2: 0x006D73D0 + * ax: verticalG + * dx: lateralG + * esi: vehicle + */ +void vehicle_get_g_forces(rct_vehicle *vehicle, int *verticalG, int *lateralG) +{ + int gForceVert = (((sint64)0x280000) * RCT2_ADDRESS(0x009A37E4, sint32)[vehicle->var_1F]) >> 32; + gForceVert = (((sint64)gForceVert) * RCT2_ADDRESS(0x009A39C4, sint32)[vehicle->var_20]) >> 32; + int lateralFactor = 0, vertFactor = 0; + + // Note shr has meant some of the below functions cast a known negative number to + // unsigned. Possibly an original bug but will be left implemented. + switch (vehicle->track_type >> 2) { + case TRACK_ELEM_FLAT: + case TRACK_ELEM_END_STATION: + case TRACK_ELEM_BEGIN_STATION: + case TRACK_ELEM_MIDDLE_STATION: + case TRACK_ELEM_25_DEG_UP: + case TRACK_ELEM_60_DEG_UP: // + case TRACK_ELEM_25_DEG_DOWN: + case TRACK_ELEM_60_DEG_DOWN: // + case TRACK_ELEM_FLAT_TO_LEFT_BANK: + case TRACK_ELEM_FLAT_TO_RIGHT_BANK: + case TRACK_ELEM_LEFT_BANK_TO_FLAT: + case TRACK_ELEM_RIGHT_BANK_TO_FLAT:// + case TRACK_ELEM_LEFT_BANK: + case TRACK_ELEM_RIGHT_BANK: + case TRACK_ELEM_TOWER_BASE: + case TRACK_ELEM_TOWER_SECTION: + case TRACK_ELEM_FLAT_COVERED: + case TRACK_ELEM_25_DEG_UP_COVERED: + case TRACK_ELEM_60_DEG_UP_COVERED: + case TRACK_ELEM_25_DEG_DOWN_COVERED: + case TRACK_ELEM_60_DEG_DOWN_COVERED: + case TRACK_ELEM_BRAKES: + case TRACK_ELEM_ROTATION_CONTROL_TOGGLE: + case TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP: + case TRACK_ELEM_25_DEG_UP_LEFT_BANKED: + case TRACK_ELEM_25_DEG_UP_RIGHT_BANKED: + case TRACK_ELEM_WATERFALL: + case TRACK_ELEM_RAPIDS: + case TRACK_ELEM_ON_RIDE_PHOTO: + case TRACK_ELEM_25_DEG_DOWN_LEFT_BANKED: + case TRACK_ELEM_25_DEG_DOWN_RIGHT_BANKED: + case TRACK_ELEM_WHIRLPOOL: + case TRACK_ELEM_REVERSE_WHOA_BELLY_VERTICAL: + case TRACK_ELEM_90_DEG_UP: + case TRACK_ELEM_90_DEG_DOWN: + case TRACK_ELEM_DIAG_FLAT: + case TRACK_ELEM_DIAG_25_DEG_UP: + case TRACK_ELEM_DIAG_60_DEG_UP: + case TRACK_ELEM_DIAG_25_DEG_DOWN: + case TRACK_ELEM_DIAG_60_DEG_DOWN: + case TRACK_ELEM_DIAG_FLAT_TO_LEFT_BANK: + case TRACK_ELEM_DIAG_FLAT_TO_RIGHT_BANK: + case TRACK_ELEM_DIAG_LEFT_BANK_TO_FLAT: + case TRACK_ELEM_DIAG_RIGHT_BANK_TO_FLAT: + case TRACK_ELEM_DIAG_LEFT_BANK: + case TRACK_ELEM_DIAG_RIGHT_BANK: + case TRACK_ELEM_LOG_FLUME_REVERSER: + case TRACK_ELEM_SPINNING_TUNNEL: + case TRACK_ELEM_POWERED_LIFT: + case TRACK_ELEM_MINI_GOLF_HOLE_A: + case TRACK_ELEM_MINI_GOLF_HOLE_B: + case TRACK_ELEM_MINI_GOLF_HOLE_C: + case TRACK_ELEM_MINI_GOLF_HOLE_D: + case TRACK_ELEM_MINI_GOLF_HOLE_E: + case TRACK_ELEM_LEFT_REVERSER: + case TRACK_ELEM_RIGHT_REVERSER: + case TRACK_ELEM_AIR_THRUST_VERTICAL_DOWN: + case TRACK_ELEM_BLOCK_BRAKES: + case TRACK_ELEM_25_DEG_UP_TO_LEFT_BANKED_25_DEG_UP: + case TRACK_ELEM_25_DEG_UP_TO_RIGHT_BANKED_25_DEG_UP: + case TRACK_ELEM_LEFT_BANKED_25_DEG_UP_TO_25_DEG_UP: + case TRACK_ELEM_RIGHT_BANKED_25_DEG_UP_TO_25_DEG_UP: + case TRACK_ELEM_25_DEG_DOWN_TO_LEFT_BANKED_25_DEG_DOWN: + case TRACK_ELEM_25_DEG_DOWN_TO_RIGHT_BANKED_25_DEG_DOWN: + case TRACK_ELEM_LEFT_BANKED_25_DEG_DOWN_TO_25_DEG_DOWN: + case TRACK_ELEM_RIGHT_BANKED_25_DEG_DOWN_TO_25_DEG_DOWN: + case TRACK_ELEM_LEFT_QUARTER_TURN_1_TILE_90_DEG_UP: + case TRACK_ELEM_RIGHT_QUARTER_TURN_1_TILE_90_DEG_UP: + case TRACK_ELEM_LEFT_QUARTER_TURN_1_TILE_90_DEG_DOWN: + case TRACK_ELEM_RIGHT_QUARTER_TURN_1_TILE_90_DEG_DOWN: + //6d73FF + // Do nothing + break; + case TRACK_ELEM_FLAT_TO_25_DEG_UP:// + case TRACK_ELEM_25_DEG_DOWN_TO_FLAT:// + case TRACK_ELEM_LEFT_BANK_TO_25_DEG_UP: + case TRACK_ELEM_RIGHT_BANK_TO_25_DEG_UP: + case TRACK_ELEM_25_DEG_DOWN_TO_LEFT_BANK: + case TRACK_ELEM_25_DEG_DOWN_TO_RIGHT_BANK: + case TRACK_ELEM_FLAT_TO_25_DEG_UP_COVERED: + case TRACK_ELEM_25_DEG_DOWN_TO_FLAT_COVERED: + case TRACK_ELEM_LEFT_BANKED_FLAT_TO_LEFT_BANKED_25_DEG_UP: + case TRACK_ELEM_RIGHT_BANKED_FLAT_TO_RIGHT_BANKED_25_DEG_UP: + case TRACK_ELEM_LEFT_BANKED_25_DEG_DOWN_TO_LEFT_BANKED_FLAT: + case TRACK_ELEM_RIGHT_BANKED_25_DEG_DOWN_TO_RIGHT_BANKED_FLAT: + case TRACK_ELEM_FLAT_TO_LEFT_BANKED_25_DEG_UP: + case TRACK_ELEM_FLAT_TO_RIGHT_BANKED_25_DEG_UP: + case TRACK_ELEM_LEFT_BANKED_25_DEG_DOWN_TO_FLAT: + case TRACK_ELEM_RIGHT_BANKED_25_DEG_DOWN_TO_FLAT: + vertFactor = 103; + //6d7509 + break; + case TRACK_ELEM_25_DEG_UP_TO_FLAT:// + case TRACK_ELEM_FLAT_TO_25_DEG_DOWN:// + case TRACK_ELEM_25_DEG_UP_TO_LEFT_BANK: + case TRACK_ELEM_25_DEG_UP_TO_RIGHT_BANK: + case TRACK_ELEM_LEFT_BANK_TO_25_DEG_DOWN: + case TRACK_ELEM_RIGHT_BANK_TO_25_DEG_DOWN: + case TRACK_ELEM_25_DEG_UP_TO_FLAT_COVERED: + case TRACK_ELEM_FLAT_TO_25_DEG_DOWN_COVERED: + case TRACK_ELEM_CABLE_LIFT_HILL: + case TRACK_ELEM_LEFT_BANKED_25_DEG_UP_TO_LEFT_BANKED_FLAT: + case TRACK_ELEM_RIGHT_BANKED_25_DEG_UP_TO_RIGHT_BANKED_FLAT: + case TRACK_ELEM_LEFT_BANKED_FLAT_TO_LEFT_BANKED_25_DEG_DOWN: + case TRACK_ELEM_RIGHT_BANKED_FLAT_TO_RIGHT_BANKED_25_DEG_DOWN: + case TRACK_ELEM_LEFT_BANKED_25_DEG_UP_TO_FLAT: + case TRACK_ELEM_RIGHT_BANKED_25_DEG_UP_TO_FLAT: + case TRACK_ELEM_FLAT_TO_LEFT_BANKED_25_DEG_DOWN: + case TRACK_ELEM_FLAT_TO_RIGHT_BANKED_25_DEG_DOWN: + vertFactor = -103; + //6d7569 + break; + case TRACK_ELEM_25_DEG_UP_TO_60_DEG_UP:// + case TRACK_ELEM_60_DEG_DOWN_TO_25_DEG_DOWN:// + case TRACK_ELEM_25_DEG_UP_TO_60_DEG_UP_COVERED: + case TRACK_ELEM_60_DEG_DOWN_TO_25_DEG_DOWN_COVERED: + vertFactor = 82; + //6d7545 + break; + case TRACK_ELEM_60_DEG_UP_TO_25_DEG_UP:// + case TRACK_ELEM_25_DEG_DOWN_TO_60_DEG_DOWN:// + case TRACK_ELEM_60_DEG_UP_TO_25_DEG_UP_COVERED: + case TRACK_ELEM_25_DEG_DOWN_TO_60_DEG_DOWN_COVERED: + vertFactor = -82; + //6d7551 + break; + case TRACK_ELEM_LEFT_QUARTER_TURN_5_TILES:// + case TRACK_ELEM_LEFT_QUARTER_TURN_5_TILES_25_DEG_UP: + case TRACK_ELEM_LEFT_QUARTER_TURN_5_TILES_25_DEG_DOWN: + case TRACK_ELEM_LEFT_TWIST_DOWN_TO_UP: + case TRACK_ELEM_LEFT_TWIST_UP_TO_DOWN: + case TRACK_ELEM_LEFT_QUARTER_TURN_5_TILES_COVERED: + case TRACK_ELEM_LEFT_QUARTER_HELIX_LARGE_UP: + case TRACK_ELEM_LEFT_QUARTER_HELIX_LARGE_DOWN: + case TRACK_ELEM_LEFT_FLYER_TWIST_UP: + case TRACK_ELEM_LEFT_FLYER_TWIST_DOWN: + case TRACK_ELEM_LEFT_HEARTLINE_ROLL: + lateralFactor = 98; + //6d7590 + break; + case TRACK_ELEM_RIGHT_QUARTER_TURN_5_TILES:// + case TRACK_ELEM_RIGHT_QUARTER_TURN_5_TILES_25_DEG_UP: + case TRACK_ELEM_RIGHT_QUARTER_TURN_5_TILES_25_DEG_DOWN: + case TRACK_ELEM_RIGHT_TWIST_DOWN_TO_UP: + case TRACK_ELEM_RIGHT_TWIST_UP_TO_DOWN: + case TRACK_ELEM_RIGHT_QUARTER_TURN_5_TILES_COVERED: + case TRACK_ELEM_RIGHT_QUARTER_HELIX_LARGE_UP: + case TRACK_ELEM_RIGHT_QUARTER_HELIX_LARGE_DOWN: + case TRACK_ELEM_RIGHT_FLYER_TWIST_UP: + case TRACK_ELEM_RIGHT_FLYER_TWIST_DOWN: + case TRACK_ELEM_RIGHT_HEARTLINE_ROLL: + lateralFactor = -98; + //6d75B7 + break; + case TRACK_ELEM_BANKED_LEFT_QUARTER_TURN_5_TILES: + case TRACK_ELEM_LEFT_HALF_BANKED_HELIX_UP_LARGE: + case TRACK_ELEM_LEFT_HALF_BANKED_HELIX_DOWN_LARGE: + case TRACK_ELEM_LEFT_QUARTER_BANKED_HELIX_LARGE_UP: + case TRACK_ELEM_LEFT_QUARTER_BANKED_HELIX_LARGE_DOWN: + vertFactor = 200; + lateralFactor = 160; + //6d75E1 + break; + case TRACK_ELEM_BANKED_RIGHT_QUARTER_TURN_5_TILES: + case TRACK_ELEM_RIGHT_HALF_BANKED_HELIX_UP_LARGE: + case TRACK_ELEM_RIGHT_HALF_BANKED_HELIX_DOWN_LARGE: + case TRACK_ELEM_RIGHT_QUARTER_BANKED_HELIX_LARGE_UP: + case TRACK_ELEM_RIGHT_QUARTER_BANKED_HELIX_LARGE_DOWN: + vertFactor = 200; + lateralFactor = -160; + //6d75F0 + break; + case TRACK_ELEM_S_BEND_LEFT: + case TRACK_ELEM_S_BEND_LEFT_COVERED: + lateralFactor = (vehicle->track_progress < 48) ? 98 : -98; + //6d75FF + break; + case TRACK_ELEM_S_BEND_RIGHT: + case TRACK_ELEM_S_BEND_RIGHT_COVERED: + lateralFactor = (vehicle->track_progress < 48) ? -98 : 98; + //6d7608 + break; + case TRACK_ELEM_LEFT_VERTICAL_LOOP: + case TRACK_ELEM_RIGHT_VERTICAL_LOOP: + vertFactor = (abs(vehicle->track_progress - 155) / 2) + 28; + //6d7690 + break; + case TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES: + case TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES_25_DEG_UP: + case TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES_25_DEG_DOWN: + case TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES_COVERED: + case TRACK_ELEM_LEFT_CURVED_LIFT_HILL: + lateralFactor = 59; + //6d7704 + break; + case TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES: + case TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES_25_DEG_UP: + case TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES_25_DEG_DOWN: + case TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES_COVERED: + case TRACK_ELEM_RIGHT_CURVED_LIFT_HILL: + lateralFactor = -59; + //6d7710 + break; + case TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES_BANK: + case TRACK_ELEM_LEFT_HALF_BANKED_HELIX_UP_SMALL: + case TRACK_ELEM_LEFT_HALF_BANKED_HELIX_DOWN_SMALL: + vertFactor = 100; + lateralFactor = 100; + //6d7782 + break; + case TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES_BANK: + case TRACK_ELEM_RIGHT_HALF_BANKED_HELIX_UP_SMALL: + case TRACK_ELEM_RIGHT_HALF_BANKED_HELIX_DOWN_SMALL: + vertFactor = 100; + lateralFactor = -100; + //6d778E + break; + case TRACK_ELEM_LEFT_QUARTER_TURN_1_TILE: + lateralFactor = 45; + //6d779A + break; + case TRACK_ELEM_RIGHT_QUARTER_TURN_1_TILE: + lateralFactor = -45; + //6d77A3 + break; + case TRACK_ELEM_HALF_LOOP_UP: + case TRACK_ELEM_FLYER_HALF_LOOP_UP: + vertFactor = (((uint16)(-(vehicle->track_progress - 155))) / 2) + 28; + //6d763E + break; + case TRACK_ELEM_HALF_LOOP_DOWN: + case TRACK_ELEM_FLYER_HALF_LOOP_DOWN: + vertFactor = (vehicle->track_progress / 2) + 28; + //6d7656 + break; + case TRACK_ELEM_LEFT_CORKSCREW_UP: + case TRACK_ELEM_RIGHT_CORKSCREW_DOWN: + case TRACK_ELEM_LEFT_FLYER_CORKSCREW_UP: + case TRACK_ELEM_RIGHT_FLYER_CORKSCREW_DOWN: + vertFactor = 52; + lateralFactor = 70; + //6d76AA + break; + case TRACK_ELEM_RIGHT_CORKSCREW_UP: + case TRACK_ELEM_LEFT_CORKSCREW_DOWN: + case TRACK_ELEM_RIGHT_FLYER_CORKSCREW_UP: + case TRACK_ELEM_LEFT_FLYER_CORKSCREW_DOWN: + vertFactor = 52; + lateralFactor = -70; + //6d76B9 + break; + case TRACK_ELEM_FLAT_TO_60_DEG_UP: + case TRACK_ELEM_60_DEG_DOWN_TO_FLAT: + vertFactor = 56; + //6d747C + break; + case TRACK_ELEM_60_DEG_UP_TO_FLAT: + case TRACK_ELEM_FLAT_TO_60_DEG_DOWN: + case TRACK_ELEM_BRAKE_FOR_DROP: + vertFactor = -56; + //6d7488 + break; + case TRACK_ELEM_LEFT_QUARTER_TURN_1_TILE_60_DEG_UP: + case TRACK_ELEM_LEFT_QUARTER_TURN_1_TILE_60_DEG_DOWN: + lateralFactor = 88; + //6d7770 + break; + case TRACK_ELEM_RIGHT_QUARTER_TURN_1_TILE_60_DEG_UP: + case TRACK_ELEM_RIGHT_QUARTER_TURN_1_TILE_60_DEG_DOWN: + lateralFactor = -88; + //6d7779 + break; + case TRACK_ELEM_WATER_SPLASH: + vertFactor = -150; + if (vehicle->track_progress < 32) + break; + vertFactor = 150; + if (vehicle->track_progress < 64) + break; + vertFactor = 0; + if (vehicle->track_progress < 96) + break; + vertFactor = 150; + if (vehicle->track_progress < 128) + break; + vertFactor = -150; + //6d7408 + break; + case TRACK_ELEM_FLAT_TO_60_DEG_UP_LONG_BASE: + case TRACK_ELEM_FLAT_TO_60_DEG_DOWN_LONG_BASE: + vertFactor = 160; + //6d74F1 + break; + case TRACK_ELEM_60_DEG_UP_TO_FLAT_LONG_BASE: + case TRACK_ELEM_60_DEG_UP_TO_FLAT_LONG_BASE_122: + vertFactor = -160; + //6d74FD + break; + case TRACK_ELEM_REVERSE_WHOA_BELLY_SLOPE: + case TRACK_ELEM_AIR_THRUST_VERTICAL_DOWN_TO_LEVEL: + vertFactor = 120; + //6d7458 + break; + case TRACK_ELEM_60_DEG_UP_TO_90_DEG_UP: + case TRACK_ELEM_90_DEG_DOWN_TO_60_DEG_DOWN: + vertFactor = 110; + //6d7515 + break; + case TRACK_ELEM_90_DEG_UP_TO_60_DEG_UP: + case TRACK_ELEM_60_DEG_DOWN_TO_90_DEG_DOWN: + vertFactor = -110; + //6d7521 + break; + case TRACK_ELEM_LEFT_EIGHTH_TO_DIAG: + case TRACK_ELEM_LEFT_EIGHTH_TO_ORTHOGONAL: + lateralFactor = 137; + //6d7575 + break; + case TRACK_ELEM_RIGHT_EIGHTH_TO_DIAG: + case TRACK_ELEM_RIGHT_EIGHTH_TO_ORTHOGONAL: + lateralFactor = -137; + //6d759C + break; + case TRACK_ELEM_LEFT_EIGHTH_BANK_TO_DIAG: + case TRACK_ELEM_LEFT_EIGHTH_BANK_TO_ORTHOGONAL: + vertFactor = 270; + lateralFactor = 200; + //6d75C3 + break; + case TRACK_ELEM_RIGHT_EIGHTH_BANK_TO_DIAG: + case TRACK_ELEM_RIGHT_EIGHTH_BANK_TO_ORTHOGONAL: + vertFactor = 270; + lateralFactor = -200; + //6d75D2 + break; + case TRACK_ELEM_DIAG_FLAT_TO_25_DEG_UP: + case TRACK_ELEM_DIAG_25_DEG_DOWN_TO_FLAT: + case TRACK_ELEM_DIAG_LEFT_BANK_TO_25_DEG_UP: + case TRACK_ELEM_DIAG_RIGHT_BANK_TO_25_DEG_UP: + case TRACK_ELEM_DIAG_25_DEG_DOWN_TO_LEFT_BANK: + case TRACK_ELEM_DIAG_25_DEG_DOWN_TO_RIGHT_BANK: + vertFactor = 113; + //6d7494 + break; + case TRACK_ELEM_DIAG_25_DEG_UP_TO_FLAT: + case TRACK_ELEM_DIAG_FLAT_TO_25_DEG_DOWN: + case TRACK_ELEM_DIAG_25_DEG_UP_TO_LEFT_BANK: + case TRACK_ELEM_DIAG_25_DEG_UP_TO_RIGHT_BANK: + case TRACK_ELEM_DIAG_LEFT_BANK_TO_25_DEG_DOWN: + case TRACK_ELEM_DIAG_RIGHT_BANK_TO_25_DEG_DOWN: + vertFactor = -113; + //6d755D + break; + case TRACK_ELEM_DIAG_25_DEG_UP_TO_60_DEG_UP: + case TRACK_ELEM_DIAG_60_DEG_DOWN_TO_25_DEG_DOWN: + vertFactor = 95; + //6D752D + break; + case TRACK_ELEM_DIAG_60_DEG_UP_TO_25_DEG_UP: + case TRACK_ELEM_DIAG_25_DEG_DOWN_TO_60_DEG_DOWN: + vertFactor = -95; + //6D7539 + break; + case TRACK_ELEM_DIAG_FLAT_TO_60_DEG_UP: + case TRACK_ELEM_DIAG_60_DEG_DOWN_TO_FLAT: + vertFactor = 60; + //6D7464 + break; + case TRACK_ELEM_DIAG_60_DEG_UP_TO_FLAT: + case TRACK_ELEM_DIAG_FLAT_TO_60_DEG_DOWN: + vertFactor = -60; + //6d7470 + break; + case TRACK_ELEM_LEFT_BARREL_ROLL_UP_TO_DOWN: + case TRACK_ELEM_LEFT_BARREL_ROLL_DOWN_TO_UP: + vertFactor = 170; + lateralFactor = 115; + //6d7581 + break; + case TRACK_ELEM_RIGHT_BARREL_ROLL_UP_TO_DOWN: + case TRACK_ELEM_RIGHT_BARREL_ROLL_DOWN_TO_UP: + vertFactor = 170; + lateralFactor = -115; + //6d75A8 + break; + case TRACK_ELEM_LEFT_BANK_TO_LEFT_QUARTER_TURN_3_TILES_25_DEG_UP: + vertFactor = -(vehicle->track_progress / 2) + 134; + lateralFactor = 90; + //6d771C + break; + case TRACK_ELEM_RIGHT_BANK_TO_RIGHT_QUARTER_TURN_3_TILES_25_DEG_UP: + vertFactor = -(vehicle->track_progress / 2) + 134; + lateralFactor = -90; + //6D7746 + break; + case TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES_25_DEG_DOWN_TO_LEFT_BANK: + vertFactor = -(vehicle->track_progress / 2) + 134; + lateralFactor = 90; + //6D7731 identical to 6d771c + break; + case TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES_25_DEG_DOWN_TO_RIGHT_BANK: + vertFactor = -(vehicle->track_progress / 2) + 134; + lateralFactor = -90; + //6D775B identical to 6d7746 + break; + case TRACK_ELEM_LEFT_LARGE_HALF_LOOP_UP: + case TRACK_ELEM_RIGHT_LARGE_HALF_LOOP_UP: + vertFactor = (((uint16)(-(vehicle->track_progress - 311))) / 4) + 46; + //6d7666 + break; + case TRACK_ELEM_RIGHT_LARGE_HALF_LOOP_DOWN: + case TRACK_ELEM_LEFT_LARGE_HALF_LOOP_DOWN: + vertFactor = (vehicle->track_progress / 4) + 46; + //6d767F + break; + case TRACK_ELEM_HEARTLINE_TRANSFER_UP: + vertFactor = 103; + if (vehicle->track_progress < 32) + break; + vertFactor = -103; + if (vehicle->track_progress < 64) + break; + vertFactor = 0; + if (vehicle->track_progress < 96) + break; + vertFactor = 103; + if (vehicle->track_progress < 128) + break; + vertFactor = -103; + //6d74A0 + break; + case TRACK_ELEM_HEARTLINE_TRANSFER_DOWN: + vertFactor = -103; + if (vehicle->track_progress < 32) + break; + vertFactor = 103; + if (vehicle->track_progress < 64) + break; + vertFactor = 0; + if (vehicle->track_progress < 96) + break; + vertFactor = -103; + if (vehicle->track_progress < 128) + break; + vertFactor = 103; + //6D74CA + break; + case TRACK_ELEM_MULTIDIM_INVERTED_FLAT_TO_90_DEG_QUARTER_LOOP_DOWN: + case TRACK_ELEM_INVERTED_FLAT_TO_90_DEG_QUARTER_LOOP_DOWN: + case TRACK_ELEM_MULTIDIM_FLAT_TO_90_DEG_DOWN_QUARTER_LOOP: + vertFactor = (vehicle->track_progress / 4) + 55; + //6d762D + break; + case TRACK_ELEM_90_DEG_TO_INVERTED_FLAT_QUARTER_LOOP_UP: + case TRACK_ELEM_MULTIDIM_90_DEG_UP_TO_INVERTED_FLAT_QUARTER_LOOP: + case 255: + vertFactor = (((uint16)(-(vehicle->track_progress - 137))) / 4) + 55; + //6D7614 + break; + case TRACK_ELEM_AIR_THRUST_TOP_CAP: + vertFactor = -60; + //6D744C + break; + case TRACK_ELEM_LEFT_BANKED_QUARTER_TURN_3_TILE_25_DEG_UP: + case TRACK_ELEM_LEFT_BANKED_QUARTER_TURN_3_TILE_25_DEG_DOWN: + vertFactor = 200; + lateralFactor = 100; + //6d76C8 + break; + case TRACK_ELEM_RIGHT_BANKED_QUARTER_TURN_3_TILE_25_DEG_UP: + case TRACK_ELEM_RIGHT_BANKED_QUARTER_TURN_3_TILE_25_DEG_DOWN: + vertFactor = 200; + lateralFactor = -100; + //6d76d7 + break; + case TRACK_ELEM_LEFT_BANKED_QUARTER_TURN_5_TILE_25_DEG_UP: + case TRACK_ELEM_LEFT_BANKED_QUARTER_TURN_5_TILE_25_DEG_DOWN: + vertFactor = 200; + lateralFactor = 160; + //6D76E6 + break; + case TRACK_ELEM_RIGHT_BANKED_QUARTER_TURN_5_TILE_25_DEG_UP: + case TRACK_ELEM_RIGHT_BANKED_QUARTER_TURN_5_TILE_25_DEG_DOWN: + vertFactor = 200; + lateralFactor = -160; + //6d76F5 + break; + } + + int gForceLateral = 0; + + if (vertFactor != 0) { + gForceVert += abs(vehicle->velocity) * 98 / vertFactor; + } + + if (lateralFactor != 0) { + gForceLateral += abs(vehicle->velocity) * 98 / lateralFactor; + } + + gForceVert *= 10; + gForceLateral *= 10; + gForceVert >>= 16; + gForceLateral >>= 16; + + // Call original version so we can test if our result is the same as the original + int eax, ebx, ecx, edx, esi, edi, ebp; + esi = (int)vehicle; + RCT2_CALLFUNC_X(0x006D73D0, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + if (gForceVert != (sint16)(eax & 0xFFFF)) + assert(gForceVert == (sint16)(eax & 0xFFFF)); + if (gForceLateral != (sint16)(edx & 0xFFFF)) + assert(gForceLateral == (sint16)(edx & 0xFFFF)); + + if (verticalG != NULL) *verticalG = (sint16)(gForceVert & 0xFFFF); + if (lateralG != NULL) *lateralG = (sint16)(gForceLateral & 0xFFFF); +} + +void vehicle_set_map_toolbar(rct_vehicle *vehicle) +{ + rct_ride *ride; + int vehicleIndex; + + ride = GET_RIDE(vehicle->ride); + + while (vehicle->is_child) { + vehicle = GET_VEHICLE(vehicle->prev_vehicle_on_ride); + } + + for (vehicleIndex = 0; vehicleIndex < 32; vehicleIndex++) + if (ride->vehicles[vehicleIndex] == vehicle->sprite_index) + break; + + RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 0, uint16) = 2215; + RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 2, uint16) = 1165; + RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 4, uint16) = ride->name; + RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 6, uint32) = ride->name_arguments; + RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 10, uint16) = RideNameConvention[ride->type].vehicle_name + 2; + RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 12, uint16) = vehicleIndex + 1; + + int arg0, arg1; + ride_get_status(vehicle->ride, &arg0, &arg1); + RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 14, uint16) = (uint16)arg0; + RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 16, uint32) = (uint16)arg1; +} + +rct_vehicle *vehicle_get_head(rct_vehicle *vehicle) +{ + rct_vehicle *prevVehicle; + + for (;;) { + prevVehicle = GET_VEHICLE(vehicle->prev_vehicle_on_ride); + if (prevVehicle->next_vehicle_on_train == SPRITE_INDEX_NULL) + break; + + vehicle = prevVehicle; + } + + return vehicle; +} + +rct_vehicle *vehicle_get_tail(rct_vehicle *vehicle) +{ + uint16 spriteIndex; + + while ((spriteIndex = vehicle->next_vehicle_on_train) != SPRITE_INDEX_NULL) { + vehicle = GET_VEHICLE(spriteIndex); + } + return vehicle; +} + +int vehicle_is_used_in_pairs(rct_vehicle *vehicle) +{ + return vehicle->num_seats & VEHICLE_SEAT_PAIR_FLAG; +} + +/** + * + * rct2: 0x006DA44E + */ +static int vehicle_update_motion_bumper_car(rct_vehicle* vehicle) { + RCT2_GLOBAL(0x00F64E18, uint32) = 0; + rct_ride* ride = GET_RIDE(vehicle->ride); + + sint32 nextVelocity = vehicle->velocity + vehicle->acceleration; + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN) && + ride->breakdown_reason_pending == BREAKDOWN_SAFETY_CUT_OUT) { + nextVelocity = 0; + } + vehicle->velocity = nextVelocity; + + RCT2_GLOBAL(0x00F64E08, sint32) = nextVelocity; + RCT2_GLOBAL(0x00F64E0C, sint32) = (nextVelocity / 1024) * 42; + RCT2_GLOBAL(0x00F64E10, uint32) = 1; + + vehicle->acceleration = 0; + if (!(ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN)) || + ride->breakdown_reason_pending != BREAKDOWN_SAFETY_CUT_OUT) { + if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 1 && + vehicle->var_34 != 0) { + + if (vehicle->var_34 > 0) { + vehicle->var_34--; + vehicle->sprite_direction += 2; + } + else { + vehicle->var_34++; + vehicle->sprite_direction -= 2; + } + vehicle->sprite_direction &= 0x1E; + vehicle_invalidate(vehicle); + } + else if ((scenario_rand() & 0xFFFF) <= 2849) { + if (vehicle->var_35 & (1 << 6)) + vehicle->sprite_direction -= 2; + else + vehicle->sprite_direction += 2; + vehicle->sprite_direction &= 0x1E; + vehicle_invalidate(vehicle); + } + } + + uint16 collideSprite = 0xFFFF; + + if (vehicle->var_C4 != 0) { + uint8 oldC4 = vehicle->var_C4 & 0x1E; + vehicle->var_C4 = 0; + + rct_xyz16 location = { + .x = vehicle->x, + .y = vehicle->y, + .z = vehicle->z + }; + + location.x += RCT2_ADDRESS(0x009A36C4, sint16)[oldC4 * 4]; + location.y += RCT2_ADDRESS(0x009A36C6, sint16)[oldC4 * 4]; + location.x += RCT2_ADDRESS(0x009A36CC, sint16)[oldC4 * 4]; + location.y += RCT2_ADDRESS(0x009A36CE, sint16)[oldC4 * 4]; + + if (!vehicle_update_bumper_car_collision(vehicle, location.x, location.y, &collideSprite)) { + vehicle_invalidate(vehicle); + sprite_move( + location.x, + location.y, + location.z, + (rct_sprite*)vehicle + ); + vehicle_invalidate(vehicle); + } + } + + vehicle->remaining_distance += RCT2_GLOBAL(0x00F64E0C, sint32); + + if (vehicle->remaining_distance >= 13962) { + vehicle->var_B8 &= ~(1 << 1); + rct_xyz16 *unk_F64E20 = RCT2_ADDRESS(0x00F64E20, rct_xyz16); + unk_F64E20->x = vehicle->x; + unk_F64E20->y = vehicle->y; + unk_F64E20->z = vehicle->z; + + vehicle_invalidate(vehicle); + + while (1) { + vehicle->var_35++; + uint8 direction = vehicle->sprite_direction; + direction |= vehicle->var_35 & 1; + + rct_xyz16 location = *unk_F64E20; + location.x += RCT2_ADDRESS(0x009A36C4, sint16)[direction * 4]; + location.y += RCT2_ADDRESS(0x009A36C6, sint16)[direction * 4]; + + if (vehicle_update_bumper_car_collision(vehicle, location.x, location.y, &collideSprite)) + break; + + vehicle->remaining_distance -= RCT2_ADDRESS(0x009A36C8, sint16)[direction * 4]; + unk_F64E20->x = location.x; + unk_F64E20->y = location.y; + if (vehicle->remaining_distance < 13962) { + break; + } + RCT2_GLOBAL(0x00F64E10, uint32)++; + } + + if (vehicle->remaining_distance >= 13962) { + sint32 oldVelocity = vehicle->velocity; + vehicle->remaining_distance = 0; + vehicle->velocity = 0; + uint8 direction = vehicle->sprite_direction | 1; + + if (collideSprite != 0xFFFF) { + vehicle->var_34 = scenario_rand() & 1 ? 1 : -1; + + if (oldVelocity >= 131072) { + rct_vehicle* collideVehicle = GET_VEHICLE(collideSprite); + collideVehicle->var_C4 = direction; + vehicle->var_C4 = direction ^ (1 << 4); + } + } + else { + vehicle->var_34 = scenario_rand() & 1 ? 6 : -6; + + if (oldVelocity >= 131072) { + vehicle->var_C4 = direction ^ (1 << 4); + } + } + } + + sprite_move( + unk_F64E20->x, + unk_F64E20->y, + unk_F64E20->z, + (rct_sprite*)vehicle + ); + vehicle_invalidate(vehicle); + } + + sint32 eax = vehicle->velocity / 2; + sint32 edx = vehicle->velocity >> 8; + edx *= edx; + if (vehicle->velocity < 0) + edx = -edx; + edx >>= 5; + eax += edx; + eax /= vehicle->friction; + rct_ride_type* rideEntry = GET_RIDE_ENTRY(vehicle->ride_subtype); + rct_ride_type_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type]; + + if (!(vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_3)) { + vehicle->acceleration = -eax; + return RCT2_GLOBAL(0x00F64E18, uint32); + } + + sint32 ebx = (vehicle->speed * vehicle->friction) >> 2; + sint32 _eax = vehicle->speed << 14; + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_3) { + _eax = -_eax; + } + _eax -= vehicle->velocity; + _eax *= vehicle->powered_acceleration * 2; + _eax /= ebx; + + vehicle->acceleration = _eax - eax; + return RCT2_GLOBAL(0x00F64E18, uint32); +} + + + +/** + * + * rct2: 0x006DD365 + */ +static bool vehicle_update_bumper_car_collision(rct_vehicle *vehicle, sint16 x, sint16 y, uint16 *spriteId) +{ + uint16 bp = (vehicle->var_44 * 30) >> 9; + uint32 trackType = vehicle->track_type >> 2; + + sint16 rideLeft = vehicle->track_x + RCT2_ADDRESS(0x0099E228, uint8)[trackType * 4]; + sint16 rideRight = vehicle->track_x + RCT2_ADDRESS(0x0099E22A, uint8)[trackType * 4]; + sint16 rideTop = vehicle->track_y + RCT2_ADDRESS(0x0099E229, uint8)[trackType * 4]; + sint16 rideBottom = vehicle->track_y + RCT2_ADDRESS(0x0099E22B, uint8)[trackType * 4]; + + if (x - bp < rideLeft || + y - bp < rideTop || + x + bp > rideRight || + y + bp > rideBottom) { + if (spriteId != NULL) + *spriteId = 0xFFFF; + return true; + } + + uint16 location = (y / 32) | ((x / 32) << 8); + + + uint8 rideIndex = vehicle->ride; + for (sint32* ebp = RCT2_ADDRESS(0x009A37C4, sint32); ebp <= RCT2_ADDRESS(0x009A37E4, sint32); ebp++) { + uint16 spriteIdx = RCT2_ADDRESS(0xF1EF60, uint16)[location]; + for (rct_vehicle* vehicle2 = GET_VEHICLE(spriteIdx); spriteIdx != 0xFFFF; spriteIdx = vehicle2->next_in_quadrant) { + vehicle2 = GET_VEHICLE(spriteIdx); + + if (vehicle2 == vehicle) + continue; + + if (vehicle2->sprite_identifier != SPRITE_IDENTIFIER_VEHICLE) + continue; + + if (vehicle2->ride != rideIndex) + continue; + + int distX = abs(x - vehicle2->x); + if (distX > 32768) + continue; + + int distY = abs(y - vehicle2->y); + if (distY > 32768) + continue; + + int ecx = (vehicle->var_44 + vehicle2->var_44) / 2; + ecx *= 30; + ecx >>= 8; + if (max(distX, distY) < ecx) { + if (spriteId != NULL) + *spriteId = vehicle2->sprite_index; + return true; + } + } + location += *ebp; + } + + return false; +} + +// rct2: 0x009A2970 +const sint32 *dword_9A2970 = (sint32*)0x009A2970; + +/** + * + * rct2: 0x006DAB90 + */ +static void vehicle_update_track_motion_up_stop_check(rct_vehicle *vehicle) +{ + rct_ride_type_vehicle *vehicleEntry = vehicle_get_vehicle_entry(vehicle); + int verticalG, lateralG; + + // No up stops (coaster types) + if (vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_1) { + int trackType = vehicle->track_type >> 2; + if (!track_element_is_covered(trackType)) { + vehicle_get_g_forces(vehicle, &verticalG, &lateralG); + lateralG = abs(lateralG); + if (lateralG <= 150) { + if (dword_9A2970[vehicle->var_1F] < 0) { + if (verticalG > -40) { + return; + } + } else if (verticalG > -80) { + return; + } + } + + if (vehicle->var_1F != 8) { + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_DERAILED; + } + } + } else if (vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_2) { + // No up stops bobsleigh type + int trackType = vehicle->track_type >> 2; + if (!track_element_is_covered(trackType)) { + vehicle_get_g_forces(vehicle, &verticalG, &lateralG); + + if (dword_9A2970[vehicle->var_1F] < 0) { + if (verticalG > -45) { + return; + } + } else { + if (verticalG > -80) { + return; + } + } + + if (vehicle->var_1F != 8 && vehicle->var_1F != 55) { + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_DERAILED; + } + } + } +} + +/** + * + * rct2: 0x006DAC43 + */ +static void sub_6DAB4C_chunk_2(rct_vehicle *vehicle) +{ + rct_ride *ride = GET_RIDE(vehicle->ride); + rct_ride_type_vehicle *vehicleEntry = vehicle_get_vehicle_entry(vehicle); + + // Is chair lift type + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_12) { + sint32 velocity = ride->speed << 16; + if (RCT2_GLOBAL(0x00F64E34, uint8) == 0) { + velocity = 0; + } + vehicle->velocity = velocity; + vehicle->acceleration = 0; + } + + int trackType = vehicle->track_type >> 2; + switch (trackType) { + case TRACK_ELEM_END_STATION: + case TRACK_ELEM_BLOCK_BRAKES: + if (ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT || ride_is_block_sectioned(ride)) { + break; + } + return; + case TRACK_ELEM_25_DEG_UP_TO_FLAT: + case TRACK_ELEM_60_DEG_UP_TO_FLAT: + case TRACK_ELEM_CABLE_LIFT_HILL: + case TRACK_ELEM_DIAG_25_DEG_UP_TO_FLAT: + case TRACK_ELEM_DIAG_60_DEG_UP_TO_FLAT: + if (ride_is_block_sectioned(ride)) { + break; + } + return; + default: + return; + } + + rct_map_element *trackElement = map_get_track_element_at_of_type( + vehicle->track_x, + vehicle->track_y, + vehicle->track_z >> 3, + trackType + ); + if (trackType == TRACK_ELEM_END_STATION) { + if (trackElement->flags & (1 << 5)) { + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_10; + } + } else if (trackType == TRACK_ELEM_CABLE_LIFT_HILL || trackType == TRACK_ELEM_BLOCK_BRAKES || track_element_is_lift_hill(trackElement)) { + if (!(trackElement->flags & (1 << 5))) { + if (trackType == TRACK_ELEM_BLOCK_BRAKES && vehicle->velocity >= 0) { + if (vehicle->velocity <= 0x20364) { + vehicle->velocity = 0x20364; + vehicle->acceleration = 0; + } else { + vehicle->velocity -= vehicle->velocity >> 4; + vehicle->acceleration = 0; + } + } + return; + } + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_10; + vehicle->acceleration = 0; + if (vehicle->velocity <= 0x20000) { + vehicle->velocity = 0; + } + vehicle->velocity -= vehicle->velocity >> 3; + } +} + +/** + * + * rct2: 0x006DADAE + */ +static void sub_6DAB4C_chunk_3(rct_vehicle *vehicle) +{ + sint32 nextVelocity = vehicle->acceleration + vehicle->velocity; + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_7) { + nextVelocity = 0; + } + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_10) { + vehicle->var_D2--; + if (vehicle->var_D2 == -70) { + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_10; + } + if (vehicle->var_D2 >= 0) { + nextVelocity = 0; + vehicle->acceleration = 0; + } + } + vehicle->velocity = nextVelocity; + + RCT2_GLOBAL(0x00F64E08, sint32) = nextVelocity; + RCT2_GLOBAL(0x00F64E0C, sint32) = (nextVelocity >> 10) * 42; +} + +static void vehicle_update_block_breaks_open_previous_section(rct_vehicle *vehicle, rct_map_element *mapElement) +{ + int x = vehicle->track_x; + int y = vehicle->track_y; + int z = vehicle->track_z; + track_begin_end trackBeginEnd; + do { + if (!track_block_get_previous(x, y, mapElement, &trackBeginEnd)) { + return; + } + if (trackBeginEnd.begin_x == vehicle->track_x && + trackBeginEnd.begin_y == vehicle->track_y && + mapElement == trackBeginEnd.begin_element) { + return; + } + + x = trackBeginEnd.end_x; + y = trackBeginEnd.end_y; + z = trackBeginEnd.begin_z; + mapElement = trackBeginEnd.begin_element; + } while (!track_element_is_block_start(trackBeginEnd.begin_element)); + + mapElement = map_get_track_element_at(x, y, z >> 3); + if (mapElement == NULL) { + return; + } + mapElement->flags &= ~MAP_ELEMENT_FLAG_BLOCK_BREAK_CLOSED; + map_invalidate_element(x, y, mapElement); + + int trackType = mapElement->properties.track.type; + if (trackType == TRACK_ELEM_BLOCK_BRAKES || trackType == TRACK_ELEM_END_STATION) { + rct_ride *ride = GET_RIDE(vehicle->ride); + if (ride_is_block_sectioned(ride)) { + audio_play_sound_at_location(SOUND_48, x, y, z); + } + } +} + +static int vehicle_get_swing_amount(rct_vehicle *vehicle) +{ + int trackType = vehicle->track_type >> 2; + switch (trackType) { + case TRACK_ELEM_LEFT_QUARTER_TURN_5_TILES: + case TRACK_ELEM_BANKED_LEFT_QUARTER_TURN_5_TILES: + case TRACK_ELEM_LEFT_QUARTER_TURN_5_TILES_25_DEG_UP: + case TRACK_ELEM_LEFT_QUARTER_TURN_5_TILES_25_DEG_DOWN: + case TRACK_ELEM_LEFT_QUARTER_TURN_5_TILES_COVERED: + case TRACK_ELEM_LEFT_HALF_BANKED_HELIX_UP_LARGE: + case TRACK_ELEM_LEFT_HALF_BANKED_HELIX_DOWN_LARGE: + case TRACK_ELEM_LEFT_QUARTER_BANKED_HELIX_LARGE_UP: + case TRACK_ELEM_LEFT_QUARTER_BANKED_HELIX_LARGE_DOWN: + case TRACK_ELEM_LEFT_QUARTER_HELIX_LARGE_UP: + case TRACK_ELEM_LEFT_QUARTER_HELIX_LARGE_DOWN: + case TRACK_ELEM_LEFT_BANKED_QUARTER_TURN_5_TILE_25_DEG_UP: + case TRACK_ELEM_LEFT_BANKED_QUARTER_TURN_5_TILE_25_DEG_DOWN: + // loc_6D67E1 + return 14; + + case TRACK_ELEM_RIGHT_QUARTER_TURN_5_TILES: + case TRACK_ELEM_BANKED_RIGHT_QUARTER_TURN_5_TILES: + case TRACK_ELEM_RIGHT_QUARTER_TURN_5_TILES_25_DEG_UP: + case TRACK_ELEM_RIGHT_QUARTER_TURN_5_TILES_25_DEG_DOWN: + case TRACK_ELEM_RIGHT_QUARTER_TURN_5_TILES_COVERED: + case TRACK_ELEM_RIGHT_HALF_BANKED_HELIX_UP_LARGE: + case TRACK_ELEM_RIGHT_HALF_BANKED_HELIX_DOWN_LARGE: + case TRACK_ELEM_RIGHT_QUARTER_BANKED_HELIX_LARGE_UP: + case TRACK_ELEM_RIGHT_QUARTER_BANKED_HELIX_LARGE_DOWN: + case TRACK_ELEM_RIGHT_QUARTER_HELIX_LARGE_UP: + case TRACK_ELEM_RIGHT_QUARTER_HELIX_LARGE_DOWN: + case TRACK_ELEM_RIGHT_BANKED_QUARTER_TURN_5_TILE_25_DEG_UP: + case TRACK_ELEM_RIGHT_BANKED_QUARTER_TURN_5_TILE_25_DEG_DOWN: + // loc_6D6804 + return -14; + + case TRACK_ELEM_S_BEND_LEFT: + case TRACK_ELEM_S_BEND_LEFT_COVERED: + // loc_6D67EF + if (vehicle->var_34 < 48) { + return 14; + } else { + return -15; + } + + case TRACK_ELEM_S_BEND_RIGHT: + case TRACK_ELEM_S_BEND_RIGHT_COVERED: + // loc_6D67CC + if (vehicle->var_34 < 48) { + return -14; + } else { + return 15; + } + + case TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES: + case TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES_BANK: + case TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES_25_DEG_UP: + case TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES_25_DEG_DOWN: + case TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES_COVERED: + case TRACK_ELEM_LEFT_HALF_BANKED_HELIX_UP_SMALL: + case TRACK_ELEM_LEFT_HALF_BANKED_HELIX_DOWN_SMALL: + case TRACK_ELEM_LEFT_BANK_TO_LEFT_QUARTER_TURN_3_TILES_25_DEG_UP: + case TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES_25_DEG_DOWN_TO_LEFT_BANK: + case TRACK_ELEM_LEFT_CURVED_LIFT_HILL: + case TRACK_ELEM_LEFT_BANKED_QUARTER_TURN_3_TILE_25_DEG_UP: + case TRACK_ELEM_LEFT_BANKED_QUARTER_TURN_3_TILE_25_DEG_DOWN: + // loc_6D67BE + return 13; + + case TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES: + case TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES_BANK: + case TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES_25_DEG_UP: + case TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES_25_DEG_DOWN: + case TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES_COVERED: + case TRACK_ELEM_RIGHT_HALF_BANKED_HELIX_UP_SMALL: + case TRACK_ELEM_RIGHT_HALF_BANKED_HELIX_DOWN_SMALL: + case TRACK_ELEM_RIGHT_BANK_TO_RIGHT_QUARTER_TURN_3_TILES_25_DEG_UP: + case TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES_25_DEG_DOWN_TO_RIGHT_BANK: + case TRACK_ELEM_RIGHT_CURVED_LIFT_HILL: + case TRACK_ELEM_RIGHT_BANKED_QUARTER_TURN_3_TILE_25_DEG_UP: + case TRACK_ELEM_RIGHT_BANKED_QUARTER_TURN_3_TILE_25_DEG_DOWN: + // loc_6D67B0 + return -13; + + case TRACK_ELEM_LEFT_QUARTER_TURN_1_TILE: + case TRACK_ELEM_LEFT_QUARTER_TURN_1_TILE_60_DEG_UP: + case TRACK_ELEM_LEFT_QUARTER_TURN_1_TILE_60_DEG_DOWN: + // loc_6D67A2 + return 12; + + case TRACK_ELEM_RIGHT_QUARTER_TURN_1_TILE: + case TRACK_ELEM_RIGHT_QUARTER_TURN_1_TILE_60_DEG_UP: + case TRACK_ELEM_RIGHT_QUARTER_TURN_1_TILE_60_DEG_DOWN: + // loc_6D6794 + return -12; + + case TRACK_ELEM_LEFT_EIGHTH_TO_DIAG: + case TRACK_ELEM_LEFT_EIGHTH_TO_ORTHOGONAL: + case TRACK_ELEM_LEFT_EIGHTH_BANK_TO_DIAG: + case TRACK_ELEM_LEFT_EIGHTH_BANK_TO_ORTHOGONAL: + // loc_6D67D3 + return 15; + + case TRACK_ELEM_RIGHT_EIGHTH_TO_DIAG: + case TRACK_ELEM_RIGHT_EIGHTH_TO_ORTHOGONAL: + case TRACK_ELEM_RIGHT_EIGHTH_BANK_TO_DIAG: + case TRACK_ELEM_RIGHT_EIGHTH_BANK_TO_ORTHOGONAL: + // loc_6D67F6 + return -15; + } + return 0; +} + +/** + * + * rct2: 0x006D6776 + */ +static void vehicle_update_swinging_car(rct_vehicle *vehicle) +{ + sint32 dword_F64E08 = RCT2_GLOBAL(0x00F64E08, sint32); + vehicle->var_4E = (-vehicle->swinging_car_var_0) >> 6; + int swingAmount = vehicle_get_swing_amount(vehicle); + if (swingAmount < 0) { + vehicle->var_4E -= dword_F64E08 >> (-swingAmount); + } + else if (swingAmount > 0){ + vehicle->var_4E += dword_F64E08 >> swingAmount; + } + + rct_ride_type_vehicle *vehicleEntry = vehicle_get_vehicle_entry(vehicle); + sint16 dx = 3185; + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_5) { + dx = 5006; + } + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_9) { + dx = 1820; + } + sint16 cx = -dx; + + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_11) { + dx = 5370; + cx = -5370; + + int trackType = vehicle->track_type >> 2; + switch (trackType) { + case TRACK_ELEM_BANKED_LEFT_QUARTER_TURN_5_TILES: + case TRACK_ELEM_LEFT_BANK: + case TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES_BANK: + dx = 10831; + cx = -819; + break; + case TRACK_ELEM_BANKED_RIGHT_QUARTER_TURN_5_TILES: + case TRACK_ELEM_RIGHT_BANK: + case TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES_BANK: + dx = 819; + cx = -10831; + break; + } + + switch (trackType) { + case TRACK_ELEM_END_STATION: + case TRACK_ELEM_BEGIN_STATION: + case TRACK_ELEM_MIDDLE_STATION: + case TRACK_ELEM_BRAKES: + case TRACK_ELEM_BLOCK_BRAKES: + dx = 0; + cx = 0; + break; + } + + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_0) { + dx = 0; + cx = 0; + } + } + + vehicle->swinging_car_var_0 += vehicle->var_4E; + vehicle->var_4E -= vehicle->var_4E >> 5; + sint16 ax = vehicle->swinging_car_var_0; + if (ax > dx) { + ax = dx; + vehicle->var_4E = 0; + } + if (ax < cx) { + ax = cx; + vehicle->var_4E = 0; + } + + vehicle->swinging_car_var_0 = ax; + uint8 bl = 11; + if (ax >= -10012) { + bl = 12; + if (ax <= 10012) { + bl = 9; + if (ax >= -8191) { + bl = 10; + if (ax <= 8191) { + bl = 7; + if (ax >= -6371) { + bl = 8; + if (ax <= 6371) { + bl = 5; + if (ax >= -4550) { + bl = 6; + if (ax <= 4550) { + bl = 3; + if (ax >= -2730) { + bl = 4; + if (ax <= 2730) { + ax = 1; + if (ax >= -910) { + bl = 2; + if (ax <= 910) { + bl = 0; + } + } + } + } + } + } + } + } + } + } + } + } + if (bl != vehicle->var_4A) { + vehicle->var_4A = bl; + vehicle_invalidate(vehicle); + } +} + +#pragma region off_9A2E84 + +enum { + loc_6D673C, + loc_6D66F0, + loc_6D672B, + loc_6D6711, + loc_6D66D6, + loc_6D66C3, + loc_6D66B0, + loc_6D669A, + loc_6D6684, + loc_6D665A, + loc_6D6708, + loc_6D6703, + loc_6D66DD, + loc_6D6718 +}; + +static const uint8 off_9A2E84[256] = { + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D66F0, + loc_6D672B, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D66F0, + loc_6D672B, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D66F0, + loc_6D672B, + loc_6D66F0, + loc_6D672B, + loc_6D6711, + loc_6D66D6, + loc_6D673C, + loc_6D673C, + loc_6D66C3, + loc_6D66B0, + loc_6D66C3, + loc_6D66B0, + loc_6D66C3, + loc_6D66B0, + loc_6D66C3, + loc_6D66B0, + loc_6D669A, + loc_6D6684, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D66F0, + loc_6D672B, + loc_6D6711, + loc_6D66D6, + loc_6D66C3, + loc_6D66B0, + loc_6D66C3, + loc_6D66B0, + loc_6D66C3, + loc_6D66B0, + loc_6D66F0, + loc_6D672B, + loc_6D66F0, + loc_6D672B, + loc_6D669A, + loc_6D6684, + loc_6D669A, + loc_6D6684, + loc_6D673C, + loc_6D665A, + loc_6D673C, + loc_6D66F0, + loc_6D672B, + loc_6D66F0, + loc_6D672B, + loc_6D66F0, + loc_6D672B, + loc_6D66F0, + loc_6D672B, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D6708, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D6703, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D66DD, + loc_6D6718, + loc_6D66DD, + loc_6D6718, + loc_6D66DD, + loc_6D6718, + loc_6D66DD, + loc_6D6718, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D66C3, + loc_6D66B0, + loc_6D66C3, + loc_6D66B0, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D66C3, + loc_6D66B0, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D66C3, + loc_6D66B0, + loc_6D66C3, + loc_6D66B0, + loc_6D66F0, + loc_6D672B, + loc_6D66F0, + loc_6D672B, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C, + loc_6D673C +}; + +#pragma endregion + +/** + * + * rct2: 0x006D661F + */ +static void vehicle_update_spinning_car(rct_vehicle *vehicle) +{ + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_13) { + vehicle->var_B6 = 0; + return; + } + + rct_ride_type_vehicle *vehicleEntry = vehicle_get_vehicle_entry(vehicle); + int spinningInertia = vehicleEntry->spinning_inertia; + int trackType = vehicle->track_type >> 2; + sint32 dword_F64E08 = RCT2_GLOBAL(0x00F64E08, sint32); + int unk; + switch (off_9A2E84[trackType]) { + case loc_6D665A: + spinningInertia += 6; + unk = dword_F64E08 >> spinningInertia; + if (vehicle->sprite_index & 1) { + vehicle->var_B6 -= unk; + } else { + vehicle->var_B6 += unk; + } + break; + case loc_6D6684: + case loc_6D6703: + spinningInertia += 5; + vehicle->var_B6 -= dword_F64E08 >> spinningInertia; + break; + case loc_6D669A: + spinningInertia += 5; + vehicle->var_B6 += dword_F64E08 >> spinningInertia; + break; + case loc_6D66B0: + spinningInertia += 7; + vehicle->var_B6 -= dword_F64E08 >> spinningInertia; + break; + case loc_6D66C3: + spinningInertia += 7; + vehicle->var_B6 += dword_F64E08 >> spinningInertia; + break; + case loc_6D66D6: + if (vehicle->track_progress < 48) { + spinningInertia += 8; + vehicle->var_B6 += dword_F64E08 >> spinningInertia; + break; + } + case loc_6D66DD: + spinningInertia += 9; + vehicle->var_B6 += dword_F64E08 >> spinningInertia; + break; + case loc_6D66F0: + spinningInertia += 8; + vehicle->var_B6 += dword_F64E08 >> spinningInertia; + break; + case loc_6D6708: + if (vehicle->track_progress > 22) { + spinningInertia += 5; + vehicle->var_B6 += dword_F64E08 >> spinningInertia; + } + break; + case loc_6D6711: + if (vehicle->track_progress < 48) { + spinningInertia += 8; + vehicle->var_B6 += dword_F64E08 >> spinningInertia; + break; + } + case loc_6D6718: + spinningInertia += 9; + vehicle->var_B6 -= dword_F64E08 >> spinningInertia; + break; + case loc_6D672B: + spinningInertia += 8; + vehicle->var_B6 += dword_F64E08 >> spinningInertia; + break; + } + + unk = clamp(-0x600, vehicle->var_B6, 0x600); + vehicle->var_B6 = unk; + vehicle->var_BA += unk >> 8; + vehicle->var_B6 -= unk >> vehicleEntry->spinning_friction; + vehicle_invalidate(vehicle); +} + +/** + * + * rct2: 0x006734B2 + */ +static void steam_particle_create(sint16 x, sint16 y, sint16 z) +{ + rct_map_element *mapElement = map_get_surface_element_at(x >> 5, y >> 5); + if (mapElement != NULL && z > mapElement->base_height * 8) { + rct_steam_particle *steam = (rct_steam_particle*)create_sprite(2); + steam->sprite_width = 20; + steam->sprite_height_negative = 18; + steam->sprite_height_positive = 16; + steam->sprite_identifier = SPRITE_IDENTIFIER_MISC; + steam->misc_identifier = SPRITE_MISC_STEAM_PARTICLE; + steam->var_26 = 256; + steam->var_24 = 0; + sprite_move(x, y, z, (rct_sprite*)steam); + } +} + +/** + * + * rct2: 0x006D63D4 + */ +static void sub_6D63D4(rct_vehicle *vehicle) +{ + uint8 al, ah; + uint32 eax; + + uint32 *var_C8 = (uint32*)&vehicle->var_C8; + rct_ride_type_vehicle *vehicleEntry = vehicle_get_vehicle_entry(vehicle); + switch (vehicleEntry->var_11) { + case 1: // loc_6D652B + *var_C8 += RCT2_GLOBAL(0x00F64E08, uint32); + al = (*var_C8 >> 20) & 3; + if (vehicle->var_C5 != al) { + ah = al; + al = vehicle->var_C5; + vehicle->var_C5 = ah; + al &= 0x02; + ah &= 0x02; + if (al != ah) { + rct_ride *ride = GET_RIDE(vehicle->ride); + if (ride->entrance_style == RIDE_ENTRANCE_STYLE_PLAIN || + (vehicle->status != VEHICLE_STATUS_MOVING_TO_END_OF_STATION && + vehicle->status != VEHICLE_STATUS_ARRIVING) + ) { + int index = vehicle->sprite_direction >> 1; + if (vehicle->var_1F == 2) { + index += 16; + } + if (vehicle->var_1F == 6) { + index += 32; + } + steam_particle_create( + vehicle->x + SteamParticleOffsets[index].x, + vehicle->y + SteamParticleOffsets[index].y, + vehicle->z + SteamParticleOffsets[index].z + ); + } + } + vehicle_invalidate(vehicle); + } + break; + case 2: // loc_6D6424 + *var_C8 += RCT2_GLOBAL(0x00F64E08, uint32); + al = (*var_C8 >> 18) & 2; + if (vehicle->var_C5 != al) { + vehicle->var_C5 = al; + vehicle_invalidate(vehicle); + } + break; + case 3: // loc_6D6482 + *var_C8 += RCT2_GLOBAL(0x00F64E08, uint32); + eax = ((*var_C8 >> 13) & 0xFF) * 6; + ah = (eax >> 8) & 0xFF; + if (vehicle->var_C5 != ah) { + vehicle->var_C5 = ah; + vehicle_invalidate(vehicle); + } + break; + case 4: // loc_6D64F7 + *var_C8 += RCT2_GLOBAL(0x00F64E08, uint32); + eax = ((*var_C8 >> 13) & 0xFF) * 7; + ah = (eax >> 8) & 0xFF; + if (vehicle->var_C5 != ah) { + vehicle->var_C5 = ah; + vehicle_invalidate(vehicle); + } + break; + case 5: // loc_6D6453 + *var_C8 += RCT2_GLOBAL(0x00F64E08, uint32); + al = (*var_C8 >> 19) & 1; + if (vehicle->var_C5 != al) { + vehicle->var_C5 = al; + vehicle_invalidate(vehicle); + } + break; + case 6: // loc_6D65C3 + if (vehicle->var_C8 <= 0xCCCC) { + vehicle->var_C8 += 0x3333; + } else { + vehicle->var_C8 += 0x3333; + vehicle->var_C8 += 1; + vehicle->var_C8 &= 7; + vehicle_invalidate(vehicle); + } + break; + case 7: // loc_6D63F5 + *var_C8 += RCT2_GLOBAL(0x00F64E08, uint32); + al = (*var_C8 >> 18) & 3; + if (vehicle->var_C5 != al) { + vehicle->var_C5 = al; + vehicle_invalidate(vehicle); + } + break; + case 8: // loc_6D64B6 + if (vehicle->num_peeps != 0) { + *var_C8 += RCT2_GLOBAL(0x00F64E08, uint32); + eax = ((*var_C8 >> 13) & 0xFF) << 2; + ah = (eax >> 8) & 0xFF; + if (vehicle->var_C5 != ah) { + vehicle->var_C5 = ah; + vehicle_invalidate(vehicle); + } + } + break; + case 9: // loc_6D65E1 + ah = vehicle->var_D9; + al = vehicle->var_D8; + if (al != ah) { + if (vehicle->var_C8 <= 0xCCCC) { + vehicle->var_C8 += 0x3333; + } else { + vehicle->var_C8 += 0x3333; + uint8 bl = al + 1; + if (al >= ah) { + bl -= 2; + } + vehicle->var_D8 = bl; + vehicle->var_C5 = (bl - 4) & 7; + vehicle_invalidate(vehicle); + } + } + break; + } +} + +/** + * + * rct2: 0x006DEDB1 + */ +static void vehicle_play_scenery_door_open_sound(rct_vehicle *vehicle, rct_map_element *mapElement) +{ + rct_scenery_entry *wallEntry = g_wallSceneryEntries[mapElement->properties.fence.type]; + int doorSoundType = (wallEntry->wall.flags2 >> 1) & 3; + if (doorSoundType != 0) { + int soundId = DoorOpenSoundIds[doorSoundType - 1]; + if (soundId != 255) { + audio_play_sound_at_location(soundId, vehicle->x, vehicle->track_y, vehicle->track_z); + } + } +} + +/** + * + * rct2: 0x006DED7A + */ +static void vehicle_play_scenery_door_close_sound(rct_vehicle *vehicle, rct_map_element *mapElement) +{ + rct_scenery_entry *wallEntry = g_wallSceneryEntries[mapElement->properties.fence.type]; + int doorSoundType = (wallEntry->wall.flags2 >> 1) & 3; + if (doorSoundType != 0) { + int soundId = DoorCloseSoundIds[doorSoundType - 1]; + if (soundId != 255) { + audio_play_sound_at_location(soundId, vehicle->x, vehicle->track_y, vehicle->track_z); + } + } +} + +/** + * + * rct2: 0x006DEE93 + */ +static void vehicle_update_scenery_door(rct_vehicle *vehicle) +{ + int trackType = vehicle->track_type >> 2; + const rct_preview_track *trackBlock = TrackBlocks[trackType]; + while ((trackBlock + 1)->index != 255) { + trackBlock++; + } + const rct_track_coordinates *trackCoordinates = &TrackCoordinates[trackType]; + int x = floor2(vehicle->x, 32); + int y = floor2(vehicle->y, 32); + int z = (vehicle->track_z - trackBlock->z + trackCoordinates->z_end) >> 3; + int direction = (vehicle->track_direction + trackCoordinates->rotation_end) & 3; + + rct_map_element *mapElement = map_get_fence_element_at(x, y, z, direction); + if (mapElement == NULL) { + return; + } + + if (vehicle->next_vehicle_on_train != SPRITE_INDEX_NULL) { + mapElement->properties.fence.item[2] &= 7; + mapElement->properties.fence.item[2] |= 8; + map_animation_create(MAP_ANIMATION_TYPE_WALL_UNKNOWN, x, y, z); + vehicle_play_scenery_door_open_sound(vehicle, mapElement); + } else { + mapElement->properties.fence.item[2] &= 7; + mapElement->properties.fence.item[2] |= 0x30; + vehicle_play_scenery_door_close_sound(vehicle, mapElement); + } +} + +/** + * + * rct2: 0x006DB38B + */ +static bool loc_6DB38B(rct_vehicle *vehicle, rct_map_element *mapElement) +{ + // Get bank + int bankStart = track_get_actual_bank_3(vehicle, mapElement); + + // Get vangle + int trackType = mapElement->properties.track.type; + int vangleStart = gTrackDefinitions[trackType].vangle_start; + + // ? + uint16 angleAndBank = vangleStart | (bankStart << 8); + if (angleAndBank != RCT2_GLOBAL(0x00F64E36, uint16)) { + return false; + } + + return true; +} + +void loc_6DB481(rct_vehicle *vehicle) +{ + uint16 probability = 0x8000; + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_6) { + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_6; + } else { + probability = 0x0A3D; + } + if ((scenario_rand() & 0xFFFF) <= probability) { + vehicle->var_CD += 2; + } +} + +/** + * + * rct2: 0x006DB545 + */ +static void vehicle_trigger_on_ride_photo(rct_vehicle *vehicle, rct_map_element *mapElement) +{ + mapElement->properties.track.sequence &= 0x0F; + mapElement->properties.track.sequence |= 0x30; + map_animation_create( + MAP_ANIMATION_TYPE_TRACK_ONRIDEPHOTO, + vehicle->track_x, + vehicle->track_y, + mapElement->base_height + ); +} + +/** + * + * rct2: 0x006DEDE8 + */ +static void sub_6DEDE8(rct_vehicle *vehicle) +{ + int trackType = vehicle->track_type >> 2; + const rct_preview_track *trackBlock = TrackBlocks[trackType]; + const rct_track_coordinates *trackCoordinates = &TrackCoordinates[trackType]; + int x = vehicle->track_x; + int y = vehicle->track_y; + int z = (vehicle->track_z - trackBlock->z + trackCoordinates->z_begin) >> 3; + int direction = (vehicle->track_direction + trackCoordinates->rotation_begin) & 3; + direction ^= 2; + + rct_map_element *mapElement = map_get_fence_element_at(x, y, z, direction); + if (mapElement == NULL) { + return; + } + + if (vehicle->next_vehicle_on_train != SPRITE_INDEX_NULL) { + mapElement->properties.fence.item[2] &= 7; + mapElement->properties.fence.item[2] |= 0x88; + map_animation_create(MAP_ANIMATION_TYPE_WALL_UNKNOWN, x, y, z); + vehicle_play_scenery_door_open_sound(vehicle, mapElement); + } else { + mapElement->properties.fence.item[2] &= 7; + mapElement->properties.fence.item[2] |= 0xB0; + vehicle_play_scenery_door_close_sound(vehicle, mapElement); + } +} + +static void vehicle_update_play_water_splash_sound() +{ + if (RCT2_GLOBAL(0x00F64E08, sint32) <= 0x20364) { + return; + } + + audio_play_sound_at_location( + SOUND_WATER_SPLASH, + unk_F64E20->x, + unk_F64E20->y, + unk_F64E20->z + ); +} + +/** + * + * rct2: 0x006DB59E + */ +static void vehicle_update_handle_water_splash(rct_vehicle *vehicle) +{ + rct_ride_type *rideEntry = GET_RIDE_ENTRY(vehicle->ride_subtype); + int trackType = vehicle->track_type >> 2; + + if (!(rideEntry->flags & RIDE_ENTRY_FLAG_8)) { + if (rideEntry->flags & RIDE_ENTRY_FLAG_9) { + if (!vehicle->is_child) { + if (track_element_is_covered(trackType)) { + rct_vehicle *nextVehicle = GET_VEHICLE(vehicle->next_vehicle_on_ride); + rct_vehicle *nextNextVehicle = GET_VEHICLE(nextVehicle->next_vehicle_on_ride); + if (!track_element_is_covered(nextNextVehicle->track_type >> 2)) { + if (vehicle->track_progress == 4) { + vehicle_update_play_water_splash_sound(); + } + } + } + } + } + } else { + if (trackType == TRACK_ELEM_25_DEG_DOWN_TO_FLAT) { + if (vehicle->track_progress == 12) { + vehicle_update_play_water_splash_sound(); + } + } + } + if (!vehicle->is_child) { + if (trackType == TRACK_ELEM_WATER_SPLASH) { + if (vehicle->track_progress == 48) { + vehicle_update_play_water_splash_sound(); + } + } + } +} + +/** + * + * rct2: 0x006DB807 + */ +static void sub_6DB807(rct_vehicle *vehicle) +{ + const rct_vehicle_info *moveInfo = vehicle_get_move_info( + vehicle->var_CD, + vehicle->track_type, + vehicle->track_progress + ); + int x = vehicle->track_x + moveInfo->x; + int y = vehicle->track_y + moveInfo->y; + int z = vehicle->z; + sprite_move(x, y, z, (rct_sprite*)vehicle); +} + +/** + * Collision Detection + * rct2: 0x006DD078 + * @param vehicle (esi) + * @param x (ax) + * @param y (cx) + * @param z (dx) + * @param otherVehicleIndex (bp) + */ +static bool vehicle_update_motion_collision_detection( + rct_vehicle *vehicle, sint16 x, sint16 y, sint16 z, uint16* otherVehicleIndex +) { + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_1) return false; + + rct_ride_type_vehicle *vehicleEntry = vehicle_get_vehicle_entry(vehicle); + + if (!(vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_6)){ + vehicle->var_C4 = 0; + rct_vehicle* collideVehicle = GET_VEHICLE(*otherVehicleIndex); + + if (vehicle == collideVehicle) return false; + + sint32 x_diff = abs(x - collideVehicle->x); + if (x_diff > 0x7FFF) return false; + + sint32 y_diff = abs(y - collideVehicle->y); + if (y_diff > 0x7FFF) return false; + + if (x_diff + y_diff > 0xFFFF) return false; + + sint32 z_diff = abs(z - collideVehicle->z); + if (x_diff + y_diff + z_diff > 0xFFFF) return false; + + uint16 ecx = min(vehicle->var_44 + collideVehicle->var_44, 560); + ecx = ((ecx >> 1) * 30) >> 8; + + if (x_diff + y_diff + z_diff >= ecx) return false; + + uint8 direction = (vehicle->sprite_direction - collideVehicle->sprite_direction + 7) & 0x1F; + if (direction >= 0xF) return false; + + return true; + } + + uint16 eax = ((x / 32) << 8) + (y / 32); + // TODO change to using a better technique + uint32* ebp = RCT2_ADDRESS(0x009A37C4, uint32); + bool mayCollide = false; + uint16 collideId = 0xFFFF; + rct_vehicle* collideVehicle = NULL; + for(; ebp <= RCT2_ADDRESS(0x009A37E4, uint32); ebp++){ + collideId = RCT2_ADDRESS(0x00F1EF60, uint16)[eax]; + for(; collideId != 0xFFFF; collideId = collideVehicle->next_in_quadrant){ + collideVehicle = GET_VEHICLE(collideId); + if (collideVehicle == vehicle) continue; + + if (collideVehicle->sprite_identifier != SPRITE_IDENTIFIER_VEHICLE) continue; + + sint32 z_diff = abs(collideVehicle->z - z); + + if (z_diff > 16) continue; + + rct_ride_type_vehicle* collideType = vehicle_get_vehicle_entry(collideVehicle); + + if (!(collideType->flags_b & VEHICLE_ENTRY_FLAG_B_6)) continue; + + uint32 x_diff = abs(collideVehicle->x - x); + if (x_diff > 0x7FFF) continue; + + uint32 y_diff = abs(collideVehicle->y - y); + if (y_diff > 0x7FFF) continue; + + if (x_diff + y_diff > 0xFFFF) continue; + + uint8 cl = min(vehicle->var_CD, collideVehicle->var_CD); + uint8 ch = max(vehicle->var_CD, collideVehicle->var_CD); + if (cl != ch){ + if (cl == 5 && ch == 6) continue; + } + + uint32 ecx = vehicle->var_44 + collideVehicle->var_44; + ecx = ((ecx >> 1) * 30) >> 8; + + if (x_diff + y_diff >= ecx) continue; + + if (!(collideType->flags_b & VEHICLE_ENTRY_FLAG_B_14)){ + mayCollide = true; + break; + } + + uint8 direction = (vehicle->sprite_direction - collideVehicle->sprite_direction - 6) & 0x1F; + + if (direction < 0x14) continue; + + uint32 next_x_diff = abs(x + RCT2_ADDRESS(0x009A3B04, sint16)[((4 + vehicle->sprite_direction) >> 3) * 2] - collideVehicle->x); + uint32 next_y_diff = abs(y + RCT2_ADDRESS(0x009A3B06, sint16)[((4 + vehicle->sprite_direction) >> 3) * 2] - collideVehicle->y); + + if (next_x_diff + next_y_diff < x_diff + y_diff){ + mayCollide = true; + break; + } + + } + if (mayCollide == true) { + break; + } + + // TODO change this + eax += *ebp; + } + + if (mayCollide == false) { + vehicle->var_C4 = 0; + return false; + } + + vehicle->var_C4++; + if (vehicle->var_C4 < 200) { + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_6; + if (otherVehicleIndex != NULL) + *otherVehicleIndex = collideId; + return true; + } + + // TODO Is it possible for collideVehicle to be NULL? + + if (vehicle->status == VEHICLE_STATUS_MOVING_TO_END_OF_STATION){ + if (vehicle->sprite_direction == 0) { + if (vehicle->x <= collideVehicle->x) { + return false; + } + } + else if (vehicle->sprite_direction == 8) { + if (vehicle->y >= collideVehicle->y) { + return false; + } + } + else if (vehicle->sprite_direction == 16) { + if (vehicle->x >= collideVehicle->x) { + return false; + } + } + else if (vehicle->sprite_direction == 24) { + if (vehicle->y <= collideVehicle->y) { + return false; + } + } + } + + if (collideVehicle->status == VEHICLE_STATUS_TRAVELLING_BOAT && + vehicle->status != VEHICLE_STATUS_ARRIVING && + vehicle->status != VEHICLE_STATUS_TRAVELLING + ) { + return false; + } + + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_6; + if (otherVehicleIndex != NULL) + *otherVehicleIndex = collideId; + return true; +} + +/** + * + * rct2: 0x006DB7D6 + */ +static void sub_6DB7D6(rct_vehicle *vehicle) +{ + rct_vehicle *previousVehicle = GET_VEHICLE(vehicle->prev_vehicle_on_ride); + rct_vehicle *nextVehicle = GET_VEHICLE(vehicle->next_vehicle_on_ride); + + vehicle->track_progress = 168; + vehicle->vehicle_type ^= 1; + + previousVehicle->track_progress = 86; + nextVehicle->track_progress = 158; + + sub_6DB807(nextVehicle); + sub_6DB807(previousVehicle); +} + +/** + * + * rct2: 0x006DBF3E + */ +void sub_6DBF3E(rct_vehicle *vehicle) +{ + rct_ride_type_vehicle *vehicleEntry = vehicle_get_vehicle_entry(vehicle); + + vehicle->acceleration = (uint32)((sint32)vehicle->acceleration / RCT2_GLOBAL(0x00F64E10, sint32)); + if (vehicle->var_CD == 2) { + return; + } + + int trackType = vehicle->track_type >> 2; + if (!(RCT2_GLOBAL(0x0099BA64 + (trackType * 16), uint32) & 0x10)) { + return; + } + + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_3; + + rct_map_element *mapElement = map_get_track_element_at_of_type_seq( + vehicle->track_x, + vehicle->track_y, + vehicle->track_z >> 3, + trackType, + 0 + ); + if (RCT2_GLOBAL(0x00F64E1C, uint32) == 0xFFFFFFFF) { + RCT2_GLOBAL(0x00F64E1C, uint32) = (mapElement->properties.track.sequence >> 4) & 7; + } + + if (trackType == TRACK_ELEM_TOWER_BASE && + vehicle == RCT2_GLOBAL(0x00F64E04, rct_vehicle*) + ) { + if (vehicle->track_progress > 3 && !(vehicle->update_flags & VEHICLE_UPDATE_FLAG_3)) { + rct_xy_element input, output; + int outputZ, outputDirection; + + input.x = vehicle->track_x; + input.y = vehicle->track_y; + input.element = mapElement; + if (!track_block_get_next(&input, &output, &outputZ, &outputDirection)) { + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_12; + } + } + + if (vehicle->track_progress <= 3) { + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION; + } + } + + if (trackType != TRACK_ELEM_END_STATION || + vehicle != RCT2_GLOBAL(0x00F64E04, rct_vehicle*) + ) { + return; + } + + uint16 ax = vehicle->track_progress; + if (RCT2_GLOBAL(0x00F64E08, sint32) < 0) { + if (ax <= 22) { + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION; + } + } else { + uint16 cx = 17; + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_12) { + cx = 6; + } + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_14) { + cx = vehicle->var_CD == 6 ? 18 : 20; + } + + if (ax > cx) { + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION; + } + } +} + +/** + * + * rct2: 0x006DB08C + */ +bool vehicle_update_track_motion_forwards_get_new_track(rct_vehicle *vehicle, uint16 trackType, rct_ride* ride, rct_ride_type* rideEntry) { + registers regs = { 0 }; + + RCT2_GLOBAL(0x00F64E36, uint8) = gTrackDefinitions[trackType].vangle_end; + RCT2_GLOBAL(0x00F64E37, uint8) = gTrackDefinitions[trackType].bank_end; + rct_map_element *mapElement = map_get_track_element_at_of_type_seq( + vehicle->track_x, + vehicle->track_y, + vehicle->track_z >> 3, + trackType, + 0 + ); + if (trackType == TRACK_ELEM_CABLE_LIFT_HILL && vehicle == RCT2_GLOBAL(0x00F64E04, rct_vehicle*)) { + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_11; + } + + if (track_element_is_block_start(mapElement)) { + if (vehicle->next_vehicle_on_train == SPRITE_INDEX_NULL) { + mapElement->flags |= MAP_ELEMENT_FLAG_BLOCK_BREAK_CLOSED; + if (trackType == TRACK_ELEM_BLOCK_BRAKES || trackType == TRACK_ELEM_END_STATION) { + if (!(rideEntry->vehicles[0].flags_b & VEHICLE_ENTRY_FLAG_B_3)) { + audio_play_sound_at_location(SOUND_49, vehicle->track_x, vehicle->track_y, vehicle->track_z); + } + } + map_invalidate_element(vehicle->track_x, vehicle->track_z, mapElement); + vehicle_update_block_breaks_open_previous_section(vehicle, mapElement); + } + } + + // TODO check if getting the vehicle entry again is necessary + rct_ride_type_vehicle* vehicleEntry = vehicle_get_vehicle_entry(vehicle); + + if (vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_8) { + vehicle_update_scenery_door(vehicle); + } + + switch (vehicle->var_CD) { + default: + goto loc_6DB358; + case 2: + case 3: + vehicle->var_CD = 2; + goto loc_6DB32A; + case 4: + vehicle->var_CD = 1; + goto loc_6DB358; + case 7: + vehicle->var_CD = 6; + goto loc_6DB358; + case 8: + vehicle->var_CD = 5; + goto loc_6DB358; + } + +loc_6DB32A: + { + track_begin_end trackBeginEnd; + if (!track_block_get_previous(vehicle->track_x, vehicle->track_y, mapElement, &trackBeginEnd)) { + return false; + } + regs.eax = trackBeginEnd.begin_x; + regs.ecx = trackBeginEnd.begin_y; + regs.edx = trackBeginEnd.begin_z; + regs.bl = trackBeginEnd.begin_direction; + mapElement = trackBeginEnd.begin_element; + } + goto loc_6DB41D; + +loc_6DB358: + { + rct_xy_element xyElement; + int z, direction; + xyElement.x = vehicle->track_x; + xyElement.y = vehicle->track_y; + xyElement.element = mapElement; + if (!track_block_get_next(&xyElement, &xyElement, &z, &direction)) { + return false; + } + mapElement = xyElement.element; + regs.eax = xyElement.x; + regs.ecx = xyElement.y; + regs.edx = z; + regs.bl = direction; + } + if (mapElement->properties.track.type == 211 || + mapElement->properties.track.type == 212 + ) { + if (!vehicle->is_child && vehicle->velocity <= 0x30000) { + vehicle->velocity = 0; + } + } + + if (!loc_6DB38B(vehicle, mapElement)) { + return false; + } + + // Update VEHICLE_UPDATE_FLAG_11 flag + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_11; + int rideType = GET_RIDE(mapElement->properties.track.ride_index)->type; + if (RideData4[rideType].flags & RIDE_TYPE_FLAG4_3) { + if (mapElement->properties.track.colour & 4) { + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_11; + } + } + +loc_6DB41D: + vehicle->track_x = regs.ax; + vehicle->track_y = regs.cx; + vehicle->track_z = regs.dx; + + // TODO check if getting the vehicle entry again is necessary + vehicleEntry = vehicle_get_vehicle_entry(vehicle); + + if ((vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_14) && vehicle->var_CD < 7) { + trackType = mapElement->properties.track.type; + if (trackType == TRACK_ELEM_FLAT) { + loc_6DB481(vehicle); + } + else if (ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) { + if (track_element_is_station(mapElement)) { + loc_6DB481(vehicle); + } + } + } + + if (vehicle->var_CD != 0 && vehicle->var_CD < 5) { + regs.ax >>= 5; + regs.cx >>= 5; + regs.ah = regs.cl; + regs.dx >>= 3; + if (regs.ax != ride->var_13C || regs.dl != ride->var_13F) { + if (regs.ax == ride->var_13A && regs.dl == ride->var_13E) { + vehicle->var_CD = 4; + } + } + else { + vehicle->var_CD = 3; + } + } + + // loc_6DB500 + // Update VEHICLE_UPDATE_FLAG_0 + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_0; + if (track_element_is_lift_hill(mapElement)) { + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_0; + } + + trackType = mapElement->properties.track.type; + if (trackType != TRACK_ELEM_BRAKES) { + vehicle->var_D9 = mapElement->properties.track.colour >> 4; + } + vehicle->track_direction = regs.bl & 3; + vehicle->track_type |= trackType << 2; + vehicle->var_CF = (mapElement->properties.track.sequence >> 3) & 0x1E; + if (trackType == TRACK_ELEM_ON_RIDE_PHOTO) { + vehicle_trigger_on_ride_photo(vehicle, mapElement); + } + if (trackType == TRACK_ELEM_ROTATION_CONTROL_TOGGLE) { + vehicle->update_flags ^= VEHICLE_UPDATE_FLAG_13; + } + if (vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_8) { + sub_6DEDE8(vehicle); + } + return true; +} + +/** + * + * rct2: 0x006DAEB9 + */ +bool vehicle_update_track_motion_forwards(rct_vehicle *vehicle, rct_ride_type_vehicle* vehicleEntry, rct_ride* ride, rct_ride_type* rideEntry) { + registers regs = { 0 }; +loc_6DAEB9: + regs.edi = vehicle->track_type; + regs.cx = vehicle->track_type >> 2; + + int trackType = vehicle->track_type >> 2; + if (trackType == TRACK_ELEM_HEARTLINE_TRANSFER_UP || trackType == TRACK_ELEM_HEARTLINE_TRANSFER_DOWN) { + if (vehicle->track_progress == 80) { + vehicle->vehicle_type ^= 1; + vehicleEntry = vehicle_get_vehicle_entry(vehicle); + } + if (RCT2_GLOBAL(0x00F64E08, sint32) >= 0x40000) { + vehicle->acceleration = -RCT2_GLOBAL(0x00F64E08, sint32) * 8; + } + else if (RCT2_GLOBAL(0x00F64E08, sint32) < 0x20000) { + vehicle->acceleration = 0x50000; + } + } + else if (trackType == TRACK_ELEM_BRAKES) { + if (!( + ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN && + ride->breakdown_reason_pending == BREAKDOWN_BRAKES_FAILURE && + ride->mechanic_status == RIDE_MECHANIC_STATUS_4 + )) { + regs.eax = vehicle->var_CF << 16; + if (regs.eax < RCT2_GLOBAL(0x00F64E08, sint32)) { + vehicle->acceleration = -RCT2_GLOBAL(0x00F64E08, sint32) * 16; + } + else if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 0x0F)) { + if (RCT2_GLOBAL(0x00F64E2C, uint8) == 0) { + RCT2_GLOBAL(0x00F64E2C, uint8)++; + audio_play_sound_at_location(SOUND_51, vehicle->x, vehicle->y, vehicle->z); + } + } + } + } + + if ((trackType == TRACK_ELEM_FLAT && ride->type == RIDE_TYPE_REVERSE_FREEFALL_COASTER) || + (trackType == TRACK_ELEM_POWERED_LIFT) + ) { + vehicle->acceleration = RCT2_GLOBAL(0x0097CF40 + (ride->type * 8) + 7, uint8) << 16; + } + if (trackType == TRACK_ELEM_BRAKE_FOR_DROP) { + if (!vehicle->is_child) { + if (!(vehicle->update_flags & VEHICLE_UPDATE_FLAG_10)) { + if (vehicle->track_progress >= 8) { + vehicle->acceleration = -RCT2_GLOBAL(0x00F64E08, sint32) * 16; + if (vehicle->track_progress >= 24) { + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_10; + vehicle->var_D2 = 90; + } + } + } + } + } + if (trackType == TRACK_ELEM_LOG_FLUME_REVERSER) { + if (vehicle->track_progress != 16 || vehicle->velocity < 0x40000) { + if (vehicle->track_progress == 32) { + vehicle->vehicle_type = vehicleEntry->var_58; + vehicleEntry = vehicle_get_vehicle_entry(vehicle); + } + } + else { + vehicle->track_progress += 17; + } + } + + regs.ax = vehicle->track_progress + 1; + + const rct_vehicle_info *moveInfo = vehicle_get_move_info( + vehicle->var_CD, + vehicle->track_type, + 0 + ); + + // Track Total Progress is in the two bytes before the move info list + uint16 trackTotalProgress = *((uint16*)((int)moveInfo - 2)); + if (regs.ax >= trackTotalProgress) { + if (!vehicle_update_track_motion_forwards_get_new_track(vehicle, trackType, ride, rideEntry)) { + goto loc_6DB94A; + } + regs.ax = 0; + } + + vehicle->track_progress = regs.ax; + vehicle_update_handle_water_splash(vehicle); + + // loc_6DB706 + moveInfo = vehicle_get_move_info( + vehicle->var_CD, + vehicle->track_type, + vehicle->track_progress + ); + sint16 x = vehicle->track_x + moveInfo->x; + sint16 y = vehicle->track_y + moveInfo->y; + sint16 z = vehicle->track_z + moveInfo->z + RCT2_GLOBAL(0x0097D21A + (ride->type * 8), sint8); + + trackType = vehicle->track_type >> 2; + regs.ebx = 0; + if (x != unk_F64E20->x) { regs.ebx |= 1; } + if (y != unk_F64E20->y) { regs.ebx |= 2; } + if (z != unk_F64E20->z) { regs.ebx |= 4; } + if (vehicle->var_CD == 15 && + (trackType == TRACK_ELEM_LEFT_REVERSER || + trackType == TRACK_ELEM_RIGHT_REVERSER) && + vehicle->track_progress >= 30 && + vehicle->track_progress <= 66 + ) { + regs.ebx |= 8; + } + + if (vehicle->var_CD == 16 && + (trackType == TRACK_ELEM_LEFT_REVERSER || + trackType == TRACK_ELEM_RIGHT_REVERSER) && + vehicle->track_progress == 96 + ) { + sub_6DB7D6(vehicle); + + const rct_vehicle_info *moveInfo2 = vehicle_get_move_info( + vehicle->var_CD, + vehicle->track_type, + vehicle->track_progress + ); + x = vehicle->x + moveInfo2->x; + y = vehicle->y + moveInfo2->y; + } + + // loc_6DB8A5 + regs.ebx = RCT2_ADDRESS(0x009A2930, sint32)[regs.ebx]; + vehicle->remaining_distance -= regs.ebx; + unk_F64E20->x = x; + unk_F64E20->y = y; + unk_F64E20->z = z; + vehicle->sprite_direction = moveInfo->direction; + vehicle->var_20 = moveInfo->var_08; + vehicle->var_1F = moveInfo->var_07; + + regs.ebx = moveInfo->var_07; + + if ((vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_9) && moveInfo->var_07 != 0) { + vehicle->var_4A = 0; + vehicle->swinging_car_var_0 = 0; + vehicle->var_4E = 0; + } + + // vehicle == frontVehicle + if (vehicle == RCT2_GLOBAL(0x00F64E00, rct_vehicle*)) { + if (RCT2_GLOBAL(0x00F64E08, sint32) >= 0) { + regs.bp = vehicle->prev_vehicle_on_ride; + if (vehicle_update_motion_collision_detection(vehicle, x, y, z, (uint16 *)®s.bp)) { + goto loc_6DB967; + } + } + } + + // loc_6DB928 + if (vehicle->remaining_distance < 0x368A) { + return true; + } + + regs.ebx = dword_9A2970[regs.ebx]; + vehicle->acceleration += regs.ebx; + RCT2_GLOBAL(0x00F64E10, uint32)++; + goto loc_6DAEB9; + +loc_6DB94A: + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_5; + regs.eax = vehicle->remaining_distance + 1; + RCT2_GLOBAL(0x00F64E0C, sint32) -= regs.eax; + vehicle->remaining_distance = 0xFFFFFFFF; + regs.ebx = vehicle->var_1F; + return false; + +loc_6DB967: + regs.eax = vehicle->remaining_distance + 1; + RCT2_GLOBAL(0x00F64E0C, sint32) -= regs.eax; + vehicle->remaining_distance -= regs.eax; + + // Might need to be bp rather than vehicle, but hopefully not + rct_vehicle *head = vehicle_get_head(GET_VEHICLE(regs.bp)); + + regs.eax = abs(vehicle->velocity - head->velocity); + if (!(rideEntry->flags & RIDE_ENTRY_FLAG_18)) { + if (regs.eax > 0xE0000) { + if (!(vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_6)) { + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_COLLISION; + } + } + } + + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_14) { + vehicle->velocity -= vehicle->velocity >> 2; + } + else { + sint32 newHeadVelocity = vehicle->velocity >> 1; + vehicle->velocity = head->velocity >> 1; + head->velocity = newHeadVelocity; + } + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_1; + return false; +} + +/** + * + * rct2: 0x006DBAA6 + */ +bool vehicle_update_track_motion_backwards_get_new_track(rct_vehicle *vehicle, uint16 trackType, rct_ride* ride, rct_ride_type* rideEntry, uint16* progress) { + RCT2_GLOBAL(0x00F64E36, uint8) = gTrackDefinitions[trackType].vangle_start; + RCT2_GLOBAL(0x00F64E37, uint8) = gTrackDefinitions[trackType].bank_start; + rct_map_element* mapElement = map_get_track_element_at_of_type_seq( + vehicle->track_x, + vehicle->track_y, + vehicle->track_z >> 3, + trackType, + 0 + ); + + bool nextTileBackwards = true; + int direction; +//loc_6DBB08:; + sint16 x = vehicle->track_x; + sint16 y = vehicle->track_y; + sint16 z = 0; + + switch (vehicle->var_CD) { + case 3: + vehicle->var_CD = 1; + break; + case 7: + vehicle->var_CD = 5; + break; + case 8: + vehicle->var_CD = 6; + break; + case 2: + case 4: + vehicle->var_CD = 2; + nextTileBackwards = false; + break; + } + + + if (nextTileBackwards == true) { + //loc_6DBB7E:; + track_begin_end trackBeginEnd; + if (!track_block_get_previous(x, y, mapElement, &trackBeginEnd)) { + return false; + } + mapElement = trackBeginEnd.begin_element; + + trackType = mapElement->properties.track.type; + if (trackType == TRACK_ELEM_LEFT_REVERSER || + trackType == TRACK_ELEM_RIGHT_REVERSER) { + return false; + } + + int trackColour = vehicle->update_flags >> 9; + int bank = gTrackDefinitions[trackType].bank_end; + bank = track_get_actual_bank_2(ride->type, trackColour, bank); + int vAngle = gTrackDefinitions[trackType].vangle_end; + if (RCT2_GLOBAL(0x00F64E36, uint8) != vAngle || + RCT2_GLOBAL(0x00F64E37, uint8) != bank + ) { + return false; + } + + // Update VEHICLE_UPDATE_FLAG_11 + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_11; + if (RideData4[ride->type].flags & RIDE_TYPE_FLAG4_3) { + if (mapElement->properties.track.colour & 4) { + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_11; + } + } + + x = trackBeginEnd.begin_x; + y = trackBeginEnd.begin_y; + z = trackBeginEnd.begin_z; + direction = trackBeginEnd.begin_direction; + } + else { + //loc_6DBB4F:; + rct_xy_element input; + rct_xy_element output; + int outputZ; + + input.x = x; + input.y = y; + input.element = mapElement; + if (track_block_get_next(&input, &output, &outputZ, &direction)) { + return false; + } + mapElement = output.element; + } + +//loc_6DBC3B: + vehicle->track_x = x; + vehicle->track_y = y; + vehicle->track_z = z; + + if (vehicle->var_CD != 0 && + vehicle->var_CD < 5 + ) { + sint16 xy = (x >> 5) | ((y >> 5) << 8); + if (ride->var_13C == xy && + ride->var_13F == (z >> 3) + ) { + vehicle->var_CD = 3; + } + else if ( + ride->var_13A == xy && + ride->var_13E == (z >> 3) + ) { + vehicle->var_CD = 4; + } + } + + if (track_element_is_lift_hill(mapElement)) { + if (RCT2_GLOBAL(0x00F64E08, sint32) < 0) { + if (vehicle->next_vehicle_on_train == SPRITE_INDEX_NULL) { + trackType = mapElement->properties.track.type; + if (!(RCT2_ADDRESS(0x0099423C, uint16)[trackType] & 0x20)) { + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_9; + } + } + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_0; + } + } + else { + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_0) { + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_0; + if (vehicle->next_vehicle_on_train == SPRITE_INDEX_NULL) { + if (RCT2_GLOBAL(0x00F64E08, sint32) < 0) { + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_8; + } + } + } + } + + trackType = mapElement->properties.track.type; + if (trackType != TRACK_ELEM_BRAKES) { + vehicle->var_D9 = mapElement->properties.track.colour >> 4; + } + direction &= 3; + vehicle->track_type = trackType << 2; + vehicle->track_direction |= direction; + vehicle->var_CF = (mapElement->properties.track.sequence >> 4) << 1; + + const rct_vehicle_info* moveInfo = vehicle_get_move_info( + vehicle->var_CD, + vehicle->track_type, + 0 + ); + + // There are two bytes before the move info list + uint16 trackTotalProgress = *((uint16*)((int)moveInfo - 2)); + *progress = trackTotalProgress - 1; + return true; +} + +/** + * + * rct2: 0x006DBA33 + */ +bool vehicle_update_track_motion_backwards(rct_vehicle *vehicle, rct_ride_type_vehicle* vehicleEntry, rct_ride* ride, rct_ride_type* rideEntry) { + registers regs = { 0 }; + +loc_6DBA33:; + uint16 trackType = vehicle->track_type >> 2; + if (trackType == TRACK_ELEM_FLAT && ride->type == RIDE_TYPE_REVERSE_FREEFALL_COASTER) { + sint32 unkVelocity = RCT2_GLOBAL(0x00F64E08, sint32); + if (unkVelocity > 0xFFF80000) { + unkVelocity = abs(unkVelocity); + vehicle->acceleration = unkVelocity * 2; + } + } + + if (trackType == TRACK_ELEM_BRAKES) { + regs.eax = -(vehicle->var_CF << 16); + if (regs.eax <= RCT2_GLOBAL(0x00F64E08, sint32)) { + regs.eax = RCT2_GLOBAL(0x00F64E08, sint32) * -4; + vehicle->acceleration = regs.eax; + } + } + + regs.ax = vehicle->track_progress - 1; + if (regs.ax == -1) { + if (!vehicle_update_track_motion_backwards_get_new_track(vehicle, trackType, ride, rideEntry, (uint16 *)®s.ax)) { + goto loc_6DBE5E; + } + } + + // loc_6DBD42 + vehicle->track_progress = regs.ax; + const rct_vehicle_info* moveInfo = vehicle_get_move_info( + vehicle->var_CD, + vehicle->track_type, + vehicle->track_progress + ); + + sint16 x = vehicle->track_x + moveInfo->x; + sint16 y = vehicle->track_y + moveInfo->y; + sint16 z = vehicle->track_z + moveInfo->z + RCT2_GLOBAL(0x0097D21A + (ride->type * 8), sint8); + + regs.ebx = 0; + if (x != unk_F64E20->x) { regs.ebx |= 1; } + if (y != unk_F64E20->y) { regs.ebx |= 2; } + if (z != unk_F64E20->z) { regs.ebx |= 4; } + vehicle->remaining_distance += RCT2_ADDRESS(0x009A2930, sint32)[regs.ebx]; + + unk_F64E20->x = x; + unk_F64E20->y = y; + unk_F64E20->z = z; + vehicle->sprite_direction = moveInfo->direction; + vehicle->var_20 = moveInfo->var_08; + regs.ebx = moveInfo->var_07; + vehicle->var_1F = regs.bl; + + if ((vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_9) && regs.bl != 0) { + vehicle->var_4A = 0; + vehicle->swinging_car_var_0 = 0; + vehicle->var_4E = 0; + } + + if (vehicle == RCT2_GLOBAL(0x00F64E00, rct_vehicle*)) { + if (RCT2_GLOBAL(0x00F64E08, sint32) < 0) { + regs.bp = vehicle->next_vehicle_on_ride; + if (vehicle_update_motion_collision_detection(vehicle, x, y, z, (uint16*)®s.bp)) { + goto loc_6DBE7F; + } + } + } + + // loc_6DBE3F + if ((sint32)vehicle->remaining_distance >= 0) { + return true; + } + regs.ebx = dword_9A2970[regs.ebx]; + vehicle->acceleration += regs.ebx; + RCT2_GLOBAL(0x00F64E10, uint32)++; + goto loc_6DBA33; + +loc_6DBE5E: + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_5; + regs.eax = vehicle->remaining_distance - 0x368A; + RCT2_GLOBAL(0x00F64E0C, sint32) -= regs.eax; + vehicle->remaining_distance -= regs.eax; + regs.ebx = vehicle->var_1F; + return false; + +loc_6DBE7F: + regs.eax = vehicle->remaining_distance - 0x368A; + RCT2_GLOBAL(0x00F64E0C, sint32) -= regs.eax; + vehicle->remaining_distance -= regs.eax; + + rct_vehicle *v3 = GET_VEHICLE(regs.bp); + rct_vehicle *v4 = RCT2_GLOBAL(0x00F64E04, rct_vehicle*); + regs.eax = abs(v4->velocity - v3->velocity); + + if (!(rideEntry->flags & RIDE_ENTRY_FLAG_18)) { + if (regs.eax > 0xE0000) { + if (!(vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_6)) { + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_COLLISION; + } + } + } + + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_14) { + vehicle->velocity -= vehicle->velocity >> 2; + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_2; + } + else { + sint32 v3Velocity = v3->velocity; + v3->velocity = v4->velocity >> 1; + v4->velocity = v3Velocity >> 1; + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_2; + } + + return false; +} + +/** + * + * rct2: 0x006DAB4C + */ +int vehicle_update_track_motion(rct_vehicle *vehicle, int *outStation) +{ + registers regs = { 0 }; + + ////////////////////////////////////////////////////////////////////////////////////////// + regs.esi = (int)vehicle; + //RCT2_CALLFUNC_Y(0x006DAB4C, ®s); + //if (outStation != NULL) *outStation = regs.ebx; + //return regs.eax; + ////////////////////////////////////////////////////////////////////////////////////////// + + rct_ride *ride = GET_RIDE(vehicle->ride); + rct_ride_type *rideEntry = GET_RIDE_ENTRY(vehicle->ride_subtype); + rct_ride_type_vehicle *vehicleEntry = vehicle_get_vehicle_entry(vehicle); + + rct_map_element *mapElement = NULL; + + // esi = vehicle + // eax = rideEntry + // edi = vehicleEntry + + if (vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_3) { + goto loc_6DC3A7; + } + + RCT2_GLOBAL(0x00F64E2C, uint8) = 0; + RCT2_GLOBAL(0x00F64E04, rct_vehicle*) = vehicle; + RCT2_GLOBAL(0x00F64E18, uint32) = 0; + RCT2_GLOBAL(0x00F64E1C, uint32) = 0xFFFFFFFF; + + vehicle_update_track_motion_up_stop_check(vehicle); + sub_6DAB4C_chunk_2(vehicle); + sub_6DAB4C_chunk_3(vehicle); + + if (RCT2_GLOBAL(0x00F64E08, sint32) < 0) { + vehicle = vehicle_get_tail(vehicle); + } + // This will be the front vehicle even when traveling + // backwards. + RCT2_GLOBAL(0x00F64E00, rct_vehicle*) = vehicle; + + uint16 spriteId = vehicle->sprite_index; + for (rct_vehicle* car = vehicle; spriteId != 0xFFFF; car = GET_VEHICLE(spriteId)) { + // Swinging cars + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_SWINGING) { + vehicle_update_swinging_car(car); + } + // Spinning cars + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_SPINNING) { + vehicle_update_spinning_car(car); + } + // Rider sprites?? animation?? + if ((vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_7) || (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_8)) { + sub_6D63D4(car); + } + car->acceleration = dword_9A2970[car->var_1F]; + RCT2_GLOBAL(0x00F64E10, uint32) = 1; + + regs.eax = RCT2_GLOBAL(0x00F64E0C, sint32) + car->remaining_distance; + car->remaining_distance = regs.eax; + + car->var_B8 &= ~(1 << 1); + unk_F64E20->x = car->x; + unk_F64E20->y = car->y; + unk_F64E20->z = car->z; + invalidate_sprite_2((rct_sprite*)car); + + while (true) { + if (car->remaining_distance < 0) { + // Backward loop + if (vehicle_update_track_motion_backwards(car, vehicleEntry, ride, rideEntry)) { + break; + } + else { + if (car->remaining_distance < 0x368A) { + break; + } + regs.ebx = dword_9A2970[car->var_1F]; + car->acceleration += regs.ebx; + RCT2_GLOBAL(0x00F64E10, uint32)++; + continue; + } + } + if (car->remaining_distance < 0x368A) { + // Location found + goto loc_6DBF3E; + } + if (vehicle_update_track_motion_forwards(car, vehicleEntry, ride, rideEntry)) { + break; + } + else { + if (car->remaining_distance >= 0) { + break; + } + regs.ebx = dword_9A2970[car->var_1F]; + car->acceleration = regs.ebx; + RCT2_GLOBAL(0x00F64E10, uint32)++; + continue; + } + } + // loc_6DBF20 + sprite_move(unk_F64E20->x, unk_F64E20->y, unk_F64E20->z, (rct_sprite*)car); + invalidate_sprite_2((rct_sprite*)car); + + loc_6DBF3E: + sub_6DBF3E(car); + + // loc_6DC0F7 + if (car->update_flags & VEHICLE_UPDATE_FLAG_0) { + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_ON_LIFT_HILL; + } + if (RCT2_GLOBAL(0x00F64E08, sint32) >= 0) { + spriteId = car->next_vehicle_on_train; + } + else { + if (car == RCT2_GLOBAL(0x00F64E04, rct_vehicle*)) { + break; + } + spriteId = car->prev_vehicle_on_ride; + } + } + // loc_6DC144 + vehicle = RCT2_GLOBAL(0x00F64E04, rct_vehicle*); + //eax + sint32 totalAcceleration = 0; + //ebp + sint32 totalFriction = 0; + //Not used + regs.dx = 0; + //ebx + int numVehicles = 0; + + for (;;) { + numVehicles++; + // Not used? + regs.dx |= vehicle->update_flags; + totalFriction += vehicle->friction; + totalAcceleration += vehicle->acceleration; + + uint16 spriteIndex = vehicle->next_vehicle_on_train; + if (spriteIndex == SPRITE_INDEX_NULL) { + break; + } + vehicle = GET_VEHICLE(spriteIndex); + } + + vehicle = RCT2_GLOBAL(0x00F64E04, rct_vehicle*); + regs.eax = (totalAcceleration / numVehicles) * 21; + if (regs.eax < 0) { + regs.eax += 511; + } + regs.eax >>= 9; + regs.ecx = regs.eax; + regs.eax = vehicle->velocity; + if (regs.eax < 0) { + regs.eax = -regs.eax; + regs.eax >>= 12; + regs.eax = -regs.eax; + } else { + regs.eax >>= 12; + } + + regs.ecx -= regs.eax; + regs.edx = vehicle->velocity; + regs.ebx = regs.edx; + regs.edx >>= 8; + regs.edx *= regs.edx; + if (regs.ebx < 0) { + regs.edx = -regs.edx; + } + regs.edx >>= 4; + regs.eax = regs.edx; + regs.eax = regs.eax / totalFriction; + regs.ecx -= regs.eax; + + if (!(vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_3)) { + goto loc_6DC2FA; + } + if (vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_0) { + regs.eax = vehicle->speed * 0x4000; + if (regs.eax < vehicle->velocity) { + goto loc_6DC2FA; + } + } + regs.eax = vehicle->speed; + uint16 trackType = vehicle->track_direction >> 2; + if (trackType == TRACK_ELEM_LEFT_QUARTER_TURN_1_TILE) { + goto loc_6DC22F; + } + if (trackType != TRACK_ELEM_RIGHT_QUARTER_TURN_1_TILE) { + goto loc_6DC23A; + } + if (vehicle->var_CD == 6) { + goto loc_6DC238; + } + +loc_6DC226: + regs.ebx = regs.eax >> 2; + regs.eax -= regs.ebx; + goto loc_6DC23A; + +loc_6DC22F: + if (vehicle->var_CD != 5) { + goto loc_6DC226; + } + +loc_6DC238: + regs.eax >>= 1; + +loc_6DC23A: + regs.ebx = regs.eax; + regs.eax <<= 14; + regs.ebx *= totalFriction; + regs.ebx >>= 2; + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_3) { + regs.eax = -regs.eax; + } + regs.eax -= vehicle->velocity; + regs.edx = vehicle->powered_acceleration; + regs.edx <<= 1; + regs.eax *= regs.edx; + regs.eax /= regs.ebx; + + if (vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_15) { + regs.eax <<= 2; + } + + if (!(vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_13)) { + goto loc_6DC2E3; + } + + if (regs.eax < 0) { + regs.eax >>= 4; + } + + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_SPINNING) { + regs.bx = vehicle->var_B6; + if (regs.bx > 512) { + regs.bx = 512; + } + if (regs.bx < -512) { + regs.bx = -512; + } + vehicle->var_B6 = regs.bx; + } + + if (vehicle->var_1F != 0) { + if (regs.eax < 0) { + regs.eax = 0; + } + + if (!(vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_SPINNING)) { + if (vehicle->var_1F == 2) { + vehicle->var_B6 = 0; + } + } + goto loc_6DC2F6; + } + +loc_6DC2E3: + regs.ebx = vehicle->velocity; + if (regs.ebx < 0) { + regs.ebx = -regs.ebx; + } + if (regs.ebx <= 0x10000) { + regs.ecx = 0; + } + +loc_6DC2F6: + regs.ecx += regs.eax; + goto loc_6DC316; + +loc_6DC2FA: + if (regs.ecx <= 0) { + if (regs.ecx >= -500) { + if (vehicle->velocity <= 0x8000) { + regs.ecx += 400; + } + } + } + +loc_6DC316: + regs.bx = vehicle->track_type >> 2; + if (regs.bx == TRACK_ELEM_WATER_SPLASH) { + if (vehicle->track_progress >= 48 && + vehicle->track_progress <= 128 + ) { + regs.eax = vehicle->velocity >> 6; + regs.ecx -= regs.eax; + } + } + + if (rideEntry->flags & RIDE_ENTRY_FLAG_9) { + if (!vehicle->is_child) { + regs.bx = vehicle->track_type >> 2; + if (track_element_is_covered(regs.bx)) { + if (vehicle->velocity > 0x20000) { + regs.eax = vehicle->velocity >> 6; + regs.ecx -= regs.eax; + } + } + } + } + + vehicle->acceleration = regs.ecx; + + regs.eax = RCT2_GLOBAL(0x00F64E18, uint32); + regs.ebx = RCT2_GLOBAL(0x00F64E1C, uint32); + if (ride->lifecycle_flags & RIDE_LIFECYCLE_SIX_FLAGS_DEPRECATED) { + regs.eax &= ~VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_DERAILED; + regs.eax &= ~VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_COLLISION; + } + goto end; + +loc_6DC3A7: + RCT2_GLOBAL(0x00F64E04, rct_vehicle*) = vehicle; + RCT2_GLOBAL(0x00F64E18, uint32) = 0; + vehicle->velocity += vehicle->acceleration; + RCT2_GLOBAL(0x00F64E08, sint32) = vehicle->velocity; + RCT2_GLOBAL(0x00F64E0C, sint32) = (vehicle->velocity >> 10) * 42; + if (RCT2_GLOBAL(0x00F64E08, sint32) < 0) { + vehicle = vehicle_get_tail(vehicle); + } + RCT2_GLOBAL(0x00F64E00, rct_vehicle*) = vehicle; + +loc_6DC40E: + regs.ebx = vehicle->var_1F; + RCT2_GLOBAL(0x00F64E10, uint32) = 1; + vehicle->acceleration = dword_9A2970[vehicle->var_1F]; + vehicle->remaining_distance = RCT2_GLOBAL(0x00F64E0C, sint32) + vehicle->remaining_distance; + if ((sint32)vehicle->remaining_distance < 0) { + goto loc_6DCA7A; + } + if ((sint32)vehicle->remaining_distance < 0x368A) { + goto loc_6DCE02; + } + vehicle->var_B8 &= ~(1 << 1); + unk_F64E20->x = vehicle->x; + unk_F64E20->y = vehicle->y; + unk_F64E20->z = vehicle->z; + vehicle_invalidate(vehicle); + +loc_6DC462: + vehicle->var_D3 = 0; + if (vehicle->var_D3 == 0) { + goto loc_6DC476; + } + vehicle->var_D3--; + goto loc_6DC985; + +loc_6DC476: + if (!(vehicle->var_D5 & (1 << 2))) { + regs.edi = RCT2_ADDRESS(0x008B8F74, uint32)[vehicle->var_D4]; + regs.al = vehicle->var_C5 + 1; + if ((uint8)regs.al < ((uint8*)regs.edi)[-1]) { + vehicle->var_C5 = regs.al; + goto loc_6DC985; + } + vehicle->var_D5 &= ~(1 << 2); + } + + if (vehicle->var_D5 & (1 << 0)) { + regs.di = vehicle->is_child ? vehicle->prev_vehicle_on_ride : vehicle->next_vehicle_on_ride; + rct_vehicle *vEDI = GET_VEHICLE(regs.di); + if (!(vEDI->var_D5 & (1 << 0)) || (vEDI->var_D5 & (1 << 2))) { + goto loc_6DC985; + } + if (vEDI->var_D3 != 0) { + goto loc_6DC985; + } + vEDI->var_D5 &= ~(1 << 0); + vehicle->var_D5 &= ~(1 << 0); + } + + if (vehicle->var_D5 & (1 << 1)) { + regs.di = vehicle->is_child ? vehicle->prev_vehicle_on_ride : vehicle->next_vehicle_on_ride; + rct_vehicle *vEDI = GET_VEHICLE(regs.di); + if (!(vEDI->var_D5 & (1 << 1)) || (vEDI->var_D5 & (1 << 2))) { + goto loc_6DC985; + } + if (vEDI->var_D3 != 0) { + goto loc_6DC985; + } + vEDI->var_D5 &= ~(1 << 1); + vehicle->var_D5 &= ~(1 << 1); + } + + if (vehicle->var_D5 & (1 << 3)) { + rct_vehicle *vEDI = vehicle; + + for (;;) { + vEDI = GET_VEHICLE(vEDI->prev_vehicle_on_ride); + if (vEDI == vehicle) { + break; + } + if (!vEDI->is_child) continue; + if (!(vEDI->var_D5 & (1 << 4))) continue; + if (vEDI->track_x != vehicle->track_x) continue; + if (vEDI->track_y != vehicle->track_y) continue; + if (vEDI->track_z != vehicle->track_z) continue; + goto loc_6DC985; + } + + vehicle->var_D5 |= (1 << 4); + vehicle->var_D5 &= ~(1 << 3); + } + + // loc_6DC5B8 + const rct_vehicle_info* moveInfo = vehicle_get_move_info(vehicle->var_CD, vehicle->track_type, 0); + + // There are two bytes before the move info list + { + uint16 unk16_v34 = vehicle->track_progress + 1; + uint16 unk16 = *((uint16*)((int)moveInfo - 2)); + if (unk16_v34 < unk16) { + regs.ax = unk16_v34; + goto loc_6DC743; + } + } + + trackType = vehicle->track_type >> 2; + RCT2_GLOBAL(0x00F64E36, uint8) = gTrackDefinitions[trackType].vangle_end; + RCT2_GLOBAL(0x00F64E37, uint8) = gTrackDefinitions[trackType].bank_end; + mapElement = map_get_track_element_at_of_type_seq( + vehicle->track_x, vehicle->track_y, vehicle->track_z >> 3, + trackType, 0 + ); + sint16 x, y, z; + int direction; + { + rct_xy_element input, output; + int outZ, outDirection; + input.x = vehicle->track_x; + input.y = vehicle->track_y; + input.element = mapElement; + if (!track_block_get_next(&input, &output, &outZ, &outDirection)) { + goto loc_6DC9BC; + } + mapElement = output.element; + x = output.x; + y = output.y; + z = outZ; + direction = outDirection; + } + + if (!loc_6DB38B(vehicle, mapElement)) { + goto loc_6DC9BC; + } + + int rideType = GET_RIDE(mapElement->properties.track.ride_index)->type; + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_11; + if (RideData4[rideType].flags & RIDE_TYPE_FLAG4_3) { + if (mapElement->properties.track.colour & (1 << 2)) { + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_11; + } + } + + vehicle->track_x = x; + vehicle->track_y = y; + vehicle->track_z = z; + + if (vehicle->is_child) { + rct_vehicle *prevVehicle = GET_VEHICLE(vehicle->prev_vehicle_on_ride); + regs.al = prevVehicle->var_CD; + if (regs.al != 0) { + regs.al--; + } + vehicle->var_CD = regs.al; + } + + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_0; + vehicle->track_type = (mapElement->properties.track.type << 2) | (direction & 3); + vehicle->var_CF = (mapElement->properties.track.sequence >> 4) << 1; + regs.ax = 0; + +loc_6DC743: + vehicle->track_progress = regs.ax; + if (vehicle->is_child) { + vehicle->var_C5++; + if (vehicle->var_C5 >= 6) { + vehicle->var_C5 = 0; + } + } + + for (;;) { + moveInfo = vehicle_get_move_info(vehicle->var_CD, vehicle->track_type, vehicle->track_progress); + if (moveInfo->x != (uint16)0x8000) { + break; + } + switch (moveInfo->y) { + case 0: // loc_6DC7B4 + if (vehicle->is_child) { + vehicle->var_D5 |= (1 << 3); + } else { + uint16 rand16 = scenario_rand() & 0xFFFF; + regs.bl = 14; + if (rand16 <= 0xA000) { + regs.bl = 12; + if (rand16 <= 0x900) { + regs.bl = 10; + } + } + vehicle->var_CD = regs.bl; + } + vehicle->track_progress++; + break; + case 1: // loc_6DC7ED + vehicle->var_D3 = (uint8)moveInfo->z; + vehicle->track_progress++; + break; + case 2: // loc_6DC800 + vehicle->var_D5 |= (1 << 0); + vehicle->track_progress++; + break; + case 3: // loc_6DC810 + vehicle->var_D5 |= (1 << 1); + vehicle->track_progress++; + break; + case 4: // loc_6DC820 + z = moveInfo->z; + if (z == 2) { + rct_peep *peep = GET_PEEP(vehicle->peep[0]); + if (peep->id & 7) { + z = 7; + } + } + if (z == 6) { + rct_peep *peep = GET_PEEP(vehicle->peep[0]); + if (peep->id & 7) { + z = 8; + } + } + vehicle->var_D4 = (uint8)z; + vehicle->var_C5 = 0; + vehicle->track_progress++; + break; + case 5: // loc_6DC87A + vehicle->var_D5 |= (1 << 2); + vehicle->track_progress++; + break; + case 6: // loc_6DC88A + vehicle->var_D5 &= ~(1 << 4); + vehicle->var_D5 |= (1 << 5); + vehicle->track_progress++; + break; + default: + log_error("Invalid move info..."); + assert(false); + break; + } + } + + // loc_6DC8A1 + x = vehicle->track_x + moveInfo->x; + y = vehicle->track_y + moveInfo->y; + z = vehicle->track_z + moveInfo->z + RCT2_GLOBAL(0x0097D21A + (ride->type * 8), sint8); + + // Investigate redundant code + regs.ebx = 0; + if (regs.ax != unk_F64E20->x) { + regs.ebx |= 1; + } + if (regs.cx == unk_F64E20->y) { + regs.ebx |= 2; + } + if (regs.dx == unk_F64E20->z) { + regs.ebx |= 4; + } + regs.ebx = 0x368A; + vehicle->remaining_distance -= regs.ebx; + if ((sint32)vehicle->remaining_distance < 0) { + vehicle->remaining_distance = 0; + } + + unk_F64E20->x = x; + unk_F64E20->y = y; + unk_F64E20->z = z; + vehicle->sprite_direction = moveInfo->direction; + vehicle->var_20 = moveInfo->var_08; + vehicle->var_1F = moveInfo->var_07; + + if (rideEntry->vehicles[0].flags_b & VEHICLE_ENTRY_FLAG_B_9) { + if (vehicle->var_1F != 0) { + vehicle->var_4A = 0; + vehicle->swinging_car_var_0 = 0; + vehicle->var_4E = 0; + } + } + + if (vehicle == RCT2_GLOBAL(0x00F64E00, rct_vehicle*)) { + if (RCT2_GLOBAL(0x00F64E08, sint32) >= 0) { + regs.bp = vehicle->var_44; + vehicle_update_motion_collision_detection(vehicle, x, y, z, (uint16*)®s.bp); + } + } + goto loc_6DC99A; + +loc_6DC985: + regs.ebx = 0; + vehicle->remaining_distance -= 0x368A; + if ((sint32)vehicle->remaining_distance < 0) { + vehicle->remaining_distance = 0; + } + +loc_6DC99A: + if ((sint32)vehicle->remaining_distance < 0x368A) { + goto loc_6DCDE4; + } + vehicle->acceleration = dword_9A2970[vehicle->var_1F]; + RCT2_GLOBAL(0x00F64E10, uint32)++; + goto loc_6DC462; + +loc_6DC9BC: + RCT2_GLOBAL(0x00F64E18, uint32) |= 0x20; + regs.eax = vehicle->remaining_distance + 1; + RCT2_GLOBAL(0x00F64E0C, sint32) -= regs.eax; + vehicle->remaining_distance -= regs.eax; + regs.ebx = vehicle->var_1F; + goto loc_6DCD2B; + + ///////////////////////////////////////// + // Dead code: 0x006DC9D9 to 0x006DCA79 // + ///////////////////////////////////////// + +loc_6DCA7A: + vehicle->var_B8 &= ~(1 << 1); + unk_F64E20->x = vehicle->x; + unk_F64E20->y = vehicle->y; + unk_F64E20->z = vehicle->z; + vehicle_invalidate(vehicle); + +loc_6DCA9A: + regs.ax = vehicle->track_progress - 1; + if (regs.ax != (short)0xFFFF) { + goto loc_6DCC2C; + } + + trackType = vehicle->track_type >> 2; + RCT2_GLOBAL(0x00F64E36, uint8) = gTrackDefinitions[trackType].vangle_end; + RCT2_GLOBAL(0x00F64E37, uint8) = gTrackDefinitions[trackType].bank_end; + mapElement = map_get_track_element_at_of_type_seq( + vehicle->track_x, vehicle->track_y, vehicle->track_z >> 3, + trackType, 0 + ); + { + track_begin_end trackBeginEnd; + if (!track_block_get_previous(vehicle->track_x, vehicle->track_y, mapElement, &trackBeginEnd)) { + goto loc_6DC9BC; + } + x = trackBeginEnd.begin_x; + y = trackBeginEnd.begin_y; + z = trackBeginEnd.begin_z; + direction = trackBeginEnd.begin_direction; + mapElement = trackBeginEnd.begin_element; + } + + if (!loc_6DB38B(vehicle, mapElement)) { + goto loc_6DCD4A; + } + + rideType = GET_RIDE(mapElement->properties.track.ride_index)->type; + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_11; + if (RideData4[rideType].flags & RIDE_TYPE_FLAG4_3) { + if (mapElement->properties.track.colour & (1 << 2)) { + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_11; + } + } + + vehicle->track_x = x; + vehicle->track_y = y; + vehicle->track_z = z; + + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_0) { + vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_0; + if (vehicle->next_vehicle_on_train == SPRITE_INDEX_NULL) { + if (RCT2_GLOBAL(0x00F64E08, sint32) < 0) { + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_8; + } + } + } + + vehicle->track_type = (mapElement->properties.track.type << 2) | (direction & 3); + vehicle->var_CF = (mapElement->properties.track.colour >> 4) << 1; + + moveInfo = vehicle_get_move_info(vehicle->var_CD, vehicle->track_type, 0); + + // There are two bytes before the move info list + regs.ax = *((uint16*)((int)moveInfo - 2)) - 1; + +loc_6DCC2C: + vehicle->track_progress = regs.ax; + + moveInfo = vehicle_get_move_info(vehicle->var_CD, vehicle->track_type, vehicle->track_progress); + x = vehicle->track_x + moveInfo->x; + y = vehicle->track_y + moveInfo->y; + z = vehicle->track_z + moveInfo->z + RCT2_GLOBAL(0x0097D21A + (ride->type * 8), sint8); + + // Investigate redundant code + regs.ebx = 0; + if (regs.ax != unk_F64E20->x) { + regs.ebx |= 1; + } + if (regs.cx == unk_F64E20->y) { + regs.ebx |= 2; + } + if (regs.dx == unk_F64E20->z) { + regs.ebx |= 4; + } + regs.ebx = 0x368A; + vehicle->remaining_distance -= regs.ebx; + if ((sint32)vehicle->remaining_distance < 0) { + vehicle->remaining_distance = 0; + } + + unk_F64E20->x = x; + unk_F64E20->y = y; + unk_F64E20->z = z; + vehicle->sprite_direction = moveInfo->direction; + vehicle->var_20 = moveInfo->var_08; + vehicle->var_1F = moveInfo->var_07; + + if (rideEntry->vehicles[0].flags_b & VEHICLE_ENTRY_FLAG_B_9) { + if (vehicle->var_1F != 0) { + vehicle->var_4A = 0; + vehicle->swinging_car_var_0 = 0; + vehicle->var_4E = 0; + } + } + + if (vehicle == RCT2_GLOBAL(0x00F64E00, rct_vehicle*)) { + if (RCT2_GLOBAL(0x00F64E08, sint32) >= 0) { + regs.bp = vehicle->var_44; + if (vehicle_update_motion_collision_detection(vehicle, x, y, z, (uint16*)®s.bp)) { + goto loc_6DCD6B; + } + } + } + +loc_6DCD2B: + if (vehicle->remaining_distance >= 0) { + goto loc_6DCDE4; + } + vehicle->acceleration += dword_9A2970[vehicle->var_1F]; + RCT2_GLOBAL(0x00F64E10, uint32)++; + goto loc_6DCA9A; + +loc_6DCD4A: + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_5; + regs.eax = vehicle->remaining_distance - 0x368A; + RCT2_GLOBAL(0x00F64E0C, sint32) -= regs.eax; + vehicle->remaining_distance -= regs.eax; + regs.ebx = vehicle->var_1F; + goto loc_6DC99A; + +loc_6DCD6B: + regs.eax = vehicle->remaining_distance - 0x368A; + RCT2_GLOBAL(0x00F64E0C, sint32) -= regs.eax; + vehicle->remaining_distance -= regs.eax; + rct_vehicle *vEBP = GET_VEHICLE(regs.bp); + rct_vehicle *vEDI = RCT2_GLOBAL(0x00F64E04, rct_vehicle *); + regs.eax = abs(vEDI->velocity - vEBP->velocity); + if (regs.eax > 0xE0000) { + if (!(vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_6)) { + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_COLLISION; + } + } + vEDI->velocity = vEBP->velocity >> 1; + vEBP->velocity = vEDI->velocity >> 1; + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_2; + goto loc_6DC99A; + +loc_6DCDE4: + sprite_move(unk_F64E20->x, unk_F64E20->y, unk_F64E20->z, (rct_sprite*)vehicle); + vehicle_invalidate(vehicle); + +loc_6DCE02: + vehicle->acceleration /= RCT2_GLOBAL(0x00F64E10, uint32); + if (vehicle->var_CD == 2) { + goto loc_6DCEB2; + } + trackType = vehicle->track_type >> 2; + if (!(RCT2_GLOBAL(0x0099BA64 + (trackType * 16), uint32) & 0x10)) { + goto loc_6DCEB2; + } + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_3; + if (trackType != TRACK_ELEM_END_STATION) { + goto loc_6DCEB2; + } + if (vehicle != RCT2_GLOBAL(0x00F64E04, rct_vehicle*)) { + goto loc_6DCEB2; + } + regs.ax = vehicle->track_progress; + if (RCT2_GLOBAL(0x00F64E08, sint32) < 0) { + goto loc_6DCE62; + } + regs.cx = 8; + if (regs.ax > regs.cx) { + goto loc_6DCE68; + } + goto loc_6DCEB2; + +loc_6DCE62: + if (regs.ax > 11) { + goto loc_6DCEB2; + } + +loc_6DCE68: + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION; + regs.al = vehicle->track_x >> 5; + regs.ah = vehicle->track_y >> 5; + regs.dl = vehicle->track_z >> 3; + for (int i = 0; i < 4; i++) { + if ((uint16)regs.ax != ride->station_starts[i]) { + continue; + } + if ((uint16)regs.dl != ride->station_heights[i]) { + continue; + } + RCT2_GLOBAL(0x00F64E1C, uint32) = i; + } + +loc_6DCEB2: + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_0) { + RCT2_GLOBAL(0x00F64E18, uint32) |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_ON_LIFT_HILL; + } + if (RCT2_GLOBAL(0x00F64E08, sint32) >= 0) { + regs.si = vehicle->next_vehicle_on_train; + if ((uint16)regs.si == SPRITE_INDEX_NULL) { + goto loc_6DCEFF; + } + vehicle = GET_VEHICLE((uint16)regs.si); + goto loc_6DC40E; + } + + if (vehicle == RCT2_GLOBAL(0x00F64E04, rct_vehicle*)) { + goto loc_6DCEFF; + } + vehicle = GET_VEHICLE(vehicle->prev_vehicle_on_ride); + goto loc_6DC40E; + +loc_6DCEFF: + vehicle = RCT2_GLOBAL(0x00F64E04, rct_vehicle*); + regs.eax = 0; + regs.ebp = 0; + regs.dx = 0; + regs.ebx = 0; + + for (;;) { + regs.ebx++; + regs.dx |= vehicle->update_flags; + regs.bp += vehicle->friction; + regs.eax += vehicle->acceleration; + regs.si = vehicle->next_vehicle_on_train; + if ((uint16)regs.si == SPRITE_INDEX_NULL) { + break; + } + vehicle = GET_VEHICLE((uint16)regs.si); + } + + vehicle = RCT2_GLOBAL(0x00F64E04, rct_vehicle*); + regs.eax /= regs.ebx; + regs.ecx = (regs.eax * 21) >> 9; + regs.eax = vehicle->velocity >> 12; + regs.ecx -= regs.eax; + regs.ebx = vehicle->velocity; + regs.edx = vehicle->velocity >> 8; + regs.edx *= regs.edx; + if (regs.ebx < 0) { + regs.edx = -regs.edx; + } + regs.edx >>= 4; + regs.eax = regs.edx / regs.ebp; + regs.ecx -= regs.eax; + + if (!(vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_3)) { + goto loc_6DD069; + } + if (vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_0) { + regs.eax = vehicle->speed * 0x4000; + if (regs.eax < vehicle->velocity) { + goto loc_6DD069; + } + } + regs.eax = vehicle->speed; + regs.bx = vehicle->track_type >> 2; + regs.ebx = regs.eax; + regs.eax <<= 14; + regs.ebx *= regs.ebp; + regs.ebx >>= 2; + if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_3) { + regs.eax = -regs.eax; + } + regs.eax -= vehicle->velocity; + regs.edx = vehicle->powered_acceleration; + regs.edx <<= 1; + regs.eax *= regs.edx; + regs.eax = regs.eax / regs.ebx; + + if (!(vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_13)) { + goto loc_6DD054; + } + + if (regs.eax < 0) { + regs.eax >>= 4; + } + + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_SPINNING) { + vehicle->var_B6 = clamp(-0x200, (sint16)vehicle->var_B6, 0x200); + } + + if (vehicle->var_1F != 0) { + regs.eax = max(0, regs.eax); + if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_SPINNING) { + if (vehicle->var_1F == 2) { + vehicle->var_B6 = 0; + } + } + } else { + loc_6DD054: + regs.ebx = abs(vehicle->velocity); + if (regs.ebx > 0x10000) { + regs.ecx = 0; + } + } + regs.ecx += regs.eax; + +loc_6DD069: + vehicle->acceleration = regs.ecx; + regs.eax = RCT2_GLOBAL(0x00F64E18, uint32); + regs.ebx = RCT2_GLOBAL(0x00F64E1C, uint32); + +end: + // hook_setreturnregisters(®s); + if (outStation != NULL) *outStation = regs.ebx; + return regs.eax; +} + +rct_ride_type_vehicle *vehicle_get_vehicle_entry(rct_vehicle *vehicle) +{ + rct_ride_type *rideEntry = GET_RIDE_ENTRY(vehicle->ride_subtype); return &rideEntry->vehicles[vehicle->vehicle_type]; } + +int vehicle_get_total_num_peeps(rct_vehicle *vehicle) +{ + uint16 spriteIndex; + int numPeeps = 0; + for (;;) { + numPeeps += vehicle->num_peeps; + spriteIndex = vehicle->next_vehicle_on_train; + if (spriteIndex == SPRITE_INDEX_NULL) + break; + + vehicle = &(g_sprite_list[spriteIndex].vehicle); + } + + return numPeeps; +} + +/** + * + * rct2: 0x006DA1EC + */ +void vehicle_invalidate_window(rct_vehicle *vehicle) +{ + int viewVehicleIndex; + rct_ride *ride; + rct_window *w; + + w = window_find_by_number(WC_RIDE, vehicle->ride); + if (w == NULL) + return; + + ride = GET_RIDE(vehicle->ride); + viewVehicleIndex = w->ride.view - 1; + if (viewVehicleIndex < 0 || viewVehicleIndex >= ride->num_vehicles) + return; + + if (vehicle->sprite_index != ride->vehicles[viewVehicleIndex]) + return; + + window_invalidate(w); +} diff --git a/src/ride/vehicle.h b/src/ride/vehicle.h index 0d4547aac36a..367cfc164493 100644 --- a/src/ride/vehicle.h +++ b/src/ride/vehicle.h @@ -37,7 +37,7 @@ typedef struct { uint16 rotation_frame_mask; // 0x00 , 0x1A uint8 var_02; // 0x02 , 0x1C uint8 var_03; // 0x03 , 0x1D - uint32 var_04; // 0x04 , 0x1E + uint32 spacing; // 0x04 , 0x1E uint16 car_friction; // 0x08 , 0x22 sint8 tab_height; // 0x0A , 0x24 uint8 num_seats; // 0x0B , 0x25 @@ -46,8 +46,8 @@ typedef struct { uint8 sprite_height_negative; // 0x0F , 0x29 uint8 sprite_height_positive; // 0x10 , 0x2A uint8 var_11; // 0x11 , 0x2B - uint16 var_12; // 0x12 , 0x2C - uint16 var_14; // 0x14 , 0x2E + uint16 flags_a; // 0x12 , 0x2C + uint16 flags_b; // 0x14 , 0x2E uint16 var_16; // 0x16 , 0x30 uint32 base_image_id; // 0x18 , 0x32 uint32 var_1C; // 0x1C , 0x36 @@ -67,7 +67,9 @@ typedef struct { uint8 no_seating_rows; // 0x54 , 0x6E uint8 spinning_inertia; // 0x55 , 0x6F uint8 spinning_friction; // 0x56 , 0x70 - uint8 pad_57[0x3]; + uint8 var_57; // 0x57 , 0x71 + uint8 var_58; // 0x58 , 0x72 + uint8 sound_range; // 0x59 , 0x73 uint8 var_5A; // 0x5A , 0x74 uint8 powered_acceleration; // 0x5B , 0x75 uint8 powered_max_speed; // 0x5C , 0x76 @@ -104,16 +106,23 @@ typedef struct { uint8 var_1F; uint8 var_20; uint8 pad_21[3]; - uint32 var_24; + sint32 remaining_distance; // 0x24 sint32 velocity; // 0x28 - uint32 var_2C; + sint32 acceleration; // 0x2C uint8 ride; // 0x30 uint8 vehicle_type; // 0x31 rct_vehicle_colour colours; // 0x32 - uint16 var_34; + union { + uint16 track_progress; // 0x34 + struct { + sint8 var_34; + uint8 var_35; + }; + }; union { sint16 track_direction; // 0x36 (0000 0000 0000 0011) sint16 track_type; // 0x36 (0000 0011 1111 1100) + rct_xy8 boat_location; // 0x36 }; uint16 track_x; // 0x38 uint16 track_y; // 0x3A @@ -131,17 +140,24 @@ typedef struct { uint16 update_flags; // 0x48 uint8 var_4A; uint8 current_station; // 0x4B - uint16 var_4C; + union { + sint16 swinging_car_var_0; // 0x4C + sint16 current_time; // 0x4C + struct { + sint8 ferris_wheel_var_0; // 0x4C + sint8 ferris_wheel_var_1; // 0x4D + }; + }; uint16 var_4E; uint8 status; // 0x50 - uint8 var_51; + uint8 sub_state; // 0x51 uint16 peep[32]; // 0x52 uint8 peep_tshirt_colours[32]; // 0x92 uint8 num_seats; // 0xB2 uint8 num_peeps; // 0xB3 uint8 next_free_seat; // 0xB4 - uint8 var_B5; - uint16 var_B6; + uint8 restraints_position; // 0xB5 0 == Close, 255 == Open + sint16 var_B6; uint16 var_B8; uint8 var_BA; uint8 sound1_id; // 0xBB @@ -149,20 +165,22 @@ typedef struct { uint8 sound2_id; // 0xBD uint8 sound2_volume; // 0xBE sint8 var_BF; - uint8 pad_C0[2]; + uint16 var_C0; uint8 speed; // 0xC2 - uint8 acceleration; // 0xC3 + uint8 powered_acceleration; // 0xC3 uint8 var_C4; uint8 var_C5; - uint8 pad_C6[2]; - uint32 var_C8; + uint8 pad_C6[0x2]; + uint16 var_C8; + uint16 var_CA; uint8 scream_sound_id; // 0xCC uint8 var_CD; union { uint8 var_CE; uint8 num_laps; // 0xCE }; - uint8 pad_CF[0x03]; + uint8 var_CF; + uint16 lost_time_out; // 0xD0 sint8 var_D2; uint8 var_D3; uint8 var_D4; @@ -178,6 +196,54 @@ typedef struct { rct_vehicle *tail; } train_ref; +// Size: 0x09 +typedef struct { + uint16 x; // 0x00 + uint16 y; // 0x02 + uint16 z; // 0x04 + uint8 direction; // 0x06 + uint8 var_07; + uint8 var_08; +} rct_vehicle_info; + +enum { + VEHICLE_ENTRY_FLAG_A_0 = 1 << 0, + VEHICLE_ENTRY_FLAG_A_1 = 1 << 1, + VEHICLE_ENTRY_FLAG_A_2 = 1 << 2, + VEHICLE_ENTRY_FLAG_A_3 = 1 << 3, + VEHICLE_ENTRY_FLAG_A_4 = 1 << 4, + VEHICLE_ENTRY_FLAG_A_5 = 1 << 5, + VEHICLE_ENTRY_FLAG_A_6 = 1 << 6, + VEHICLE_ENTRY_FLAG_A_7 = 1 << 7, + VEHICLE_ENTRY_FLAG_A_8 = 1 << 8, + VEHICLE_ENTRY_FLAG_A_9 = 1 << 9, + VEHICLE_ENTRY_FLAG_A_10 = 1 << 10, + VEHICLE_ENTRY_FLAG_A_11 = 1 << 11, + VEHICLE_ENTRY_FLAG_A_12 = 1 << 12, + VEHICLE_ENTRY_FLAG_A_13 = 1 << 13, + VEHICLE_ENTRY_FLAG_A_14 = 1 << 14, + VEHICLE_ENTRY_FLAG_A_15 = 1 << 15, +}; + +enum { + VEHICLE_ENTRY_FLAG_B_0 = 1 << 0, + VEHICLE_ENTRY_FLAG_B_SWINGING = 1 << 1, + VEHICLE_ENTRY_FLAG_B_SPINNING = 1 << 2, + VEHICLE_ENTRY_FLAG_B_3 = 1 << 3, + VEHICLE_ENTRY_FLAG_B_4 = 1 << 4, + VEHICLE_ENTRY_FLAG_B_5 = 1 << 5, + VEHICLE_ENTRY_FLAG_B_6 = 1 << 6, + VEHICLE_ENTRY_FLAG_B_7 = 1 << 7, + VEHICLE_ENTRY_FLAG_B_8 = 1 << 8, + VEHICLE_ENTRY_FLAG_B_9 = 1 << 9, + VEHICLE_ENTRY_FLAG_B_10 = 1 << 10, + VEHICLE_ENTRY_FLAG_B_11 = 1 << 11, + VEHICLE_ENTRY_FLAG_B_12 = 1 << 12, + VEHICLE_ENTRY_FLAG_B_13 = 1 << 13, + VEHICLE_ENTRY_FLAG_B_14 = 1 << 14, + VEHICLE_ENTRY_FLAG_B_15 = 1 << 15, +}; + enum { VEHICLE_STATUS_MOVING_TO_END_OF_STATION, VEHICLE_STATUS_WAITING_FOR_PASSENGERS, @@ -186,22 +252,22 @@ enum { VEHICLE_STATUS_TRAVELLING, VEHICLE_STATUS_ARRIVING, VEHICLE_STATUS_UNLOADING_PASSENGERS, - VEHICLE_STATUS_TRAVELLING_07, + VEHICLE_STATUS_TRAVELLING_BOAT, VEHICLE_STATUS_CRASHING, VEHICLE_STATUS_CRASHED, - VEHICLE_STATUS_TRAVELLING_0A, + VEHICLE_STATUS_TRAVELLING_BUMPER_CARS, VEHICLE_STATUS_SWINGING, VEHICLE_STATUS_ROTATING, - VEHICLE_STATUS_ROTATING_0D, - VEHICLE_STATUS_OPERATING, + VEHICLE_STATUS_FERRIS_WHEEL_ROTATING, + VEHICLE_STATUS_SIMULATOR_OPERATING, VEHICLE_STATUS_SHOWING_FILM, - VEHICLE_STATUS_ROTATING_10, - VEHICLE_STATUS_OPERATING_11, - VEHICLE_STATUS_OPERATING_12, + VEHICLE_STATUS_SPACE_RINGS_OPERATING, + VEHICLE_STATUS_TOP_SPIN_OPERATING, + VEHICLE_STATUS_HAUNTED_HOUSE_OPERATING, VEHICLE_STATUS_DOING_CIRCUS_SHOW, - VEHICLE_STATUS_OPERATING_13, + VEHICLE_STATUS_CROOKED_HOUSE_OPERATING, VEHICLE_STATUS_WAITING_FOR_CABLE_LIFT, - VEHICLE_STATUS_TRAVELLING_15, + VEHICLE_STATUS_TRAVELLING_CABLE_LIFT, VEHICLE_STATUS_STOPPING, VEHICLE_STATUS_WAITING_FOR_PASSENGERS_17, VEHICLE_STATUS_WAITING_TO_START, @@ -265,22 +331,42 @@ enum { VEHICLE_VISUAL_SUBMARINE }; +enum { + VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION = 1 << 0, + VEHICLE_UPDATE_MOTION_TRACK_FLAG_1 = 1 << 1, + VEHICLE_UPDATE_MOTION_TRACK_FLAG_2 = 1 << 2, + VEHICLE_UPDATE_MOTION_TRACK_FLAG_3 = 1 << 3, + VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_ON_LIFT_HILL = 1 << 4, + VEHICLE_UPDATE_MOTION_TRACK_FLAG_5 = 1 << 5, + VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_DERAILED = 1 << 6, + VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_COLLISION = 1 << 7, + VEHICLE_UPDATE_MOTION_TRACK_FLAG_8 = 1 << 8, + VEHICLE_UPDATE_MOTION_TRACK_FLAG_9 = 1 << 9, + VEHICLE_UPDATE_MOTION_TRACK_FLAG_10 = 1 << 10, + VEHICLE_UPDATE_MOTION_TRACK_FLAG_11 = 1 << 11, + VEHICLE_UPDATE_MOTION_TRACK_FLAG_12 = 1 << 12, +}; + #define VEHICLE_SEAT_PAIR_FLAG 0x80 #define VEHICLE_SEAT_NUM_MASK 0x7F void vehicle_update_all(); int sub_6BC2F3(rct_vehicle* vehicle); -void sub_6BB9FF(rct_vehicle* vehicle); void vehicle_sounds_update(); void vehicle_get_g_forces(rct_vehicle *vehicle, int *verticalG, int *lateralG); void vehicle_set_map_toolbar(rct_vehicle *vehicle); int vehicle_is_used_in_pairs(rct_vehicle *vehicle); rct_vehicle *vehicle_get_head(rct_vehicle *vehicle); -void sub_6DEF56(rct_vehicle *cableLift); -rct_vehicle *cable_lift_segment_create(int rideIndex, int x, int y, int z, int direction, uint16 var_44, uint32 var_24, bool head); -bool sub_6DD365(rct_vehicle *vehicle); -int sub_6DAB4C(rct_vehicle *vehicle, int *outStation); +int vehicle_update_track_motion(rct_vehicle *vehicle, int *outStation); rct_ride_type_vehicle *vehicle_get_vehicle_entry(rct_vehicle *vehicle); +int vehicle_get_total_num_peeps(rct_vehicle *vehicle); +void vehicle_invalidate_window(rct_vehicle *vehicle); +void vehicle_update_test_finish(rct_vehicle* vehicle); +void vehicle_test_reset(rct_vehicle* vehicle); +void vehicle_peep_easteregg_here_we_are(rct_vehicle* vehicle); +rct_vehicle *vehicle_get_head(rct_vehicle *vehicle); +rct_vehicle *vehicle_get_tail(rct_vehicle *vehicle); +const rct_vehicle_info *vehicle_get_move_info(int cd, int typeAndDirection, int offset); /** Helper macro until rides are stored in this module. */ #define GET_VEHICLE(sprite_index) &(g_sprite_list[sprite_index].vehicle) diff --git a/src/windows/ride.c b/src/windows/ride.c index 6c3f2b96b787..940d1eccdce9 100644 --- a/src/windows/ride.c +++ b/src/windows/ride.c @@ -1059,7 +1059,7 @@ static void window_ride_draw_tab_vehicle(rct_drawpixelinfo *dpi, rct_window *w) spriteIndex = 32; if (w->page == WINDOW_RIDE_PAGE_VEHICLE) spriteIndex += w->frame_no; - spriteIndex /= (rideVehicleEntry->var_12 & 0x800) ? 4 : 2; + spriteIndex /= (rideVehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_11) ? 4 : 2; spriteIndex &= rideVehicleEntry->rotation_frame_mask; spriteIndex *= rideVehicleEntry->var_16; spriteIndex += rideVehicleEntry->base_image_id; @@ -2114,7 +2114,12 @@ static rct_string_id window_ride_get_status_vehicle(rct_window *w, void *argumen vehicle = &(g_sprite_list[vehicleSpriteIndex].vehicle); if (vehicle->status != VEHICLE_STATUS_CRASHING && vehicle->status != VEHICLE_STATUS_CRASHED) { int trackType = vehicle->track_type >> 2; - if (trackType == 216 || trackType == 123 || trackType == 9 || trackType == 63 || trackType == 147 || trackType == 155) { + if (trackType == 216 || + trackType == TRACK_ELEM_CABLE_LIFT_HILL || + trackType == TRACK_ELEM_25_DEG_UP_TO_FLAT || + trackType == TRACK_ELEM_60_DEG_UP_TO_FLAT || + trackType == TRACK_ELEM_DIAG_25_DEG_UP_TO_FLAT || + trackType == TRACK_ELEM_DIAG_60_DEG_UP_TO_FLAT) { if ((RCT2_ADDRESS(0x01357644, uint32)[ride->type] & 0x40) && vehicle->velocity == 0) { RCT2_GLOBAL((int)arguments + 0, uint16) = STR_STOPPED_BY_BLOCK_BRAKES; return 1191; @@ -2647,8 +2652,8 @@ static void window_ride_vehicle_scrollpaint(rct_window *w, rct_drawpixelinfo *dp // For each car in train for (j = 0; j < ride->num_cars_per_train; j++) { rct_ride_type_vehicle* rideVehicleEntry = &rideEntry->vehicles[trainLayout[j]]; - x += rideVehicleEntry->var_04 / 17432; - y -= (rideVehicleEntry->var_04 / 2) / 17432; + x += rideVehicleEntry->spacing / 17432; + y -= (rideVehicleEntry->spacing / 2) / 17432; // Get colour of vehicle switch (ride->colour_scheme_type & 3) { @@ -2665,7 +2670,7 @@ static void window_ride_vehicle_scrollpaint(rct_window *w, rct_drawpixelinfo *dp vehicleColour = ride_get_vehicle_colour(ride, vehicleColourIndex); spriteIndex = 16; - if (rideVehicleEntry->var_12 & 0x800) + if (rideVehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_11) spriteIndex /= 2; spriteIndex &= rideVehicleEntry->rotation_frame_mask; @@ -2680,8 +2685,8 @@ static void window_ride_vehicle_scrollpaint(rct_window *w, rct_drawpixelinfo *dp nextSpriteToDraw->tertiary_colour = vehicleColour.additional_2; nextSpriteToDraw++; - x += rideVehicleEntry->var_04 / 17432; - y -= (rideVehicleEntry->var_04 / 2) / 17432; + x += rideVehicleEntry->spacing / 17432; + y -= (rideVehicleEntry->spacing / 2) / 17432; } if (ride->type == RIDE_TYPE_REVERSER_ROLLER_COASTER) { @@ -2931,11 +2936,11 @@ static void window_ride_operating_mousedown(int widgetIndex, rct_window *w, rct_ window_ride_mode_tweak_decrease(w); break; case WIDX_LIFT_HILL_SPEED_INCREASE: - parameter_check = gConfigCheat.fast_lift_hill ? 255 : RCT2_GLOBAL(0x0097D7CA + (ride->type * 4), uint8); + parameter_check = gConfigCheat.fast_lift_hill ? 255 : RideLiftData[ride->type].maximum_speed; set_operating_setting(w->number, 8, min(ride->lift_hill_speed + 1, parameter_check)); break; case WIDX_LIFT_HILL_SPEED_DECREASE: - parameter_check = gConfigCheat.fast_lift_hill ? 0 : RCT2_GLOBAL(0x0097D7C9 + (ride->type * 4), uint8); + parameter_check = gConfigCheat.fast_lift_hill ? 0 : RideLiftData[ride->type].minimum_speed; set_operating_setting(w->number, 8, max(ride->lift_hill_speed - 1, parameter_check)); break; case WIDX_MINIMUM_LENGTH_INCREASE: @@ -4138,9 +4143,9 @@ static void window_ride_colour_invalidate(rct_window *w) for (int i = 0; i < ride->num_cars_per_train; i++) { uint8 vehicleTypeIndex = trainLayout[i]; - colourFlags |= rideEntry->vehicles[vehicleTypeIndex].var_14; + colourFlags |= rideEntry->vehicles[vehicleTypeIndex].flags_b; colourFlags = ror32(colourFlags, 16); - colourFlags |= rideEntry->vehicles[vehicleTypeIndex].var_12; + colourFlags |= rideEntry->vehicles[vehicleTypeIndex].flags_a; colourFlags = ror32(colourFlags, 16); } @@ -4330,7 +4335,7 @@ static void window_ride_colour_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi y += rideVehicleEntry->tab_height; // Draw the coloured spinning vehicle - spriteIndex = rideVehicleEntry->var_12 & 0x800 ? w->frame_no / 4 : w->frame_no / 2; + spriteIndex = rideVehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_11 ? w->frame_no / 4 : w->frame_no / 2; spriteIndex &= rideVehicleEntry->rotation_frame_mask; spriteIndex *= rideVehicleEntry->var_16; spriteIndex += rideVehicleEntry->base_image_id; diff --git a/src/world/map.c b/src/world/map.c index 656f942b7ba3..f407d0663f04 100644 --- a/src/world/map.c +++ b/src/world/map.c @@ -5061,7 +5061,7 @@ void game_command_set_sign_style(int* eax, int* ebx, int* ecx, int* edx, int* es } /** - * Gets the map element at x, y, z. + * Gets the track element at x, y, z. * @param x x units, not tiles. * @param y y units, not tiles. * @param z Base height. @@ -5080,10 +5080,11 @@ rct_map_element *map_get_track_element_at(int x, int y, int z) } /** - * Gets the map element at x, y, z. + * Gets the track element at x, y, z that is the given track type. * @param x x units, not tiles. * @param y y units, not tiles. * @param z Base height. + * @param trackType */ rct_map_element *map_get_track_element_at_of_type(int x, int y, int z, int trackType) { @@ -5098,3 +5099,26 @@ rct_map_element *map_get_track_element_at_of_type(int x, int y, int z, int track return NULL; } + +/** + * Gets the track element at x, y, z that is the given track type and sequence. + * @param x x units, not tiles. + * @param y y units, not tiles. + * @param z Base height. + * @param trackType + * @param sequence + */ +rct_map_element *map_get_track_element_at_of_type_seq(int x, int y, int z, int trackType, int sequence) +{ + rct_map_element *mapElement = map_get_first_element_at(x >> 5, y >> 5); + do { + if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_TRACK) continue; + if (mapElement->base_height != z) continue; + if (mapElement->properties.track.type != trackType) continue; + if ((mapElement->properties.track.sequence & 0x0F) != sequence) continue; + + return mapElement; + } while (!map_element_is_last_for_tile(mapElement++)); + + return NULL; +} diff --git a/src/world/map.h b/src/world/map.h index 2d2ceb5e797b..ae00c163d6fd 100644 --- a/src/world/map.h +++ b/src/world/map.h @@ -141,6 +141,7 @@ enum { enum { MAP_ELEMENT_FLAG_GHOST = (1 << 4), MAP_ELEMENT_FLAG_BROKEN = (1 << 5), + MAP_ELEMENT_FLAG_BLOCK_BREAK_CLOSED = (1 << 5), MAP_ELEMENT_FLAG_LAST_TILE = (1 << 7) }; @@ -395,5 +396,6 @@ bool map_large_scenery_get_origin( rct_map_element *map_get_track_element_at(int x, int y, int z); rct_map_element *map_get_track_element_at_of_type(int x, int y, int z, int trackType); +rct_map_element *map_get_track_element_at_of_type_seq(int x, int y, int z, int trackType, int sequence); #endif diff --git a/src/world/particle.c b/src/world/particle.c index 14481183f65b..60e8ecdfebc1 100644 --- a/src/world/particle.c +++ b/src/world/particle.c @@ -16,14 +16,14 @@ void crashed_vehicle_particle_create(rct_vehicle_colour colours, int x, int y, i sprite->sprite_height_negative = 8; sprite->sprite_height_positive = 8; sprite->sprite_identifier = SPRITE_IDENTIFIER_MISC; - sprite_move(x, y, z + 4, (rct_sprite*)sprite); + sprite_move(x, y, z, (rct_sprite*)sprite); sprite->misc_identifier = SPRITE_MISC_CRASHED_VEHICLE_PARTICLE; sprite->var_26 = (util_rand() & 0xFF) * 12; sprite->var_24 = (util_rand() & 0x7F) + 140; sprite->var_2E = ((util_rand() & 0xFF) * 5) >> 8; - sprite->acceleration_x = (util_rand() & 0xFFFF) * 4; - sprite->acceleration_y = (util_rand() & 0xFFFF) * 4; + sprite->acceleration_x = ((sint16)(util_rand() & 0xFFFF)) * 4; + sprite->acceleration_y = ((sint16)(util_rand() & 0xFFFF)) * 4; sprite->acceleration_z = (util_rand() & 0xFFFF) * 4 + 0x10000; sprite->velocity_x = 0; sprite->velocity_y = 0; diff --git a/src/world/sprite.c b/src/world/sprite.c index 96a9b1eebb3e..77580416182b 100644 --- a/src/world/sprite.c +++ b/src/world/sprite.c @@ -275,18 +275,23 @@ void move_sprite_to_list(rct_sprite *sprite, uint8 cl) * * rct2: 0x00673200 */ -static void sprite_misc_0_update(rct_sprite *sprite) +static void sprite_steam_particle_update(rct_steam_particle *steam) { - invalidate_sprite_2(sprite); - - int original_var24 = sprite->unknown.var_24; - sprite->unknown.var_24 += 0x5555; - if (sprite->unknown.var_24 < 0x5555) { - sprite_move(sprite->unknown.x, sprite->unknown.y, sprite->unknown.z + 1, sprite); + invalidate_sprite_2((rct_sprite*)steam); + + int original_var24 = steam->var_24; + steam->var_24 += 0x5555; + if (steam->var_24 < 0x5555) { + sprite_move( + steam->x, + steam->y, + steam->z + 1, + (rct_sprite*)steam + ); } - sprite->unknown.var_26 += 64; - if (sprite->unknown.var_26 >= (56 * 64)) { - sprite_remove(sprite); + steam->var_26 += 64; + if (steam->var_26 >= (56 * 64)) { + sprite_remove((rct_sprite*)steam); } } @@ -359,8 +364,8 @@ static void sprite_misc_5_update(rct_sprite *sprite) void sprite_misc_update(rct_sprite *sprite) { switch (sprite->unknown.misc_identifier) { - case SPRITE_MISC_0: - sprite_misc_0_update(sprite); + case SPRITE_MISC_STEAM_PARTICLE: + sprite_steam_particle_update((rct_steam_particle*)sprite); break; case SPRITE_MISC_MONEY_EFFECT: money_effect_update(&sprite->money_effect); diff --git a/src/world/sprite.h b/src/world/sprite.h index 9ce80c93b7f8..fd3223127351 100644 --- a/src/world/sprite.h +++ b/src/world/sprite.h @@ -282,6 +282,35 @@ typedef struct { uint16 var_26; } rct_crash_splash; +typedef struct { + uint8 sprite_identifier; // 0x00 + uint8 misc_identifier; // 0x01 + uint16 next_in_quadrant; // 0x02 + uint16 next; // 0x04 + uint16 previous; // 0x06 + uint8 linked_list_type_offset; // 0x08 Valid values are SPRITE_LINKEDLIST_OFFSET_... + // Height from center of sprite to bottom + uint8 sprite_height_negative; // 0x09 + uint16 sprite_index; // 0x0A + uint16 var_0C; + sint16 x; // 0x0E + sint16 y; // 0x10 + sint16 z; // 0x12 + // Width from center of sprite to edge + uint8 sprite_width; // 0x14 + // Height from center of sprite to top + uint8 sprite_height_positive; // 0x15 + sint16 sprite_left; // 0x16 + sint16 sprite_top; // 0x18 + sint16 sprite_right; // 0x1A + sint16 sprite_bottom; // 0x1C + uint8 sprite_direction; // 0x1E + uint8 pad_1F[3]; // 0x1F + uint16 name_string_idx; // 0x22 + uint16 var_24; + uint16 var_26; +} rct_steam_particle; + /** * Sprite structure. * size: 0x0100 @@ -298,6 +327,7 @@ typedef union { rct_money_effect money_effect; rct_crashed_vehicle_particle crashed_vehicle_particle; rct_crash_splash crash_splash; + rct_steam_particle steam_particle; } rct_sprite; typedef struct { @@ -318,7 +348,7 @@ typedef struct { } rct_sprite_entry; enum { - SPRITE_MISC_0, + SPRITE_MISC_STEAM_PARTICLE, SPRITE_MISC_MONEY_EFFECT, SPRITE_MISC_CRASHED_VEHICLE_PARTICLE, SPRITE_MISC_3, // (related to vehicle crash, probably crash particles)