diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj
index dd3f6892d1..67c5d17b36 100644
--- a/src/openrct2/libopenrct2.vcxproj
+++ b/src/openrct2/libopenrct2.vcxproj
@@ -414,6 +414,7 @@
+
@@ -870,6 +871,7 @@
+
diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp
index d4f7bb88a4..e5e132ceb1 100644
--- a/src/openrct2/scripting/ScriptEngine.cpp
+++ b/src/openrct2/scripting/ScriptEngine.cpp
@@ -48,6 +48,7 @@
# include "bindings/world/ScParkMessage.hpp"
# include "bindings/world/ScScenario.hpp"
# include "bindings/world/ScTile.hpp"
+# include "bindings/world/ScTileElement.hpp"
# include
# include
diff --git a/src/openrct2/scripting/bindings/world/ScTile.hpp b/src/openrct2/scripting/bindings/world/ScTile.hpp
index 182b20f9ef..2126d5cc0e 100644
--- a/src/openrct2/scripting/bindings/world/ScTile.hpp
+++ b/src/openrct2/scripting/bindings/world/ScTile.hpp
@@ -21,6 +21,7 @@
# include "../../../world/Surface.h"
# include "../../Duktape.hpp"
# include "../../ScriptEngine.h"
+# include "ScTileElement.hpp"
# include
# include
@@ -28,1556 +29,6 @@
namespace OpenRCT2::Scripting
{
- class ScSurfaceElement;
-
- class ScTileElement
- {
- protected:
- CoordsXY _coords;
- TileElement* _element;
-
- public:
- ScTileElement(const CoordsXY& coords, TileElement* element)
- : _coords(coords)
- , _element(element)
- {
- }
-
- private:
- std::string type_get() const
- {
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_SURFACE:
- return "surface";
- case TILE_ELEMENT_TYPE_PATH:
- return "footpath";
- case TILE_ELEMENT_TYPE_TRACK:
- return "track";
- case TILE_ELEMENT_TYPE_SMALL_SCENERY:
- return "small_scenery";
- case TILE_ELEMENT_TYPE_ENTRANCE:
- return "entrance";
- case TILE_ELEMENT_TYPE_WALL:
- return "wall";
- case TILE_ELEMENT_TYPE_LARGE_SCENERY:
- return "large_scenery";
- case TILE_ELEMENT_TYPE_BANNER:
- return "banner";
- case TILE_ELEMENT_TYPE_CORRUPT:
- return "openrct2_corrupt_deprecated";
- default:
- return "unknown";
- }
- }
-
- void type_set(std::string value)
- {
- auto type = _element->type;
- if (value == "surface")
- type = TILE_ELEMENT_TYPE_SURFACE;
- else if (value == "footpath")
- type = TILE_ELEMENT_TYPE_PATH;
- else if (value == "track")
- type = TILE_ELEMENT_TYPE_TRACK;
- else if (value == "small_scenery")
- type = TILE_ELEMENT_TYPE_SMALL_SCENERY;
- else if (value == "entrance")
- type = TILE_ELEMENT_TYPE_ENTRANCE;
- else if (value == "wall")
- type = TILE_ELEMENT_TYPE_WALL;
- else if (value == "large_scenery")
- type = TILE_ELEMENT_TYPE_LARGE_SCENERY;
- else if (value == "banner")
- type = TILE_ELEMENT_TYPE_BANNER;
- else
- {
- if (value == "openrct2_corrupt_deprecated")
- std::puts(
- "Creation of new corrupt elements is deprecated. To hide elements, use the 'hidden' property instead.");
- return;
- }
-
- _element->type = type;
- Invalidate();
- }
-
- uint8_t baseHeight_get() const
- {
- return _element->base_height;
- }
- void baseHeight_set(uint8_t newBaseHeight)
- {
- ThrowIfGameStateNotMutable();
- _element->base_height = newBaseHeight;
- Invalidate();
- }
-
- uint16_t baseZ_get() const
- {
- return _element->GetBaseZ();
- }
- void baseZ_set(uint16_t value)
- {
- ThrowIfGameStateNotMutable();
- _element->SetBaseZ(value);
- Invalidate();
- }
-
- uint8_t clearanceHeight_get() const
- {
- return _element->clearance_height;
- }
- void clearanceHeight_set(uint8_t newClearanceHeight)
- {
- ThrowIfGameStateNotMutable();
- _element->clearance_height = newClearanceHeight;
- Invalidate();
- }
-
- uint16_t clearanceZ_get() const
- {
- return _element->GetClearanceZ();
- }
- void clearanceZ_set(uint16_t value)
- {
- ThrowIfGameStateNotMutable();
- _element->SetClearanceZ(value);
- Invalidate();
- }
-
- DukValue slope_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_SURFACE:
- {
- auto el = _element->AsSurface();
- duk_push_int(ctx, el->GetSlope());
- break;
- }
- case TILE_ELEMENT_TYPE_WALL:
- {
- auto el = _element->AsWall();
- duk_push_int(ctx, el->GetSlope());
- break;
- }
- default:
- {
- duk_push_null(ctx);
- break;
- }
- }
- return DukValue::take_from_stack(ctx);
- }
- void slope_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_SURFACE:
- {
- auto el = _element->AsSurface();
- el->SetSlope(value);
- Invalidate();
- break;
- }
- case TILE_ELEMENT_TYPE_WALL:
- {
- auto el = _element->AsWall();
- el->SetSlope(value);
- Invalidate();
- break;
- }
- }
- }
-
- DukValue waterHeight_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsSurface();
- if (el != nullptr)
- duk_push_int(ctx, el->GetWaterHeight());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void waterHeight_set(int32_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsSurface();
- if (el != nullptr)
- {
- el->SetWaterHeight(value);
- Invalidate();
- }
- }
-
- DukValue surfaceStyle_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsSurface();
- if (el != nullptr)
- duk_push_int(ctx, el->GetSurfaceStyle());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void surfaceStyle_set(uint32_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsSurface();
- if (el != nullptr)
- {
- el->SetSurfaceStyle(value);
- Invalidate();
- }
- }
-
- DukValue edgeStyle_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsSurface();
- if (el != nullptr)
- duk_push_int(ctx, el->GetEdgeStyle());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void edgeStyle_set(uint32_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsSurface();
- if (el != nullptr)
- {
- el->SetEdgeStyle(value);
- Invalidate();
- }
- }
-
- DukValue grassLength_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsSurface();
- if (el != nullptr)
- duk_push_int(ctx, el->GetGrassLength());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void grassLength_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsSurface();
- if (el != nullptr)
- {
- // TODO: Give warning when value > GRASS_LENGTH_CLUMPS_2
- el->SetGrassLengthAndInvalidate(value, _coords);
- Invalidate();
- }
- }
-
- DukValue hasOwnership_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsSurface();
- if (el != nullptr)
- duk_push_boolean(ctx, el->GetOwnership() & OWNERSHIP_OWNED);
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
-
- DukValue hasConstructionRights_get()
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsSurface();
- if (el != nullptr)
- {
- auto ownership = el->GetOwnership();
- duk_push_boolean(ctx, (ownership & OWNERSHIP_OWNED) || (ownership & OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED));
- }
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
-
- DukValue ownership_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsSurface();
- if (el != nullptr)
- duk_push_int(ctx, el->GetOwnership());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void ownership_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsSurface();
- if (el != nullptr)
- {
- el->SetOwnership(value);
- Invalidate();
- }
- }
-
- DukValue parkFences_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsSurface();
- if (el != nullptr)
- duk_push_int(ctx, el->GetParkFences());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void parkFences_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsSurface();
- if (el != nullptr)
- {
- el->SetParkFences(value);
- Invalidate();
- }
- }
-
- DukValue trackType_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsTrack();
- if (el != nullptr)
- duk_push_int(ctx, el->GetTrackType());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void trackType_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsTrack();
- if (el != nullptr)
- {
- el->SetTrackType(value);
- Invalidate();
- }
- }
-
- DukValue sequence_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_LARGE_SCENERY:
- {
- auto el = _element->AsLargeScenery();
- duk_push_int(ctx, el->GetSequenceIndex());
- break;
- }
- case TILE_ELEMENT_TYPE_TRACK:
- {
- auto el = _element->AsTrack();
- if (get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
- duk_push_int(ctx, el->GetSequenceIndex());
- else
- duk_push_null(ctx);
- break;
- }
- case TILE_ELEMENT_TYPE_ENTRANCE:
- {
- auto el = _element->AsEntrance();
- duk_push_int(ctx, el->GetSequenceIndex());
- break;
- }
- default:
- {
- duk_push_null(ctx);
- break;
- }
- }
- return DukValue::take_from_stack(ctx);
- }
- void sequence_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_LARGE_SCENERY:
- {
- auto el = _element->AsLargeScenery();
- el->SetSequenceIndex(value);
- Invalidate();
- break;
- }
- case TILE_ELEMENT_TYPE_TRACK:
- {
- auto el = _element->AsTrack();
- if (get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
- {
- el->SetSequenceIndex(value);
- Invalidate();
- }
- break;
- }
- case TILE_ELEMENT_TYPE_ENTRANCE:
- {
- auto el = _element->AsEntrance();
- el->SetSequenceIndex(value);
- Invalidate();
- break;
- }
- }
- }
-
- DukValue ride_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_PATH:
- {
- auto el = _element->AsPath();
- if (el->IsQueue() && el->GetRideIndex() != RIDE_ID_NULL)
- duk_push_int(ctx, el->GetRideIndex());
- else
- duk_push_null(ctx);
- break;
- }
- case TILE_ELEMENT_TYPE_TRACK:
- {
- auto el = _element->AsTrack();
- duk_push_int(ctx, el->GetRideIndex());
- break;
- }
- case TILE_ELEMENT_TYPE_ENTRANCE:
- {
- auto el = _element->AsEntrance();
- duk_push_int(ctx, el->GetRideIndex());
- break;
- }
- default:
- {
- duk_push_null(ctx);
- break;
- }
- }
- return DukValue::take_from_stack(ctx);
- }
- void ride_set(ride_id_t value)
- {
- ThrowIfGameStateNotMutable();
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_PATH:
- {
- auto el = _element->AsPath();
- if (!el->HasAddition())
- {
- el->SetRideIndex(value);
- Invalidate();
- }
- break;
- }
- case TILE_ELEMENT_TYPE_TRACK:
- {
- auto el = _element->AsTrack();
- el->SetRideIndex(value);
- Invalidate();
- break;
- }
- case TILE_ELEMENT_TYPE_ENTRANCE:
- {
- auto el = _element->AsEntrance();
- el->SetRideIndex(value);
- Invalidate();
- break;
- }
- }
- }
-
- DukValue station_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_PATH:
- {
- auto el = _element->AsPath();
- if (el->IsQueue() && el->GetRideIndex() != RIDE_ID_NULL)
- duk_push_int(ctx, el->GetStationIndex());
- else
- duk_push_null(ctx);
- break;
- }
- case TILE_ELEMENT_TYPE_TRACK:
- {
- auto el = _element->AsTrack();
- if (el->IsStation())
- duk_push_int(ctx, el->GetStationIndex());
- else
- duk_push_null(ctx);
- break;
- }
- case TILE_ELEMENT_TYPE_ENTRANCE:
- {
- auto el = _element->AsEntrance();
- duk_push_int(ctx, el->GetStationIndex());
- break;
- }
- default:
- {
- duk_push_null(ctx);
- break;
- }
- }
- return DukValue::take_from_stack(ctx);
- }
- void station_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_PATH:
- {
- auto el = _element->AsPath();
- el->SetStationIndex(value);
- Invalidate();
- break;
- }
- case TILE_ELEMENT_TYPE_TRACK:
- {
- auto el = _element->AsTrack();
- el->SetStationIndex(value);
- Invalidate();
- break;
- }
- case TILE_ELEMENT_TYPE_ENTRANCE:
- {
- auto el = _element->AsEntrance();
- el->SetStationIndex(value);
- Invalidate();
- break;
- }
- }
- }
-
- DukValue hasChainLift_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsTrack();
- if (el != nullptr)
- duk_push_boolean(ctx, el->HasChain());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void hasChainLift_set(bool value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsTrack();
- if (el != nullptr)
- {
- el->SetHasChain(value);
- Invalidate();
- }
- }
-
- DukValue mazeEntry_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsTrack();
- if (el != nullptr && get_ride(el->GetRideIndex())->type == RIDE_TYPE_MAZE)
- duk_push_int(ctx, el->GetMazeEntry());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void mazeEntry_set(uint16_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsTrack();
- if (el != nullptr)
- if (get_ride(el->GetRideIndex())->type == RIDE_TYPE_MAZE)
- {
- el->SetMazeEntry(value);
- Invalidate();
- }
- }
-
- DukValue colourScheme_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsTrack();
- if (el != nullptr && get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
- duk_push_int(ctx, el->GetColourScheme());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void colourScheme_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsTrack();
- if (el != nullptr)
- if (get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
- {
- el->SetColourScheme(value);
- Invalidate();
- }
- }
-
- DukValue seatRotation_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsTrack();
- if (el != nullptr && get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
- duk_push_int(ctx, el->GetSeatRotation());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void seatRotation_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsTrack();
- if (el != nullptr)
- if (get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
- {
- el->SetSeatRotation(value);
- Invalidate();
- }
- }
-
- DukValue brakeBoosterSpeed_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsTrack();
- if (el != nullptr && TrackTypeHasSpeedSetting(el->GetTrackType()))
- duk_push_int(ctx, el->GetBrakeBoosterSpeed());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void brakeBoosterSpeed_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsTrack();
- if (el != nullptr)
- if (TrackTypeHasSpeedSetting(el->GetTrackType()))
- {
- el->SetBrakeBoosterSpeed(value);
- Invalidate();
- }
- }
-
- DukValue isInverted_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsTrack();
- if (el != nullptr)
- duk_push_boolean(ctx, el->IsInverted());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void isInverted_set(bool value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsTrack();
- if (el != nullptr)
- {
- el->SetInverted(value);
- Invalidate();
- }
- }
-
- DukValue hasCableLift_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsTrack();
- if (el != nullptr)
- duk_push_boolean(ctx, el->HasCableLift());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void hasCableLift_set(bool value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsTrack();
- if (el != nullptr)
- {
- el->SetHasCableLift(value);
- Invalidate();
- }
- }
-
- DukValue object_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_PATH:
- {
- auto el = _element->AsPath();
- duk_push_int(ctx, el->GetSurfaceEntryIndex());
- break;
- }
- case TILE_ELEMENT_TYPE_SMALL_SCENERY:
- {
- auto el = _element->AsSmallScenery();
- duk_push_int(ctx, el->GetEntryIndex());
- break;
- }
- case TILE_ELEMENT_TYPE_LARGE_SCENERY:
- {
- auto el = _element->AsLargeScenery();
- duk_push_int(ctx, el->GetEntryIndex());
- break;
- }
- case TILE_ELEMENT_TYPE_WALL:
- {
- auto el = _element->AsWall();
- duk_push_int(ctx, el->GetEntryIndex());
- break;
- }
- case TILE_ELEMENT_TYPE_ENTRANCE:
- {
- auto el = _element->AsEntrance();
- duk_push_int(ctx, el->GetEntranceType());
- break;
- }
- default:
- {
- duk_push_null(ctx);
- break;
- }
- }
- return DukValue::take_from_stack(ctx);
- }
- void object_set(uint32_t value)
- {
- ThrowIfGameStateNotMutable();
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_PATH:
- {
- auto el = _element->AsPath();
- el->SetSurfaceEntryIndex(value & 0xFF);
- Invalidate();
- break;
- }
- case TILE_ELEMENT_TYPE_SMALL_SCENERY:
- {
- auto el = _element->AsSmallScenery();
- el->SetEntryIndex(value & 0xFF);
- Invalidate();
- break;
- }
- case TILE_ELEMENT_TYPE_LARGE_SCENERY:
- {
- auto el = _element->AsLargeScenery();
- el->SetEntryIndex(value);
- Invalidate();
- break;
- }
- case TILE_ELEMENT_TYPE_WALL:
- {
- auto el = _element->AsWall();
- el->SetEntryIndex(value & 0xFFFF);
- Invalidate();
- break;
- }
- case TILE_ELEMENT_TYPE_ENTRANCE:
- {
- auto el = _element->AsEntrance();
- el->SetEntranceType(value & 0xFF);
- Invalidate();
- break;
- }
- }
- }
-
- bool isHidden_get() const
- {
- // TODO: Simply return the 'hidden' field once corrupt elements are superseded.
- const TileElement* element = map_get_first_element_at(_coords);
- bool previousElementWasUsefulCorrupt = false;
- do
- {
- if (element == _element)
- return previousElementWasUsefulCorrupt;
-
- if (element->GetType() == TILE_ELEMENT_TYPE_CORRUPT)
- previousElementWasUsefulCorrupt = !previousElementWasUsefulCorrupt;
- else
- previousElementWasUsefulCorrupt = false;
- } while (!(element++)->IsLastForTile());
-
- Guard::Assert(false);
- return false;
- }
- void isHidden_set(bool hide)
- {
- // TODO: Simply update the 'hidden' field once corrupt elements are superseded.
- ThrowIfGameStateNotMutable();
- const bool isHidden = isHidden_get();
- if (hide == isHidden)
- return;
-
- if (hide)
- {
- // Get index of our current element (has to be done now before inserting the corrupt element)
- const auto elementIndex = _element - map_get_first_element_at(_coords);
-
- // Insert corrupt element at the end of the list for this tile
- // Note: Z = MAX_ELEMENT_HEIGHT to guarantee this
- TileElement* insertedElement = tile_element_insert(
- { _coords, MAX_ELEMENT_HEIGHT * COORDS_Z_STEP }, 0, TileElementType::Corrupt);
- if (insertedElement == nullptr)
- {
- // TODO: Show error
- return;
- }
-
- // Since inserting a new element may move the tile elements in memory, we have to update the local pointer
- _element = map_get_first_element_at(_coords) + elementIndex;
-
- // Move the corrupt element down in the list until it's right under our element
- while (insertedElement > _element)
- {
- std::swap(*insertedElement, *(insertedElement - 1));
- insertedElement--;
-
- // Un-swap the last-for-tile flag
- if (insertedElement->IsLastForTile())
- {
- insertedElement->SetLastForTile(false);
- (insertedElement + 1)->SetLastForTile(true);
- }
- }
-
- // Now the corrupt element took the hidden element's place, increment it by one
- _element++;
-
- // Update base and clearance heights of inserted corrupt element to match the element to hide
- insertedElement->base_height = insertedElement->clearance_height = _element->base_height;
- }
- else
- {
- TileElement* const elementToRemove = _element - 1;
- Guard::Assert(elementToRemove->GetType() == TILE_ELEMENT_TYPE_CORRUPT);
- tile_element_remove(elementToRemove);
- _element--;
- }
-
- Invalidate();
- }
-
- DukValue age_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsSmallScenery();
- if (el != nullptr)
- duk_push_int(ctx, el->GetAge());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void age_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsSmallScenery();
- if (el != nullptr)
- {
- el->SetAge(value);
- Invalidate();
- }
- }
-
- DukValue quadrant_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsSmallScenery();
- if (el != nullptr)
- duk_push_int(ctx, el->GetSceneryQuadrant());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void quadrant_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsSmallScenery();
- if (el != nullptr)
- {
- el->SetSceneryQuadrant(value);
- Invalidate();
- }
- }
-
- uint8_t occupiedQuadrants_get() const
- {
- return _element->GetOccupiedQuadrants();
- }
- void occupiedQuadrants_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- _element->SetOccupiedQuadrants(value);
- Invalidate();
- }
-
- bool isGhost_get() const
- {
- return _element->IsGhost();
- }
- void isGhost_set(bool value)
- {
- ThrowIfGameStateNotMutable();
- _element->SetGhost(value);
- Invalidate();
- }
-
- DukValue primaryColour_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_SMALL_SCENERY:
- {
- auto el = _element->AsSmallScenery();
- duk_push_int(ctx, el->GetPrimaryColour());
- break;
- }
- case TILE_ELEMENT_TYPE_LARGE_SCENERY:
- {
- auto el = _element->AsLargeScenery();
- duk_push_int(ctx, el->GetPrimaryColour());
- break;
- }
- case TILE_ELEMENT_TYPE_WALL:
- {
- auto el = _element->AsWall();
- duk_push_int(ctx, el->GetPrimaryColour());
- break;
- }
- default:
- {
- duk_push_null(ctx);
- break;
- }
- }
- return DukValue::take_from_stack(ctx);
- }
- void primaryColour_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_SMALL_SCENERY:
- {
- auto el = _element->AsSmallScenery();
- el->SetPrimaryColour(value);
- Invalidate();
- break;
- }
- case TILE_ELEMENT_TYPE_LARGE_SCENERY:
- {
- auto el = _element->AsLargeScenery();
- el->SetPrimaryColour(value);
- Invalidate();
- break;
- }
- case TILE_ELEMENT_TYPE_WALL:
- {
- auto el = _element->AsWall();
- el->SetPrimaryColour(value);
- Invalidate();
- break;
- }
- }
- }
-
- DukValue secondaryColour_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_SMALL_SCENERY:
- {
- auto el = _element->AsSmallScenery();
- duk_push_int(ctx, el->GetSecondaryColour());
- break;
- }
- case TILE_ELEMENT_TYPE_LARGE_SCENERY:
- {
- auto el = _element->AsLargeScenery();
- duk_push_int(ctx, el->GetSecondaryColour());
- break;
- }
- case TILE_ELEMENT_TYPE_WALL:
- {
- auto el = _element->AsWall();
- duk_push_int(ctx, el->GetSecondaryColour());
- break;
- }
- default:
- {
- duk_push_null(ctx);
- break;
- }
- }
- return DukValue::take_from_stack(ctx);
- }
- void secondaryColour_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_SMALL_SCENERY:
- {
- auto el = _element->AsSmallScenery();
- el->SetSecondaryColour(value);
- Invalidate();
- break;
- }
- case TILE_ELEMENT_TYPE_LARGE_SCENERY:
- {
- auto el = _element->AsLargeScenery();
- el->SetSecondaryColour(value);
- Invalidate();
- break;
- }
- case TILE_ELEMENT_TYPE_WALL:
- {
- auto el = _element->AsWall();
- el->SetSecondaryColour(value);
- Invalidate();
- break;
- }
- }
- }
-
- DukValue tertiaryColour_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsWall();
- if (el != nullptr)
- duk_push_int(ctx, el->GetTertiaryColour());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void tertiaryColour_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsWall();
- if (el != nullptr)
- {
- el->SetTertiaryColour(value);
- Invalidate();
- }
- }
-
- DukValue bannerIndex_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- BannerIndex idx = _element->GetBannerIndex();
- if (idx == BANNER_INDEX_NULL)
- duk_push_null(ctx);
- else
- duk_push_int(ctx, idx);
- return DukValue::take_from_stack(ctx);
- }
- void bannerIndex_set(uint16_t value)
- {
- ThrowIfGameStateNotMutable();
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_LARGE_SCENERY:
- {
- auto el = _element->AsLargeScenery();
- el->SetBannerIndex(value);
- Invalidate();
- break;
- }
- case TILE_ELEMENT_TYPE_WALL:
- {
- auto el = _element->AsWall();
- el->SetBannerIndex(value);
- Invalidate();
- break;
- }
- case TILE_ELEMENT_TYPE_BANNER:
- {
- auto el = _element->AsBanner();
- el->SetIndex(value);
- Invalidate();
- break;
- }
- }
- }
-
- // Deprecated in favor of seperate 'edges' and 'corners' properties,
- // left here to maintain compatibility with older plugins.
- /** @deprecated */
- uint8_t edgesAndCorners_get() const
- {
- auto el = _element->AsPath();
- return el != nullptr ? el->GetEdgesAndCorners() : 0;
- }
- /** @deprecated */
- void edgesAndCorners_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsPath();
- if (el != nullptr)
- {
- el->SetEdgesAndCorners(value);
- Invalidate();
- }
- }
-
- DukValue edges_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsPath();
- if (el != nullptr)
- duk_push_int(ctx, el->GetEdges());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void edges_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsPath();
- if (el != nullptr)
- {
- el->SetEdges(value);
- Invalidate();
- }
- }
-
- DukValue corners_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsPath();
- if (el != nullptr)
- duk_push_int(ctx, el->GetCorners());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void corners_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsPath();
- if (el != nullptr)
- {
- el->SetCorners(value);
- Invalidate();
- }
- }
-
- DukValue slopeDirection_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsPath();
- if (el != nullptr && el->IsSloped())
- duk_push_int(ctx, el->GetSlopeDirection());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void slopeDirection_set(const DukValue& value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsPath();
- if (el != nullptr)
- {
- if (value.type() == DukValue::Type::NUMBER)
- {
- el->SetSloped(true);
- el->SetSlopeDirection(value.as_int());
- }
- else
- {
- el->SetSloped(false);
- el->SetSlopeDirection(0);
- }
- Invalidate();
- }
- }
-
- DukValue isQueue_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsPath();
- if (el != nullptr)
- duk_push_boolean(ctx, el->IsQueue());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void isQueue_set(bool value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsPath();
- if (el != nullptr)
- {
- el->SetIsQueue(value);
- Invalidate();
- }
- }
-
- DukValue queueBannerDirection_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsPath();
- if (el != nullptr && el->HasQueueBanner())
- duk_push_int(ctx, el->GetQueueBannerDirection());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void queueBannerDirection_set(const DukValue& value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsPath();
- if (el != nullptr)
- {
- if (value.type() == DukValue::Type::NUMBER)
- {
- el->SetHasQueueBanner(true);
- el->SetQueueBannerDirection(value.as_int());
- }
- else
- {
- el->SetHasQueueBanner(false);
- el->SetQueueBannerDirection(0);
- }
- Invalidate();
- }
- }
-
- DukValue isBlockedByVehicle_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsPath();
- if (el != nullptr)
- duk_push_boolean(ctx, el->IsBlockedByVehicle());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void isBlockedByVehicle_set(bool value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsPath();
- if (el != nullptr)
- {
- el->SetIsBlockedByVehicle(value);
- Invalidate();
- }
- }
-
- DukValue isWide_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsPath();
- if (el != nullptr)
- duk_push_boolean(ctx, el->IsWide());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void isWide_set(bool value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsPath();
- if (el != nullptr)
- {
- el->SetWide(value);
- Invalidate();
- }
- }
-
- DukValue addition_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsPath();
- if (el != nullptr && el->HasAddition())
- duk_push_int(ctx, el->GetAddition() - 1);
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void addition_set(const DukValue& value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsPath();
- if (el != nullptr)
- {
- if (value.type() == DukValue::Type::NUMBER)
- {
- auto addition = value.as_int();
- if (addition >= 0 && addition <= 254)
- {
- el->SetAddition(addition + 1);
- }
- }
- else
- {
- el->SetAddition(0);
- }
- Invalidate();
- }
- }
-
- DukValue additionStatus_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsPath();
- if (el != nullptr && el->HasAddition())
- duk_push_int(ctx, el->GetAdditionStatus());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void additionStatus_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsPath();
- if (el != nullptr)
- if (el->HasAddition())
- {
- el->SetAdditionStatus(value);
- Invalidate();
- }
- }
-
- DukValue isAdditionBroken_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsPath();
- if (el != nullptr && el->HasAddition())
- duk_push_boolean(ctx, el->IsBroken());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void isAdditionBroken_set(bool value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsPath();
- if (el != nullptr)
- {
- el->SetIsBroken(value);
- Invalidate();
- }
- }
-
- DukValue isAdditionGhost_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsPath();
- if (el != nullptr && el->HasAddition())
- duk_push_boolean(ctx, el->AdditionIsGhost());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void isAdditionGhost_set(bool value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsPath();
- if (el != nullptr)
- {
- el->SetAdditionIsGhost(value);
- Invalidate();
- }
- }
-
- DukValue footpathObject_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- auto el = _element->AsEntrance();
- if (el != nullptr)
- duk_push_int(ctx, el->GetPathType());
- else
- duk_push_null(ctx);
- return DukValue::take_from_stack(ctx);
- }
- void footpathObject_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- auto el = _element->AsEntrance();
- if (el != nullptr)
- {
- el->SetPathType(value);
- Invalidate();
- }
- }
-
- DukValue direction_get() const
- {
- auto ctx = GetContext()->GetScriptEngine().GetContext();
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_BANNER:
- {
- auto el = _element->AsBanner();
- duk_push_int(ctx, el->GetPosition());
- break;
- }
- case TILE_ELEMENT_TYPE_PATH:
- case TILE_ELEMENT_TYPE_SURFACE:
- {
- duk_push_null(ctx);
- break;
- }
- default:
- {
- duk_push_int(ctx, _element->GetDirection());
- break;
- }
- }
- return DukValue::take_from_stack(ctx);
- }
- void direction_set(uint8_t value)
- {
- ThrowIfGameStateNotMutable();
- switch (_element->GetType())
- {
- case TILE_ELEMENT_TYPE_BANNER:
- {
- auto el = _element->AsBanner();
- el->SetPosition(value);
- Invalidate();
- break;
- }
- case TILE_ELEMENT_TYPE_PATH:
- case TILE_ELEMENT_TYPE_SURFACE:
- {
- break;
- }
- default:
- {
- _element->SetDirection(value);
- Invalidate();
- }
- }
- }
-
- void Invalidate()
- {
- map_invalidate_tile_full(_coords);
- }
-
- public:
- static void Register(duk_context* ctx)
- {
- // All
- dukglue_register_property(ctx, &ScTileElement::type_get, &ScTileElement::type_set, "type");
- dukglue_register_property(ctx, &ScTileElement::baseHeight_get, &ScTileElement::baseHeight_set, "baseHeight");
- dukglue_register_property(ctx, &ScTileElement::baseZ_get, &ScTileElement::baseZ_set, "baseZ");
- dukglue_register_property(
- ctx, &ScTileElement::clearanceHeight_get, &ScTileElement::clearanceHeight_set, "clearanceHeight");
- dukglue_register_property(ctx, &ScTileElement::clearanceZ_get, &ScTileElement::clearanceZ_set, "clearanceZ");
- dukglue_register_property(
- ctx, &ScTileElement::occupiedQuadrants_get, &ScTileElement::occupiedQuadrants_set, "occupiedQuadrants");
- dukglue_register_property(ctx, &ScTileElement::isGhost_get, &ScTileElement::isGhost_set, "isGhost");
- dukglue_register_property(ctx, &ScTileElement::isHidden_get, &ScTileElement::isHidden_set, "isHidden");
-
- // Track | Small Scenery | Wall | Entrance | Large Scenery | Banner
- dukglue_register_property(ctx, &ScTileElement::direction_get, &ScTileElement::direction_set, "direction");
-
- // Path | Small Scenery | Wall | Entrance | Large Scenery
- dukglue_register_property(ctx, &ScTileElement::object_get, &ScTileElement::object_set, "object");
-
- // Small Scenery | Wall | Large Scenery
- dukglue_register_property(
- ctx, &ScTileElement::primaryColour_get, &ScTileElement::primaryColour_set, "primaryColour");
- dukglue_register_property(
- ctx, &ScTileElement::secondaryColour_get, &ScTileElement::secondaryColour_set, "secondaryColour");
-
- // Wall | Large Scenery | Banner
- dukglue_register_property(ctx, &ScTileElement::bannerIndex_get, &ScTileElement::bannerIndex_set, "bannerIndex");
-
- // Path | Track | Entrance
- dukglue_register_property(ctx, &ScTileElement::ride_get, &ScTileElement::ride_set, "ride");
- dukglue_register_property(ctx, &ScTileElement::station_get, &ScTileElement::station_set, "station");
-
- // Track | Entrance | Large Scenery
- dukglue_register_property(ctx, &ScTileElement::sequence_get, &ScTileElement::sequence_set, "sequence");
-
- // Surface | Wall
- dukglue_register_property(ctx, &ScTileElement::slope_get, &ScTileElement::slope_set, "slope");
-
- // Surface only
- dukglue_register_property(ctx, &ScTileElement::waterHeight_get, &ScTileElement::waterHeight_set, "waterHeight");
- dukglue_register_property(ctx, &ScTileElement::surfaceStyle_get, &ScTileElement::surfaceStyle_set, "surfaceStyle");
- dukglue_register_property(ctx, &ScTileElement::edgeStyle_get, &ScTileElement::edgeStyle_set, "edgeStyle");
- dukglue_register_property(ctx, &ScTileElement::grassLength_get, &ScTileElement::grassLength_set, "grassLength");
- dukglue_register_property(ctx, &ScTileElement::hasOwnership_get, nullptr, "hasOwnership");
- dukglue_register_property(ctx, &ScTileElement::hasConstructionRights_get, nullptr, "hasConstructionRights");
- dukglue_register_property(ctx, &ScTileElement::ownership_get, &ScTileElement::ownership_set, "ownership");
- dukglue_register_property(ctx, &ScTileElement::parkFences_get, &ScTileElement::parkFences_set, "parkFences");
-
- // Footpath only
- dukglue_register_property(
- ctx, &ScTileElement::edgesAndCorners_get, &ScTileElement::edgesAndCorners_set, "edgesAndCorners");
- dukglue_register_property(ctx, &ScTileElement::edges_get, &ScTileElement::edges_set, "edges");
- dukglue_register_property(ctx, &ScTileElement::corners_get, &ScTileElement::corners_set, "corners");
- dukglue_register_property(
- ctx, &ScTileElement::slopeDirection_get, &ScTileElement::slopeDirection_set, "slopeDirection");
- dukglue_register_property(ctx, &ScTileElement::isQueue_get, &ScTileElement::isQueue_set, "isQueue");
- dukglue_register_property(
- ctx, &ScTileElement::queueBannerDirection_get, &ScTileElement::queueBannerDirection_set,
- "queueBannerDirection");
- dukglue_register_property(ctx, &ScTileElement::queueBannerDirection_get, &ScTileElement::edges_set, "test");
-
- dukglue_register_property(
- ctx, &ScTileElement::isBlockedByVehicle_get, &ScTileElement::isBlockedByVehicle_set, "isBlockedByVehicle");
- dukglue_register_property(ctx, &ScTileElement::isWide_get, &ScTileElement::isWide_set, "isWide");
-
- dukglue_register_property(ctx, &ScTileElement::addition_get, &ScTileElement::addition_set, "addition");
- dukglue_register_property(
- ctx, &ScTileElement::additionStatus_get, &ScTileElement::additionStatus_set, "additionStatus");
- dukglue_register_property(
- ctx, &ScTileElement::isAdditionBroken_get, &ScTileElement::isAdditionBroken_set, "isAdditionBroken");
- dukglue_register_property(
- ctx, &ScTileElement::isAdditionGhost_get, &ScTileElement::isAdditionGhost_set, "isAdditionGhost");
-
- // Track only
- dukglue_register_property(ctx, &ScTileElement::trackType_get, &ScTileElement::trackType_set, "trackType");
- dukglue_register_property(ctx, &ScTileElement::mazeEntry_get, &ScTileElement::mazeEntry_set, "mazeEntry");
- dukglue_register_property(ctx, &ScTileElement::colourScheme_get, &ScTileElement::colourScheme_set, "colourScheme");
- dukglue_register_property(ctx, &ScTileElement::seatRotation_get, &ScTileElement::seatRotation_set, "seatRotation");
- dukglue_register_property(
- ctx, &ScTileElement::brakeBoosterSpeed_get, &ScTileElement::brakeBoosterSpeed_set, "brakeBoosterSpeed");
- 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");
-
- // Small Scenery only
- dukglue_register_property(ctx, &ScTileElement::age_get, &ScTileElement::age_set, "age");
- dukglue_register_property(ctx, &ScTileElement::quadrant_get, &ScTileElement::quadrant_set, "quadrant");
-
- // Wall only
- dukglue_register_property(
- ctx, &ScTileElement::tertiaryColour_get, &ScTileElement::tertiaryColour_set, "tertiaryColour");
-
- // Entrance only
- dukglue_register_property(
- ctx, &ScTileElement::footpathObject_get, &ScTileElement::footpathObject_set, "footpathObject");
- }
- };
-
class ScTile
{
private:
diff --git a/src/openrct2/scripting/bindings/world/ScTileElement.cpp b/src/openrct2/scripting/bindings/world/ScTileElement.cpp
new file mode 100644
index 0000000000..40251c55a4
--- /dev/null
+++ b/src/openrct2/scripting/bindings/world/ScTileElement.cpp
@@ -0,0 +1,1569 @@
+/*****************************************************************************
+ * Copyright (c) 2014-2021 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 "ScTileElement.hpp"
+
+# include "../../../Context.h"
+# include "../../../common.h"
+# include "../../../core/Guard.hpp"
+# include "../../../ride/Track.h"
+# include "../../../world/Footpath.h"
+# include "../../../world/Scenery.h"
+# include "../../../world/Sprite.h"
+# include "../../../world/Surface.h"
+# include "../../Duktape.hpp"
+# include "../../ScriptEngine.h"
+
+# include
+# include
+# include
+
+namespace OpenRCT2::Scripting
+{
+ ScTileElement::ScTileElement(const CoordsXY& coords, TileElement* element)
+ : _coords(coords)
+ , _element(element)
+ {
+ }
+
+ std::string ScTileElement::type_get() const
+ {
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_SURFACE:
+ return "surface";
+ case TILE_ELEMENT_TYPE_PATH:
+ return "footpath";
+ case TILE_ELEMENT_TYPE_TRACK:
+ return "track";
+ case TILE_ELEMENT_TYPE_SMALL_SCENERY:
+ return "small_scenery";
+ case TILE_ELEMENT_TYPE_ENTRANCE:
+ return "entrance";
+ case TILE_ELEMENT_TYPE_WALL:
+ return "wall";
+ case TILE_ELEMENT_TYPE_LARGE_SCENERY:
+ return "large_scenery";
+ case TILE_ELEMENT_TYPE_BANNER:
+ return "banner";
+ case TILE_ELEMENT_TYPE_CORRUPT:
+ return "openrct2_corrupt_deprecated";
+ default:
+ return "unknown";
+ }
+ }
+
+ void ScTileElement::type_set(std::string value)
+ {
+ auto type = _element->type;
+ if (value == "surface")
+ type = TILE_ELEMENT_TYPE_SURFACE;
+ else if (value == "footpath")
+ type = TILE_ELEMENT_TYPE_PATH;
+ else if (value == "track")
+ type = TILE_ELEMENT_TYPE_TRACK;
+ else if (value == "small_scenery")
+ type = TILE_ELEMENT_TYPE_SMALL_SCENERY;
+ else if (value == "entrance")
+ type = TILE_ELEMENT_TYPE_ENTRANCE;
+ else if (value == "wall")
+ type = TILE_ELEMENT_TYPE_WALL;
+ else if (value == "large_scenery")
+ type = TILE_ELEMENT_TYPE_LARGE_SCENERY;
+ else if (value == "banner")
+ type = TILE_ELEMENT_TYPE_BANNER;
+ else
+ {
+ if (value == "openrct2_corrupt_deprecated")
+ std::puts(
+ "Creation of new corrupt elements is deprecated. To hide elements, use the 'hidden' property instead.");
+ return;
+ }
+
+ _element->type = type;
+ Invalidate();
+ }
+
+ uint8_t ScTileElement::baseHeight_get() const
+ {
+ return _element->base_height;
+ }
+ void ScTileElement::baseHeight_set(uint8_t newBaseHeight)
+ {
+ ThrowIfGameStateNotMutable();
+ _element->base_height = newBaseHeight;
+ Invalidate();
+ }
+
+ uint16_t ScTileElement::baseZ_get() const
+ {
+ return _element->GetBaseZ();
+ }
+ void ScTileElement::baseZ_set(uint16_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ _element->SetBaseZ(value);
+ Invalidate();
+ }
+
+ uint8_t ScTileElement::clearanceHeight_get() const
+ {
+ return _element->clearance_height;
+ }
+ void ScTileElement::clearanceHeight_set(uint8_t newClearanceHeight)
+ {
+ ThrowIfGameStateNotMutable();
+ _element->clearance_height = newClearanceHeight;
+ Invalidate();
+ }
+
+ uint16_t ScTileElement::clearanceZ_get() const
+ {
+ return _element->GetClearanceZ();
+ }
+ void ScTileElement::clearanceZ_set(uint16_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ _element->SetClearanceZ(value);
+ Invalidate();
+ }
+
+ DukValue ScTileElement::slope_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_SURFACE:
+ {
+ auto el = _element->AsSurface();
+ duk_push_int(ctx, el->GetSlope());
+ break;
+ }
+ case TILE_ELEMENT_TYPE_WALL:
+ {
+ auto el = _element->AsWall();
+ duk_push_int(ctx, el->GetSlope());
+ break;
+ }
+ default:
+ {
+ duk_push_null(ctx);
+ break;
+ }
+ }
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::slope_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_SURFACE:
+ {
+ auto el = _element->AsSurface();
+ el->SetSlope(value);
+ Invalidate();
+ break;
+ }
+ case TILE_ELEMENT_TYPE_WALL:
+ {
+ auto el = _element->AsWall();
+ el->SetSlope(value);
+ Invalidate();
+ break;
+ }
+ }
+ }
+
+ DukValue ScTileElement::waterHeight_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsSurface();
+ if (el != nullptr)
+ duk_push_int(ctx, el->GetWaterHeight());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::waterHeight_set(int32_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsSurface();
+ if (el != nullptr)
+ {
+ el->SetWaterHeight(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::surfaceStyle_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsSurface();
+ if (el != nullptr)
+ duk_push_int(ctx, el->GetSurfaceStyle());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::surfaceStyle_set(uint32_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsSurface();
+ if (el != nullptr)
+ {
+ el->SetSurfaceStyle(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::edgeStyle_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsSurface();
+ if (el != nullptr)
+ duk_push_int(ctx, el->GetEdgeStyle());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::edgeStyle_set(uint32_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsSurface();
+ if (el != nullptr)
+ {
+ el->SetEdgeStyle(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::grassLength_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsSurface();
+ if (el != nullptr)
+ duk_push_int(ctx, el->GetGrassLength());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::grassLength_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsSurface();
+ if (el != nullptr)
+ {
+ // TODO: Give warning when value > GRASS_LENGTH_CLUMPS_2
+ el->SetGrassLengthAndInvalidate(value, _coords);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::hasOwnership_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsSurface();
+ if (el != nullptr)
+ duk_push_boolean(ctx, el->GetOwnership() & OWNERSHIP_OWNED);
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+
+ DukValue ScTileElement::hasConstructionRights_get()
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsSurface();
+ if (el != nullptr)
+ {
+ auto ownership = el->GetOwnership();
+ duk_push_boolean(ctx, (ownership & OWNERSHIP_OWNED) || (ownership & OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED));
+ }
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+
+ DukValue ScTileElement::ownership_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsSurface();
+ if (el != nullptr)
+ duk_push_int(ctx, el->GetOwnership());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::ownership_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsSurface();
+ if (el != nullptr)
+ {
+ el->SetOwnership(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::parkFences_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsSurface();
+ if (el != nullptr)
+ duk_push_int(ctx, el->GetParkFences());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::parkFences_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsSurface();
+ if (el != nullptr)
+ {
+ el->SetParkFences(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::trackType_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsTrack();
+ if (el != nullptr)
+ duk_push_int(ctx, el->GetTrackType());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::trackType_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsTrack();
+ if (el != nullptr)
+ {
+ el->SetTrackType(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::sequence_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_LARGE_SCENERY:
+ {
+ auto el = _element->AsLargeScenery();
+ duk_push_int(ctx, el->GetSequenceIndex());
+ break;
+ }
+ case TILE_ELEMENT_TYPE_TRACK:
+ {
+ auto el = _element->AsTrack();
+ if (get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
+ duk_push_int(ctx, el->GetSequenceIndex());
+ else
+ duk_push_null(ctx);
+ break;
+ }
+ case TILE_ELEMENT_TYPE_ENTRANCE:
+ {
+ auto el = _element->AsEntrance();
+ duk_push_int(ctx, el->GetSequenceIndex());
+ break;
+ }
+ default:
+ {
+ duk_push_null(ctx);
+ break;
+ }
+ }
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::sequence_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_LARGE_SCENERY:
+ {
+ auto el = _element->AsLargeScenery();
+ el->SetSequenceIndex(value);
+ Invalidate();
+ break;
+ }
+ case TILE_ELEMENT_TYPE_TRACK:
+ {
+ auto el = _element->AsTrack();
+ if (get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
+ {
+ el->SetSequenceIndex(value);
+ Invalidate();
+ }
+ break;
+ }
+ case TILE_ELEMENT_TYPE_ENTRANCE:
+ {
+ auto el = _element->AsEntrance();
+ el->SetSequenceIndex(value);
+ Invalidate();
+ break;
+ }
+ }
+ }
+
+ DukValue ScTileElement::ride_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_PATH:
+ {
+ auto el = _element->AsPath();
+ if (el->IsQueue() && el->GetRideIndex() != RIDE_ID_NULL)
+ duk_push_int(ctx, el->GetRideIndex());
+ else
+ duk_push_null(ctx);
+ break;
+ }
+ case TILE_ELEMENT_TYPE_TRACK:
+ {
+ auto el = _element->AsTrack();
+ duk_push_int(ctx, el->GetRideIndex());
+ break;
+ }
+ case TILE_ELEMENT_TYPE_ENTRANCE:
+ {
+ auto el = _element->AsEntrance();
+ duk_push_int(ctx, el->GetRideIndex());
+ break;
+ }
+ default:
+ {
+ duk_push_null(ctx);
+ break;
+ }
+ }
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::ride_set(ride_id_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_PATH:
+ {
+ auto el = _element->AsPath();
+ if (!el->HasAddition())
+ {
+ el->SetRideIndex(value);
+ Invalidate();
+ }
+ break;
+ }
+ case TILE_ELEMENT_TYPE_TRACK:
+ {
+ auto el = _element->AsTrack();
+ el->SetRideIndex(value);
+ Invalidate();
+ break;
+ }
+ case TILE_ELEMENT_TYPE_ENTRANCE:
+ {
+ auto el = _element->AsEntrance();
+ el->SetRideIndex(value);
+ Invalidate();
+ break;
+ }
+ }
+ }
+
+ DukValue ScTileElement::station_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_PATH:
+ {
+ auto el = _element->AsPath();
+ if (el->IsQueue() && el->GetRideIndex() != RIDE_ID_NULL)
+ duk_push_int(ctx, el->GetStationIndex());
+ else
+ duk_push_null(ctx);
+ break;
+ }
+ case TILE_ELEMENT_TYPE_TRACK:
+ {
+ auto el = _element->AsTrack();
+ if (el->IsStation())
+ duk_push_int(ctx, el->GetStationIndex());
+ else
+ duk_push_null(ctx);
+ break;
+ }
+ case TILE_ELEMENT_TYPE_ENTRANCE:
+ {
+ auto el = _element->AsEntrance();
+ duk_push_int(ctx, el->GetStationIndex());
+ break;
+ }
+ default:
+ {
+ duk_push_null(ctx);
+ break;
+ }
+ }
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::station_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_PATH:
+ {
+ auto el = _element->AsPath();
+ el->SetStationIndex(value);
+ Invalidate();
+ break;
+ }
+ case TILE_ELEMENT_TYPE_TRACK:
+ {
+ auto el = _element->AsTrack();
+ el->SetStationIndex(value);
+ Invalidate();
+ break;
+ }
+ case TILE_ELEMENT_TYPE_ENTRANCE:
+ {
+ auto el = _element->AsEntrance();
+ el->SetStationIndex(value);
+ Invalidate();
+ break;
+ }
+ }
+ }
+
+ DukValue ScTileElement::hasChainLift_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsTrack();
+ if (el != nullptr)
+ duk_push_boolean(ctx, el->HasChain());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::hasChainLift_set(bool value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsTrack();
+ if (el != nullptr)
+ {
+ el->SetHasChain(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::mazeEntry_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsTrack();
+ if (el != nullptr && get_ride(el->GetRideIndex())->type == RIDE_TYPE_MAZE)
+ duk_push_int(ctx, el->GetMazeEntry());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::mazeEntry_set(uint16_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsTrack();
+ if (el != nullptr)
+ if (get_ride(el->GetRideIndex())->type == RIDE_TYPE_MAZE)
+ {
+ el->SetMazeEntry(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::colourScheme_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsTrack();
+ if (el != nullptr && get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
+ duk_push_int(ctx, el->GetColourScheme());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::colourScheme_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsTrack();
+ if (el != nullptr)
+ if (get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
+ {
+ el->SetColourScheme(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::seatRotation_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsTrack();
+ if (el != nullptr && get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
+ duk_push_int(ctx, el->GetSeatRotation());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::seatRotation_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsTrack();
+ if (el != nullptr)
+ if (get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
+ {
+ el->SetSeatRotation(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::brakeBoosterSpeed_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsTrack();
+ if (el != nullptr && TrackTypeHasSpeedSetting(el->GetTrackType()))
+ duk_push_int(ctx, el->GetBrakeBoosterSpeed());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::brakeBoosterSpeed_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsTrack();
+ if (el != nullptr)
+ if (TrackTypeHasSpeedSetting(el->GetTrackType()))
+ {
+ el->SetBrakeBoosterSpeed(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::isInverted_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsTrack();
+ if (el != nullptr)
+ duk_push_boolean(ctx, el->IsInverted());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::isInverted_set(bool value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsTrack();
+ if (el != nullptr)
+ {
+ el->SetInverted(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::hasCableLift_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsTrack();
+ if (el != nullptr)
+ duk_push_boolean(ctx, el->HasCableLift());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::hasCableLift_set(bool value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsTrack();
+ if (el != nullptr)
+ {
+ el->SetHasCableLift(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::object_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_PATH:
+ {
+ auto el = _element->AsPath();
+ duk_push_int(ctx, el->GetSurfaceEntryIndex());
+ break;
+ }
+ case TILE_ELEMENT_TYPE_SMALL_SCENERY:
+ {
+ auto el = _element->AsSmallScenery();
+ duk_push_int(ctx, el->GetEntryIndex());
+ break;
+ }
+ case TILE_ELEMENT_TYPE_LARGE_SCENERY:
+ {
+ auto el = _element->AsLargeScenery();
+ duk_push_int(ctx, el->GetEntryIndex());
+ break;
+ }
+ case TILE_ELEMENT_TYPE_WALL:
+ {
+ auto el = _element->AsWall();
+ duk_push_int(ctx, el->GetEntryIndex());
+ break;
+ }
+ case TILE_ELEMENT_TYPE_ENTRANCE:
+ {
+ auto el = _element->AsEntrance();
+ duk_push_int(ctx, el->GetEntranceType());
+ break;
+ }
+ default:
+ {
+ duk_push_null(ctx);
+ break;
+ }
+ }
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::object_set(uint32_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_PATH:
+ {
+ auto el = _element->AsPath();
+ el->SetSurfaceEntryIndex(value & 0xFF);
+ Invalidate();
+ break;
+ }
+ case TILE_ELEMENT_TYPE_SMALL_SCENERY:
+ {
+ auto el = _element->AsSmallScenery();
+ el->SetEntryIndex(value & 0xFF);
+ Invalidate();
+ break;
+ }
+ case TILE_ELEMENT_TYPE_LARGE_SCENERY:
+ {
+ auto el = _element->AsLargeScenery();
+ el->SetEntryIndex(value);
+ Invalidate();
+ break;
+ }
+ case TILE_ELEMENT_TYPE_WALL:
+ {
+ auto el = _element->AsWall();
+ el->SetEntryIndex(value & 0xFFFF);
+ Invalidate();
+ break;
+ }
+ case TILE_ELEMENT_TYPE_ENTRANCE:
+ {
+ auto el = _element->AsEntrance();
+ el->SetEntranceType(value & 0xFF);
+ Invalidate();
+ break;
+ }
+ }
+ }
+
+ bool ScTileElement::isHidden_get() const
+ {
+ // TODO: Simply return the 'hidden' field once corrupt elements are superseded.
+ const TileElement* element = map_get_first_element_at(_coords);
+ bool previousElementWasUsefulCorrupt = false;
+ do
+ {
+ if (element == _element)
+ return previousElementWasUsefulCorrupt;
+
+ if (element->GetType() == TILE_ELEMENT_TYPE_CORRUPT)
+ previousElementWasUsefulCorrupt = !previousElementWasUsefulCorrupt;
+ else
+ previousElementWasUsefulCorrupt = false;
+ } while (!(element++)->IsLastForTile());
+
+ Guard::Assert(false);
+ return false;
+ }
+ void ScTileElement::isHidden_set(bool hide)
+ {
+ // TODO: Simply update the 'hidden' field once corrupt elements are superseded.
+ ThrowIfGameStateNotMutable();
+ const bool isHidden = isHidden_get();
+ if (hide == isHidden)
+ return;
+
+ if (hide)
+ {
+ // Get index of our current element (has to be done now before inserting the corrupt element)
+ const auto elementIndex = _element - map_get_first_element_at(_coords);
+
+ // Insert corrupt element at the end of the list for this tile
+ // Note: Z = MAX_ELEMENT_HEIGHT to guarantee this
+ TileElement* insertedElement = tile_element_insert(
+ { _coords, MAX_ELEMENT_HEIGHT * COORDS_Z_STEP }, 0, TileElementType::Corrupt);
+ if (insertedElement == nullptr)
+ {
+ // TODO: Show error
+ return;
+ }
+
+ // Since inserting a new element may move the tile elements in memory, we have to update the local pointer
+ _element = map_get_first_element_at(_coords) + elementIndex;
+
+ // Move the corrupt element down in the list until it's right under our element
+ while (insertedElement > _element)
+ {
+ std::swap(*insertedElement, *(insertedElement - 1));
+ insertedElement--;
+
+ // Un-swap the last-for-tile flag
+ if (insertedElement->IsLastForTile())
+ {
+ insertedElement->SetLastForTile(false);
+ (insertedElement + 1)->SetLastForTile(true);
+ }
+ }
+
+ // Now the corrupt element took the hidden element's place, increment it by one
+ _element++;
+
+ // Update base and clearance heights of inserted corrupt element to match the element to hide
+ insertedElement->base_height = insertedElement->clearance_height = _element->base_height;
+ }
+ else
+ {
+ TileElement* const elementToRemove = _element - 1;
+ Guard::Assert(elementToRemove->GetType() == TILE_ELEMENT_TYPE_CORRUPT);
+ tile_element_remove(elementToRemove);
+ _element--;
+ }
+
+ Invalidate();
+ }
+
+ DukValue ScTileElement::age_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsSmallScenery();
+ if (el != nullptr)
+ duk_push_int(ctx, el->GetAge());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::age_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsSmallScenery();
+ if (el != nullptr)
+ {
+ el->SetAge(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::quadrant_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsSmallScenery();
+ if (el != nullptr)
+ duk_push_int(ctx, el->GetSceneryQuadrant());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::quadrant_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsSmallScenery();
+ if (el != nullptr)
+ {
+ el->SetSceneryQuadrant(value);
+ Invalidate();
+ }
+ }
+
+ uint8_t ScTileElement::occupiedQuadrants_get() const
+ {
+ return _element->GetOccupiedQuadrants();
+ }
+ void ScTileElement::occupiedQuadrants_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ _element->SetOccupiedQuadrants(value);
+ Invalidate();
+ }
+
+ bool ScTileElement::isGhost_get() const
+ {
+ return _element->IsGhost();
+ }
+ void ScTileElement::isGhost_set(bool value)
+ {
+ ThrowIfGameStateNotMutable();
+ _element->SetGhost(value);
+ Invalidate();
+ }
+
+ DukValue ScTileElement::primaryColour_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_SMALL_SCENERY:
+ {
+ auto el = _element->AsSmallScenery();
+ duk_push_int(ctx, el->GetPrimaryColour());
+ break;
+ }
+ case TILE_ELEMENT_TYPE_LARGE_SCENERY:
+ {
+ auto el = _element->AsLargeScenery();
+ duk_push_int(ctx, el->GetPrimaryColour());
+ break;
+ }
+ case TILE_ELEMENT_TYPE_WALL:
+ {
+ auto el = _element->AsWall();
+ duk_push_int(ctx, el->GetPrimaryColour());
+ break;
+ }
+ default:
+ {
+ duk_push_null(ctx);
+ break;
+ }
+ }
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::primaryColour_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_SMALL_SCENERY:
+ {
+ auto el = _element->AsSmallScenery();
+ el->SetPrimaryColour(value);
+ Invalidate();
+ break;
+ }
+ case TILE_ELEMENT_TYPE_LARGE_SCENERY:
+ {
+ auto el = _element->AsLargeScenery();
+ el->SetPrimaryColour(value);
+ Invalidate();
+ break;
+ }
+ case TILE_ELEMENT_TYPE_WALL:
+ {
+ auto el = _element->AsWall();
+ el->SetPrimaryColour(value);
+ Invalidate();
+ break;
+ }
+ }
+ }
+
+ DukValue ScTileElement::secondaryColour_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_SMALL_SCENERY:
+ {
+ auto el = _element->AsSmallScenery();
+ duk_push_int(ctx, el->GetSecondaryColour());
+ break;
+ }
+ case TILE_ELEMENT_TYPE_LARGE_SCENERY:
+ {
+ auto el = _element->AsLargeScenery();
+ duk_push_int(ctx, el->GetSecondaryColour());
+ break;
+ }
+ case TILE_ELEMENT_TYPE_WALL:
+ {
+ auto el = _element->AsWall();
+ duk_push_int(ctx, el->GetSecondaryColour());
+ break;
+ }
+ default:
+ {
+ duk_push_null(ctx);
+ break;
+ }
+ }
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::secondaryColour_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_SMALL_SCENERY:
+ {
+ auto el = _element->AsSmallScenery();
+ el->SetSecondaryColour(value);
+ Invalidate();
+ break;
+ }
+ case TILE_ELEMENT_TYPE_LARGE_SCENERY:
+ {
+ auto el = _element->AsLargeScenery();
+ el->SetSecondaryColour(value);
+ Invalidate();
+ break;
+ }
+ case TILE_ELEMENT_TYPE_WALL:
+ {
+ auto el = _element->AsWall();
+ el->SetSecondaryColour(value);
+ Invalidate();
+ break;
+ }
+ }
+ }
+
+ DukValue ScTileElement::tertiaryColour_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsWall();
+ if (el != nullptr)
+ duk_push_int(ctx, el->GetTertiaryColour());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::tertiaryColour_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsWall();
+ if (el != nullptr)
+ {
+ el->SetTertiaryColour(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::bannerIndex_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ BannerIndex idx = _element->GetBannerIndex();
+ if (idx == BANNER_INDEX_NULL)
+ duk_push_null(ctx);
+ else
+ duk_push_int(ctx, idx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::bannerIndex_set(uint16_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_LARGE_SCENERY:
+ {
+ auto el = _element->AsLargeScenery();
+ el->SetBannerIndex(value);
+ Invalidate();
+ break;
+ }
+ case TILE_ELEMENT_TYPE_WALL:
+ {
+ auto el = _element->AsWall();
+ el->SetBannerIndex(value);
+ Invalidate();
+ break;
+ }
+ case TILE_ELEMENT_TYPE_BANNER:
+ {
+ auto el = _element->AsBanner();
+ el->SetIndex(value);
+ Invalidate();
+ break;
+ }
+ }
+ }
+
+ // Deprecated in favor of seperate 'edges' and 'corners' properties,
+ // left here to maintain compatibility with older plugins.
+ /** @deprecated */
+ uint8_t ScTileElement::edgesAndCorners_get() const
+ {
+ auto el = _element->AsPath();
+ return el != nullptr ? el->GetEdgesAndCorners() : 0;
+ }
+ /** @deprecated */
+ void ScTileElement::edgesAndCorners_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsPath();
+ if (el != nullptr)
+ {
+ el->SetEdgesAndCorners(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::edges_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsPath();
+ if (el != nullptr)
+ duk_push_int(ctx, el->GetEdges());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::edges_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsPath();
+ if (el != nullptr)
+ {
+ el->SetEdges(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::corners_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsPath();
+ if (el != nullptr)
+ duk_push_int(ctx, el->GetCorners());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::corners_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsPath();
+ if (el != nullptr)
+ {
+ el->SetCorners(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::slopeDirection_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsPath();
+ if (el != nullptr && el->IsSloped())
+ duk_push_int(ctx, el->GetSlopeDirection());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::slopeDirection_set(const DukValue& value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsPath();
+ if (el != nullptr)
+ {
+ if (value.type() == DukValue::Type::NUMBER)
+ {
+ el->SetSloped(true);
+ el->SetSlopeDirection(value.as_int());
+ }
+ else
+ {
+ el->SetSloped(false);
+ el->SetSlopeDirection(0);
+ }
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::isQueue_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsPath();
+ if (el != nullptr)
+ duk_push_boolean(ctx, el->IsQueue());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::isQueue_set(bool value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsPath();
+ if (el != nullptr)
+ {
+ el->SetIsQueue(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::queueBannerDirection_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsPath();
+ if (el != nullptr && el->HasQueueBanner())
+ duk_push_int(ctx, el->GetQueueBannerDirection());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::queueBannerDirection_set(const DukValue& value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsPath();
+ if (el != nullptr)
+ {
+ if (value.type() == DukValue::Type::NUMBER)
+ {
+ el->SetHasQueueBanner(true);
+ el->SetQueueBannerDirection(value.as_int());
+ }
+ else
+ {
+ el->SetHasQueueBanner(false);
+ el->SetQueueBannerDirection(0);
+ }
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::isBlockedByVehicle_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsPath();
+ if (el != nullptr)
+ duk_push_boolean(ctx, el->IsBlockedByVehicle());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::isBlockedByVehicle_set(bool value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsPath();
+ if (el != nullptr)
+ {
+ el->SetIsBlockedByVehicle(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::isWide_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsPath();
+ if (el != nullptr)
+ duk_push_boolean(ctx, el->IsWide());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::isWide_set(bool value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsPath();
+ if (el != nullptr)
+ {
+ el->SetWide(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::addition_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsPath();
+ if (el != nullptr && el->HasAddition())
+ duk_push_int(ctx, el->GetAddition() - 1);
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::addition_set(const DukValue& value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsPath();
+ if (el != nullptr)
+ {
+ if (value.type() == DukValue::Type::NUMBER)
+ {
+ auto addition = value.as_int();
+ if (addition >= 0 && addition <= 254)
+ {
+ el->SetAddition(addition + 1);
+ }
+ }
+ else
+ {
+ el->SetAddition(0);
+ }
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::additionStatus_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsPath();
+ if (el != nullptr && el->HasAddition())
+ duk_push_int(ctx, el->GetAdditionStatus());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::additionStatus_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsPath();
+ if (el != nullptr)
+ if (el->HasAddition())
+ {
+ el->SetAdditionStatus(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::isAdditionBroken_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsPath();
+ if (el != nullptr && el->HasAddition())
+ duk_push_boolean(ctx, el->IsBroken());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::isAdditionBroken_set(bool value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsPath();
+ if (el != nullptr)
+ {
+ el->SetIsBroken(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::isAdditionGhost_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsPath();
+ if (el != nullptr && el->HasAddition())
+ duk_push_boolean(ctx, el->AdditionIsGhost());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::isAdditionGhost_set(bool value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsPath();
+ if (el != nullptr)
+ {
+ el->SetAdditionIsGhost(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::footpathObject_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ auto el = _element->AsEntrance();
+ if (el != nullptr)
+ duk_push_int(ctx, el->GetPathType());
+ else
+ duk_push_null(ctx);
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::footpathObject_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto el = _element->AsEntrance();
+ if (el != nullptr)
+ {
+ el->SetPathType(value);
+ Invalidate();
+ }
+ }
+
+ DukValue ScTileElement::direction_get() const
+ {
+ auto ctx = GetContext()->GetScriptEngine().GetContext();
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_BANNER:
+ {
+ auto el = _element->AsBanner();
+ duk_push_int(ctx, el->GetPosition());
+ break;
+ }
+ case TILE_ELEMENT_TYPE_PATH:
+ case TILE_ELEMENT_TYPE_SURFACE:
+ {
+ duk_push_null(ctx);
+ break;
+ }
+ default:
+ {
+ duk_push_int(ctx, _element->GetDirection());
+ break;
+ }
+ }
+ return DukValue::take_from_stack(ctx);
+ }
+ void ScTileElement::direction_set(uint8_t value)
+ {
+ ThrowIfGameStateNotMutable();
+ switch (_element->GetType())
+ {
+ case TILE_ELEMENT_TYPE_BANNER:
+ {
+ auto el = _element->AsBanner();
+ el->SetPosition(value);
+ Invalidate();
+ break;
+ }
+ case TILE_ELEMENT_TYPE_PATH:
+ case TILE_ELEMENT_TYPE_SURFACE:
+ {
+ break;
+ }
+ default:
+ {
+ _element->SetDirection(value);
+ Invalidate();
+ }
+ }
+ }
+
+ void ScTileElement::Invalidate()
+ {
+ map_invalidate_tile_full(_coords);
+ }
+
+ void ScTileElement::Register(duk_context* ctx)
+ {
+ // All
+ dukglue_register_property(ctx, &ScTileElement::type_get, &ScTileElement::type_set, "type");
+ dukglue_register_property(ctx, &ScTileElement::baseHeight_get, &ScTileElement::baseHeight_set, "baseHeight");
+ dukglue_register_property(ctx, &ScTileElement::baseZ_get, &ScTileElement::baseZ_set, "baseZ");
+ dukglue_register_property(
+ ctx, &ScTileElement::clearanceHeight_get, &ScTileElement::clearanceHeight_set, "clearanceHeight");
+ dukglue_register_property(ctx, &ScTileElement::clearanceZ_get, &ScTileElement::clearanceZ_set, "clearanceZ");
+ dukglue_register_property(
+ ctx, &ScTileElement::occupiedQuadrants_get, &ScTileElement::occupiedQuadrants_set, "occupiedQuadrants");
+ dukglue_register_property(ctx, &ScTileElement::isGhost_get, &ScTileElement::isGhost_set, "isGhost");
+ dukglue_register_property(ctx, &ScTileElement::isHidden_get, &ScTileElement::isHidden_set, "isHidden");
+
+ // Track | Small Scenery | Wall | Entrance | Large Scenery | Banner
+ dukglue_register_property(ctx, &ScTileElement::direction_get, &ScTileElement::direction_set, "direction");
+
+ // Path | Small Scenery | Wall | Entrance | Large Scenery
+ dukglue_register_property(ctx, &ScTileElement::object_get, &ScTileElement::object_set, "object");
+
+ // Small Scenery | Wall | Large Scenery
+ dukglue_register_property(ctx, &ScTileElement::primaryColour_get, &ScTileElement::primaryColour_set, "primaryColour");
+ dukglue_register_property(
+ ctx, &ScTileElement::secondaryColour_get, &ScTileElement::secondaryColour_set, "secondaryColour");
+
+ // Wall | Large Scenery | Banner
+ dukglue_register_property(ctx, &ScTileElement::bannerIndex_get, &ScTileElement::bannerIndex_set, "bannerIndex");
+
+ // Path | Track | Entrance
+ dukglue_register_property(ctx, &ScTileElement::ride_get, &ScTileElement::ride_set, "ride");
+ dukglue_register_property(ctx, &ScTileElement::station_get, &ScTileElement::station_set, "station");
+
+ // Track | Entrance | Large Scenery
+ dukglue_register_property(ctx, &ScTileElement::sequence_get, &ScTileElement::sequence_set, "sequence");
+
+ // Surface | Wall
+ dukglue_register_property(ctx, &ScTileElement::slope_get, &ScTileElement::slope_set, "slope");
+
+ // Surface only
+ dukglue_register_property(ctx, &ScTileElement::waterHeight_get, &ScTileElement::waterHeight_set, "waterHeight");
+ dukglue_register_property(ctx, &ScTileElement::surfaceStyle_get, &ScTileElement::surfaceStyle_set, "surfaceStyle");
+ dukglue_register_property(ctx, &ScTileElement::edgeStyle_get, &ScTileElement::edgeStyle_set, "edgeStyle");
+ dukglue_register_property(ctx, &ScTileElement::grassLength_get, &ScTileElement::grassLength_set, "grassLength");
+ dukglue_register_property(ctx, &ScTileElement::hasOwnership_get, nullptr, "hasOwnership");
+ dukglue_register_property(ctx, &ScTileElement::hasConstructionRights_get, nullptr, "hasConstructionRights");
+ dukglue_register_property(ctx, &ScTileElement::ownership_get, &ScTileElement::ownership_set, "ownership");
+ dukglue_register_property(ctx, &ScTileElement::parkFences_get, &ScTileElement::parkFences_set, "parkFences");
+
+ // Footpath only
+ dukglue_register_property(
+ ctx, &ScTileElement::edgesAndCorners_get, &ScTileElement::edgesAndCorners_set, "edgesAndCorners");
+ dukglue_register_property(ctx, &ScTileElement::edges_get, &ScTileElement::edges_set, "edges");
+ dukglue_register_property(ctx, &ScTileElement::corners_get, &ScTileElement::corners_set, "corners");
+ dukglue_register_property(
+ ctx, &ScTileElement::slopeDirection_get, &ScTileElement::slopeDirection_set, "slopeDirection");
+ dukglue_register_property(ctx, &ScTileElement::isQueue_get, &ScTileElement::isQueue_set, "isQueue");
+ dukglue_register_property(
+ ctx, &ScTileElement::queueBannerDirection_get, &ScTileElement::queueBannerDirection_set, "queueBannerDirection");
+ dukglue_register_property(ctx, &ScTileElement::queueBannerDirection_get, &ScTileElement::edges_set, "test");
+
+ dukglue_register_property(
+ ctx, &ScTileElement::isBlockedByVehicle_get, &ScTileElement::isBlockedByVehicle_set, "isBlockedByVehicle");
+ dukglue_register_property(ctx, &ScTileElement::isWide_get, &ScTileElement::isWide_set, "isWide");
+
+ dukglue_register_property(ctx, &ScTileElement::addition_get, &ScTileElement::addition_set, "addition");
+ dukglue_register_property(
+ ctx, &ScTileElement::additionStatus_get, &ScTileElement::additionStatus_set, "additionStatus");
+ dukglue_register_property(
+ ctx, &ScTileElement::isAdditionBroken_get, &ScTileElement::isAdditionBroken_set, "isAdditionBroken");
+ dukglue_register_property(
+ ctx, &ScTileElement::isAdditionGhost_get, &ScTileElement::isAdditionGhost_set, "isAdditionGhost");
+
+ // Track only
+ dukglue_register_property(ctx, &ScTileElement::trackType_get, &ScTileElement::trackType_set, "trackType");
+ dukglue_register_property(ctx, &ScTileElement::mazeEntry_get, &ScTileElement::mazeEntry_set, "mazeEntry");
+ dukglue_register_property(ctx, &ScTileElement::colourScheme_get, &ScTileElement::colourScheme_set, "colourScheme");
+ dukglue_register_property(ctx, &ScTileElement::seatRotation_get, &ScTileElement::seatRotation_set, "seatRotation");
+ dukglue_register_property(
+ ctx, &ScTileElement::brakeBoosterSpeed_get, &ScTileElement::brakeBoosterSpeed_set, "brakeBoosterSpeed");
+ 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");
+
+ // Small Scenery only
+ dukglue_register_property(ctx, &ScTileElement::age_get, &ScTileElement::age_set, "age");
+ dukglue_register_property(ctx, &ScTileElement::quadrant_get, &ScTileElement::quadrant_set, "quadrant");
+
+ // Wall only
+ dukglue_register_property(
+ ctx, &ScTileElement::tertiaryColour_get, &ScTileElement::tertiaryColour_set, "tertiaryColour");
+
+ // Entrance only
+ dukglue_register_property(
+ ctx, &ScTileElement::footpathObject_get, &ScTileElement::footpathObject_set, "footpathObject");
+ }
+
+} // namespace OpenRCT2::Scripting
+
+#endif
diff --git a/src/openrct2/scripting/bindings/world/ScTileElement.hpp b/src/openrct2/scripting/bindings/world/ScTileElement.hpp
new file mode 100644
index 0000000000..9b0fcd7060
--- /dev/null
+++ b/src/openrct2/scripting/bindings/world/ScTileElement.hpp
@@ -0,0 +1,198 @@
+/*****************************************************************************
+ * Copyright (c) 2014-2021 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 "../../../Context.h"
+# include "../../../common.h"
+# include "../../../core/Guard.hpp"
+# include "../../../ride/Track.h"
+# include "../../../world/Footpath.h"
+# include "../../../world/Scenery.h"
+# include "../../../world/Sprite.h"
+# include "../../../world/Surface.h"
+# include "../../Duktape.hpp"
+# include "../../ScriptEngine.h"
+
+# include
+# include
+# include
+
+namespace OpenRCT2::Scripting
+{
+ class ScTileElement
+ {
+ protected:
+ CoordsXY _coords;
+ TileElement* _element;
+
+ public:
+ ScTileElement(const CoordsXY& coords, TileElement* element);
+
+ private:
+ std::string type_get() const;
+ void type_set(std::string value);
+
+ uint8_t baseHeight_get() const;
+ void baseHeight_set(uint8_t newBaseHeight);
+
+ uint16_t baseZ_get() const;
+ void baseZ_set(uint16_t value);
+
+ uint8_t clearanceHeight_get() const;
+ void clearanceHeight_set(uint8_t newClearanceHeight);
+
+ uint16_t clearanceZ_get() const;
+ void clearanceZ_set(uint16_t value);
+
+ DukValue slope_get() const;
+ void slope_set(uint8_t value);
+
+ DukValue waterHeight_get() const;
+ void waterHeight_set(int32_t value);
+
+ DukValue surfaceStyle_get() const;
+ void surfaceStyle_set(uint32_t value);
+
+ DukValue edgeStyle_get() const;
+ void edgeStyle_set(uint32_t value);
+
+ DukValue grassLength_get() const;
+ void grassLength_set(uint8_t value);
+
+ DukValue hasOwnership_get() const;
+
+ DukValue hasConstructionRights_get();
+
+ DukValue ownership_get() const;
+ void ownership_set(uint8_t value);
+
+ DukValue parkFences_get() const;
+ void parkFences_set(uint8_t value);
+
+ DukValue trackType_get() const;
+ void trackType_set(uint8_t value);
+
+ DukValue sequence_get() const;
+ void sequence_set(uint8_t value);
+
+ DukValue ride_get() const;
+ void ride_set(ride_id_t value);
+
+ DukValue station_get() const;
+ void station_set(uint8_t value);
+
+ DukValue hasChainLift_get() const;
+ void hasChainLift_set(bool value);
+
+ DukValue mazeEntry_get() const;
+ void mazeEntry_set(uint16_t value);
+
+ DukValue colourScheme_get() const;
+ void colourScheme_set(uint8_t value);
+
+ DukValue seatRotation_get() const;
+ void seatRotation_set(uint8_t value);
+
+ DukValue brakeBoosterSpeed_get() const;
+ void brakeBoosterSpeed_set(uint8_t value);
+
+ DukValue isInverted_get() const;
+ void isInverted_set(bool value);
+
+ DukValue hasCableLift_get() const;
+ void hasCableLift_set(bool value);
+
+ DukValue object_get() const;
+ void object_set(uint32_t value);
+
+ bool isHidden_get() const;
+ void isHidden_set(bool hide);
+
+ DukValue age_get() const;
+ void age_set(uint8_t value);
+
+ DukValue quadrant_get() const;
+ void quadrant_set(uint8_t value);
+
+ uint8_t occupiedQuadrants_get() const;
+ void occupiedQuadrants_set(uint8_t value);
+
+ bool isGhost_get() const;
+ void isGhost_set(bool value);
+
+ DukValue primaryColour_get() const;
+ void primaryColour_set(uint8_t value);
+
+ DukValue secondaryColour_get() const;
+ void secondaryColour_set(uint8_t value);
+
+ DukValue tertiaryColour_get() const;
+ void tertiaryColour_set(uint8_t value);
+
+ DukValue bannerIndex_get() const;
+ void bannerIndex_set(uint16_t value);
+
+ // Deprecated in favor of seperate 'edges' and 'corners' properties,
+ // left here to maintain compatibility with older plugins.
+ /** @deprecated */
+ uint8_t edgesAndCorners_get() const;
+ /** @deprecated */
+ void edgesAndCorners_set(uint8_t value);
+
+ DukValue edges_get() const;
+ void edges_set(uint8_t value);
+
+ DukValue corners_get() const;
+ void corners_set(uint8_t value);
+
+ DukValue slopeDirection_get() const;
+ void slopeDirection_set(const DukValue& value);
+
+ DukValue isQueue_get() const;
+ void isQueue_set(bool value);
+
+ DukValue queueBannerDirection_get() const;
+ void queueBannerDirection_set(const DukValue& value);
+
+ DukValue isBlockedByVehicle_get() const;
+ void isBlockedByVehicle_set(bool value);
+
+ DukValue isWide_get() const;
+ void isWide_set(bool value);
+
+ DukValue addition_get() const;
+ void addition_set(const DukValue& value);
+
+ DukValue additionStatus_get() const;
+ void additionStatus_set(uint8_t value);
+
+ DukValue isAdditionBroken_get() const;
+ void isAdditionBroken_set(bool value);
+
+ DukValue isAdditionGhost_get() const;
+ void isAdditionGhost_set(bool value);
+
+ DukValue footpathObject_get() const;
+ void footpathObject_set(uint8_t value);
+
+ DukValue direction_get() const;
+ void direction_set(uint8_t value);
+
+ void Invalidate();
+
+ public:
+ static void Register(duk_context* ctx);
+ };
+
+} // namespace OpenRCT2::Scripting
+
+#endif