diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj
index 67c5d17b36..7029514897 100644
--- a/src/openrct2/libopenrct2.vcxproj
+++ b/src/openrct2/libopenrct2.vcxproj
@@ -871,6 +871,7 @@
+
diff --git a/src/openrct2/scripting/bindings/world/ScTile.cpp b/src/openrct2/scripting/bindings/world/ScTile.cpp
new file mode 100644
index 0000000000..28d4d2751b
--- /dev/null
+++ b/src/openrct2/scripting/bindings/world/ScTile.cpp
@@ -0,0 +1,243 @@
+/*****************************************************************************
+ * 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 "ScTile.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 "ScTileElement.hpp"
+
+# include
+# include
+# include
+
+namespace OpenRCT2::Scripting
+{
+ ScTile::ScTile(const CoordsXY& coords)
+ : _coords(coords)
+ {
+ }
+
+ int32_t ScTile::x_get() const
+ {
+ return _coords.x / COORDS_XY_STEP;
+ }
+
+ int32_t ScTile::y_get() const
+ {
+ return _coords.y / COORDS_XY_STEP;
+ }
+
+ uint32_t ScTile::numElements_get() const
+ {
+ auto first = GetFirstElement();
+ return static_cast(GetNumElements(first));
+ }
+
+ std::vector> ScTile::elements_get() const
+ {
+ std::vector> result;
+ auto first = GetFirstElement();
+ auto currentNumElements = GetNumElements(first);
+ if (currentNumElements != 0)
+ {
+ result.reserve(currentNumElements);
+ for (size_t i = 0; i < currentNumElements; i++)
+ {
+ result.push_back(std::make_shared(_coords, &first[i]));
+ }
+ }
+ return result;
+ }
+
+ DukValue ScTile::data_get() const
+ {
+ auto ctx = GetDukContext();
+ auto first = map_get_first_element_at(_coords);
+ auto dataLen = GetNumElements(first) * sizeof(TileElement);
+ auto data = duk_push_fixed_buffer(ctx, dataLen);
+ if (first != nullptr)
+ {
+ std::memcpy(data, first, dataLen);
+ }
+ duk_push_buffer_object(ctx, -1, 0, dataLen, DUK_BUFOBJ_UINT8ARRAY);
+ return DukValue::take_from_stack(ctx);
+ }
+
+ void ScTile::data_set(DukValue value)
+ {
+ ThrowIfGameStateNotMutable();
+ auto ctx = value.context();
+ value.push();
+ if (duk_is_buffer_data(ctx, -1))
+ {
+ duk_size_t dataLen{};
+ auto data = duk_get_buffer_data(ctx, -1, &dataLen);
+ auto numElements = dataLen / sizeof(TileElement);
+ if (numElements == 0)
+ {
+ map_set_tile_element(TileCoordsXY(_coords), nullptr);
+ }
+ else
+ {
+ auto first = GetFirstElement();
+ auto currentNumElements = GetNumElements(first);
+ if (numElements > currentNumElements)
+ {
+ // Allocate space for the extra tile elements (inefficient but works)
+ auto pos = TileCoordsXYZ(TileCoordsXY(_coords), 0).ToCoordsXYZ();
+ auto numToInsert = numElements - currentNumElements;
+ for (size_t i = 0; i < numToInsert; i++)
+ {
+ tile_element_insert(pos, 0, TileElementType::Surface);
+ }
+
+ // Copy data to element span
+ first = map_get_first_element_at(_coords);
+ currentNumElements = GetNumElements(first);
+ if (currentNumElements != 0)
+ {
+ std::memcpy(first, data, currentNumElements * sizeof(TileElement));
+ // Safely force last tile flag for last element to avoid read overrun
+ first[numElements - 1].SetLastForTile(true);
+ }
+ }
+ else
+ {
+ std::memcpy(first, data, numElements * sizeof(TileElement));
+ // Safely force last tile flag for last element to avoid read overrun
+ first[numElements - 1].SetLastForTile(true);
+ }
+ }
+ map_invalidate_tile_full(_coords);
+ }
+ }
+
+ std::shared_ptr ScTile::getElement(uint32_t index) const
+ {
+ auto first = GetFirstElement();
+ if (static_cast(index) < GetNumElements(first))
+ {
+ return std::make_shared(_coords, &first[index]);
+ }
+ return {};
+ }
+
+ std::shared_ptr ScTile::insertElement(uint32_t index)
+ {
+ ThrowIfGameStateNotMutable();
+ std::shared_ptr result;
+ auto first = GetFirstElement();
+ auto origNumElements = GetNumElements(first);
+ if (index <= origNumElements)
+ {
+ std::vector data(first, first + origNumElements);
+
+ auto pos = TileCoordsXYZ(TileCoordsXY(_coords), 0).ToCoordsXYZ();
+ auto newElement = tile_element_insert(pos, 0, TileElementType::Surface);
+ if (newElement == nullptr)
+ {
+ auto ctx = GetDukContext();
+ duk_error(ctx, DUK_ERR_ERROR, "Unable to allocate element.");
+ }
+ else
+ {
+ // Inefficient, requires a dedicated method in tile element manager
+ first = GetFirstElement();
+ // Copy elements before index
+ if (index > 0)
+ {
+ std::memcpy(first, &data[0], index * sizeof(TileElement));
+ }
+ // Zero new element
+ std::memset(first + index, 0, sizeof(TileElement));
+ // Copy elements after index
+ if (index < origNumElements)
+ {
+ std::memcpy(first + index + 1, &data[index], (origNumElements - index) * sizeof(TileElement));
+ }
+ for (size_t i = 0; i < origNumElements; i++)
+ {
+ first[i].SetLastForTile(false);
+ }
+ first[origNumElements].SetLastForTile(true);
+ map_invalidate_tile_full(_coords);
+ result = std::make_shared(_coords, &first[index]);
+ }
+ }
+ else
+ {
+ auto ctx = GetDukContext();
+ duk_error(ctx, DUK_ERR_RANGE_ERROR, "Index must be between zero and the number of elements on the tile.");
+ }
+ return result;
+ }
+
+ void ScTile::removeElement(uint32_t index)
+ {
+ ThrowIfGameStateNotMutable();
+ auto first = GetFirstElement();
+ if (index < GetNumElements(first))
+ {
+ tile_element_remove(&first[index]);
+ map_invalidate_tile_full(_coords);
+ }
+ }
+
+ TileElement* ScTile::GetFirstElement() const
+ {
+ return map_get_first_element_at(_coords);
+ }
+
+ size_t ScTile::GetNumElements(const TileElement* first)
+ {
+ size_t count = 0;
+ if (first != nullptr)
+ {
+ auto element = first;
+ do
+ {
+ count++;
+ } while (!(element++)->IsLastForTile());
+ }
+ return count;
+ }
+
+ duk_context* ScTile::GetDukContext() const
+ {
+ auto& scriptEngine = GetContext()->GetScriptEngine();
+ auto ctx = scriptEngine.GetContext();
+ return ctx;
+ }
+
+ void ScTile::Register(duk_context* ctx)
+ {
+ dukglue_register_property(ctx, &ScTile::x_get, nullptr, "x");
+ dukglue_register_property(ctx, &ScTile::y_get, nullptr, "y");
+ dukglue_register_property(ctx, &ScTile::elements_get, nullptr, "elements");
+ dukglue_register_property(ctx, &ScTile::numElements_get, nullptr, "numElements");
+ dukglue_register_property(ctx, &ScTile::data_get, &ScTile::data_set, "data");
+ dukglue_register_method(ctx, &ScTile::getElement, "getElement");
+ dukglue_register_method(ctx, &ScTile::insertElement, "insertElement");
+ dukglue_register_method(ctx, &ScTile::removeElement, "removeElement");
+ }
+
+} // namespace OpenRCT2::Scripting
+
+#endif
diff --git a/src/openrct2/scripting/bindings/world/ScTile.hpp b/src/openrct2/scripting/bindings/world/ScTile.hpp
index 2126d5cc0e..1698377c42 100644
--- a/src/openrct2/scripting/bindings/world/ScTile.hpp
+++ b/src/openrct2/scripting/bindings/world/ScTile.hpp
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2014-2020 OpenRCT2 developers
+ * 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
@@ -11,21 +11,14 @@
#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 "ScTileElement.hpp"
# include
# include
# include
+# include
namespace OpenRCT2::Scripting
{
@@ -35,216 +28,33 @@ namespace OpenRCT2::Scripting
CoordsXY _coords;
public:
- ScTile(const CoordsXY& coords)
- : _coords(coords)
- {
- }
+ ScTile(const CoordsXY& coords);
private:
- int32_t x_get() const
- {
- return _coords.x / COORDS_XY_STEP;
- }
+ int32_t x_get() const;
- int32_t y_get() const
- {
- return _coords.y / COORDS_XY_STEP;
- }
+ int32_t y_get() const;
- uint32_t numElements_get() const
- {
- auto first = GetFirstElement();
- return static_cast(GetNumElements(first));
- }
+ uint32_t numElements_get() const;
- std::vector> elements_get() const
- {
- std::vector> result;
- auto first = GetFirstElement();
- auto currentNumElements = GetNumElements(first);
- if (currentNumElements != 0)
- {
- result.reserve(currentNumElements);
- for (size_t i = 0; i < currentNumElements; i++)
- {
- result.push_back(std::make_shared(_coords, &first[i]));
- }
- }
- return result;
- }
+ std::vector> elements_get() const;
- DukValue data_get() const
- {
- auto ctx = GetDukContext();
- auto first = map_get_first_element_at(_coords);
- auto dataLen = GetNumElements(first) * sizeof(TileElement);
- auto data = duk_push_fixed_buffer(ctx, dataLen);
- if (first != nullptr)
- {
- std::memcpy(data, first, dataLen);
- }
- duk_push_buffer_object(ctx, -1, 0, dataLen, DUK_BUFOBJ_UINT8ARRAY);
- return DukValue::take_from_stack(ctx);
- }
+ DukValue data_get() const;
+ void data_set(DukValue value);
- void data_set(DukValue value)
- {
- ThrowIfGameStateNotMutable();
- auto ctx = value.context();
- value.push();
- if (duk_is_buffer_data(ctx, -1))
- {
- duk_size_t dataLen{};
- auto data = duk_get_buffer_data(ctx, -1, &dataLen);
- auto numElements = dataLen / sizeof(TileElement);
- if (numElements == 0)
- {
- map_set_tile_element(TileCoordsXY(_coords), nullptr);
- }
- else
- {
- auto first = GetFirstElement();
- auto currentNumElements = GetNumElements(first);
- if (numElements > currentNumElements)
- {
- // Allocate space for the extra tile elements (inefficient but works)
- auto pos = TileCoordsXYZ(TileCoordsXY(_coords), 0).ToCoordsXYZ();
- auto numToInsert = numElements - currentNumElements;
- for (size_t i = 0; i < numToInsert; i++)
- {
- tile_element_insert(pos, 0, TileElementType::Surface);
- }
+ std::shared_ptr getElement(uint32_t index) const;
+ std::shared_ptr insertElement(uint32_t index);
- // Copy data to element span
- first = map_get_first_element_at(_coords);
- currentNumElements = GetNumElements(first);
- if (currentNumElements != 0)
- {
- std::memcpy(first, data, currentNumElements * sizeof(TileElement));
- // Safely force last tile flag for last element to avoid read overrun
- first[numElements - 1].SetLastForTile(true);
- }
- }
- else
- {
- std::memcpy(first, data, numElements * sizeof(TileElement));
- // Safely force last tile flag for last element to avoid read overrun
- first[numElements - 1].SetLastForTile(true);
- }
- }
- map_invalidate_tile_full(_coords);
- }
- }
+ void removeElement(uint32_t index);
- std::shared_ptr getElement(uint32_t index) const
- {
- auto first = GetFirstElement();
- if (static_cast(index) < GetNumElements(first))
- {
- return std::make_shared(_coords, &first[index]);
- }
- return {};
- }
+ TileElement* GetFirstElement() const;
- std::shared_ptr insertElement(uint32_t index)
- {
- ThrowIfGameStateNotMutable();
- std::shared_ptr result;
- auto first = GetFirstElement();
- auto origNumElements = GetNumElements(first);
- if (index <= origNumElements)
- {
- std::vector data(first, first + origNumElements);
+ static size_t GetNumElements(const TileElement* first);
- auto pos = TileCoordsXYZ(TileCoordsXY(_coords), 0).ToCoordsXYZ();
- auto newElement = tile_element_insert(pos, 0, TileElementType::Surface);
- if (newElement == nullptr)
- {
- auto ctx = GetDukContext();
- duk_error(ctx, DUK_ERR_ERROR, "Unable to allocate element.");
- }
- else
- {
- // Inefficient, requires a dedicated method in tile element manager
- first = GetFirstElement();
- // Copy elements before index
- if (index > 0)
- {
- std::memcpy(first, &data[0], index * sizeof(TileElement));
- }
- // Zero new element
- std::memset(first + index, 0, sizeof(TileElement));
- // Copy elements after index
- if (index < origNumElements)
- {
- std::memcpy(first + index + 1, &data[index], (origNumElements - index) * sizeof(TileElement));
- }
- for (size_t i = 0; i < origNumElements; i++)
- {
- first[i].SetLastForTile(false);
- }
- first[origNumElements].SetLastForTile(true);
- map_invalidate_tile_full(_coords);
- result = std::make_shared(_coords, &first[index]);
- }
- }
- else
- {
- auto ctx = GetDukContext();
- duk_error(ctx, DUK_ERR_RANGE_ERROR, "Index must be between zero and the number of elements on the tile.");
- }
- return result;
- }
-
- void removeElement(uint32_t index)
- {
- ThrowIfGameStateNotMutable();
- auto first = GetFirstElement();
- if (index < GetNumElements(first))
- {
- tile_element_remove(&first[index]);
- map_invalidate_tile_full(_coords);
- }
- }
-
- TileElement* GetFirstElement() const
- {
- return map_get_first_element_at(_coords);
- }
-
- static size_t GetNumElements(const TileElement* first)
- {
- size_t count = 0;
- if (first != nullptr)
- {
- auto element = first;
- do
- {
- count++;
- } while (!(element++)->IsLastForTile());
- }
- return count;
- }
-
- duk_context* GetDukContext() const
- {
- auto& scriptEngine = GetContext()->GetScriptEngine();
- auto ctx = scriptEngine.GetContext();
- return ctx;
- }
+ duk_context* GetDukContext() const;
public:
- static void Register(duk_context* ctx)
- {
- dukglue_register_property(ctx, &ScTile::x_get, nullptr, "x");
- dukglue_register_property(ctx, &ScTile::y_get, nullptr, "y");
- dukglue_register_property(ctx, &ScTile::elements_get, nullptr, "elements");
- dukglue_register_property(ctx, &ScTile::numElements_get, nullptr, "numElements");
- dukglue_register_property(ctx, &ScTile::data_get, &ScTile::data_set, "data");
- dukglue_register_method(ctx, &ScTile::getElement, "getElement");
- dukglue_register_method(ctx, &ScTile::insertElement, "insertElement");
- dukglue_register_method(ctx, &ScTile::removeElement, "removeElement");
- }
+ static void Register(duk_context* ctx);
};
} // namespace OpenRCT2::Scripting