1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-26 00:04:43 +01:00
Files
OpenRCT2/src/openrct2/peep/Guest.cpp
2018-05-05 11:04:52 +01:00

2302 lines
64 KiB
C++

#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 "Peep.h"
#include "../world/Sprite.h"
#include "../ride/Ride.h"
#include "../ride/Station.h"
#include "../ride/RideData.h"
#include "../ride/Track.h"
#include "../core/Guard.hpp"
#include "../core/Util.hpp"
#include "../world/Footpath.h"
#include "../management/Marketing.h"
#include "../management/Finance.h"
#include "../management/NewsItem.h"
#include "../localisation/Localisation.h"
#include "../config/Config.h"
#include "../scenario/Scenario.h"
// Locations of the spiral slide platform that a peep walks from the entrance of the ride to the
// entrance of the slide. Up to 4 waypoints for each 4 sides that an ride entrance can be located
// and 4 different rotations of the ride. 4 * 4 * 4 = 64 locations.
static constexpr const CoordsXY SpiralSlideWalkingPath[64] = {
{ 56, 8 },
{ 8, 8 },
{ 8, 32 },
{ 32, 32 },
{ 8, 8 },
{ 8, 8 },
{ 8, 32 },
{ 32, 32 },
{ 8, 32 },
{ 8, 32 },
{ 8, 32 },
{ 32, 32 },
{ 8, 56 },
{ 8, 32 },
{ 8, 32 },
{ 32, 32 },
{ 56, 24 },
{ 32, 24 },
{ 32, 24 },
{ 32, 0 },
{ 56, -24 },
{ 56, 24 },
{ 32, 24 },
{ 32, 0 },
{ 8, 24 },
{ 32, 24 },
{ 32, 24 },
{ 32, 0 },
{ 32, 24 },
{ 32, 24 },
{ 32, 24 },
{ 32, 0 },
{ 24, 0 },
{ 24, 0 },
{ 24, 0 },
{ 0, 0 },
{ 24, -24 },
{ 24, 0 },
{ 24, 0 },
{ 0, 0 },
{ -24, -24 },
{ 24, -24 },
{ 24, 0 },
{ 0, 0 },
{ 24, 24 },
{ 24, 0 },
{ 24, 0 },
{ 0, 0 },
{ 24, 8 },
{ 0, 8 },
{ 0, 8 },
{ 0, 32 },
{ 0, 8 },
{ 0, 8 },
{ 0, 8 },
{ 0, 32 },
{ -24, 8 },
{ 0, 8 },
{ 0, 8 },
{ 0, 32 },
{ -24, 56 },
{ -24, 8 },
{ 0, 8 },
{ 0, 32 },
};
/**
*
* rct2: 0x00691677
*/
void rct_peep::TryGetUpFromSitting()
{
// Eats all food first
if (peep_has_food(this))
return;
time_to_sitdown--;
if (time_to_sitdown)
return;
SetState(PEEP_STATE_WALKING);
// Set destination to the centre of the tile.
destination_x = (x & 0xFFE0) + 16;
destination_y = (y & 0xFFE0) + 16;
destination_tolerance = 5;
UpdateCurrentActionSpriteType();
}
/** rct2: 0x00981F2C, 0x00981F2E */
static constexpr const LocationXY16 _981F2C[] = {
{ 7, 12 }, { 12, 25 }, { 25, 20 }, { 20, 7 }, { 7, 20 }, { 20, 25 }, { 25, 12 }, { 12, 7 },
};
/**
*
* rct2: 0x0069152B
*/
void rct_peep::UpdateSitting()
{
if (sub_state == PEEP_SITTING_TRYING_TO_SIT)
{
if (!CheckForPath())
return;
// 691541
uint8 pathingResult;
PerformNextAction(pathingResult);
if (!(pathingResult & PATHING_DESTINATION_REACHED))
return;
sint32 ebx = var_37 & 0x7;
LocationXYZ16 loc =
{
(x & 0xFFE0) + _981F2C[ebx].x,
(y & 0xFFE0) + _981F2C[ebx].y,
z
};
Invalidate();
sprite_move(loc.x, loc.y, loc.z, (rct_sprite *)this);
sprite_direction = ((var_37 + 2) & 3) * 8;
Invalidate();
action = PEEP_ACTION_NONE_1;
next_action_sprite_type = 7;
SwitchNextActionSpriteType();
sub_state = PEEP_SITTING_SAT_DOWN;
// Sets time to sit on seat
time_to_sitdown = (129 - energy) * 16 + 50;
}
else if (sub_state == PEEP_SITTING_SAT_DOWN)
{
if (action < PEEP_ACTION_NONE_1)
{
UpdateAction();
if (action != PEEP_ACTION_NONE_2)
return;
action = PEEP_ACTION_NONE_1;
TryGetUpFromSitting();
return;
}
if ((peep_flags & PEEP_FLAGS_LEAVING_PARK))
{
SetState(PEEP_STATE_WALKING);
// Set destination to the centre of the tile
destination_x = (x & 0xFFE0) + 16;
destination_y = (y & 0xFFE0) + 16;
destination_tolerance = 5;
UpdateCurrentActionSpriteType();
return;
}
if (sprite_type == PEEP_SPRITE_TYPE_UMBRELLA)
{
TryGetUpFromSitting();
return;
}
if (peep_has_food(this))
{
if ((scenario_rand() & 0xFFFF) > 1310)
{
TryGetUpFromSitting();
return;
}
action = PEEP_ACTION_SITTING_EAT_FOOD;
action_frame = 0;
action_sprite_image_offset = 0;
UpdateCurrentActionSpriteType();
Invalidate();
return;
}
sint32 rand = scenario_rand();
if ((rand & 0xFFFF) > 131)
{
TryGetUpFromSitting();
return;
}
if (sprite_type == PEEP_SPRITE_TYPE_BALLOON || sprite_type == PEEP_SPRITE_TYPE_HAT)
{
TryGetUpFromSitting();
return;
}
action = PEEP_ACTION_SITTING_LOOK_AROUND_LEFT;
if (rand & 0x80000000)
{
action = PEEP_ACTION_SITTING_LOOK_AROUND_RIGHT;
}
if (rand & 0x40000000)
{
action = PEEP_ACTION_SITTING_CHECK_WATCH;
}
action_frame = 0;
action_sprite_image_offset = 0;
UpdateCurrentActionSpriteType();
Invalidate();
return;
}
}
/**
*
* rct2: 0x006966A9
*/
void remove_peep_from_queue(rct_peep * peep)
{
Ride * ride = get_ride(peep->current_ride);
uint8 cur_station = peep->current_ride_station;
// Make sure we don't underflow, building while paused might reset it to 0 where peeps have
// not yet left the queue.
if (ride->queue_length[cur_station] > 0)
{
ride->queue_length[cur_station]--;
}
if (peep->sprite_index == ride->last_peep_in_queue[cur_station])
{
ride->last_peep_in_queue[cur_station] = peep->next_in_queue;
return;
}
uint16 spriteId = ride->last_peep_in_queue[cur_station];
while (spriteId != SPRITE_INDEX_NULL)
{
rct_peep * other_peep = GET_PEEP(spriteId);
if (peep->sprite_index == other_peep->next_in_queue)
{
other_peep->next_in_queue = peep->next_in_queue;
return;
}
spriteId = other_peep->next_in_queue;
}
}
/**
*
* rct2: 0x00691C6E
*/
static rct_vehicle * peep_choose_car_from_ride(rct_peep * peep, Ride * ride, std::vector<uint8> &car_array)
{
uint8 chosen_car = scenario_rand();
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_G_FORCES) && ((chosen_car & 0xC) != 0xC))
{
chosen_car = (scenario_rand() & 1) ? 0 : (uint8)car_array.size() - 1;
}
else
{
chosen_car = (chosen_car * (uint16)car_array.size()) >> 8;
}
peep->current_car = car_array[chosen_car];
rct_vehicle * vehicle = GET_VEHICLE(ride->vehicles[peep->current_train]);
for (sint32 i = peep->current_car; i > 0; --i)
{
vehicle = GET_VEHICLE(vehicle->next_vehicle_on_train);
}
return vehicle;
}
/**
*
* rct2: 0x00691CD1
*/
static void peep_choose_seat_from_car(rct_peep * peep, Ride * ride, rct_vehicle * vehicle)
{
uint8 chosen_seat = vehicle->next_free_seat;
if (ride->mode == RIDE_MODE_FORWARD_ROTATION || ride->mode == RIDE_MODE_BACKWARD_ROTATION)
{
chosen_seat = (((~vehicle->vehicle_sprite_type + 1) >> 3) & 0xF) * 2;
if (vehicle->next_free_seat & 1)
{
chosen_seat++;
}
}
peep->current_seat = chosen_seat;
vehicle->next_free_seat++;
vehicle->peep[peep->current_seat] = peep->sprite_index;
vehicle->peep_tshirt_colours[peep->current_seat] = peep->tshirt_colour;
}
/**
*
* rct2: 0x00691D27
*/
static void peep_go_to_ride_entrance(rct_peep * peep, Ride * ride)
{
TileCoordsXYZD location = ride_get_entrance_location(peep->current_ride, peep->current_ride_station);
Guard::Assert(!location.isNull());
sint32 x = location.x;
sint32 y = location.y;
uint8 direction = location.direction;
x *= 32;
y *= 32;
x += 16;
y += 16;
sint16 x_shift = word_981D6C[direction].x;
sint16 y_shift = word_981D6C[direction].y;
uint8 shift_multiplier = 21;
rct_ride_entry * rideEntry = get_ride_entry(ride->subtype);
if (rideEntry != nullptr)
{
if (rideEntry->vehicles[rideEntry->default_vehicle].flags & VEHICLE_ENTRY_FLAG_MINI_GOLF ||
rideEntry->vehicles[rideEntry->default_vehicle].flags & (VEHICLE_ENTRY_FLAG_CHAIRLIFT | VEHICLE_ENTRY_FLAG_GO_KART))
{
shift_multiplier = 32;
}
}
x_shift *= shift_multiplier;
y_shift *= shift_multiplier;
x += x_shift;
y += y_shift;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerance = 2;
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_ENTERING_RIDE;
peep->sub_state = PEEP_RIDE_IN_ENTRANCE;
peep_window_state_update(peep);
peep->rejoin_queue_timeout = 0;
peep->time_on_ride = 0;
remove_peep_from_queue(peep);
}
static bool peep_find_vehicle_to_enter(rct_peep * peep, Ride * ride, std::vector<uint8> &car_array)
{
uint8 chosen_train = 0xFF;
if (ride->mode == RIDE_MODE_BUMPERCAR || ride->mode == RIDE_MODE_RACE)
{
if (ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING)
return false;
for (sint32 i = 0; i < ride->num_vehicles; ++i)
{
rct_vehicle * vehicle = GET_VEHICLE(ride->vehicles[i]);
if (vehicle->next_free_seat >= vehicle->num_seats)
continue;
if (vehicle->status != VEHICLE_STATUS_WAITING_FOR_PASSENGERS)
continue;
chosen_train = i;
break;
}
}
else
{
chosen_train = ride->train_at_station[peep->current_ride_station];
}
if (chosen_train == 0xFF)
{
return false;
}
peep->current_train = chosen_train;
sint32 i = 0;
uint16 vehicle_id = ride->vehicles[chosen_train];
rct_vehicle * vehicle = GET_VEHICLE(vehicle_id);
for (; vehicle_id != SPRITE_INDEX_NULL; vehicle_id = vehicle->next_vehicle_on_train, i++)
{
vehicle = GET_VEHICLE(vehicle_id);
uint8 num_seats = vehicle->num_seats;
if (vehicle_is_used_in_pairs(vehicle))
{
num_seats &= VEHICLE_SEAT_NUM_MASK;
if (vehicle->next_free_seat & 1)
{
peep->current_car = i;
peep_choose_seat_from_car(peep, ride, vehicle);
peep_go_to_ride_entrance(peep, ride);
return false;
}
}
if (num_seats == vehicle->next_free_seat)
continue;
if (ride->mode == RIDE_MODE_FORWARD_ROTATION || ride->mode == RIDE_MODE_BACKWARD_ROTATION)
{
uint8 position = (((~vehicle->vehicle_sprite_type + 1) >> 3) & 0xF) * 2;
if (vehicle->peep[position] != SPRITE_INDEX_NULL)
continue;
}
car_array.push_back(i);
}
return !car_array.empty();
}
static void peep_update_ride_at_entrance_try_leave(rct_peep * peep)
{
// Destination Tolerance is zero when peep has completely
// entered entrance
if (peep->destination_tolerance == 0)
{
remove_peep_from_queue(peep);
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_FALLING;
peep_window_state_update(peep);
}
}
static bool peep_check_ride_price_at_entrance(rct_peep * peep, Ride * ride, money32 ridePrice)
{
if ((peep->item_standard_flags & PEEP_ITEM_VOUCHER) &&
peep->voucher_type == VOUCHER_TYPE_RIDE_FREE &&
peep->voucher_arguments == peep->current_ride)
return true;
if (peep->cash_in_pocket <= 0)
{
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_SPENT_MONEY, PEEP_THOUGHT_ITEM_NONE);
peep_update_ride_at_entrance_try_leave(peep);
return false;
}
if (ridePrice > peep->cash_in_pocket)
{
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_CANT_AFFORD_0, peep->current_ride);
peep_update_ride_at_entrance_try_leave(peep);
return false;
}
uint16 value = ride->value;
if (value != RIDE_VALUE_UNDEFINED)
{
if (value * 2 < ridePrice)
{
peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_BAD_VALUE, peep->current_ride);
peep_update_ride_at_entrance_try_leave(peep);
return false;
}
}
return true;
}
/**
*
* rct2: 0x00691A3B
*/
void rct_peep::UpdateRideAtEntrance()
{
Ride * ride = get_ride(current_ride);
// The peep will keep advancing in the entranceway
// whilst in this state. When it has reached the very
// front of the queue destination tolerance is set to
// zero to indicate it is final decision time (try_leave will pass).
// When a peep has to return to the queue without getting on a ride
// this is the state it will return to.
if (destination_tolerance != 0)
{
Invalidate();
sint16 actionX, actionY, xy_distance;
if (UpdateAction(&actionX, &actionY, &xy_distance))
{
sint16 actionZ = z;
if (xy_distance < 16)
{
auto entrance = ride_get_entrance_location(ride, current_ride_station);
actionZ = entrance.z * 8 + 2;
}
sprite_move(actionX, actionY, actionZ, (rct_sprite *)this);
Invalidate();
}
else
{
destination_tolerance = 0;
sprite_direction ^= (1 << 4);
}
}
std::vector<uint8> carArray;
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_VEHICLES))
{
if (ride->num_riders >= ride->operation_option)
return;
}
else
{
if (!peep_find_vehicle_to_enter(this, ride, carArray))
return;
}
if (ride->status != RIDE_STATUS_OPEN || ride->vehicle_change_timeout != 0)
{
peep_update_ride_at_entrance_try_leave(this);
return;
}
if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)
return;
money16 ridePrice = ride_get_price(ride);
if (ridePrice != 0)
{
if (!peep_check_ride_price_at_entrance(this, ride, ridePrice))
return;
}
if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_VEHICLES))
{
rct_vehicle * vehicle = peep_choose_car_from_ride(this, ride, carArray);
peep_choose_seat_from_car(this, ride, vehicle);
}
peep_go_to_ride_entrance(this, ride);
}
/** rct2: 0x00981FD4, 0x00981FD6 */
static constexpr const LocationXY16 _981FD4[] = {
{ 8, 8 },
{ 8, 24 },
{ 24, 24 },
{ 24, 8 },
};
static void peep_update_ride_leave_entrance_maze(rct_peep * peep, Ride * ride, TileCoordsXYZD &entrance_loc)
{
peep->maze_last_edge = entrance_loc.direction + 1;
entrance_loc.x *= 32;
entrance_loc.y *= 32;
entrance_loc.x += TileDirectionDelta[entrance_loc.direction].x;
entrance_loc.y += TileDirectionDelta[entrance_loc.direction].y;
uint8 direction = entrance_loc.direction * 4 + 11;
if (scenario_rand() & 0x40)
{
direction += 4;
peep->maze_last_edge += 2;
}
direction &= 0xF;
// Direction is 11, 15, 3, or 7
peep->var_37 = direction;
peep->maze_last_edge &= 3;
entrance_loc.x += _981FD4[direction / 4].x;
entrance_loc.y += _981FD4[direction / 4].y;
peep->destination_x = entrance_loc.x;
peep->destination_y = entrance_loc.y;
peep->destination_tolerance = 3;
ride->cur_num_customers++;
peep_on_enter_or_exit_ride(peep, peep->current_ride, 0);
peep->sub_state = PEEP_RIDE_MAZE_PATHFINDING;
}
static void peep_update_ride_leave_entrance_spiral_slide(rct_peep * peep, Ride * ride, TileCoordsXYZD &entrance_loc)
{
entrance_loc.x = ride->station_starts[peep->current_ride_station].x * 32;
entrance_loc.y = ride->station_starts[peep->current_ride_station].y * 32;
rct_tile_element * tile_element = ride_get_station_start_track_element(ride, peep->current_ride_station);
uint8 direction_track = (tile_element == nullptr ? 0 : tile_element_get_direction(tile_element));
peep->var_37 = (entrance_loc.direction << 2) | (direction_track << 4);
const CoordsXY slidePlatformDestination = SpiralSlideWalkingPath[peep->var_37];
entrance_loc.x += slidePlatformDestination.x;
entrance_loc.y += slidePlatformDestination.y;
peep->destination_x = entrance_loc.x;
peep->destination_y = entrance_loc.y;
peep->current_car = 0;
ride->cur_num_customers++;
peep_on_enter_or_exit_ride(peep, peep->current_ride, 0);
peep->sub_state = PEEP_RIDE_APPROACH_SPIRAL_SLIDE;
}
static uint8 peep_get_waypointed_seat_location(rct_peep * peep, Ride * ride, rct_ride_entry_vehicle * vehicle_type, uint8 track_direction)
{
// The seatlocation can be split into segments around the ride base
// to decide the segment first split off the segmentable seat location
// from the fixed section
uint8 seatLocationSegment = peep->current_seat & 0x7;
uint8 seatLocationFixed = peep->current_seat & 0xF8;
// Enterprise has more segments (8) compared to the normal (4)
if (ride->type != RIDE_TYPE_ENTERPRISE)
track_direction *= 2;
// Type 1 loading doesn't do segments and all peeps go to the same
// location on the ride
if (vehicle_type->peep_loading_waypoint_segments == 0)
{
track_direction /= 2;
seatLocationSegment = 0;
seatLocationFixed = 0;
}
seatLocationSegment += track_direction;
seatLocationSegment &= 0x7;
return seatLocationSegment + seatLocationFixed;
}
static void peep_update_ride_leave_entrance_waypoints(rct_peep * peep, Ride * ride)
{
TileCoordsXYZD entranceLocation = ride_get_entrance_location(peep->current_ride, peep->current_ride_station);
Guard::Assert(!entranceLocation.isNull());
uint8 direction_entrance = entranceLocation.direction;
LocationXY16 waypoint;
waypoint.x = ride->station_starts[peep->current_ride_station].x * 32 + 16;
waypoint.y = ride->station_starts[peep->current_ride_station].y * 32 + 16;
rct_tile_element * tile_element = ride_get_station_start_track_element(ride, peep->current_ride_station);
uint8 direction_track = (tile_element == nullptr ? 0 : tile_element_get_direction(tile_element));
auto vehicle = GET_VEHICLE(ride->vehicles[peep->current_train]);
auto ride_entry = get_ride_entry(vehicle->ride_subtype);
auto vehicle_type = &ride_entry->vehicles[vehicle->vehicle_type];
peep->var_37 = (direction_entrance | peep_get_waypointed_seat_location(peep, ride, vehicle_type, direction_track) * 4) * 4;
if (ride->type == RIDE_TYPE_ENTERPRISE)
{
waypoint.x = vehicle->x;
waypoint.y = vehicle->y;
}
Guard::Assert(vehicle_type->peep_loading_waypoints.size() >= (size_t)(peep->var_37 / 4));
waypoint.x += vehicle_type->peep_loading_waypoints[peep->var_37 / 4][0].x;
waypoint.y += vehicle_type->peep_loading_waypoints[peep->var_37 / 4][0].y;
peep->destination_x = waypoint.x;
peep->destination_y = waypoint.y;
peep->sub_state = PEEP_RIDE_APPROACH_VEHICLE_WAYPOINTS;
}
/**
*
* rct2: 0x006921D3
*/
void rct_peep::UpdateRideAdvanceThroughEntrance()
{
sint16 actionX, actionY, actionZ, xy_distance;
Ride * ride = get_ride(current_ride);
rct_ride_entry * ride_entry = get_ride_entry(ride->subtype);
if (UpdateAction(&actionX, &actionY, &xy_distance))
{
uint16 distanceThreshold = 16;
if (ride_entry != nullptr)
{
uint8 vehicle = ride_entry->default_vehicle;
if (ride_entry->vehicles[vehicle].flags & VEHICLE_ENTRY_FLAG_MINI_GOLF ||
ride_entry->vehicles[vehicle].flags & (VEHICLE_ENTRY_FLAG_CHAIRLIFT | VEHICLE_ENTRY_FLAG_GO_KART))
{
distanceThreshold = 28;
}
}
if (sub_state == PEEP_RIDE_IN_ENTRANCE && xy_distance < distanceThreshold)
{
sub_state = PEEP_RIDE_FREE_VEHICLE_CHECK;
}
Invalidate();
actionZ = ride->station_heights[current_ride_station] * 8;
distanceThreshold += 4;
if (xy_distance < distanceThreshold)
{
actionZ += RideData5[ride->type].z;
}
sprite_move(actionX, actionY, actionZ, (rct_sprite *)this);
Invalidate();
return;
}
Guard::Assert(sub_state == PEEP_RIDE_LEAVE_ENTRANCE, "Peep substate should be LEAVE_ENTRACE");
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_VEHICLES))
{
TileCoordsXYZD entranceLocation = ride_get_entrance_location(current_ride, current_ride_station);
Guard::Assert(!entranceLocation.isNull());
if (ride->type == RIDE_TYPE_MAZE)
{
peep_update_ride_leave_entrance_maze(this, ride, entranceLocation);
return;
}
Guard::Assert(ride->type == RIDE_TYPE_SPIRAL_SLIDE);
peep_update_ride_leave_entrance_spiral_slide(this, ride, entranceLocation);
return;
}
rct_vehicle * vehicle = GET_VEHICLE(ride->vehicles[current_train]);
for (sint32 i = current_car; i != 0; --i)
{
vehicle = GET_VEHICLE(vehicle->next_vehicle_on_train);
}
ride_entry = get_ride_entry(vehicle->ride_subtype);
if (ride_entry == nullptr)
{
return;
}
rct_ride_entry_vehicle * vehicle_type = &ride_entry->vehicles[vehicle->vehicle_type];
if (vehicle_type->flags & VEHICLE_ENTRY_FLAG_LOADING_WAYPOINTS)
{
peep_update_ride_leave_entrance_waypoints(this, ride);
return;
}
if (vehicle_type->flags & VEHICLE_ENTRY_FLAG_DODGEM_CAR_PLACEMENT)
{
destination_x = vehicle->x;
destination_y = vehicle->y;
destination_tolerance = 15;
sub_state = PEEP_RIDE_APPROACH_VEHICLE;
return;
}
sint8 load_position = 0;
// Safe, in case current seat > number of loading positions
uint16 numSeatPositions = static_cast<uint16>(vehicle_type->peep_loading_positions.size());
if (numSeatPositions != 0)
{
size_t loadPositionIndex = numSeatPositions - 1;
if (current_seat < numSeatPositions)
{
loadPositionIndex = current_seat;
}
load_position = vehicle_type->peep_loading_positions[loadPositionIndex];
}
switch (vehicle->sprite_direction / 8)
{
case 0:
destination_x = vehicle->x - load_position;
break;
case 1:
destination_y = vehicle->y + load_position;
break;
case 2:
destination_x = vehicle->x + load_position;
break;
case 3:
destination_y = vehicle->y - load_position;
break;
}
sub_state = PEEP_RIDE_APPROACH_VEHICLE;
}
/**
*
* rct2: 0x0069321D
*/
static void peep_go_to_ride_exit(rct_peep * peep, Ride * ride, sint16 x, sint16 y, sint16 z, uint8 exit_direction)
{
z += RideData5[ride->type].z;
sprite_move(x, y, z, (rct_sprite *)peep);
invalidate_sprite_2((rct_sprite *)peep);
Guard::Assert(peep->current_ride_station < MAX_STATIONS);
auto exit = ride_get_exit_location(ride, peep->current_ride_station);
Guard::Assert(!exit.isNull());
x = exit.x;
y = exit.y;
x *= 32;
y *= 32;
x += 16;
y += 16;
sint16 x_shift = word_981D6C[exit_direction].x;
sint16 y_shift = word_981D6C[exit_direction].y;
sint16 shift_multiplier = 20;
rct_ride_entry * rideEntry = get_ride_entry(ride->subtype);
if (rideEntry != nullptr)
{
rct_ride_entry_vehicle * vehicle_entry = &rideEntry->vehicles[rideEntry->default_vehicle];
if (vehicle_entry->flags & VEHICLE_ENTRY_FLAG_MINI_GOLF ||
vehicle_entry->flags & (VEHICLE_ENTRY_FLAG_CHAIRLIFT | VEHICLE_ENTRY_FLAG_GO_KART))
{
shift_multiplier = 32;
}
}
x_shift *= shift_multiplier;
y_shift *= shift_multiplier;
x -= x_shift;
y -= y_shift;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerance = 2;
peep->sprite_direction = exit_direction * 8;
peep->sub_state = PEEP_RIDE_APPROACH_EXIT;
}
/**
*
* rct2: 0x006920B4
*/
void rct_peep::UpdateRideFreeVehicleEnterRide(Ride * ride)
{
money16 ridePrice = ride_get_price(ride);
if (ridePrice != 0)
{
if ((item_standard_flags & PEEP_ITEM_VOUCHER) && (voucher_type == VOUCHER_TYPE_RIDE_FREE) &&
(voucher_arguments == current_ride))
{
item_standard_flags &= ~PEEP_ITEM_VOUCHER;
window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY;
}
else
{
ride->total_profit += ridePrice;
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME;
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_PARK_RIDE_TICKETS;
SpendMoney(paid_on_rides, ridePrice);
}
}
sub_state = PEEP_RIDE_LEAVE_ENTRANCE;
uint8 queueTime = days_in_queue;
if (queueTime < 253)
queueTime += 3;
queueTime /= 2;
if (queueTime != ride->queue_time[current_ride_station])
{
ride->queue_time[current_ride_station] = queueTime;
window_invalidate_by_number(WC_RIDE, current_ride);
}
if (peep_flags & PEEP_FLAGS_TRACKING)
{
set_format_arg(0, rct_string_id, name_string_idx);
set_format_arg(2, uint32, id);
set_format_arg(6, rct_string_id, ride->name);
set_format_arg(8, uint32, ride->name_arguments);
rct_string_id msg_string;
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IN_RIDE))
msg_string = STR_PEEP_TRACKING_PEEP_IS_IN_X;
else
msg_string = STR_PEEP_TRACKING_PEEP_IS_ON_X;
if (gConfigNotifications.guest_on_ride)
{
news_item_add_to_queue(NEWS_ITEM_PEEP_ON_RIDE, msg_string, sprite_index);
}
}
if (ride->type == RIDE_TYPE_SPIRAL_SLIDE)
{
SwitchToSpecialSprite(1);
}
UpdateRideAdvanceThroughEntrance();
}
/**
*
* rct2: 0x00691FD4
*/
static void peep_update_ride_no_free_vehicle_rejoin_queue(rct_peep * peep, Ride * ride)
{
TileCoordsXYZD entranceLocation = ride_get_entrance_location(peep->current_ride, peep->current_ride_station);
sint32 x = entranceLocation.x * 32;
sint32 y = entranceLocation.y * 32;
x += 16 - word_981D6C[entranceLocation.direction].x * 20;
y += 16 - word_981D6C[entranceLocation.direction].y * 20;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerance = 2;
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_QUEUING_FRONT;
peep->sub_state = PEEP_RIDE_AT_ENTRANCE;
peep_window_state_update(peep);
ride_queue_insert_guest_at_front(ride, peep->current_ride_station, peep);
}
/**
*
* rct2: 0x00691E42
* Note: Before this was the entry
* point for sub state 1 and 3. The
* check has been removed that would
* branch it out to 1 and 3. Now uses
* separate functions.
*/
void rct_peep::UpdateRideFreeVehicleCheck()
{
Ride * ride = get_ride(current_ride);
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_VEHICLES))
{
if (ride->status != RIDE_STATUS_OPEN || ride->vehicle_change_timeout != 0 || (++rejoin_queue_timeout) == 0)
{
peep_update_ride_no_free_vehicle_rejoin_queue(this, ride);
return;
}
UpdateRideFreeVehicleEnterRide(ride);
return;
}
rct_vehicle * vehicle = GET_VEHICLE(ride->vehicles[current_train]);
for (sint32 i = current_car; i != 0; --i)
{
vehicle = GET_VEHICLE(vehicle->next_vehicle_on_train);
}
rct_ride_entry * ride_entry = get_ride_entry(vehicle->ride_subtype);
if (ride_entry == nullptr)
{
return;
}
if (ride_entry->vehicles[0].flags & VEHICLE_ENTRY_FLAG_MINI_GOLF)
{
vehicle->mini_golf_flags &= ~(1 << 5);
for (size_t i = 0; i < ride->num_vehicles; ++i)
{
if (ride->vehicles[i] == SPRITE_INDEX_NULL)
continue;
rct_vehicle * train = GET_VEHICLE(ride->vehicles[i]);
rct_vehicle * second_vehicle = GET_VEHICLE(train->next_vehicle_on_train);
if (second_vehicle->num_peeps == 0)
continue;
if (second_vehicle->mini_golf_flags & (1 << 5))
continue;
return;
}
}
if (!vehicle_is_used_in_pairs(vehicle))
{
UpdateRideFreeVehicleEnterRide(ride);
return;
}
if (ride->mode == RIDE_MODE_FORWARD_ROTATION || ride->mode == RIDE_MODE_BACKWARD_ROTATION)
{
if (current_seat & 1 || !(vehicle->next_free_seat & 1))
{
UpdateRideFreeVehicleEnterRide(ride);
return;
}
}
else
{
uint8 current_seat = (current_seat & 0xFE) + 1;
if (current_seat < vehicle->next_free_seat)
{
UpdateRideFreeVehicleEnterRide(ride);
return;
}
}
rct_vehicle * currentTrain = GET_VEHICLE(ride->vehicles[current_train]);
if (ride->status == RIDE_STATUS_OPEN && ++rejoin_queue_timeout != 0 &&
!(currentTrain->update_flags & VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART))
{
return;
}
if (ride->mode != RIDE_MODE_FORWARD_ROTATION && ride->mode != RIDE_MODE_BACKWARD_ROTATION)
{
if (vehicle->next_free_seat - 1 != current_seat)
return;
}
vehicle->next_free_seat--;
vehicle->peep[current_seat] = SPRITE_INDEX_NULL;
peep_update_ride_no_free_vehicle_rejoin_queue(this, ride);
}
void rct_peep::UpdateRideApproachVehicle()
{
sint16 actionX, actionY, xy_distance;
if (!UpdateAction(&actionX, &actionY, &xy_distance))
{
sub_state = PEEP_RIDE_ENTER_VEHICLE;
return;
}
Invalidate();
sprite_move(actionX, actionY, z, (rct_sprite *)this);
Invalidate();
}
void rct_peep::UpdateRideEnterVehicle()
{
Ride * ride = get_ride(current_ride);
rct_vehicle * vehicle = GET_VEHICLE(ride->vehicles[current_train]);
for (sint32 i = current_car; i != 0; --i)
{
vehicle = GET_VEHICLE(vehicle->next_vehicle_on_train);
}
if (ride->mode != RIDE_MODE_FORWARD_ROTATION && ride->mode != RIDE_MODE_BACKWARD_ROTATION)
{
if (current_seat != vehicle->num_peeps)
return;
}
if (vehicle_is_used_in_pairs(vehicle))
{
rct_peep * seated_peep = GET_PEEP(vehicle->peep[current_seat ^ 1]);
if (seated_peep->sub_state != PEEP_RIDE_ENTER_VEHICLE)
return;
vehicle->num_peeps++;
ride->cur_num_customers++;
vehicle->mass += seated_peep->mass;
seated_peep->Invalidate();
sprite_move(LOCATION_NULL, 0, 0, (rct_sprite *)seated_peep);
seated_peep->SetState(PEEP_STATE_ON_RIDE);
seated_peep->time_on_ride = 0;
seated_peep->sub_state = PEEP_RIDE_ON_RIDE;
seated_peep->OnEnterRide(current_ride);
}
vehicle->num_peeps++;
ride->cur_num_customers++;
vehicle->mass += mass;
invalidate_sprite_2((rct_sprite *)vehicle);
Invalidate();
sprite_move(LOCATION_NULL, 0, 0, (rct_sprite *)this);
SetState(PEEP_STATE_ON_RIDE);
time_on_ride = 0;
sub_state = PEEP_RIDE_ON_RIDE;
OnEnterRide(current_ride);
}
/**
*
* rct2: 0x00693028
*/
void rct_peep::UpdateRideLeaveVehicle()
{
Ride * ride = get_ride(current_ride);
rct_vehicle * vehicle = GET_VEHICLE(ride->vehicles[current_train]);
uint8 ride_station = vehicle->current_station;
for (sint32 i = current_car; i != 0; --i)
{
vehicle = GET_VEHICLE(vehicle->next_vehicle_on_train);
}
// Check if ride is NOT Ferris Wheel.
if (ride->mode != RIDE_MODE_FORWARD_ROTATION && ride->mode != RIDE_MODE_BACKWARD_ROTATION)
{
if (vehicle->num_peeps - 1 != current_seat)
return;
}
action_sprite_image_offset++;
if (action_sprite_image_offset & 3)
return;
action_sprite_image_offset = 0;
vehicle->num_peeps--;
vehicle->mass -= mass;
invalidate_sprite_2((rct_sprite *)vehicle);
if (ride_station >= MAX_STATIONS)
{
// HACK #5658: Some parks have hacked rides which end up in this state
sint8 bestStationIndex = ride_get_first_valid_station_exit(ride);
if (bestStationIndex == -1)
{
bestStationIndex = 0;
}
ride_station = bestStationIndex;
}
current_ride_station = ride_station;
rct_ride_entry * rideEntry = get_ride_entry(vehicle->ride_subtype);
if (rideEntry == nullptr)
{
return;
}
rct_ride_entry_vehicle * vehicle_entry = &rideEntry->vehicles[vehicle->vehicle_type];
if (!(vehicle_entry->flags & VEHICLE_ENTRY_FLAG_LOADING_WAYPOINTS))
{
assert(current_ride_station < MAX_STATIONS);
TileCoordsXYZD exitLocation = ride_get_exit_location(current_ride, current_ride_station);
CoordsXYZD platformLocation;
platformLocation.z = ride->station_heights[current_ride_station];
platformLocation.direction = exitLocation.direction ^ (1 << 1);
if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_16))
{
for (; vehicle->is_child; vehicle = GET_VEHICLE(vehicle->prev_vehicle_on_ride))
{
uint16 trackType = vehicle->track_type >> 2;
if (trackType == TRACK_ELEM_FLAT || trackType > TRACK_ELEM_MIDDLE_STATION)
continue;
rct_tile_element * inner_map = map_get_first_element_at(vehicle->track_x / 32, vehicle->track_y / 32);
for (;; inner_map++)
{
if (inner_map->GetType() != TILE_ELEMENT_TYPE_TRACK)
continue;
if (inner_map->base_height == vehicle->track_z / 8)
break;
}
uint8 stationIndex = tile_element_get_station(inner_map);
if (stationIndex == current_ride_station)
break;
}
uint8 shiftMultiplier = 12;
uint8 specialDirection = platformLocation.direction;
rideEntry = get_ride_entry(ride->subtype);
if (rideEntry != nullptr)
{
vehicle_entry = &rideEntry->vehicles[rideEntry->default_vehicle];
if (vehicle_entry->flags & VEHICLE_ENTRY_FLAG_GO_KART)
{
shiftMultiplier = 9;
}
if (vehicle_entry->flags & (VEHICLE_ENTRY_FLAG_CHAIRLIFT | VEHICLE_ENTRY_FLAG_GO_KART))
{
specialDirection = ((vehicle->sprite_direction + 3) / 8) + 1;
specialDirection &= 3;
if (vehicle->var_CD == 6)
specialDirection ^= (1 << 1);
}
}
sint16 xShift = word_981D6C[specialDirection].x;
sint16 yShift = word_981D6C[specialDirection].y;
platformLocation.x = vehicle->x + xShift * shiftMultiplier;
platformLocation.y = vehicle->y + yShift * shiftMultiplier;
platformLocation.z *= 8;
peep_go_to_ride_exit(this, ride, platformLocation.x, platformLocation.y, platformLocation.z, platformLocation.direction);
return;
}
platformLocation.x = vehicle->x + word_981D6C[platformLocation.direction].x * 12;
platformLocation.y = vehicle->y + word_981D6C[platformLocation.direction].y * 12;
sint8 loadPosition = vehicle_entry->peep_loading_positions[current_seat];
switch (vehicle->sprite_direction / 8)
{
case 0:
platformLocation.x -= loadPosition;
break;
case 1:
platformLocation.y += loadPosition;
break;
case 2:
platformLocation.x += loadPosition;
break;
case 3:
platformLocation.y -= loadPosition;
break;
}
platformLocation.z = ride->station_heights[current_ride_station] * 8;
peep_go_to_ride_exit(this, ride, platformLocation.x, platformLocation.y, platformLocation.z, platformLocation.direction);
return;
}
TileCoordsXYZD exitLocation = ride_get_exit_location(current_ride, current_ride_station);
Guard::Assert(!exitLocation.isNull());
CoordsXYZ waypointLoc;
waypointLoc.z = (sint16)exitLocation.z * 8 + RideData5[ride->type].z;
waypointLoc.x = ride->station_starts[current_ride_station].x * 32 + 16;
waypointLoc.y = ride->station_starts[current_ride_station].y * 32 + 16;
rct_tile_element * trackElement = ride_get_station_start_track_element(ride, current_ride_station);
uint8 station_direction = (trackElement == nullptr ? 0 : tile_element_get_direction(trackElement));
vehicle = GET_VEHICLE(ride->vehicles[current_train]);
rideEntry = get_ride_entry(vehicle->ride_subtype);
rct_ride_entry_vehicle * vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type];
var_37 = ((exitLocation.direction | peep_get_waypointed_seat_location(this, ride, vehicleEntry, station_direction) * 4) * 4) | 1;
if (ride->type == RIDE_TYPE_ENTERPRISE)
{
waypointLoc.x = vehicle->x;
waypointLoc.y = vehicle->y;
}
Guard::Assert(vehicleEntry->peep_loading_waypoints.size() >= (size_t)(var_37 / 4));
CoordsXYZ exitWaypointLoc = waypointLoc;
exitWaypointLoc.x += vehicleEntry->peep_loading_waypoints[var_37 / 4][2].x;
exitWaypointLoc.y += vehicleEntry->peep_loading_waypoints[var_37 / 4][2].y;
if (ride->type == RIDE_TYPE_MOTION_SIMULATOR)
exitWaypointLoc.z += 15;
sprite_move(exitWaypointLoc.x, exitWaypointLoc.y, exitWaypointLoc.z, (rct_sprite *)this);
Invalidate();
waypointLoc.x += vehicleEntry->peep_loading_waypoints[var_37 / 4][1].x;
waypointLoc.y += vehicleEntry->peep_loading_waypoints[var_37 / 4][1].y;
destination_x = waypointLoc.x;
destination_y = waypointLoc.y;
destination_tolerance = 2;
sub_state = PEEP_RIDE_APPROACH_EXIT_WAYPOINTS;
}
/**
*
* rct2: 0x00695444
*/
static void peep_on_enter_or_exit_ridebrk(rct_peep * peep, sint32 rideIndex, sint32 flags)
{
if (flags & 1)
{
peep->OnExitRide(rideIndex);
}
else
{
peep->OnEnterRide(rideIndex);
}
}
/**
*
* rct2: 0x0069376A
*/
static void peep_update_ride_prepare_for_exit(rct_peep * peep)
{
Ride * ride = get_ride(peep->current_ride);
Guard::Assert(peep->current_ride_station < Util::CountOf(ride->exits), GUARD_LINE);
auto exit = ride_get_exit_location(peep->current_ride, peep->current_ride_station);
sint16 x = exit.x;
sint16 y = exit.y;
uint8 exit_direction = exit.direction;
x *= 32;
y *= 32;
x += 16;
y += 16;
sint16 x_shift = word_981D6C[exit_direction].x;
sint16 y_shift = word_981D6C[exit_direction].y;
sint16 shift_multiplier = 20;
rct_ride_entry * ride_type = get_ride_entry(ride->subtype);
if (ride_type != nullptr)
{
rct_ride_entry_vehicle * vehicle_entry = &ride_type->vehicles[ride_type->default_vehicle];
if (vehicle_entry->flags & (VEHICLE_ENTRY_FLAG_CHAIRLIFT | VEHICLE_ENTRY_FLAG_GO_KART))
{
shift_multiplier = 32;
}
}
x_shift *= shift_multiplier;
y_shift *= shift_multiplier;
x -= x_shift;
y -= y_shift;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerance = 2;
peep->sub_state = PEEP_RIDE_IN_EXIT;
}
/**
*
* rct2: 0x0069374F
*/
void rct_peep::UpdateRideApproachExit()
{
sint16 actionX, actionY, xy_distance;
if (UpdateAction(&actionX, &actionY, &xy_distance))
{
Invalidate();
sprite_move(actionX, actionY, z, (rct_sprite *)this);
Invalidate();
return;
}
peep_update_ride_prepare_for_exit(this);
}
/**
*
* rct2: 0x0069382E
*/
void rct_peep::UpdateRideInExit()
{
sint16 actionX, actionY, xy_distance;
Ride * ride = get_ride(current_ride);
if (UpdateAction(&actionX, &actionY, &xy_distance))
{
Invalidate();
if (xy_distance >= 16)
{
sint16 actionZ = ride->station_heights[current_ride_station] * 8;
actionZ += RideData5[ride->type].z;
sprite_move(actionX, actionY, actionZ, (rct_sprite *)this);
Invalidate();
return;
}
SwitchToSpecialSprite(0);
sprite_move(actionX, actionY, z, (rct_sprite *)this);
Invalidate();
}
if (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO)
{
uint8 secondaryItem = RidePhotoItems[ride->type];
if (DecideAndBuyItem(current_ride, secondaryItem, ride->price_secondary))
{
ride->no_secondary_items_sold++;
}
}
sub_state = PEEP_RIDE_LEAVE_EXIT;
}
/**
*
* rct2: 0x006926AD
*/
void rct_peep::UpdateRideApproachVehicleWaypoints()
{
sint16 actionX, actionY, xy_distance;
Ride * ride = get_ride(current_ride);
uint8 waypoint = var_37 & 3;
if (UpdateAction(&actionX, &actionY, &xy_distance))
{
sint16 actionZ;
// Motion simulators have steps this moves the peeps up the steps
if (ride->type == RIDE_TYPE_MOTION_SIMULATOR)
{
actionZ = ride->station_heights[current_ride_station] * 8 + 2;
if (waypoint == 2)
{
xy_distance -= 12;
if (xy_distance < 0)
xy_distance = 0;
if (xy_distance <= 15)
{
actionZ += 15 - xy_distance;
}
}
}
else
{
actionZ = z;
}
Invalidate();
sprite_move(actionX, actionY, actionZ, (rct_sprite *)this);
Invalidate();
return;
}
if (waypoint == 2)
{
sub_state = PEEP_RIDE_ENTER_VEHICLE;
return;
}
waypoint++;
// This is incrementing the actual peep waypoint
var_37++;
rct_vehicle * vehicle = GET_VEHICLE(ride->vehicles[current_train]);
actionX = ride->station_starts[current_ride_station].x * 32 + 16;
actionY = ride->station_starts[current_ride_station].y * 32 + 16;
if (ride->type == RIDE_TYPE_ENTERPRISE)
{
actionX = vehicle->x;
actionY = vehicle->y;
}
rct_ride_entry * ride_entry = get_ride_entry(vehicle->ride_subtype);
if (ride_entry == nullptr)
{
return;
}
rct_ride_entry_vehicle * vehicle_type = &ride_entry->vehicles[vehicle->vehicle_type];
Guard::Assert(waypoint < 3);
actionX += vehicle_type->peep_loading_waypoints[var_37 / 4][waypoint].x;
actionY += vehicle_type->peep_loading_waypoints[var_37 / 4][waypoint].y;
destination_x = actionX;
destination_y = actionY;
}
/**
*
* rct2: 0x0069357D
*/
void rct_peep::UpdateRideApproachExitWaypoints()
{
sint16 actionX, actionY, xy_distance;
Ride * ride = get_ride(current_ride);
if (UpdateAction(&actionX, &actionY, &xy_distance))
{
sint16 actionZ;
if (ride->type == RIDE_TYPE_MOTION_SIMULATOR)
{
actionZ = ride->station_heights[current_ride_station] * 8 + 2;
if ((var_37 & 3) == 1)
{
if (xy_distance > 15)
xy_distance = 15;
actionZ += xy_distance;
}
}
else
{
actionZ = z;
}
Invalidate();
sprite_move(actionX, actionY, actionZ, (rct_sprite *)this);
Invalidate();
return;
}
if ((var_37 & 3) != 0)
{
if ((var_37 & 3) == 3)
{
peep_update_ride_prepare_for_exit(this);
return;
}
var_37--;
rct_vehicle * vehicle = GET_VEHICLE(ride->vehicles[current_train]);
actionX = ride->station_starts[current_ride_station].x * 32 + 16;
actionY = ride->station_starts[current_ride_station].y * 32 + 16;
if (ride->type == RIDE_TYPE_ENTERPRISE)
{
actionX = vehicle->x;
actionY = vehicle->y;
}
rct_ride_entry * rideEntry = get_ride_entry(vehicle->ride_subtype);
rct_ride_entry_vehicle * vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type];
Guard::Assert((var_37 & 3) < 3);
actionX += vehicleEntry->peep_loading_waypoints[var_37 / 4][var_37 & 3].x;
actionY += vehicleEntry->peep_loading_waypoints[var_37 / 4][var_37 & 3].y;
destination_x = actionX;
destination_y = actionY;
return;
}
var_37 |= 3;
auto exit = ride_get_exit_location(current_ride, current_ride_station);
actionX = exit.x;
actionY = exit.y;
uint8 exit_direction = exit.direction ^ 2;
actionX *= 32;
actionY *= 32;
actionX += 16;
actionY += 16;
sint16 x_shift = word_981D6C[exit_direction].x;
sint16 y_shift = word_981D6C[exit_direction].y;
sint16 shift_multiplier = 20;
rct_ride_entry * ride_type = get_ride_entry(ride->subtype);
rct_ride_entry_vehicle * vehicle_entry = &ride_type->vehicles[ride_type->default_vehicle];
if (vehicle_entry->flags & (VEHICLE_ENTRY_FLAG_CHAIRLIFT | VEHICLE_ENTRY_FLAG_GO_KART))
{
shift_multiplier = 32;
}
x_shift *= shift_multiplier;
y_shift *= shift_multiplier;
actionX -= x_shift;
actionY -= y_shift;
destination_x = actionX;
destination_y = actionY;
}
/**
*
* rct2: 0x006927B3
*/
void rct_peep::UpdateRideApproachSpiralSlide()
{
sint16 x, y, xy_distance;
Ride * ride = get_ride(peep->current_ride);
if (UpdateAction(&x, &y, &xy_distance, peep))
{
invalidate_sprite_2((rct_sprite *)peep);
sprite_move(x, y, peep->z, (rct_sprite *)peep);
invalidate_sprite_2((rct_sprite *)peep);
return;
}
uint8 waypoint = peep->var_37 & 3;
if (waypoint == 3)
{
peep->sub_state = 15;
peep->destination_x = 0;
peep->destination_y = 0;
peep->var_37 = (peep->var_37 / 4) & 0xC;
sprite_move(LOCATION_NULL, y, peep->z, (rct_sprite *)peep);
return;
}
else if (waypoint == 2)
{
uint8 last_ride = 0;
if (ride->status != RIDE_STATUS_OPEN)
last_ride = 1;
else if (peep->current_car++ != 0)
{
if (ride->mode == RIDE_MODE_SINGLE_RIDE_PER_ADMISSION)
last_ride = 1;
if ((uint8)(peep->current_car - 1) > (scenario_rand() & 0xF))
last_ride = 1;
}
if (last_ride)
{
auto exit = ride_get_exit_location(peep->current_ride, peep->current_ride_station);
uint8 exit_direction = exit.direction;
waypoint = 1;
peep->var_37 = (exit_direction * 4) | (peep->var_37 & 0x30) | waypoint;
x = ride->station_starts[peep->current_ride_station].x;
y = ride->station_starts[peep->current_ride_station].y;
x *= 32;
y *= 32;
assert(ride->type == RIDE_TYPE_SPIRAL_SLIDE);
const CoordsXY slidePlatformDestination = SpiralSlideWalkingPath[peep->var_37];
x += slidePlatformDestination.x;
y += slidePlatformDestination.y;
peep->destination_x = x;
peep->destination_y = y;
peep->sub_state = PEEP_RIDE_LEAVE_SPIRAL_SLIDE;
return;
}
}
waypoint++;
// Actually increment the real peep waypoint
peep->var_37++;
x = ride->station_starts[peep->current_ride_station].x;
y = ride->station_starts[peep->current_ride_station].y;
x *= 32;
y *= 32;
assert(ride->type == RIDE_TYPE_SPIRAL_SLIDE);
const CoordsXY slidePlatformDestination = SpiralSlideWalkingPath[peep->var_37];
x += slidePlatformDestination.x;
y += slidePlatformDestination.y;
peep->destination_x = x;
peep->destination_y = y;
}
/** rct2: 0x00981F0C, 0x00981F0E */
static constexpr const CoordsXY _981F0C[] = {
{ 25, 56 },
{ 56, 7 },
{ 7, -24 },
{ -24, 25 },
};
/** rct2: 0x00981F1C, 0x00981F1E */
static constexpr const CoordsXY _981F1C[] = {
{ 8, 56 },
{ 56, 24 },
{ 24, -24 },
{ -24, 8 },
};
/**
*
* rct2: 0x00692D83
*/
void rct_peep::UpdateRideOnSpiralSlide()
{
Ride * ride = get_ride(peep->current_ride);
if (ride->type != RIDE_TYPE_SPIRAL_SLIDE)
return;
if ((peep->var_37 & 3) == 0)
{
switch (peep->destination_x)
{
case 0:
peep->destination_y++;
if (peep->destination_y >= 30)
peep->destination_x++;
return;
case 1:
if (ride->slide_in_use != 0)
return;
ride->slide_in_use++;
ride->slide_peep = peep->sprite_index;
ride->slide_peep_t_shirt_colour = peep->tshirt_colour;
ride->spiral_slide_progress = 0;
peep->destination_x++;
return;
case 2:
return;
case 3:
{
sint16 x = ride->station_starts[peep->current_ride_station].x;
sint16 y = ride->station_starts[peep->current_ride_station].y;
x *= 32;
y *= 32;
uint8 direction = (peep->var_37 / 4) & 3;
sint16 dest_x = x + _981F1C[direction].x;
sint16 dest_y = y + _981F1C[direction].y;
peep->destination_x = dest_x;
peep->destination_y = dest_y;
x += _981F0C[direction].x;
y += _981F0C[direction].y;
sprite_move(x, y, peep->z, (rct_sprite *)peep);
peep->sprite_direction = (peep->var_37 & 0xC) * 2;
invalidate_sprite_2((rct_sprite *)peep);
peep->var_37++;
return;
}
default:
return;
}
}
sint16 x, y, xy_distance;
if (UpdateAction(&x, &y, &xy_distance, peep))
{
invalidate_sprite_2((rct_sprite *)peep);
sprite_move(x, y, peep->z, (rct_sprite *)peep);
invalidate_sprite_2((rct_sprite *)peep);
return;
}
uint8 waypoint = 2;
peep->var_37 = (peep->var_37 * 4 & 0x30) + waypoint;
x = ride->station_starts[peep->current_ride_station].x;
y = ride->station_starts[peep->current_ride_station].y;
x *= 32;
y *= 32;
assert(ride->type == RIDE_TYPE_SPIRAL_SLIDE);
const CoordsXY slidePlatformDestination = SpiralSlideWalkingPath[peep->var_37];
x += slidePlatformDestination.x;
y += slidePlatformDestination.y;
peep->destination_x = x;
peep->destination_y = y;
peep->sub_state = PEEP_RIDE_APPROACH_SPIRAL_SLIDE;
}
/**
*
* rct2: 0x00692C6B
*/
void rct_peep::UpdateRideLeaveSpiralSlide()
{
// Iterates through the spiral slide waypoints until it reaches
// waypoint 0. Then it readies to leave the ride by the entrance.
sint16 x, y, xy_distance;
if (UpdateAction(&x, &y, &xy_distance, peep))
{
invalidate_sprite_2((rct_sprite *)peep);
sprite_move(x, y, peep->z, (rct_sprite *)peep);
invalidate_sprite_2((rct_sprite *)peep);
return;
}
Ride * ride = get_ride(peep->current_ride);
uint8 waypoint = peep->var_37 & 3;
if (waypoint != 0)
{
if (waypoint == 3)
{
peep_update_ride_prepare_for_exit(peep);
return;
}
waypoint--;
// Actually decrement the peep waypoint
peep->var_37--;
x = ride->station_starts[peep->current_ride_station].x * 32;
y = ride->station_starts[peep->current_ride_station].y * 32;
assert(ride->type == RIDE_TYPE_SPIRAL_SLIDE);
const CoordsXY slidePlatformDestination = SpiralSlideWalkingPath[peep->var_37];
x += slidePlatformDestination.x;
y += slidePlatformDestination.y;
peep->destination_x = x;
peep->destination_y = y;
return;
}
waypoint = 3;
// Actually force the final waypoint
peep->var_37 |= 3;
auto exit = ride_get_exit_location(peep->current_ride, peep->current_ride_station);
x = exit.x * 32 + 16;
y = exit.y * 32 + 16;
uint8 exit_direction = exit.direction ^ 2;
sint16 x_shift = word_981D6C[exit_direction].x;
sint16 y_shift = word_981D6C[exit_direction].y;
sint16 shift_multiplier = 20;
x_shift *= shift_multiplier;
y_shift *= shift_multiplier;
x -= x_shift;
y -= y_shift;
peep->destination_x = x;
peep->destination_y = y;
}
/** rct2: 0x00981FE4 */
static constexpr const uint8 _981FE4[][4] = {
{ 15, 7, 15, 7 },
{ 11, 3, 11, 3 },
{ 7, 15, 7, 15 },
{ 3, 11, 3, 11 },
};
/** rct2: 0x00981FF4 */
static constexpr const uint8 _981FF4[][4] = {
{ 1, 2, 14, 0 },
{ 4, 5, 6, 2 },
{ 6, 8, 9, 10 },
{ 14, 10, 12, 13 },
};
/**
*
* rct2: 0x00692A83
*/
void rct_peep::UpdateRideMazePathfinding()
{
sint16 x, y, xy_distance;
if (UpdateAction(&x, &y, &xy_distance, peep))
{
invalidate_sprite_2((rct_sprite *)peep);
sprite_move(x, y, peep->z, (rct_sprite *)peep);
invalidate_sprite_2((rct_sprite *)peep);
return;
}
Ride * ride = get_ride(peep->current_ride);
if (peep->var_37 == 16)
{
peep_update_ride_prepare_for_exit(peep);
return;
}
if (peep->action >= PEEP_ACTION_NONE_1)
{
if (peep->energy > 64 && (scenario_rand() & 0xFFFF) <= 2427)
{
peep->action = PEEP_ACTION_JUMP;
peep->action_frame = 0;
peep->action_sprite_image_offset = 0;
UpdateCurrentActionSpriteType(peep);
invalidate_sprite_2((rct_sprite *)peep);
}
}
x = peep->destination_x & 0xFFE0;
y = peep->destination_y & 0xFFE0;
sint16 z = ride->station_heights[0];
// Find the station track element
rct_tile_element * tileElement = map_get_first_element_at(x / 32, y / 32);
do
{
if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK && z == tileElement->base_height)
break;
} while (!tile_element_is_last_for_tile(tileElement++));
uint16 maze_entry = track_element_get_maze_entry(tileElement);
uint16 open_hedges = 0;
uint8 var_37 = peep->var_37;
// var_37 is 3, 7, 11 or 15
if (maze_entry & (1 << _981FF4[var_37 / 4][3]))
{
open_hedges = 1;
}
open_hedges <<= 1;
if (maze_entry & (1 << _981FF4[var_37 / 4][2]))
{
open_hedges |= 1;
}
open_hedges <<= 1;
if (maze_entry & (1 << _981FF4[var_37 / 4][1]))
{
open_hedges |= 1;
}
open_hedges <<= 1;
if (maze_entry & (1 << _981FF4[var_37 / 4][0]))
{
open_hedges |= 1;
}
open_hedges ^= 0xF;
if (open_hedges == 0)
return;
uint8 maze_last_edge = peep->maze_last_edge ^ (1 << 1);
open_hedges &= ~(1 << maze_last_edge);
if (open_hedges == 0)
open_hedges |= (1 << maze_last_edge);
uint8 chosen_edge = scenario_rand() & 0x3;
while (!(open_hedges & (1 << chosen_edge)))
{
chosen_edge = (chosen_edge + 1) & 3;
}
x = TileDirectionDelta[chosen_edge].x / 2;
y = TileDirectionDelta[chosen_edge].y / 2;
x += peep->destination_x;
y += peep->destination_y;
uint8 type = 0;
tileElement = map_get_first_element_at(x / 32, y / 32);
do
{
if (z != tileElement->base_height)
continue;
if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK)
{
type = 1;
break;
}
if (tileElement->GetType() == TILE_ELEMENT_TYPE_ENTRANCE &&
tileElement->properties.entrance.type == ENTRANCE_TYPE_RIDE_EXIT)
{
type = 2;
break;
}
} while (!tile_element_is_last_for_tile(tileElement++));
switch (type)
{
case 0:
peep->maze_last_edge++;
peep->maze_last_edge &= 3;
return;
case 1:
peep->destination_x = x;
peep->destination_y = y;
peep->var_37 = _981FE4[peep->var_37 / 4][chosen_edge];
peep->maze_last_edge = chosen_edge;
break;
case 2:
x = peep->destination_x;
y = peep->destination_y;
if (chosen_edge & 1)
{
x &= 0xFFE0;
x += 16;
}
else
{
y &= 0xFFE0;
y += 16;
}
peep->destination_x = x;
peep->destination_y = y;
peep->var_37 = 16;
peep->maze_last_edge = chosen_edge;
break;
}
if (UpdateAction(&x, &y, &xy_distance, peep))
{
invalidate_sprite_2((rct_sprite *)peep);
sprite_move(x, y, peep->z, (rct_sprite *)peep);
invalidate_sprite_2((rct_sprite *)peep);
return;
}
}
/**
*
* rct2: 0x006938D2
*/
void rct_peep::UpdateRideLeaveExit()
{
sint16 actionX, actionY, xy_distance;
Ride * ride = get_ride(current_ride);
if (UpdateAction(&actionX, &actionY, &xy_distance))
{
Invalidate();
sprite_move(actionX, actionY, ride->station_heights[current_ride_station] * 8, (rct_sprite *)this);
Invalidate();
return;
}
OnExitRide(current_ride);
if (peep_flags & PEEP_FLAGS_TRACKING)
{
set_format_arg(0, rct_string_id, name_string_idx);
set_format_arg(2, uint32, id);
set_format_arg(6, rct_string_id, ride->name);
set_format_arg(8, uint32, ride->name_arguments);
if (gConfigNotifications.guest_left_ride)
{
news_item_add_to_queue(NEWS_ITEM_PEEP_ON_RIDE, STR_PEEP_TRACKING_LEFT_RIDE_X, sprite_index);
}
}
interaction_ride_index = 0xFF;
SetState(PEEP_STATE_FALLING);
actionX = x & 0xFFE0;
actionY = y & 0xFFE0;
// Find the station track element
rct_tile_element * tileElement = map_get_first_element_at(actionX / 32, actionY / 32);
do
{
if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH)
continue;
sint16 height = map_height_from_slope(x, y, tileElement->properties.path.type);
height += tileElement->base_height * 8;
sint16 z_diff = z - height;
if (z_diff > 0 || z_diff < -16)
continue;
sprite_move(x, y, height, (rct_sprite *)this);
Invalidate();
return;
} while (!tile_element_is_last_for_tile(tileElement++));
}
/**
*
* rct2: 0x0069299C
*/
void rct_peep::UpdateRideShopApproach()
{
sint16 x, y, xy_distance;
if (UpdateAction(&x, &y, &xy_distance, peep))
{
invalidate_sprite_2((rct_sprite *)peep);
sprite_move(x, y, peep->z, (rct_sprite *)peep);
invalidate_sprite_2((rct_sprite *)peep);
return;
}
peep->sub_state = PEEP_SHOP_INTERACT;
}
/**
*
* rct2: 0x006929BB
*/
void rct_peep::UpdateRideShopInteract()
{
sint16 x, y;
Ride * ride = get_ride(peep->current_ride);
if (ride->type == RIDE_TYPE_FIRST_AID)
{
if (peep->nausea <= 35)
{
peep->sub_state = PEEP_SHOP_LEAVE;
x = peep->next_x + 16;
y = peep->next_y + 16;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerance = 3;
peep->happiness_target = Math::Min(peep->happiness_target + 30, PEEP_MAX_HAPPINESS);
peep->happiness = peep->happiness_target;
}
else
{
peep->nausea--;
peep->nausea_target = peep->nausea;
}
return;
}
if (peep->toilet != 0)
{
peep->toilet--;
return;
}
// Do not play toilet flush sound on title screen as it's considered loud and annoying
if (!(gScreenFlags & SCREEN_FLAGS_TITLE_DEMO))
{
audio_play_sound_at_location(SOUND_TOILET_FLUSH, peep->x, peep->y, peep->z);
}
peep->sub_state = PEEP_SHOP_LEAVE;
x = peep->next_x + 16;
y = peep->next_y + 16;
peep->destination_x = x;
peep->destination_y = y;
peep->destination_tolerance = 3;
peep->happiness_target = Math::Min(peep->happiness_target + 30, PEEP_MAX_HAPPINESS);
peep->happiness = peep->happiness_target;
peep_stop_purchase_thought(peep, ride->type);
}
/**
*
* rct2: 0x00692935
*/
void rct_peep::UpdateRideShopLeave()
{
sint16 x, y, xy_distance;
if (UpdateAction(&x, &y, &xy_distance, peep))
{
invalidate_sprite_2((rct_sprite *)peep);
sprite_move(x, y, peep->z, (rct_sprite *)peep);
invalidate_sprite_2((rct_sprite *)peep);
x = peep->x & 0xFFE0;
y = peep->y & 0xFFE0;
if (x != peep->next_x)
return;
if (y != peep->next_y)
return;
}
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_WALKING;
peep_window_state_update(peep);
Ride * ride = get_ride(peep->current_ride);
ride->total_customers++;
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_CUSTOMER;
ride_update_satisfaction(ride, peep->happiness / 64);
}
/**
*
* rct2: 0x691A30
* Used by entering_ride and queueing_front */
void rct_peep::UpdateRide()
{
switch (sub_state)
{
case PEEP_RIDE_AT_ENTRANCE:
UpdateRideAtEntrance();
break;
case PEEP_RIDE_IN_ENTRANCE:
UpdateRideAdvanceThroughEntrance();
break;
case PEEP_RIDE_FREE_VEHICLE_CHECK:
UpdateRideFreeVehicleCheck();
break;
case PEEP_RIDE_LEAVE_ENTRANCE:
UpdateRideAdvanceThroughEntrance();
break;
case PEEP_RIDE_APPROACH_VEHICLE:
UpdateRideApproachVehicle();
break;
case PEEP_RIDE_ENTER_VEHICLE:
UpdateRideEnterVehicle();
break;
case PEEP_RIDE_ON_RIDE:
// No action, on ride.
break;
case PEEP_RIDE_LEAVE_VEHICLE:
UpdateRideLeaveVehicle();
break;
case PEEP_RIDE_APPROACH_EXIT:
UpdateRideApproachExit();
break;
case PEEP_RIDE_IN_EXIT:
UpdateRideInExit();
break;
case PEEP_RIDE_APPROACH_VEHICLE_WAYPOINTS:
UpdateRideApproachVehicleWaypoints();
break;
case PEEP_RIDE_APPROACH_EXIT_WAYPOINTS:
UpdateRideApproachExitWaypoints();
break;
case PEEP_RIDE_APPROACH_SPIRAL_SLIDE:
UpdateRideApproachSpiralSlide();
break;
case PEEP_RIDE_ON_SPIRAL_SLIDE:
UpdateRideOnSpiralSlide();
break;
case PEEP_RIDE_LEAVE_SPIRAL_SLIDE:
UpdateRideLeaveSpiralSlide();
break;
case PEEP_RIDE_MAZE_PATHFINDING:
UpdateRideMazePathfinding();
break;
case PEEP_RIDE_LEAVE_EXIT:
UpdateRideLeaveExit();
break;
case PEEP_SHOP_APPROACH:
UpdateRideShopApproach();
break;
case PEEP_SHOP_INTERACT:
UpdateRideShopInteract();
break;
case PEEP_SHOP_LEAVE:
UpdateRideShopLeave();
break;
default:
// Invalid peep sub-state
assert(false);
break;
}
}