From 819208f3cb5a139170c2b9671800edfb8fe4e10d Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 11 May 2020 00:00:15 +0100 Subject: [PATCH] Add more ride fields to plugin API --- distribution/openrct2.d.ts | 117 +++++++ src/openrct2/scripting/Duktape.hpp | 77 ++++- src/openrct2/scripting/ScRide.hpp | 434 ++++++++++++++++++++++++ src/openrct2/scripting/ScriptEngine.cpp | 1 + 4 files changed, 619 insertions(+), 10 deletions(-) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index aa4bc78e17..f02864eeba 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -81,6 +81,16 @@ declare global { z: number; } + /** + * A coordinate within the game. + * Each in-game tile is a size of 32x32. + * The z-coordinate raises 16 per land increment. A full-height wall is 32 in height. + * The direction is between 0 and 3. + */ + interface CoordsXYZD extends CoordsXYZ { + direction: number; + } + /** * A rectangular area specified using two coordinates. */ @@ -648,39 +658,146 @@ declare global { * The object metadata for this ride. */ readonly object: RideObject; + /** * The unique ID / index of the ride. */ readonly id: number; + /** * The type of the ride represented as the internal built-in ride type ID. */ type: number; + + /** + * Whether the ride is a ride, shop or facility. + */ + readonly classification: RideClassification; + /** * The generated or custom name of the ride. */ name: string; + + /** + * Whether the ride is open, closed or testing. + */ + readonly status: RideStatus; + + /** + * Various flags related to the operation of the ride. + */ + lifecycleFlags: number; + + /** + * The operation mode. + */ + mode: number; + + /** + * Flags related to how trains depart. + */ + departFlags: number; + + /** + * The minimum time a train will wait at the station before departing. + */ + minimumWaitingTime: number; + + /** + * The maximum time a train will wait at the station before departing. + */ + maximumWaitingTime: number; + + /** + * The head vehicle IDs associated with the ride, one for each train. + */ + readonly vehicles: number[]; + + /** + * The colour for each vehicle when the ride opens. Modifying this directly will not + * change the colour of any currently running trains nor will it reflect them if they + * have been modified. + */ + vehicleColours: VehicleColour[]; + + /** + * The track colour schemes for the ride. + */ + colourSchemes: TrackColour[]; + + /** + * The style used for the station, entrance, and exit building. + */ + stationStyle: number; + + /** + * The music track to play at each station. + */ + music: number; + + /** + * Information about each station. + */ + readonly stations: RideStation[]; + + /** + * The admission price for the ride or the cost of the primary item of the stall. + */ + price: number; + + /** + * The price of the on-ride photo or the cost of the secondary item of the stall. + */ + priceSecondary: number; + /** * The excitement metric of the ride represented as a 2 decimal point fixed integer. * For example, `652` equates to `6.52`. */ excitement: number; + /** * The intensity metric of the ride represented as a 2 decimal point fixed integer. * For example, `652` equates to `6.52`. */ intensity: number; + /** * The nausea metric of the ride represented as a 2 decimal point fixed integer. * For example, `652` equates to `6.52`. */ nausea: number; + /** * The total number of customers the ride has served since it was built. */ totalCustomers: number; } + type RideClassification = "ride" | "stall" | "facility"; + + type RideStatus = "closed" | "open" | "testing" | "simulating"; + + interface TrackColour { + main: number; + additional: number; + supports: number; + } + + interface VehicleColour { + body: number; + trim: number; + ternary: number; + } + + interface RideStation { + start: CoordsXYZ; + length: number; + entrance: CoordsXYZD; + exit: CoordsXYZD; + } + type EntityType = "car" | "duck" | "peep"; diff --git a/src/openrct2/scripting/Duktape.hpp b/src/openrct2/scripting/Duktape.hpp index 2563727885..36816c93cd 100644 --- a/src/openrct2/scripting/Duktape.hpp +++ b/src/openrct2/scripting/Duktape.hpp @@ -269,7 +269,7 @@ namespace OpenRCT2::Scripting return DukValue::take_from_stack(ctx); } - template DukValue ToDuk(duk_context* ctx, const std::optional& value) + template inline DukValue ToDuk(duk_context* ctx, const std::optional& value) { return value ? ToDuk(ctx, *value) : ToDuk(ctx, nullptr); } @@ -290,15 +290,6 @@ namespace OpenRCT2::Scripting return dukCoords.Take(); } - template<> DukValue inline ToDuk(duk_context* ctx, const CoordsXYZ& coords) - { - DukObject dukCoords(ctx); - dukCoords.Set("x", coords.x); - dukCoords.Set("y", coords.y); - dukCoords.Set("z", coords.z); - return dukCoords.Take(); - } - template<> DukValue inline ToDuk(duk_context* ctx, const ScreenCoordsXY& coords) { DukObject dukCoords(ctx); @@ -307,6 +298,72 @@ namespace OpenRCT2::Scripting return dukCoords.Take(); } + template<> inline DukValue ToDuk(duk_context* ctx, const CoordsXYZ& value) + { + if (value.isNull()) + { + return ToDuk(ctx, nullptr); + } + else + { + DukObject dukCoords(ctx); + dukCoords.Set("x", value.x); + dukCoords.Set("y", value.y); + dukCoords.Set("z", value.z); + return dukCoords.Take(); + } + } + + template<> inline CoordsXYZ FromDuk(const DukValue& value) + { + CoordsXYZ result; + if (value.type() == DukValue::Type::OBJECT) + { + result.x = AsOrDefault(value["x"], 0); + result.y = AsOrDefault(value["y"], 0); + result.z = AsOrDefault(value["z"], 0); + } + else + { + result.setNull(); + } + return result; + } + + template<> inline DukValue ToDuk(duk_context* ctx, const CoordsXYZD& value) + { + if (value.isNull()) + { + return ToDuk(ctx, nullptr); + } + else + { + DukObject dukCoords(ctx); + dukCoords.Set("x", value.x); + dukCoords.Set("y", value.y); + dukCoords.Set("z", value.z); + dukCoords.Set("direction", value.direction); + return dukCoords.Take(); + } + } + + template<> inline CoordsXYZD FromDuk(const DukValue& value) + { + CoordsXYZD result; + if (value.type() == DukValue::Type::OBJECT) + { + result.x = AsOrDefault(value["x"], 0); + result.y = AsOrDefault(value["y"], 0); + result.z = AsOrDefault(value["z"], 0); + result.direction = AsOrDefault(value["direction"], 0); + } + else + { + result.setNull(); + } + return result; + } + } // namespace OpenRCT2::Scripting #endif diff --git a/src/openrct2/scripting/ScRide.hpp b/src/openrct2/scripting/ScRide.hpp index b9c030a147..dde56e0a1c 100644 --- a/src/openrct2/scripting/ScRide.hpp +++ b/src/openrct2/scripting/ScRide.hpp @@ -11,6 +11,7 @@ #ifdef ENABLE_SCRIPTING +# include "../Context.h" # include "../common.h" # include "../ride/Ride.h" # include "Duktape.hpp" @@ -19,6 +20,160 @@ namespace OpenRCT2::Scripting { + template<> inline DukValue ToDuk(duk_context* ctx, const TrackColour& value) + { + DukObject obj(ctx); + obj.Set("main", value.main); + obj.Set("additional", value.additional); + obj.Set("supports", value.supports); + return obj.Take(); + } + + template<> TrackColour FromDuk(const DukValue& s) + { + TrackColour result{}; + result.main = AsOrDefault(s["main"], 0); + result.additional = AsOrDefault(s["additional"], 0); + result.supports = AsOrDefault(s["supports"], 0); + return result; + } + + template<> inline DukValue ToDuk(duk_context* ctx, const VehicleColour& value) + { + DukObject obj(ctx); + obj.Set("body", value.Body); + obj.Set("trim", value.Trim); + obj.Set("ternary", value.Ternary); + return obj.Take(); + } + + template<> VehicleColour FromDuk(const DukValue& s) + { + VehicleColour result{}; + result.Body = AsOrDefault(s["body"], 0); + result.Trim = AsOrDefault(s["trim"], 0); + result.Ternary = AsOrDefault(s["ternary"], 0); + return result; + } + + class ScRideStation + { + private: + ride_id_t _rideId = RIDE_ID_NULL; + StationIndex _stationIndex{}; + + public: + ScRideStation(ride_id_t rideId, StationIndex stationIndex) + : _rideId(rideId) + , _stationIndex(stationIndex) + { + } + + static void Register(duk_context* ctx) + { + dukglue_register_property(ctx, &ScRideStation::start_get, &ScRideStation::start_set, "start"); + dukglue_register_property(ctx, &ScRideStation::length_get, &ScRideStation::length_set, "length"); + dukglue_register_property(ctx, &ScRideStation::entrance_get, &ScRideStation::entrance_set, "entrance"); + dukglue_register_property(ctx, &ScRideStation::exit_get, &ScRideStation::exit_set, "exit"); + } + + private: + DukValue start_get() const + { + auto ctx = GetContext()->GetScriptEngine().GetContext(); + auto station = GetRideStation(); + if (station != nullptr) + { + auto start = CoordsXYZ(station->Start, station->GetBaseZ()); + return ToDuk(ctx, start); + } + return ToDuk(ctx, nullptr); + } + + void start_set(const DukValue& value) + { + auto station = GetRideStation(); + if (station != nullptr) + { + auto start = FromDuk(value); + station->Start = { start.x, start.y }; + station->SetBaseZ(start.z); + } + } + + int32_t length_get() const + { + auto station = GetRideStation(); + if (station != nullptr) + { + return station->Length; + } + return 0; + } + + void length_set(int32_t value) + { + auto station = GetRideStation(); + if (station != nullptr) + { + station->Length = value; + } + } + + DukValue entrance_get() const + { + auto ctx = GetContext()->GetScriptEngine().GetContext(); + auto station = GetRideStation(); + if (station != nullptr) + { + return ToDuk(ctx, station->Entrance.ToCoordsXYZD()); + } + return ToDuk(ctx, nullptr); + } + + void entrance_set(const DukValue& value) + { + auto station = GetRideStation(); + if (station != nullptr) + { + station->Entrance = FromDuk(value); + } + } + + DukValue exit_get() const + { + auto ctx = GetContext()->GetScriptEngine().GetContext(); + auto station = GetRideStation(); + if (station != nullptr) + { + return ToDuk(ctx, station->Exit.ToCoordsXYZD()); + } + return ToDuk(ctx, nullptr); + } + + void exit_set(const DukValue& value) + { + auto station = GetRideStation(); + if (station != nullptr) + { + station->Exit = FromDuk(value); + } + } + + RideStation* GetRideStation() const + { + auto ride = get_ride(_rideId); + if (ride != nullptr) + { + if (_stationIndex < std::size(ride->stations)) + { + return &ride->stations[_stationIndex]; + } + } + return nullptr; + } + }; + class ScRide { private: @@ -56,6 +211,24 @@ namespace OpenRCT2::Scripting return ride != nullptr ? ride->type : 0; } + std::string classification_get() const + { + auto ride = GetRide(); + if (ride != nullptr) + { + switch (ride->GetClassification()) + { + case RideClassification::Ride: + return "ride"; + case RideClassification::ShopOrStall: + return "stall"; + case RideClassification::KioskOrFacility: + return "facility"; + } + } + return ""; + } + std::string name_get() const { auto ride = GetRide(); @@ -71,6 +244,251 @@ namespace OpenRCT2::Scripting } } + std::string status_get() const + { + auto ride = GetRide(); + if (ride != nullptr) + { + switch (ride->status) + { + case RIDE_STATUS_CLOSED: + return "closed"; + case RIDE_STATUS_OPEN: + return "open"; + case RIDE_STATUS_TESTING: + return "testing"; + case RIDE_STATUS_SIMULATING: + return "simulating"; + } + } + return ""; + } + + uint32_t lifecycleFlags_get() const + { + auto ride = GetRide(); + return ride != nullptr ? ride->lifecycle_flags : 0; + } + + void lifecycleFlags_set(uint32_t value) + { + ThrowIfGameStateNotMutable(); + auto ride = GetRide(); + if (ride != nullptr) + { + ride->lifecycle_flags = value; + } + } + + uint8_t mode_get() const + { + auto ride = GetRide(); + return ride != nullptr ? ride->mode : 0; + } + + void mode_set(uint8_t value) + { + ThrowIfGameStateNotMutable(); + auto ride = GetRide(); + if (ride != nullptr) + { + ride->mode = value; + } + } + + uint8_t departFlags_get() const + { + auto ride = GetRide(); + return ride != nullptr ? ride->depart_flags : 0; + } + + void departFlags_set(uint8_t value) + { + ThrowIfGameStateNotMutable(); + auto ride = GetRide(); + if (ride != nullptr) + { + ride->depart_flags = value; + } + } + + uint8_t minimumWaitingTime_get() const + { + auto ride = GetRide(); + return ride != nullptr ? ride->min_waiting_time : 0; + } + + void minimumWaitingTime_set(uint8_t value) + { + ThrowIfGameStateNotMutable(); + auto ride = GetRide(); + if (ride != nullptr) + { + ride->min_waiting_time = value; + } + } + + uint8_t maximumWaitingTime_get() const + { + auto ride = GetRide(); + return ride != nullptr ? ride->max_waiting_time : 0; + } + + void maximumWaitingTime_set(uint8_t value) + { + ThrowIfGameStateNotMutable(); + auto ride = GetRide(); + if (ride != nullptr) + { + ride->max_waiting_time = value; + } + } + + std::vector vehicles_get() const + { + std::vector result; + auto ride = GetRide(); + if (ride != nullptr) + { + result.insert(result.begin(), std::begin(ride->vehicles), std::begin(ride->vehicles) + ride->num_vehicles); + } + return result; + } + + std::vector vehicleColours_get() const + { + std::vector result; + auto ride = GetRide(); + if (ride != nullptr) + { + auto ctx = GetContext()->GetScriptEngine().GetContext(); + for (const auto& vehicleColour : ride->vehicle_colours) + { + result.push_back(ToDuk(ctx, vehicleColour)); + } + } + return result; + } + + void vehicleColours_set(const std::vector& value) + { + auto ride = GetRide(); + if (ride != nullptr) + { + auto count = std::min(value.size(), std::size(ride->vehicle_colours)); + for (size_t i = 0; i < count; i++) + { + ride->vehicle_colours[i] = FromDuk(value[i]); + } + } + } + + std::vector colourSchemes_get() const + { + std::vector result; + auto ride = GetRide(); + if (ride != nullptr) + { + auto ctx = GetContext()->GetScriptEngine().GetContext(); + for (const auto& trackColour : ride->track_colour) + { + result.push_back(ToDuk(ctx, trackColour)); + } + } + return result; + } + + void colourSchemes_set(const std::vector& value) + { + auto ride = GetRide(); + if (ride != nullptr) + { + auto count = std::min(value.size(), std::size(ride->track_colour)); + for (size_t i = 0; i < count; i++) + { + ride->track_colour[i] = FromDuk(value[i]); + } + } + } + + uint8_t stationStyle_get() const + { + auto ride = GetRide(); + return ride != nullptr ? ride->entrance_style : 0; + } + + void stationStyle_set(uint8_t value) + { + ThrowIfGameStateNotMutable(); + auto ride = GetRide(); + if (ride != nullptr) + { + ride->entrance_style = value; + } + } + + uint8_t music_get() const + { + auto ride = GetRide(); + return ride != nullptr ? ride->music : 0; + } + + void music_set(uint8_t value) + { + ThrowIfGameStateNotMutable(); + auto ride = GetRide(); + if (ride != nullptr) + { + ride->music = value; + } + } + + std::vector> stations_get() const + { + std::vector> result; + auto ride = GetRide(); + if (ride != nullptr) + { + for (size_t i = 0; i < std::size(ride->stations); i++) + { + result.push_back(std::make_shared(ride->id, static_cast(i))); + } + } + return result; + } + + int32_t price_get() const + { + auto ride = GetRide(); + return ride != nullptr ? ride->price : 0; + } + + void price_set(int32_t value) + { + ThrowIfGameStateNotMutable(); + auto ride = GetRide(); + if (ride != nullptr) + { + ride->price = static_cast(value); + } + } + + int32_t priceSecondary_get() const + { + auto ride = GetRide(); + return ride != nullptr ? ride->price_secondary : 0; + } + + void priceSecondary_set(int32_t value) + { + ThrowIfGameStateNotMutable(); + auto ride = GetRide(); + if (ride != nullptr) + { + ride->price_secondary = static_cast(value); + } + } + int32_t excitement_get() const { auto ride = GetRide(); @@ -143,6 +561,22 @@ namespace OpenRCT2::Scripting dukglue_register_property(ctx, &ScRide::object_get, nullptr, "object"); dukglue_register_property(ctx, &ScRide::type_get, nullptr, "type"); dukglue_register_property(ctx, &ScRide::name_get, &ScRide::name_set, "name"); + dukglue_register_property(ctx, &ScRide::status_get, nullptr, "status"); + dukglue_register_property(ctx, &ScRide::lifecycleFlags_get, &ScRide::lifecycleFlags_set, "lifecycleFlags"); + dukglue_register_property(ctx, &ScRide::mode_get, &ScRide::mode_set, "mode"); + dukglue_register_property(ctx, &ScRide::departFlags_get, &ScRide::departFlags_set, "departFlags"); + dukglue_register_property( + ctx, &ScRide::minimumWaitingTime_get, &ScRide::minimumWaitingTime_set, "minimumWaitingTime"); + dukglue_register_property( + ctx, &ScRide::maximumWaitingTime_get, &ScRide::maximumWaitingTime_set, "maximumWaitingTime"); + dukglue_register_property(ctx, &ScRide::vehicles_get, nullptr, "vehicles"); + dukglue_register_property(ctx, &ScRide::vehicleColours_get, &ScRide::vehicleColours_set, "vehicleColours"); + dukglue_register_property(ctx, &ScRide::colourSchemes_get, &ScRide::colourSchemes_set, "colourSchemes"); + dukglue_register_property(ctx, &ScRide::stationStyle_get, &ScRide::stationStyle_set, "stationStyle"); + dukglue_register_property(ctx, &ScRide::music_get, &ScRide::music_set, "music"); + dukglue_register_property(ctx, &ScRide::stations_get, nullptr, "stations"); + dukglue_register_property(ctx, &ScRide::price_get, &ScRide::price_set, "price"); + dukglue_register_property(ctx, &ScRide::priceSecondary_get, &ScRide::priceSecondary_set, "priceSecondary"); dukglue_register_property(ctx, &ScRide::excitement_get, &ScRide::excitement_set, "excitement"); dukglue_register_property(ctx, &ScRide::intensity_get, &ScRide::intensity_set, "intensity"); dukglue_register_property(ctx, &ScRide::nausea_get, &ScRide::nausea_set, "nausea"); diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp index fc1da14adb..0971b73de8 100644 --- a/src/openrct2/scripting/ScriptEngine.cpp +++ b/src/openrct2/scripting/ScriptEngine.cpp @@ -384,6 +384,7 @@ void ScriptEngine::Initialise() ScPlayer::Register(ctx); ScPlayerGroup::Register(ctx); ScRide::Register(ctx); + ScRideStation::Register(ctx); ScRideObject::Register(ctx); ScRideObjectVehicle::Register(ctx); ScTile::Register(ctx);