From 4eef86dc50235765f3f487fed9369eaaafd460a2 Mon Sep 17 00:00:00 2001 From: Ted John Date: Sun, 13 Mar 2022 03:02:55 +0000 Subject: [PATCH] Implement track iterator --- distribution/openrct2.d.ts | 44 ++++++- src/openrct2/libopenrct2.vcxproj | 2 + src/openrct2/ride/Track.cpp | 19 +++ src/openrct2/ride/Track.h | 3 + src/openrct2/scripting/ScriptEngine.cpp | 1 + .../bindings/ride/ScTrackIterator.cpp | 122 ++++++++++++++++++ .../scripting/bindings/ride/ScTrackIterator.h | 44 +++++++ .../scripting/bindings/world/ScMap.cpp | 12 ++ .../scripting/bindings/world/ScMap.hpp | 3 + .../bindings/world/ScTileElement.cpp | 22 ++++ .../bindings/world/ScTileElement.hpp | 3 + 11 files changed, 270 insertions(+), 5 deletions(-) create mode 100644 src/openrct2/scripting/bindings/ride/ScTrackIterator.cpp create mode 100644 src/openrct2/scripting/bindings/ride/ScTrackIterator.h diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index e945771bfc..d60ede0b80 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -224,6 +224,10 @@ declare global { getAllObjects(type: ObjectType): LoadedObject[]; getAllObjects(type: "ride"): RideObject[]; + /** + * Gets the {@link TrackSegment} for the given type. + * @param type The track segment type. + */ getTrackSegment(type: number): TrackSegment | undefined; /** @@ -631,6 +635,14 @@ declare global { getAllEntitiesOnTile(type: "car", tilePos: CoordsXY): Car[]; getAllEntitiesOnTile(type: "litter", tilePos: CoordsXY): Litter[]; createEntity(type: EntityType, initializer: object): Entity; + + /** + * Gets a {@link TrackIterator} for the given track element. This can be used to + * iterate through a ride's circuit, segment by segment. + * @param location The tile coordinates. + * @param elementIndex The index of the track element on the tile. + */ + getTrackIterator(location: CoordsXY, elementIndex: number): TrackIterator | undefined; } type TileElementType = @@ -711,6 +723,7 @@ declare global { hasChainLift: boolean; isInverted: boolean; hasCableLift: boolean; + isHighlighted: boolean; } interface SmallSceneryElement extends BaseTileElement { @@ -1144,13 +1157,34 @@ declare global { /** * Gets a list of the elements that make up the track segment. */ - readonly elements: TrackSegmentElement; + readonly elements: TrackSegmentElement[]; } - interface TrackSegmentElement { - x: number; - y: number; - z: number; + interface TrackSegmentElement implements CoordsXYZ { + } + + interface TrackIterator { + /** + * The position and direction of the current track segment, from the first element. + */ + readonly position: CoordsXYZD; + + /** + * The current track segment or undefined if at the beginning or end of a disconnected circuit. + */ + readonly segment: TrackSegment | undefined; + + /** + * Moves the iterator to the previous track segment. + * @returns true if there is a previous segment, otherwise false. + */ + previous(): boolean; + + /** + * Moves the iterator to the next track segment. + * @returns true if there is a next segment, otherwise false. + */ + next(): boolean; } type EntityType = diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj index f0be46cdfe..a7b3f28571 100644 --- a/src/openrct2/libopenrct2.vcxproj +++ b/src/openrct2/libopenrct2.vcxproj @@ -468,6 +468,7 @@ + @@ -926,6 +927,7 @@ + diff --git a/src/openrct2/ride/Track.cpp b/src/openrct2/ride/Track.cpp index 97478cf8e3..64caf56b7c 100644 --- a/src/openrct2/ride/Track.cpp +++ b/src/openrct2/ride/Track.cpp @@ -664,6 +664,25 @@ bool TrackTypeHasSpeedSetting(track_type_t trackType) return trackType == TrackElemType::Brakes || trackType == TrackElemType::Booster; } +std::optional GetTrackSegmentOrigin(const CoordsXYE& posEl) +{ + auto trackEl = posEl.element->AsTrack(); + if (trackEl == nullptr) + return {}; + + const auto& ted = GetTrackElementDescriptor(trackEl->GetTrackType()); + auto direction = trackEl->GetDirection(); + auto coords = CoordsXYZ(posEl.x, posEl.y, trackEl->GetBaseZ()); + + // Subtract the current sequence's offset + const auto* trackBlock = &ted.Block[trackEl->GetSequenceIndex()]; + CoordsXY trackBlockOffset = { trackBlock->x, trackBlock->y }; + coords += trackBlockOffset.Rotate(direction_reverse(direction)); + coords.z -= trackBlock->z; + + return CoordsXYZD(coords, direction); +} + uint8_t TrackElement::GetSeatRotation() const { const auto* ride = get_ride(GetRideIndex()); diff --git a/src/openrct2/ride/Track.h b/src/openrct2/ride/Track.h index 9edb2a0b4d..2df04c482c 100644 --- a/src/openrct2/ride/Track.h +++ b/src/openrct2/ride/Track.h @@ -14,6 +14,8 @@ #include "../world/Map.h" #include "../world/TileElement.h" +#include + constexpr const uint32_t RideConstructionSpecialPieceSelected = 0x10000; constexpr const int32_t BLOCK_BRAKE_BASE_SPEED = 0x20364; @@ -581,3 +583,4 @@ bool track_remove_station_element(const CoordsXYZD& loc, RideId rideIndex, int32 money32 maze_set_track(const CoordsXYZD& coords, uint8_t flags, bool initialPlacement, RideId rideIndex, uint8_t mode); bool TrackTypeHasSpeedSetting(track_type_t trackType); +std::optional GetTrackSegmentOrigin(const CoordsXYE& posEl); diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp index dc7251e8b4..4dcd5d53d6 100644 --- a/src/openrct2/scripting/ScriptEngine.cpp +++ b/src/openrct2/scripting/ScriptEngine.cpp @@ -413,6 +413,7 @@ void ScriptEngine::Initialise() ScRideObjectVehicle::Register(ctx); ScTile::Register(ctx); ScTileElement::Register(ctx); + ScTrackIterator::Register(ctx); ScTrackSegment::Register(ctx); ScEntity::Register(ctx); ScLitter::Register(ctx); diff --git a/src/openrct2/scripting/bindings/ride/ScTrackIterator.cpp b/src/openrct2/scripting/bindings/ride/ScTrackIterator.cpp new file mode 100644 index 0000000000..14371ef7eb --- /dev/null +++ b/src/openrct2/scripting/bindings/ride/ScTrackIterator.cpp @@ -0,0 +1,122 @@ +/***************************************************************************** + * Copyright (c) 2022 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. + *****************************************************************************/ + +#ifdef ENABLE_SCRIPTING + +# include "ScTrackIterator.h" + +# include "../../../Context.h" +# include "../../../ride/Ride.h" +# include "../../../ride/TrackData.h" +# include "../../ScriptEngine.h" +# include "ScTrackSegment.h" + +using namespace OpenRCT2::Scripting; +using namespace OpenRCT2::TrackMetaData; + +std::shared_ptr ScTrackIterator::FromElement(const CoordsXY& position, int32_t elementIndex) +{ + auto el = map_get_nth_element_at(position, elementIndex); + auto origin = GetTrackSegmentOrigin(CoordsXYE(position, el)); + if (!origin) + return nullptr; + + auto trackEl = el->AsTrack(); + return std::make_shared(*origin, trackEl->GetTrackType(), trackEl->GetRideIndex()); +} + +ScTrackIterator::ScTrackIterator(const CoordsXYZD& position, track_type_t type, RideId ride) + : _position(position) + , _type(type) + , _ride(ride) +{ +} + +void ScTrackIterator::Register(duk_context* ctx) +{ + dukglue_register_property(ctx, &ScTrackIterator::position_get, nullptr, "position"); + dukglue_register_property(ctx, &ScTrackIterator::segment_get, nullptr, "segment"); + dukglue_register_method(ctx, &ScTrackIterator::previous, "previous"); + dukglue_register_method(ctx, &ScTrackIterator::next, "next"); +} + +DukValue ScTrackIterator::position_get() const +{ + auto& scriptEngine = GetContext()->GetScriptEngine(); + auto ctx = scriptEngine.GetContext(); + return ToDuk(ctx, _position); +} + +DukValue ScTrackIterator::segment_get() const +{ + auto& scriptEngine = GetContext()->GetScriptEngine(); + auto ctx = scriptEngine.GetContext(); + + if (_type >= TrackElemType::Count) + return ToDuk(ctx, undefined); + + return GetObjectAsDukValue(ctx, std::make_shared(_type)); +} + +bool ScTrackIterator::previous() +{ + auto& ted = GetTrackElementDescriptor(_type); + auto& seq0 = ted.Block; + auto pos = _position + CoordsXYZ(seq0->x, seq0->y, seq0->z); + + auto el = map_get_track_element_at(pos); + if (el == nullptr) + return false; + + auto posEl = CoordsXYE(pos.x, pos.y, reinterpret_cast(el)); + track_begin_end tbe{}; + auto value = track_block_get_previous(posEl, &tbe); + if (tbe.begin_element != nullptr) + { + auto prev = CoordsXYE(tbe.begin_x, tbe.begin_y, tbe.begin_element); + auto origin = GetTrackSegmentOrigin(prev); + if (origin) + { + _position = *origin; + _type = prev.element->AsTrack()->GetTrackType(); + return value; + } + } + return false; +} + +bool ScTrackIterator::next() +{ + auto& ted = GetTrackElementDescriptor(_type); + auto& seq0 = ted.Block; + auto pos = _position + CoordsXYZ(seq0->x, seq0->y, seq0->z); + + auto el = map_get_track_element_at(pos); + if (el == nullptr) + return false; + + auto posEl = CoordsXYE(_position.x, _position.y, reinterpret_cast(el)); + CoordsXYE next; + int32_t z{}; + int32_t direction = -1; + auto value = track_block_get_next(&posEl, &next, &z, &direction); + if (next.element != nullptr) + { + auto origin = GetTrackSegmentOrigin(next); + if (origin) + { + _position = *origin; + _type = next.element->AsTrack()->GetTrackType(); + return value; + } + } + return false; +} + +#endif diff --git a/src/openrct2/scripting/bindings/ride/ScTrackIterator.h b/src/openrct2/scripting/bindings/ride/ScTrackIterator.h new file mode 100644 index 0000000000..75827c7d95 --- /dev/null +++ b/src/openrct2/scripting/bindings/ride/ScTrackIterator.h @@ -0,0 +1,44 @@ +/***************************************************************************** + * Copyright (c) 2022 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. + *****************************************************************************/ + +#pragma once + +#ifdef ENABLE_SCRIPTING + +# include "../../../Identifiers.h" +# include "../../../world/TileElement.h" +# include "../../Duktape.hpp" + +# include + +namespace OpenRCT2::Scripting +{ + class ScTrackIterator + { + private: + CoordsXYZD _position; + track_type_t _type; + RideId _ride; + + public: + static std::shared_ptr FromElement(const CoordsXY& position, int32_t elementIndex); + static void Register(duk_context* ctx); + + ScTrackIterator(const CoordsXYZD& position, track_type_t type, RideId ride); + + private: + DukValue position_get() const; + DukValue segment_get() const; + bool previous(); + bool next(); + }; + +} // namespace OpenRCT2::Scripting + +#endif diff --git a/src/openrct2/scripting/bindings/world/ScMap.cpp b/src/openrct2/scripting/bindings/world/ScMap.cpp index e4bcfd123b..ecfbec1c69 100644 --- a/src/openrct2/scripting/bindings/world/ScMap.cpp +++ b/src/openrct2/scripting/bindings/world/ScMap.cpp @@ -31,6 +31,7 @@ # include "../entity/ScStaff.hpp" # include "../entity/ScVehicle.hpp" # include "../ride/ScRide.hpp" +# include "../ride/ScTrackIterator.h" # include "../world/ScTile.hpp" namespace OpenRCT2::Scripting @@ -303,6 +304,16 @@ namespace OpenRCT2::Scripting return res; } + DukValue ScMap::getTrackIterator(const DukValue& dukPosition, int32_t elementIndex) const + { + auto position = FromDuk(dukPosition); + auto trackIterator = ScTrackIterator::FromElement(position, elementIndex); + if (trackIterator == nullptr) + return ToDuk(_context, undefined); + + return GetObjectAsDukValue(_context, trackIterator); + } + void ScMap::Register(duk_context* ctx) { dukglue_register_property(ctx, &ScMap::size_get, nullptr, "size"); @@ -315,6 +326,7 @@ namespace OpenRCT2::Scripting dukglue_register_method(ctx, &ScMap::getAllEntities, "getAllEntities"); dukglue_register_method(ctx, &ScMap::getAllEntitiesOnTile, "getAllEntitiesOnTile"); dukglue_register_method(ctx, &ScMap::createEntity, "createEntity"); + dukglue_register_method(ctx, &ScMap::getTrackIterator, "getTrackIterator"); } DukValue ScMap::GetEntityAsDukValue(const EntityBase* sprite) const diff --git a/src/openrct2/scripting/bindings/world/ScMap.hpp b/src/openrct2/scripting/bindings/world/ScMap.hpp index 02c1c856e9..f00c863973 100644 --- a/src/openrct2/scripting/bindings/world/ScMap.hpp +++ b/src/openrct2/scripting/bindings/world/ScMap.hpp @@ -14,6 +14,7 @@ # include "../../../common.h" # include "../../Duktape.hpp" # include "../ride/ScRide.hpp" +# include "../ride/ScTrackIterator.h" # include "../world/ScTile.hpp" namespace OpenRCT2::Scripting @@ -46,6 +47,8 @@ namespace OpenRCT2::Scripting DukValue createEntity(const std::string& type, const DukValue& initializer); + DukValue getTrackIterator(const DukValue& position, int32_t elementIndex) const; + static void Register(duk_context* ctx); private: diff --git a/src/openrct2/scripting/bindings/world/ScTileElement.cpp b/src/openrct2/scripting/bindings/world/ScTileElement.cpp index 37a12f6326..be6d66b2c3 100644 --- a/src/openrct2/scripting/bindings/world/ScTileElement.cpp +++ b/src/openrct2/scripting/bindings/world/ScTileElement.cpp @@ -1075,6 +1075,27 @@ namespace OpenRCT2::Scripting Invalidate(); } + DukValue ScTileElement::isHighlighted_get() const + { + auto ctx = GetContext()->GetScriptEngine().GetContext(); + auto el = _element->AsTrack(); + if (el != nullptr) + duk_push_boolean(ctx, el->IsHighlighted()); + else + duk_push_null(ctx); + return DukValue::take_from_stack(ctx); + } + void ScTileElement::isHighlighted_set(bool value) + { + ThrowIfGameStateNotMutable(); + auto el = _element->AsTrack(); + if (el != nullptr) + { + el->SetHighlight(value); + Invalidate(); + } + } + DukValue ScTileElement::object_get() const { auto& scriptEngine = GetContext()->GetScriptEngine(); @@ -2045,6 +2066,7 @@ namespace OpenRCT2::Scripting dukglue_register_property(ctx, &ScTileElement::hasChainLift_get, &ScTileElement::hasChainLift_set, "hasChainLift"); dukglue_register_property(ctx, &ScTileElement::isInverted_get, &ScTileElement::isInverted_set, "isInverted"); dukglue_register_property(ctx, &ScTileElement::hasCableLift_get, &ScTileElement::hasCableLift_set, "hasCableLift"); + dukglue_register_property(ctx, &ScTileElement::isHighlighted_get, &ScTileElement::isHighlighted_set, "isHighlighted"); // Small Scenery only dukglue_register_property(ctx, &ScTileElement::age_get, &ScTileElement::age_set, "age"); diff --git a/src/openrct2/scripting/bindings/world/ScTileElement.hpp b/src/openrct2/scripting/bindings/world/ScTileElement.hpp index 21893cd2ea..7249557943 100644 --- a/src/openrct2/scripting/bindings/world/ScTileElement.hpp +++ b/src/openrct2/scripting/bindings/world/ScTileElement.hpp @@ -114,6 +114,9 @@ namespace OpenRCT2::Scripting DukValue hasCableLift_get() const; void hasCableLift_set(bool value); + DukValue isHighlighted_get() const; + void isHighlighted_set(bool value); + DukValue object_get() const; void object_set(const DukValue& value);