diff --git a/CMakeLists.txt b/CMakeLists.txt
index a70bb3e50a..0d68217280 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -328,7 +328,7 @@ else ()
# Compiler flags
set(DEBUG_LEVEL 0 CACHE STRING "Select debug level for compilation. Use value in range 0–3.")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstrict-aliasing -Werror -Wundef -Wmissing-declarations -Winit-self -Wall -Wextra -Wshadow")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas -Wno-missing-braces -Wno-comment -Wnonnull -Wno-unused-parameter")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas -Wno-missing-braces -Wno-comment -Wnonnull -Wno-unused-parameter -Wno-attributes")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG=${DEBUG_LEVEL}")
if(APPLE)
diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts
index cb3b6329d1..98c0a1d734 100644
--- a/distribution/openrct2.d.ts
+++ b/distribution/openrct2.d.ts
@@ -224,6 +224,12 @@ 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 | null;
+
/**
* Gets a random integer within the specified range using the game's pseudo-
* random number generator. This is part of the game state and shared across
@@ -629,12 +635,21 @@ 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 | null;
}
type TileElementType =
"surface" | "footpath" | "track" | "small_scenery" | "wall" | "entrance" | "large_scenery" | "banner";
type Direction = 0 | 1 | 2 | 3;
+ type Direction8 = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;
type TileElement =
SurfaceElement | FootpathElement | TrackElement | SmallSceneryElement | WallElement | EntranceElement
@@ -709,6 +724,7 @@ declare global {
hasChainLift: boolean;
isInverted: boolean;
hasCableLift: boolean;
+ isHighlighted: boolean;
}
interface SmallSceneryElement extends BaseTileElement {
@@ -1128,6 +1144,138 @@ declare global {
exit: CoordsXYZD;
}
+ interface TrackSegment {
+ /**
+ * The track segment type.
+ */
+ readonly type: number;
+
+ /**
+ * Gets the localised description of the track segment.
+ */
+ readonly description: string;
+
+ /**
+ * The relative starting Z position.
+ */
+ readonly beginZ: number;
+
+ /**
+ * The relative starting direction. Usually 0, but will be 4
+ * for diagonal segments.
+ */
+ readonly beginDirection: Direction8;
+
+ /**
+ * The slope angle the segment starts with.
+ */
+ readonly beginAngle: TrackSlope;
+
+ /**
+ * The kind of banking the segment starts with.
+ */
+ readonly beginBank: TrackBanking;
+
+ /**
+ * The relative ending X position.
+ */
+ readonly endX: number;
+
+ /**
+ * The relative ending Y position.
+ */
+ readonly endY: number;
+
+ /**
+ * The relative ending Z position. Negative numbers indicate
+ * that the track ends upside down.
+ */
+ readonly endZ: number;
+
+ /**
+ * The relative ending direction.
+ */
+ readonly endDirection: Direction8;
+
+
+ /**
+ * The slope angle the segment ends with.
+ */
+ readonly endAngle: TrackSlope;
+
+ /**
+ * The kind of banking the segment ends with.
+ */
+ readonly endBank: TrackBanking;
+
+ /**
+ * The length of the segment in RCT track length units.
+ *
+ * *1 metre = 1 / (2 ^ 16)*
+ */
+ readonly length: number;
+
+ /**
+ * Gets a list of the elements that make up the track segment.
+ */
+ readonly elements: TrackSegmentElement[];
+ }
+
+ enum TrackSlope {
+ None = 0,
+ Up25 = 2,
+ Up60 = 4,
+ Down25 = 6,
+ Down60 = 8,
+ Up90 = 10,
+ Down90 = 18
+ }
+
+ enum TrackBanking {
+ None = 0,
+ Left = 2,
+ Right = 4,
+ UpsideDown = 15
+ }
+
+ interface TrackSegmentElement extends CoordsXYZ {
+ }
+
+ interface TrackIterator {
+ /**
+ * The position and direction of the current track segment. Usually this is the position of the
+ * first element of the segment, however for some segments, it may be offset.
+ */
+ readonly position: CoordsXYZD;
+
+ /**
+ * The current track segment.
+ */
+ readonly segment: TrackSegment | null;
+
+ /**
+ * Gets the position of where the previous track element should start.
+ */
+ readonly previousPosition: CoordsXYZD | null;
+
+ /**
+ * Gets the position of where the next track element should start.
+ */
+ readonly nextPosition: CoordsXYZD | null;
+
+ /**
+ * 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 =
"balloon" |
"car" |
diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj
index fdc0872262..85e5e1446c 100644
--- a/src/openrct2/libopenrct2.vcxproj
+++ b/src/openrct2/libopenrct2.vcxproj
@@ -470,6 +470,8 @@
+
+
@@ -928,6 +930,8 @@
+
+
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 070f611806..87d1110c99 100644
--- a/src/openrct2/scripting/ScriptEngine.cpp
+++ b/src/openrct2/scripting/ScriptEngine.cpp
@@ -413,6 +413,8 @@ void ScriptEngine::Initialise()
ScRideObjectVehicle::Register(ctx);
ScTile::Register(ctx);
ScTileElement::Register(ctx);
+ ScTrackIterator::Register(ctx);
+ ScTrackSegment::Register(ctx);
ScEntity::Register(ctx);
ScLitter::Register(ctx);
ScVehicle::Register(ctx);
@@ -438,6 +440,8 @@ void ScriptEngine::Initialise()
dukglue_register_global(ctx, std::make_shared(ctx), "profiler");
dukglue_register_global(ctx, std::make_shared(), "scenario");
+ RegisterConstants();
+
_initialised = true;
_transientPluginsEnabled = false;
_transientPluginsStarted = false;
@@ -446,6 +450,72 @@ void ScriptEngine::Initialise()
ClearParkStorage();
}
+class ConstantBuilder
+{
+private:
+ duk_context* _ctx;
+ DukValue _obj;
+
+public:
+ ConstantBuilder(duk_context* ctx)
+ : _ctx(ctx)
+ {
+ duk_push_global_object(_ctx);
+ _obj = DukValue::take_from_stack(_ctx);
+ }
+
+ ConstantBuilder& Namespace(std::string_view ns)
+ {
+ auto flags = DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_HAVE_ENUMERABLE
+ | DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_HAVE_VALUE;
+
+ // Create a new object for namespace
+ duk_push_global_object(_ctx);
+ duk_push_lstring(_ctx, ns.data(), ns.size());
+ duk_push_object(_ctx);
+
+ // Keep a reference to the namespace object
+ duk_dup_top(_ctx);
+ _obj = DukValue::take_from_stack(_ctx);
+
+ // Place the namespace object into the global context
+ duk_def_prop(_ctx, -3, flags);
+ duk_pop(_ctx);
+
+ return *this;
+ }
+
+ ConstantBuilder& Constant(std::string_view name, int32_t value)
+ {
+ auto flags = DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_HAVE_ENUMERABLE
+ | DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_HAVE_VALUE;
+ _obj.push();
+ duk_push_lstring(_ctx, name.data(), name.size());
+ duk_push_int(_ctx, value);
+ duk_def_prop(_ctx, -3, flags);
+ duk_pop(_ctx);
+ return *this;
+ }
+};
+
+void ScriptEngine::RegisterConstants()
+{
+ ConstantBuilder builder(_context);
+ builder.Namespace("TrackSlope")
+ .Constant("None", TRACK_SLOPE_NONE)
+ .Constant("Up25", TRACK_SLOPE_UP_25)
+ .Constant("Up60", TRACK_SLOPE_UP_60)
+ .Constant("Down25", TRACK_SLOPE_DOWN_25)
+ .Constant("Down60", TRACK_SLOPE_DOWN_60)
+ .Constant("Up90", TRACK_SLOPE_UP_90)
+ .Constant("Down90", TRACK_SLOPE_DOWN_90);
+ builder.Namespace("TrackBanking")
+ .Constant("None", TRACK_BANK_NONE)
+ .Constant("BankLeft", TRACK_BANK_LEFT)
+ .Constant("BankRight", TRACK_BANK_RIGHT)
+ .Constant("UpsideDown", TRACK_BANK_UPSIDE_DOWN);
+}
+
void ScriptEngine::RefreshPlugins()
{
// Get a list of removed and added plugin files
diff --git a/src/openrct2/scripting/ScriptEngine.h b/src/openrct2/scripting/ScriptEngine.h
index af49d086df..2dbc27971d 100644
--- a/src/openrct2/scripting/ScriptEngine.h
+++ b/src/openrct2/scripting/ScriptEngine.h
@@ -252,6 +252,7 @@ namespace OpenRCT2::Scripting
# endif
private:
+ void RegisterConstants();
void RefreshPlugins();
std::vector GetPluginFiles() const;
void UnregisterPlugin(std::string_view path);
diff --git a/src/openrct2/scripting/bindings/game/ScContext.hpp b/src/openrct2/scripting/bindings/game/ScContext.hpp
index e2d960ac12..d4599101e5 100644
--- a/src/openrct2/scripting/bindings/game/ScContext.hpp
+++ b/src/openrct2/scripting/bindings/game/ScContext.hpp
@@ -23,6 +23,7 @@
# include "../game/ScConfiguration.hpp"
# include "../game/ScDisposable.hpp"
# include "../object/ScObject.hpp"
+# include "../ride/ScTrackSegment.h"
# include
# include
@@ -218,6 +219,19 @@ namespace OpenRCT2::Scripting
return result;
}
+ DukValue getTrackSegment(track_type_t type)
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ if (type >= TrackElemType::Count)
+ {
+ return ToDuk(ctx, nullptr);
+ }
+ else
+ {
+ return GetObjectAsDukValue(ctx, std::make_shared(type));
+ }
+ }
+
int32_t getRandom(int32_t min, int32_t max)
{
ThrowIfGameStateNotMutable();
@@ -457,6 +471,7 @@ namespace OpenRCT2::Scripting
dukglue_register_method(ctx, &ScContext::captureImage, "captureImage");
dukglue_register_method(ctx, &ScContext::getObject, "getObject");
dukglue_register_method(ctx, &ScContext::getAllObjects, "getAllObjects");
+ dukglue_register_method(ctx, &ScContext::getTrackSegment, "getTrackSegment");
dukglue_register_method(ctx, &ScContext::getRandom, "getRandom");
dukglue_register_method_varargs(ctx, &ScContext::formatString, "formatString");
dukglue_register_method(ctx, &ScContext::subscribe, "subscribe");
diff --git a/src/openrct2/scripting/bindings/ride/ScTrackIterator.cpp b/src/openrct2/scripting/bindings/ride/ScTrackIterator.cpp
new file mode 100644
index 0000000000..e1953fe07d
--- /dev/null
+++ b/src/openrct2/scripting/bindings/ride/ScTrackIterator.cpp
@@ -0,0 +1,164 @@
+/*****************************************************************************
+ * 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_property(ctx, &ScTrackIterator::previousPosition_get, nullptr, "previousPosition");
+ dukglue_register_property(ctx, &ScTrackIterator::nextPosition_get, nullptr, "nextPosition");
+ 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, nullptr);
+
+ return GetObjectAsDukValue(ctx, std::make_shared(_type));
+}
+
+DukValue ScTrackIterator::previousPosition_get() const
+{
+ auto& scriptEngine = GetContext()->GetScriptEngine();
+ auto ctx = scriptEngine.GetContext();
+
+ 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_of_type_seq(pos, _type, 0);
+ if (el == nullptr)
+ return ToDuk(ctx, nullptr);
+
+ auto posEl = CoordsXYE(pos.x, pos.y, reinterpret_cast(el));
+ track_begin_end tbe{};
+ track_block_get_previous(posEl, &tbe);
+ CoordsXYZD result(tbe.end_x, tbe.end_y, tbe.begin_z, tbe.begin_direction);
+ return ToDuk(ctx, result);
+}
+
+DukValue ScTrackIterator::nextPosition_get() const
+{
+ auto& scriptEngine = GetContext()->GetScriptEngine();
+ auto ctx = scriptEngine.GetContext();
+
+ 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_of_type_seq(pos, _type, 0);
+ if (el == nullptr)
+ return ToDuk(ctx, nullptr);
+
+ auto posEl = CoordsXYE(_position.x, _position.y, reinterpret_cast(el));
+ CoordsXYE next;
+ int32_t z{};
+ int32_t direction{};
+ track_block_get_next(&posEl, &next, &z, &direction);
+ CoordsXYZD result(next.x, next.y, z, direction);
+ return ToDuk(ctx, result);
+}
+
+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_of_type_seq(pos, _type, 0);
+ if (el == nullptr)
+ return false;
+
+ auto posEl = CoordsXYE(pos.x, pos.y, reinterpret_cast(el));
+ track_begin_end tbe{};
+ if (track_block_get_previous(posEl, &tbe))
+ {
+ auto prev = CoordsXYE(tbe.end_x, tbe.end_y, tbe.begin_element);
+ auto origin = GetTrackSegmentOrigin(prev);
+ if (origin)
+ {
+ _position = *origin;
+ _type = prev.element->AsTrack()->GetTrackType();
+ return true;
+ }
+ }
+ 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_of_type_seq(pos, _type, 0);
+ if (el == nullptr)
+ return false;
+
+ auto posEl = CoordsXYE(_position.x, _position.y, reinterpret_cast(el));
+ CoordsXYE next;
+ int32_t z{};
+ int32_t direction{};
+ if (track_block_get_next(&posEl, &next, &z, &direction))
+ {
+ auto origin = GetTrackSegmentOrigin(next);
+ if (origin)
+ {
+ _position = *origin;
+ _type = next.element->AsTrack()->GetTrackType();
+ return true;
+ }
+ }
+ 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..9613947814
--- /dev/null
+++ b/src/openrct2/scripting/bindings/ride/ScTrackIterator.h
@@ -0,0 +1,47 @@
+/*****************************************************************************
+ * 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;
+ [[maybe_unused]] 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;
+ DukValue previousPosition_get() const;
+ DukValue nextPosition_get() const;
+
+ bool previous();
+ bool next();
+ };
+
+} // namespace OpenRCT2::Scripting
+
+#endif
diff --git a/src/openrct2/scripting/bindings/ride/ScTrackSegment.cpp b/src/openrct2/scripting/bindings/ride/ScTrackSegment.cpp
new file mode 100644
index 0000000000..dc79a15a3e
--- /dev/null
+++ b/src/openrct2/scripting/bindings/ride/ScTrackSegment.cpp
@@ -0,0 +1,144 @@
+/*****************************************************************************
+ * 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 "ScTrackSegment.h"
+
+# include "../../../Context.h"
+# include "../../../ride/TrackData.h"
+# include "../../ScriptEngine.h"
+
+using namespace OpenRCT2::Scripting;
+using namespace OpenRCT2::TrackMetaData;
+
+ScTrackSegment::ScTrackSegment(track_type_t type)
+ : _type(type)
+{
+}
+
+void ScTrackSegment::Register(duk_context* ctx)
+{
+ dukglue_register_property(ctx, &ScTrackSegment::type_get, nullptr, "type");
+ dukglue_register_property(ctx, &ScTrackSegment::description_get, nullptr, "description");
+ dukglue_register_property(ctx, &ScTrackSegment::elements_get, nullptr, "elements");
+ dukglue_register_property(ctx, &ScTrackSegment::beginZ_get, nullptr, "beginZ");
+ dukglue_register_property(ctx, &ScTrackSegment::beginDirection_get, nullptr, "beginDirection");
+ dukglue_register_property(ctx, &ScTrackSegment::endX_get, nullptr, "endX");
+ dukglue_register_property(ctx, &ScTrackSegment::endY_get, nullptr, "endY");
+ dukglue_register_property(ctx, &ScTrackSegment::endZ_get, nullptr, "endZ");
+ dukglue_register_property(ctx, &ScTrackSegment::endDirection_get, nullptr, "endDirection");
+ dukglue_register_property(ctx, &ScTrackSegment::length_get, nullptr, "length");
+}
+
+int32_t ScTrackSegment::type_get() const
+{
+ return _type;
+}
+
+std::string ScTrackSegment::description_get() const
+{
+ const auto& ted = GetTrackElementDescriptor(_type);
+ return language_get_string(ted.Description);
+}
+
+int32_t ScTrackSegment::beginZ_get() const
+{
+ const auto& ted = GetTrackElementDescriptor(_type);
+ return ted.Coordinates.z_begin;
+}
+
+int32_t ScTrackSegment::beginDirection_get() const
+{
+ const auto& ted = GetTrackElementDescriptor(_type);
+ return ted.Coordinates.rotation_begin;
+}
+
+int32_t ScTrackSegment::beginAngle_get() const
+{
+ const auto& ted = GetTrackElementDescriptor(_type);
+ return ted.Definition.vangle_start;
+}
+
+int32_t ScTrackSegment::beginBank_get() const
+{
+ const auto& ted = GetTrackElementDescriptor(_type);
+ return ted.Definition.bank_start;
+}
+
+int32_t ScTrackSegment::endX_get() const
+{
+ const auto& ted = GetTrackElementDescriptor(_type);
+ return ted.Coordinates.x;
+}
+
+int32_t ScTrackSegment::endY_get() const
+{
+ const auto& ted = GetTrackElementDescriptor(_type);
+ return ted.Coordinates.y;
+}
+
+int32_t ScTrackSegment::endZ_get() const
+{
+ const auto& ted = GetTrackElementDescriptor(_type);
+ return ted.Coordinates.z_end;
+}
+
+int32_t ScTrackSegment::endDirection_get() const
+{
+ const auto& ted = GetTrackElementDescriptor(_type);
+ return ted.Coordinates.rotation_end;
+}
+
+int32_t ScTrackSegment::endAngle_get() const
+{
+ const auto& ted = GetTrackElementDescriptor(_type);
+ return ted.Definition.vangle_end;
+}
+
+int32_t ScTrackSegment::endBank_get() const
+{
+ const auto& ted = GetTrackElementDescriptor(_type);
+ return ted.Definition.bank_end;
+}
+
+int32_t ScTrackSegment::length_get() const
+{
+ const auto& ted = GetTrackElementDescriptor(_type);
+ return ted.PieceLength;
+}
+
+DukValue ScTrackSegment::elements_get() const
+{
+ auto& scriptEngine = GetContext()->GetScriptEngine();
+ auto ctx = scriptEngine.GetContext();
+
+ const auto& ted = GetTrackElementDescriptor(_type);
+
+ duk_push_array(ctx);
+
+ duk_uarridx_t index = 0;
+ for (auto* block = ted.Block; block->index != 0xFF; block++)
+ {
+ duk_push_object(ctx);
+ duk_push_number(ctx, block->x);
+ duk_put_prop_string(ctx, -2, "x");
+ duk_push_number(ctx, block->y);
+ duk_put_prop_string(ctx, -2, "y");
+ duk_push_number(ctx, block->z);
+ duk_put_prop_string(ctx, -2, "z");
+
+ duk_put_prop_index(ctx, -2, index);
+ index++;
+ }
+
+ return DukValue::take_from_stack(ctx);
+}
+
+#endif
diff --git a/src/openrct2/scripting/bindings/ride/ScTrackSegment.h b/src/openrct2/scripting/bindings/ride/ScTrackSegment.h
new file mode 100644
index 0000000000..24277f500f
--- /dev/null
+++ b/src/openrct2/scripting/bindings/ride/ScTrackSegment.h
@@ -0,0 +1,51 @@
+/*****************************************************************************
+ * 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 "../../../world/TileElement.h"
+# include "../../Duktape.hpp"
+
+# include
+# include
+
+namespace OpenRCT2::Scripting
+{
+ class ScTrackSegment
+ {
+ private:
+ track_type_t _type;
+
+ public:
+ ScTrackSegment(track_type_t type);
+
+ static void Register(duk_context* ctx);
+
+ private:
+ int32_t type_get() const;
+ std::string description_get() const;
+ int32_t beginZ_get() const;
+ int32_t beginDirection_get() const;
+ int32_t beginAngle_get() const;
+ int32_t beginBank_get() const;
+ int32_t endX_get() const;
+ int32_t endY_get() const;
+ int32_t endZ_get() const;
+ int32_t endDirection_get() const;
+ int32_t endAngle_get() const;
+ int32_t endBank_get() const;
+ int32_t length_get() const;
+ DukValue elements_get() const;
+ };
+
+} // 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 3ac16018b2..568771ca6e 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);