diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 225d4aea1d..6349711b93 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -20,6 +20,7 @@ - Fix: [#19506] Queue paths can be placed on level crossings by replacing an existing regular path. - Fix: [#21908] Ride mode warnings when hovering track designs. - Fix: [#22961] Clicking on the construction preview places duplicate flat rides and stalls. +- Fix: [#23359] Scripting: Add car.moveToTrack, an easier API than setting car.trackLocation directly. - Fix: [#23443] New GOG version of RCT2 is not extracted correctly. - Fix: [#23484] Stray coloured pixels on castle-themed stations and Roman-themed entrances/exits (original bug). - Fix: [#23486] Object selection minimum requirements can be bypassed with close window hotkey. diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 09bab18926..d99848edb6 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -120,8 +120,8 @@ declare global { /** * A track piece coordinate and type within the game. */ - interface CarTrackLocation extends CoordsXYZD { - trackType: number; + interface CarTrackLocation extends Readonly { + readonly trackType: number; } /** @@ -2930,7 +2930,7 @@ declare global { /** * The location and direction of where the car is on the track. */ - trackLocation: CarTrackLocation; + readonly trackLocation: CarTrackLocation; /** * The current g-forces of this car. @@ -2970,6 +2970,12 @@ declare global { * on the direction its moving in. */ travelBy(distance: number): void; + + /** + * Moves the vehicle to the track piece specified in the parameters. + * Coordinates are tile coords. + */ + moveToTrack(x: number, y: number, elemIndex: number): void; } type VehicleStatus = diff --git a/src/openrct2/ride/Vehicle.cpp b/src/openrct2/ride/Vehicle.cpp index cb22cc6252..3622e21799 100644 --- a/src/openrct2/ride/Vehicle.cpp +++ b/src/openrct2/ride/Vehicle.cpp @@ -588,6 +588,24 @@ void Vehicle::MoveRelativeDistance(int32_t distance) ClearFlag(VehicleFlags::MoveSingleCar | VehicleFlags::CollisionDisabled); } +void Vehicle::UpdateTrackChange() +{ + auto curRide = GetRide(); + if (curRide == nullptr) + return; + + const auto moveInfo = GetMoveInfo(); + if (moveInfo == nullptr || moveInfo->IsInvalid()) + return; + + _vehicleCurPosition = TrackLocation + + CoordsXYZ{ moveInfo->x, moveInfo->y, moveInfo->z + GetRideTypeDescriptor((*curRide).type).Heights.VehicleZOffset }; + Orientation = moveInfo->direction; + bank_rotation = moveInfo->bank_rotation; + Pitch = moveInfo->Pitch; + MoveTo(_vehicleCurPosition); +} + Vehicle* TryGetVehicle(EntityId spriteIndex) { return TryGetEntity(spriteIndex); diff --git a/src/openrct2/ride/Vehicle.h b/src/openrct2/ride/Vehicle.h index bf5e5b835a..11f1374b05 100644 --- a/src/openrct2/ride/Vehicle.h +++ b/src/openrct2/ride/Vehicle.h @@ -47,6 +47,11 @@ struct VehicleInfo uint8_t direction; // 0x06 uint8_t Pitch; // 0x07 uint8_t bank_rotation; // 0x08 + + bool IsInvalid() const + { + return x == 0 && y == 0 && z == 0 && direction == 0 && Pitch == 0 && bank_rotation == 0; + } }; struct SoundIdVolume; @@ -230,12 +235,14 @@ struct Vehicle : EntityBase Ride* GetRide() const; Vehicle* TrainHead() const; Vehicle* TrainTail() const; + uint16_t GetTrackProgress() const; void UpdateAnimationAnimalFlying(); void EnableCollisionsForTrain(); /** * Instantly moves the specific car forward or backwards along the track. */ void MoveRelativeDistance(int32_t distance); + void UpdateTrackChange(); OpenRCT2::TrackElemType GetTrackType() const { return static_cast(TrackTypeAndDirection >> 2); @@ -279,7 +286,6 @@ struct Vehicle : EntityBase private: const VehicleInfo* GetMoveInfo() const; - uint16_t GetTrackProgress() const; void CableLiftUpdate(); bool CableLiftUpdateTrackMotionForwards(); bool CableLiftUpdateTrackMotionBackwards(); diff --git a/src/openrct2/scripting/ScriptEngine.h b/src/openrct2/scripting/ScriptEngine.h index e3b323cd78..cb2d5ea36f 100644 --- a/src/openrct2/scripting/ScriptEngine.h +++ b/src/openrct2/scripting/ScriptEngine.h @@ -46,7 +46,7 @@ namespace OpenRCT2 namespace OpenRCT2::Scripting { - static constexpr int32_t kPluginApiVersion = 104; + static constexpr int32_t kPluginApiVersion = 105; // Versions marking breaking changes. static constexpr int32_t kApiVersionPeepDeprecation = 33; diff --git a/src/openrct2/scripting/bindings/entity/ScVehicle.cpp b/src/openrct2/scripting/bindings/entity/ScVehicle.cpp index 72c89ca0e3..d64dccd880 100644 --- a/src/openrct2/scripting/bindings/entity/ScVehicle.cpp +++ b/src/openrct2/scripting/bindings/entity/ScVehicle.cpp @@ -9,10 +9,13 @@ #include "ScVehicle.hpp" +#include "../../../world/tile_element/TrackElement.h" #include "../ride/ScRide.hpp" #ifdef ENABLE_SCRIPTING +using namespace OpenRCT2::Drawing; + namespace OpenRCT2::Scripting { static const DukEnumMap VehicleStatusMap({ @@ -75,7 +78,7 @@ namespace OpenRCT2::Scripting ctx, &ScVehicle::flag_get, &ScVehicle::flag_set, "isReversed"); dukglue_register_property(ctx, &ScVehicle::colours_get, &ScVehicle::colours_set, "colours"); - dukglue_register_property(ctx, &ScVehicle::trackLocation_get, &ScVehicle::trackLocation_set, "trackLocation"); + dukglue_register_property(ctx, &ScVehicle::trackLocation_get, nullptr, "trackLocation"); dukglue_register_property(ctx, &ScVehicle::trackProgress_get, nullptr, "trackProgress"); dukglue_register_property(ctx, &ScVehicle::remainingDistance_get, nullptr, "remainingDistance"); dukglue_register_property(ctx, &ScVehicle::subposition_get, nullptr, "subposition"); @@ -88,6 +91,7 @@ namespace OpenRCT2::Scripting dukglue_register_property(ctx, &ScVehicle::guests_get, nullptr, "guests"); dukglue_register_property(ctx, &ScVehicle::gForces_get, nullptr, "gForces"); dukglue_register_method(ctx, &ScVehicle::travelBy, "travelBy"); + dukglue_register_method(ctx, &ScVehicle::moveToTrack, "moveToTrack"); } Vehicle* ScVehicle::GetVehicle() const @@ -397,20 +401,6 @@ namespace OpenRCT2::Scripting } return ToDuk(ctx, nullptr); } - void ScVehicle::trackLocation_set(const DukValue& value) - { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); - if (vehicle != nullptr) - { - auto x = AsOrDefault(value["x"], 0); - auto y = AsOrDefault(value["y"], 0); - auto z = AsOrDefault(value["z"], 0); - vehicle->TrackLocation = CoordsXYZ(x, y, z); - vehicle->SetTrackDirection(AsOrDefault(value["direction"], 0)); - vehicle->SetTrackType(static_cast(AsOrDefault(value["trackType"], 0))); - } - } uint16_t ScVehicle::trackProgress_get() const { @@ -543,8 +533,41 @@ namespace OpenRCT2::Scripting if (vehicle != nullptr) { vehicle->MoveRelativeDistance(value); + EntityTweener::Get().RemoveEntity(vehicle); } } + + void ScVehicle::moveToTrack(int32_t x, int32_t y, int32_t elementIndex) + { + CoordsXY coords = TileCoordsXY(x, y).ToCoordsXY(); + auto vehicle = GetVehicle(); + if (vehicle == nullptr) + return; + + auto el = MapGetNthElementAt(coords, elementIndex); + if (el == nullptr) + return; + + auto origin = GetTrackSegmentOrigin(CoordsXYE(coords, el)); + if (!origin) + return; + + auto trackEl = el->AsTrack(); + + vehicle->TrackLocation.x = origin->x; + vehicle->TrackLocation.y = origin->y; + vehicle->TrackLocation.z = origin->z; + vehicle->SetTrackDirection(origin->direction); + vehicle->SetTrackType(trackEl->GetTrackType()); + + // Clip track progress to avoid being out of bounds of current piece + uint16_t trackTotalProgress = vehicle->GetTrackProgress(); + if (trackTotalProgress && vehicle->track_progress >= trackTotalProgress) + vehicle->track_progress = trackTotalProgress - 1; + + vehicle->UpdateTrackChange(); + EntityTweener::Get().RemoveEntity(vehicle); + } } // namespace OpenRCT2::Scripting #endif diff --git a/src/openrct2/scripting/bindings/entity/ScVehicle.hpp b/src/openrct2/scripting/bindings/entity/ScVehicle.hpp index bac257d814..97fce3d007 100644 --- a/src/openrct2/scripting/bindings/entity/ScVehicle.hpp +++ b/src/openrct2/scripting/bindings/entity/ScVehicle.hpp @@ -11,6 +11,7 @@ #ifdef ENABLE_SCRIPTING + #include "../../../entity/EntityTweener.h" #include "../../../ride/Ride.h" #include "ScEntity.hpp" @@ -101,6 +102,8 @@ namespace OpenRCT2::Scripting DukValue gForces_get() const; void travelBy(int32_t value); + + void moveToTrack(int32_t x, int32_t y, int32_t elementIndex); }; } // namespace OpenRCT2::Scripting