#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers /***************************************************************************** * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. * * OpenRCT2 is the work of many authors, a full list can be found in contributors.md * For more information, visit https://github.com/OpenRCT2/OpenRCT2 * * OpenRCT2 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * A full copy of the GNU General Public License can be found in licence.txt *****************************************************************************/ #pragma endregion #include "../Game.h" #include "Station.h" #include "Track.h" static void ride_update_station_blocksection(Ride * ride, sint32 stationIndex); static void ride_update_station_bumpercar(Ride * ride, sint32 stationIndex); static void ride_update_station_normal(Ride * ride, sint32 stationIndex); static void ride_update_station_race(Ride * ride, sint32 stationIndex); static void ride_race_init_vehicle_speeds(Ride * ride); static void ride_invalidate_station_start(Ride * ride, sint32 stationIndex, bool greenLight); /** * * rct2: 0x006ABFFB */ void ride_update_station(Ride * ride, sint32 stationIndex) { if (ride->station_starts[stationIndex].xy == RCT_XY8_UNDEFINED) return; switch (ride->mode) { case RIDE_MODE_RACE: ride_update_station_race(ride, stationIndex); break; case RIDE_MODE_BUMPERCAR: ride_update_station_bumpercar(ride, stationIndex); break; case RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED: case RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED: ride_update_station_blocksection(ride, stationIndex); break; default: ride_update_station_normal(ride, stationIndex); break; } } /** * * rct2: 0x006AC0A1 */ static void ride_update_station_blocksection(Ride * ride, sint32 stationIndex) { rct_tile_element * tileElement = ride_get_station_start_track_element(ride, stationIndex); if ((ride->status == RIDE_STATUS_CLOSED && ride->num_riders == 0) || (tileElement != nullptr && tileElement->flags & 0x20)) { ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG; if ((ride->station_depart[stationIndex] & STATION_DEPART_FLAG) || (tileElement != nullptr && tile_element_get_green_light(tileElement))) ride_invalidate_station_start(ride, stationIndex, false); } else { if (!(ride->station_depart[stationIndex] & STATION_DEPART_FLAG)) { ride->station_depart[stationIndex] |= STATION_DEPART_FLAG; ride_invalidate_station_start(ride, stationIndex, true); } else if (tileElement != nullptr && tile_element_get_green_light(tileElement)) { ride_invalidate_station_start(ride, stationIndex, true); } } } /** * * rct2: 0x006AC12B */ static void ride_update_station_bumpercar(Ride * ride, sint32 stationIndex) { // Change of station depart flag should really call invalidate_station_start // but since dodgems do not have station lights there is no point. if (ride->status == RIDE_STATUS_CLOSED || (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED))) { ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG; return; } if (ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) { sint32 dx = ride->time_limit * 32; sint32 dl = dx & 0xFF; sint32 dh = (dx >> 8) & 0xFF; for (size_t i = 0; i < ride->num_vehicles; i++) { rct_vehicle * vehicle = &(get_sprite(ride->vehicles[i])->vehicle); if (vehicle->var_CE < dh || (vehicle->var_CE < dh && vehicle->sub_state > dl)) continue; // End match ride->lifecycle_flags &= ~RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING; ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG; return; } // Continue match ride->station_depart[stationIndex] |= STATION_DEPART_FLAG; } else { // Check if all vehicles are ready to go for (size_t i = 0; i < ride->num_vehicles; i++) { rct_vehicle * vehicle = &(get_sprite(ride->vehicles[i])->vehicle); if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART) { ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG; return; } } // Begin the match ride->lifecycle_flags |= RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING; ride->station_depart[stationIndex] |= STATION_DEPART_FLAG; ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; } } /** * * rct2: 0x006AC02C */ static void ride_update_station_normal(Ride * ride, sint32 stationIndex) { sint32 time = ride->station_depart[stationIndex] & STATION_DEPART_MASK; if ((ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) || (ride->status == RIDE_STATUS_CLOSED && ride->num_riders == 0)) { if (time != 0 && time != 127 && !(gCurrentTicks & 7)) time--; ride->station_depart[stationIndex] = time; ride_invalidate_station_start(ride, stationIndex, false); } else { if (time == 0) { ride->station_depart[stationIndex] |= STATION_DEPART_FLAG; ride_invalidate_station_start(ride, stationIndex, true); } else { if (time != 127 && !(gCurrentTicks & 31)) time--; ride->station_depart[stationIndex] = time; ride_invalidate_station_start(ride, stationIndex, false); } } } /** * * rct2: 0x006AC1DF */ static void ride_update_station_race(Ride * ride, sint32 stationIndex) { if (ride->status == RIDE_STATUS_CLOSED || (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED))) { if (ride->station_depart[stationIndex] & STATION_DEPART_FLAG) { ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG; ride_invalidate_station_start(ride, stationIndex, false); } return; } if (ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) { sint32 numLaps = ride->num_laps; for (size_t i = 0; i < ride->num_vehicles; i++) { rct_vehicle * vehicle = &(get_sprite(ride->vehicles[i])->vehicle); if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART && vehicle->num_laps >= numLaps) { // Found a winner if (vehicle->num_peeps != 0) { rct_peep * peep = &(get_sprite(vehicle->peep[0])->peep); ride->race_winner = peep->sprite_index; ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; } // Race is over ride->lifecycle_flags &= ~RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING; if (ride->station_depart[stationIndex] & STATION_DEPART_FLAG) { ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG; ride_invalidate_station_start(ride, stationIndex, false); } return; } } // Continue racing ride->station_depart[stationIndex] |= STATION_DEPART_FLAG; } else { // Check if all vehicles are ready to go for (size_t i = 0; i < ride->num_vehicles; i++) { rct_vehicle * vehicle = &(get_sprite(ride->vehicles[i])->vehicle); if (vehicle->status != VEHICLE_STATUS_WAITING_TO_DEPART && vehicle->status != VEHICLE_STATUS_DEPARTING) { if (ride->station_depart[stationIndex] & STATION_DEPART_FLAG) { ride->station_depart[stationIndex] &= ~STATION_DEPART_FLAG; ride_invalidate_station_start(ride, stationIndex, false); } return; } } // Begin the race ride_race_init_vehicle_speeds(ride); ride->lifecycle_flags |= RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING; if (!(ride->station_depart[stationIndex] & STATION_DEPART_FLAG)) { ride->station_depart[stationIndex] |= STATION_DEPART_FLAG; ride_invalidate_station_start(ride, stationIndex, true); } ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; } } /** * * rct2: 0x006AC988 * set the speed of the go kart type vehicle at the start to a random value or alter if peep name is an easter egg * @param ride (esi) */ static void ride_race_init_vehicle_speeds(Ride * ride) { for (size_t i = 0; i < ride->num_vehicles; i++) { rct_vehicle * vehicle = &get_sprite(ride->vehicles[i])->vehicle; vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_6; rct_ride_entry * rideEntry = get_ride_entry(vehicle->ride_subtype); vehicle->speed = (scenario_rand() & 16) - 8 + rideEntry->vehicles[vehicle->vehicle_type].powered_max_speed; if (vehicle->num_peeps != 0) { rct_peep * peep = &get_sprite(vehicle->peep[0])->peep; switch (peep_get_easteregg_name_id(peep)) { case EASTEREGG_PEEP_NAME_MICHAEL_SCHUMACHER: vehicle->speed += 35; break; case EASTEREGG_PEEP_NAME_JACQUES_VILLENEUVE: vehicle->speed += 25; break; case EASTEREGG_PEEP_NAME_DAMON_HILL: vehicle->speed += 55; break; case EASTEREGG_PEEP_NAME_CHRIS_SAWYER: vehicle->speed += 14; break; case EASTEREGG_PEEP_NAME_MR_BEAN: vehicle->speed = 9; break; } } } } /** * * rct2: 0x006AC2C7 */ static void ride_invalidate_station_start(Ride * ride, sint32 stationIndex, bool greenLight) { sint32 x = ride->station_starts[stationIndex].x * 32; sint32 y = ride->station_starts[stationIndex].y * 32; rct_tile_element * tileElement = ride_get_station_start_track_element(ride, stationIndex); // If no station track found return if (tileElement == nullptr) return; tile_element_set_green_light(tileElement, greenLight); // Invalidate map tile map_invalidate_tile_zoom1(x, y, tileElement->base_height * 8, tileElement->clearance_height * 8); } rct_tile_element * ride_get_station_start_track_element(Ride * ride, sint32 stationIndex) { sint32 x = ride->station_starts[stationIndex].x; sint32 y = ride->station_starts[stationIndex].y; sint32 z = ride->station_heights[stationIndex]; // Find the station track element rct_tile_element * tileElement = map_get_first_element_at(x, y); do { if (tile_element_get_type(tileElement) == TILE_ELEMENT_TYPE_TRACK && z == tileElement->base_height) return tileElement; } while (!tile_element_is_last_for_tile(tileElement++)); return nullptr; } rct_tile_element * ride_get_station_exit_element(Ride * ride, sint32 x, sint32 y, sint32 z) { // Find the station track element rct_tile_element * tileElement = map_get_first_element_at(x, y); do { if (tile_element_get_type(tileElement) == TILE_ELEMENT_TYPE_ENTRANCE && z == tileElement->base_height) return tileElement; } while (!tile_element_is_last_for_tile(tileElement++)); return nullptr; } sint8 ride_get_first_valid_station_exit(Ride * ride) { for (sint32 i = 0; i < MAX_STATIONS; i++) { if (ride->exits[i].x != COORDS_NULL) { return i; } } return -1; } sint8 ride_get_first_valid_station_start(const Ride * ride) { for (sint8 i = 0; i < MAX_STATIONS; i++) { if (ride->station_starts[i].xy != RCT_XY8_UNDEFINED) { return i; } } return -1; } sint8 ride_get_first_empty_station_start(const Ride * ride) { for (sint8 i = 0; i < MAX_STATIONS; i++) { if (ride->station_starts[i].xy == RCT_XY8_UNDEFINED) { return i; } } return -1; } TileCoordsXYZD ride_get_entrance_location(const sint32 rideIndex, const sint32 stationIndex) { const Ride * ride = get_ride(rideIndex); return ride->entrances[stationIndex]; } TileCoordsXYZD ride_get_exit_location(const sint32 rideIndex, const sint32 stationIndex) { const Ride * ride = get_ride(rideIndex); return ride->exits[stationIndex]; } TileCoordsXYZD ride_get_entrance_location(const Ride * ride, const sint32 stationIndex) { return ride->entrances[stationIndex]; } TileCoordsXYZD ride_get_exit_location(const Ride * ride, const sint32 stationIndex) { return ride->exits[stationIndex]; } void ride_clear_entrance_location( Ride * ride, const sint32 stationIndex) { ride->entrances[stationIndex].x = COORDS_NULL; } void ride_clear_exit_location( Ride * ride, const sint32 stationIndex) { ride->exits[stationIndex].x = COORDS_NULL; } void ride_set_entrance_location(Ride * ride, const sint32 stationIndex, const TileCoordsXYZD location) { ride->entrances[stationIndex] = location; } void ride_set_exit_location(Ride * ride, const sint32 stationIndex, const TileCoordsXYZD location) { ride->exits[stationIndex] = location; }