mirror of https://github.com/OpenRCT2/OpenRCT2.git
468 lines
14 KiB
C++
468 lines
14 KiB
C++
/*****************************************************************************
|
|
* Copyright (c) 2014-2020 OpenRCT2 developers
|
|
*
|
|
* For a complete list of all authors, please refer to contributors.md
|
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
*
|
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
*****************************************************************************/
|
|
|
|
#include "CableLift.h"
|
|
|
|
#include "../audio/audio.h"
|
|
#include "../rct12/RCT12.h"
|
|
#include "../util/Util.h"
|
|
#include "../world/EntityList.h"
|
|
#include "Ride.h"
|
|
#include "RideData.h"
|
|
#include "Track.h"
|
|
#include "Vehicle.h"
|
|
#include "VehicleData.h"
|
|
|
|
#include <algorithm>
|
|
|
|
Vehicle* cable_lift_segment_create(
|
|
Ride& ride, int32_t x, int32_t y, int32_t z, int32_t direction, uint16_t var_44, int32_t remaining_distance, bool head)
|
|
{
|
|
Vehicle* current = CreateEntity<Vehicle>();
|
|
current->ride = ride.id;
|
|
current->ride_subtype = OBJECT_ENTRY_INDEX_NULL;
|
|
if (head)
|
|
{
|
|
ride.cable_lift = current->sprite_index;
|
|
}
|
|
current->SubType = head ? Vehicle::Type::Head : Vehicle::Type::Tail;
|
|
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->mass = 100;
|
|
current->num_seats = 0;
|
|
current->speed = 20;
|
|
current->powered_acceleration = 80;
|
|
current->velocity = 0;
|
|
current->acceleration = 0;
|
|
current->SwingSprite = 0;
|
|
current->SwingPosition = 0;
|
|
current->SwingSpeed = 0;
|
|
current->restraints_position = 0;
|
|
current->spin_sprite = 0;
|
|
current->spin_speed = 0;
|
|
current->sound2_flags = 0;
|
|
current->sound1_id = OpenRCT2::Audio::SoundId::Null;
|
|
current->sound2_id = OpenRCT2::Audio::SoundId::Null;
|
|
current->var_C4 = 0;
|
|
current->animation_frame = 0;
|
|
current->animationState = 0;
|
|
current->scream_sound_id = OpenRCT2::Audio::SoundId::Null;
|
|
current->Pitch = 0;
|
|
current->bank_rotation = 0;
|
|
for (auto& peep : current->peep)
|
|
{
|
|
peep = SPRITE_INDEX_NULL;
|
|
}
|
|
current->TrackSubposition = VehicleTrackSubposition::Default;
|
|
current->sprite_direction = direction << 3;
|
|
|
|
z = z * COORDS_Z_STEP;
|
|
current->TrackLocation = { x, y, z };
|
|
z += ride.GetRideTypeDescriptor().Heights.VehicleZOffset;
|
|
|
|
current->MoveTo({ 16, 16, z });
|
|
current->SetTrackType(TrackElemType::CableLiftHill);
|
|
current->SetTrackDirection(current->sprite_direction >> 3);
|
|
current->track_progress = 164;
|
|
current->update_flags = VEHICLE_UPDATE_FLAG_COLLISION_DISABLED;
|
|
current->SetState(Vehicle::Status::MovingToEndOfStation, 0);
|
|
current->num_peeps = 0;
|
|
current->next_free_seat = 0;
|
|
current->BoatLocation.SetNull();
|
|
current->IsCrashedVehicle = false;
|
|
return current;
|
|
}
|
|
|
|
void Vehicle::CableLiftUpdate()
|
|
{
|
|
switch (status)
|
|
{
|
|
case Vehicle::Status::MovingToEndOfStation:
|
|
CableLiftUpdateMovingToEndOfStation();
|
|
break;
|
|
case Vehicle::Status::WaitingForPassengers:
|
|
// Stays in this state until a train puts it into next state
|
|
break;
|
|
case Vehicle::Status::WaitingToDepart:
|
|
CableLiftUpdateWaitingToDepart();
|
|
break;
|
|
case Vehicle::Status::Departing:
|
|
CableLiftUpdateDeparting();
|
|
break;
|
|
case Vehicle::Status::Travelling:
|
|
CableLiftUpdateTravelling();
|
|
break;
|
|
case Vehicle::Status::Arriving:
|
|
CableLiftUpdateArriving();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006DF8A4
|
|
*/
|
|
void Vehicle::CableLiftUpdateMovingToEndOfStation()
|
|
{
|
|
if (velocity >= -439800)
|
|
acceleration = -2932;
|
|
|
|
if (velocity < -439800)
|
|
{
|
|
velocity -= velocity / 16;
|
|
acceleration = 0;
|
|
}
|
|
|
|
if (!(CableLiftUpdateTrackMotion() & VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION))
|
|
return;
|
|
|
|
velocity = 0;
|
|
acceleration = 0;
|
|
SetState(Vehicle::Status::WaitingForPassengers, sub_state);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006DF8F1
|
|
*/
|
|
void Vehicle::CableLiftUpdateWaitingToDepart()
|
|
{
|
|
if (velocity >= -58640)
|
|
acceleration = -14660;
|
|
|
|
if (velocity < -58640)
|
|
{
|
|
velocity -= velocity / 16;
|
|
acceleration = 0;
|
|
}
|
|
|
|
CableLiftUpdateTrackMotion();
|
|
|
|
// 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.
|
|
Vehicle* passengerVehicle = GetEntity<Vehicle>(cable_lift_target);
|
|
Vehicle* cableLiftSecondPart = GetEntity<Vehicle>(prev_vehicle_on_ride);
|
|
if (passengerVehicle == nullptr || cableLiftSecondPart == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int16_t distX = abs(passengerVehicle->x - cableLiftSecondPart->x);
|
|
int16_t distY = abs(passengerVehicle->y - cableLiftSecondPart->y);
|
|
|
|
if (distX + distY > 2)
|
|
return;
|
|
|
|
velocity = 0;
|
|
acceleration = 0;
|
|
SetState(Vehicle::Status::Departing, 0);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006DF97A
|
|
*/
|
|
void Vehicle::CableLiftUpdateDeparting()
|
|
{
|
|
sub_state++;
|
|
if (sub_state < 16)
|
|
return;
|
|
|
|
Vehicle* passengerVehicle = GetEntity<Vehicle>(cable_lift_target);
|
|
if (passengerVehicle == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
SetState(Vehicle::Status::Travelling, sub_state);
|
|
passengerVehicle->SetState(Vehicle::Status::TravellingCableLift, passengerVehicle->sub_state);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006DF99C
|
|
*/
|
|
void Vehicle::CableLiftUpdateTravelling()
|
|
{
|
|
Vehicle* passengerVehicle = GetEntity<Vehicle>(cable_lift_target);
|
|
if (passengerVehicle == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
velocity = std::min(passengerVehicle->velocity, 439800);
|
|
acceleration = 0;
|
|
if (passengerVehicle->HasUpdateFlag(VEHICLE_UPDATE_FLAG_BROKEN_TRAIN))
|
|
return;
|
|
|
|
if (!(CableLiftUpdateTrackMotion() & VEHICLE_UPDATE_MOTION_TRACK_FLAG_1))
|
|
return;
|
|
|
|
velocity = 0;
|
|
acceleration = 0;
|
|
SetState(Vehicle::Status::Arriving, 0);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006DF9F0
|
|
*/
|
|
void Vehicle::CableLiftUpdateArriving()
|
|
{
|
|
sub_state++;
|
|
if (sub_state >= 64)
|
|
SetState(Vehicle::Status::MovingToEndOfStation, sub_state);
|
|
}
|
|
|
|
bool Vehicle::CableLiftUpdateTrackMotionForwards()
|
|
{
|
|
auto curRide = GetRide();
|
|
if (curRide == nullptr)
|
|
return false;
|
|
|
|
for (; remaining_distance >= 13962; _vehicleUnkF64E10++)
|
|
{
|
|
auto trackType = GetTrackType();
|
|
if (trackType == TrackElemType::CableLiftHill && track_progress == 160)
|
|
{
|
|
_vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_1;
|
|
}
|
|
|
|
uint16_t trackProgress = track_progress + 1;
|
|
|
|
uint16_t trackTotalProgress = GetTrackProgress();
|
|
if (trackProgress >= trackTotalProgress)
|
|
{
|
|
TileElement* trackElement = map_get_track_element_at_of_type_seq(TrackLocation, trackType, 0);
|
|
|
|
CoordsXYE output;
|
|
int32_t outputZ;
|
|
int32_t outputDirection;
|
|
|
|
auto input = CoordsXYE{ TrackLocation, trackElement };
|
|
|
|
if (!track_block_get_next(&input, &output, &outputZ, &outputDirection))
|
|
return false;
|
|
|
|
if (TrackPitchAndRollEnd(trackType) != TrackPitchAndRollStart(output.element->AsTrack()->GetTrackType()))
|
|
return false;
|
|
|
|
TrackLocation = { output, outputZ };
|
|
SetTrackDirection(outputDirection);
|
|
SetTrackType(output.element->AsTrack()->GetTrackType());
|
|
trackProgress = 0;
|
|
}
|
|
|
|
track_progress = trackProgress;
|
|
const auto moveInfo = GetMoveInfo();
|
|
auto unk = CoordsXYZ{ moveInfo->x, moveInfo->y, moveInfo->z } + TrackLocation;
|
|
|
|
uint8_t bx = 0;
|
|
unk.z += GetRideTypeDescriptor(curRide->type).Heights.VehicleZOffset;
|
|
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);
|
|
|
|
remaining_distance -= dword_9A2930[bx];
|
|
unk_F64E20.x = unk.x;
|
|
unk_F64E20.y = unk.y;
|
|
unk_F64E20.z = unk.z;
|
|
|
|
sprite_direction = moveInfo->direction;
|
|
bank_rotation = moveInfo->bank_rotation;
|
|
Pitch = moveInfo->Pitch;
|
|
|
|
if (remaining_distance >= 13962)
|
|
{
|
|
acceleration += dword_9A2970[Pitch];
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Vehicle::CableLiftUpdateTrackMotionBackwards()
|
|
{
|
|
auto curRide = GetRide();
|
|
if (curRide == nullptr)
|
|
return false;
|
|
|
|
for (; remaining_distance < 0; _vehicleUnkF64E10++)
|
|
{
|
|
uint16_t trackProgress = track_progress - 1;
|
|
|
|
if (static_cast<int16_t>(trackProgress) == -1)
|
|
{
|
|
auto trackType = GetTrackType();
|
|
TileElement* trackElement = map_get_track_element_at_of_type_seq(TrackLocation, trackType, 0);
|
|
|
|
auto input = CoordsXYE{ TrackLocation, trackElement };
|
|
track_begin_end output;
|
|
|
|
if (!track_block_get_previous(input, &output))
|
|
return false;
|
|
|
|
if (TrackPitchAndRollStart(trackType) != TrackPitchAndRollEnd(output.begin_element->AsTrack()->GetTrackType()))
|
|
return false;
|
|
|
|
TrackLocation = { output.begin_x, output.begin_y, output.begin_z };
|
|
SetTrackDirection(output.begin_direction);
|
|
SetTrackType(output.begin_element->AsTrack()->GetTrackType());
|
|
|
|
if (output.begin_element->AsTrack()->GetTrackType() == TrackElemType::EndStation)
|
|
{
|
|
_vehicleMotionTrackFlags = VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION;
|
|
}
|
|
|
|
uint16_t trackTotalProgress = GetTrackProgress();
|
|
trackProgress = trackTotalProgress - 1;
|
|
}
|
|
track_progress = trackProgress;
|
|
const auto moveInfo = GetMoveInfo();
|
|
auto unk = CoordsXYZ{ moveInfo->x, moveInfo->y, moveInfo->z } + TrackLocation;
|
|
|
|
uint8_t bx = 0;
|
|
unk.z += GetRideTypeDescriptor(curRide->type).Heights.VehicleZOffset;
|
|
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);
|
|
|
|
remaining_distance += dword_9A2930[bx];
|
|
unk_F64E20.x = unk.x;
|
|
unk_F64E20.y = unk.y;
|
|
unk_F64E20.z = unk.z;
|
|
|
|
sprite_direction = moveInfo->direction;
|
|
bank_rotation = moveInfo->bank_rotation;
|
|
Pitch = moveInfo->Pitch;
|
|
|
|
if (remaining_distance < 0)
|
|
{
|
|
acceleration += dword_9A2970[Pitch];
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006DEF56
|
|
*/
|
|
int32_t Vehicle::CableLiftUpdateTrackMotion()
|
|
{
|
|
_vehicleF64E2C = 0;
|
|
gCurrentVehicle = this;
|
|
_vehicleMotionTrackFlags = 0;
|
|
_vehicleStationIndex = STATION_INDEX_NULL;
|
|
|
|
velocity += acceleration;
|
|
_vehicleVelocityF64E08 = velocity;
|
|
_vehicleVelocityF64E0C = (velocity / 1024) * 42;
|
|
|
|
Vehicle* frontVehicle = this;
|
|
if (velocity < 0)
|
|
{
|
|
frontVehicle = TrainTail();
|
|
}
|
|
|
|
_vehicleFrontVehicle = frontVehicle;
|
|
|
|
for (Vehicle* vehicle = frontVehicle; vehicle != nullptr;)
|
|
{
|
|
vehicle->acceleration = dword_9A2970[vehicle->Pitch];
|
|
_vehicleUnkF64E10 = 1;
|
|
vehicle->remaining_distance += _vehicleVelocityF64E0C;
|
|
|
|
if (vehicle->remaining_distance < 0 || vehicle->remaining_distance >= 13962)
|
|
{
|
|
unk_F64E20 = vehicle->GetLocation();
|
|
vehicle->Invalidate();
|
|
|
|
while (true)
|
|
{
|
|
if (vehicle->remaining_distance < 0)
|
|
{
|
|
if (vehicle->CableLiftUpdateTrackMotionBackwards())
|
|
{
|
|
break;
|
|
}
|
|
|
|
_vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_5;
|
|
_vehicleVelocityF64E0C -= vehicle->remaining_distance - 13962;
|
|
vehicle->remaining_distance = 13962;
|
|
vehicle->acceleration += dword_9A2970[vehicle->Pitch];
|
|
_vehicleUnkF64E10++;
|
|
continue;
|
|
}
|
|
|
|
if (vehicle->CableLiftUpdateTrackMotionForwards())
|
|
{
|
|
break;
|
|
}
|
|
|
|
_vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_5;
|
|
_vehicleVelocityF64E0C -= vehicle->remaining_distance + 1;
|
|
vehicle->remaining_distance = -1;
|
|
vehicle->acceleration += dword_9A2970[vehicle->Pitch];
|
|
_vehicleUnkF64E10++;
|
|
}
|
|
vehicle->MoveTo(unk_F64E20);
|
|
}
|
|
vehicle->acceleration /= _vehicleUnkF64E10;
|
|
if (_vehicleVelocityF64E08 >= 0)
|
|
{
|
|
vehicle = GetEntity<Vehicle>(vehicle->next_vehicle_on_train);
|
|
}
|
|
else
|
|
{
|
|
if (vehicle == this)
|
|
break;
|
|
vehicle = GetEntity<Vehicle>(vehicle->prev_vehicle_on_ride);
|
|
}
|
|
}
|
|
|
|
uint32_t vehicleCount = 0;
|
|
uint16_t massTotal = 0;
|
|
int32_t accelerationTotal = 0;
|
|
|
|
for (Vehicle* vehicle = GetEntity<Vehicle>(sprite_index); vehicle != nullptr;
|
|
vehicle = GetEntity<Vehicle>(vehicle->next_vehicle_on_train))
|
|
{
|
|
vehicleCount++;
|
|
|
|
massTotal += vehicle->mass;
|
|
accelerationTotal = add_clamp_int32_t(accelerationTotal, vehicle->acceleration);
|
|
}
|
|
|
|
int32_t newAcceleration = (accelerationTotal / vehicleCount) >> 9;
|
|
newAcceleration -= velocity >> 12;
|
|
|
|
int32_t edx = velocity >> 8;
|
|
edx *= edx;
|
|
if (velocity < 0)
|
|
{
|
|
edx = -edx;
|
|
}
|
|
edx >>= 4;
|
|
newAcceleration -= edx / massTotal;
|
|
|
|
acceleration = newAcceleration;
|
|
return _vehicleMotionTrackFlags;
|
|
}
|