mirror of
https://github.com/OpenRCT2/OpenRCT2.git
synced 2026-05-06 07:56:46 -04:00
Swap Duktape for quickjs-ng
Co-authored-by: Basssiiie <Basssiiie@users.noreply.github.com> Co-authored-by: Tulio Leao <tupaschoal@gmail.com>
This commit is contained in:
@@ -19,3 +19,4 @@ jobs:
|
||||
with:
|
||||
build_dir: 'build'
|
||||
cmake_args: '-G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Debug -DDISABLE_DISCORD_RPC=ON'
|
||||
file_filter: 'cpp,c,cc,cxx'
|
||||
|
||||
+4
-4
@@ -48,12 +48,13 @@ if (APPLE)
|
||||
set(CMAKE_SYSTEM_PROCESSOR "${CMAKE_OSX_ARCHITECTURES}" CACHE STRING "")
|
||||
endif ()
|
||||
|
||||
project(openrct2 CXX)
|
||||
# C Language required for quickjs-ng
|
||||
project(openrct2 CXX C)
|
||||
|
||||
include(cmake/platform.cmake)
|
||||
include(CMakeDependentOption)
|
||||
|
||||
# vcpkg includes its own copy of duktapeConfig.cmake; only include ours as needed.
|
||||
# vcpkg includes its own copy of *.cmake; only include ours as needed.
|
||||
if (NOT MSVC)
|
||||
include(FindPkgConfig)
|
||||
set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")
|
||||
@@ -267,7 +268,7 @@ endfunction ()
|
||||
|
||||
# Add check flags to a compile TARGET
|
||||
function (SET_CHECK_CXX_FLAGS _TARGET)
|
||||
target_compile_options("${_TARGET}" PRIVATE "${SUPPORTED_CHECK_CXX_COMPILER_FLAGS}")
|
||||
target_compile_options("${_TARGET}" PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${SUPPORTED_CHECK_CXX_COMPILER_FLAGS}>)
|
||||
endfunction()
|
||||
|
||||
# Check if a flag exists and add it to the compiler and the linker options
|
||||
@@ -395,7 +396,6 @@ endif()
|
||||
# graphics files (g2.dat, fonts.dat, palettes.dat, tracks.dat)
|
||||
if (NOT CMAKE_CROSSCOMPILING)
|
||||
set(graphics_files "g2" "fonts" "palettes" "tracks")
|
||||
|
||||
foreach(graphics_file ${graphics_files})
|
||||
set(output_file "${graphics_file}.dat")
|
||||
set(json_file "${ROOT_DIR}/resources/${graphics_file}/sprites.json")
|
||||
|
||||
@@ -69,7 +69,8 @@
|
||||
<RuntimeLibrary Condition="'$(UseSharedLibs)'=='true'">MultiThreadedDLL</RuntimeLibrary>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<AdditionalOptions>/utf-8 /std:c++20 /permissive- /Zc:externConstexpr /Zc:char8_t-</AdditionalOptions>
|
||||
<AdditionalOptions>/utf-8 /permissive- /Zc:externConstexpr /Zc:char8_t-</AdditionalOptions>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<LargeAddressAware Condition="'$(Platform)'=='Win32'">true</LargeAddressAware>
|
||||
@@ -119,7 +120,7 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<IncludePath>$(SolutionDir)src;$(SolutionDir)src\thirdparty;$(SolutionDir)src\thirdparty\duktape;$(SolutionDir)lib\$(Platform)\include;$(SolutionDir)lib\$(Platform)\include\SDL2;$(SolutionDir)lib\$(Platform)\include\crashpad;$(IncludePath)</IncludePath>
|
||||
<IncludePath>$(SolutionDir)src;$(SolutionDir)src\thirdparty;$(SolutionDir)src\thirdparty\quickjs-ng;$(SolutionDir)lib\$(Platform)\include;$(SolutionDir)lib\$(Platform)\include\SDL2;$(SolutionDir)lib\$(Platform)\include\crashpad;$(IncludePath)</IncludePath>
|
||||
<LibraryPath Condition="'$(Configuration)'=='Debug'">$(SolutionDir)lib\$(Platform)\debug\lib;$(LibraryPath)</LibraryPath>
|
||||
<LibraryPath Condition="'$(Configuration)'!='Debug'">$(SolutionDir)lib\$(Platform)\lib;$(LibraryPath)</LibraryPath>
|
||||
<LinkIncremental />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(openrct2-android CXX)
|
||||
project(openrct2-android CXX C)
|
||||
|
||||
set(CMAKE_VERBOSE_MAKEFILE on)
|
||||
|
||||
@@ -226,7 +226,7 @@ if (CCache_FOUND)
|
||||
endif (CCache_FOUND)
|
||||
|
||||
file(GLOB_RECURSE LIBOPENRCT2_SOURCES
|
||||
"${ORCT2_ROOT}/src/thirdparty/duktape/duktape.cpp"
|
||||
"${ORCT2_ROOT}/src/thirdparty/quickjs-ng/quickjs-amalgam.c"
|
||||
"${ORCT2_ROOT}/src/openrct2/*.cpp"
|
||||
"${ORCT2_ROOT}/src/openrct2/*.h"
|
||||
"${ORCT2_ROOT}/src/openrct2/*.hpp")
|
||||
@@ -255,13 +255,13 @@ target_link_libraries(openrct2-ui openrct2 android stdc++ GLESv1_CM GLESv2 SDL2m
|
||||
add_executable(openrct2-cli ${OPENRCT2_CLI_SOURCES})
|
||||
target_link_libraries(openrct2-cli openrct2 android stdc++ GLESv1_CM GLESv2)
|
||||
|
||||
target_include_directories(openrct2 PRIVATE "${ORCT2_ROOT}/src/thirdparty/duktape/")
|
||||
target_include_directories(openrct2 SYSTEM PRIVATE "${ORCT2_ROOT}/src/thirdparty/quickjs-ng")
|
||||
target_include_directories(openrct2 SYSTEM PRIVATE "${ORCT2_ROOT}/src/thirdparty")
|
||||
target_include_directories(openrct2-ui PRIVATE "${ORCT2_ROOT}/src/thirdparty/duktape/")
|
||||
target_include_directories(openrct2-ui SYSTEM PRIVATE "${ORCT2_ROOT}/src/thirdparty/quickjs-ng")
|
||||
target_include_directories(openrct2-ui PRIVATE "${ORCT2_ROOT}/src")
|
||||
target_include_directories(openrct2-ui SYSTEM PRIVATE "${ORCT2_ROOT}/src/thirdparty")
|
||||
target_include_directories(openrct2-cli PRIVATE "${ORCT2_ROOT}/src")
|
||||
target_include_directories(openrct2-cli SYSTEM PRIVATE "${ORCT2_ROOT}/src/thirdparty")
|
||||
|
||||
target_include_directories(openrct2 PRIVATE "/opt/openrct2/include/nlohmann/../")
|
||||
target_include_directories(openrct2-ui PRIVATE "/opt/openrct2/include/nlohmann/../")
|
||||
target_include_directories(openrct2-ui PRIVATE "/opt/openrct2/include/nlohmann/../")
|
||||
@@ -156,7 +156,7 @@ if (ENABLE_HEADERS_CHECK AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
add_library(openrct2-headers-check OBJECT ${OPENRCT2_HEADERS_CHECK})
|
||||
set_target_properties(openrct2-headers-check PROPERTIES LINKER_LANGUAGE CXX)
|
||||
set_source_files_properties(${OPENRCT2_HEADERS_CHECK} PROPERTIES LANGUAGE CXX)
|
||||
add_definitions("-x c++ -Wno-pragma-once-outside-header -Wno-unused-const-variable")
|
||||
target_compile_options(openrct2-headers-check PRIVATE -Wno-pragma-once-outside-header -Wno-unused-const-variable)
|
||||
get_target_property(OPENRCT2_INCLUDE_DIRS openrct2 INCLUDE_DIRECTORIES)
|
||||
set_target_properties(openrct2-headers-check PROPERTIES INCLUDE_DIRECTORIES "${OPENRCT2_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
|
||||
@@ -271,7 +271,7 @@ public:
|
||||
callback(result, std::string(path).c_str());
|
||||
}
|
||||
},
|
||||
trackDesign);
|
||||
false, trackDesign);
|
||||
return w;
|
||||
}
|
||||
case WindowClass::manageTrackDesign:
|
||||
@@ -1111,6 +1111,12 @@ public:
|
||||
CloseByClass(WindowClass::trackDesignPlace);
|
||||
}
|
||||
|
||||
void Cleanup() override
|
||||
{
|
||||
CloseByCondition([](WindowBase* w) { return true; });
|
||||
WindowCullDead();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first window with the specified window class.
|
||||
* rct2: 0x006EA8A0
|
||||
|
||||
@@ -41,12 +41,12 @@ extern void EmscriptenSaveGame(bool isTrackDesign, bool isAutosave, LoadSaveType
|
||||
namespace OpenRCT2::Ui::FileBrowser
|
||||
{
|
||||
static LoadSaveCallback _loadSaveCallback;
|
||||
static bool _loadSaveCallbackIsJs = false;
|
||||
|
||||
WindowBase* OpenPreferred(
|
||||
LoadSaveAction action, LoadSaveType type, u8string defaultPath, LoadSaveCallback callback, TrackDesign* trackDesign)
|
||||
LoadSaveAction action, LoadSaveType type, u8string defaultPath, LoadSaveCallback callback, bool isJsCallback,
|
||||
TrackDesign* trackDesign)
|
||||
{
|
||||
RegisterCallback(callback);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
if (action == LoadSaveAction::save)
|
||||
{
|
||||
@@ -69,16 +69,18 @@ namespace OpenRCT2::Ui::FileBrowser
|
||||
const bool isSave = (action == LoadSaveAction::save);
|
||||
const auto defaultDirectory = GetDir(type);
|
||||
|
||||
RegisterCallback(callback, isJsCallback);
|
||||
const u8string path = OpenSystemFileBrowser(isSave, type, defaultDirectory, defaultPath, trackDesign);
|
||||
if (!path.empty())
|
||||
{
|
||||
Select(path.c_str(), action, type, trackDesign);
|
||||
}
|
||||
UnregisterJSCallback();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Use built-in load/save window
|
||||
return Windows::LoadsaveOpen(action, type, defaultPath, callback, trackDesign);
|
||||
return Windows::LoadsaveOpen(action, type, defaultPath, callback, isJsCallback, trackDesign);
|
||||
}
|
||||
|
||||
bool ListItemSort(LoadSaveListItem& a, LoadSaveListItem& b)
|
||||
@@ -227,9 +229,19 @@ namespace OpenRCT2::Ui::FileBrowser
|
||||
return result;
|
||||
}
|
||||
|
||||
void RegisterCallback(LoadSaveCallback callback)
|
||||
void RegisterCallback(LoadSaveCallback callback, bool isJsCallback)
|
||||
{
|
||||
_loadSaveCallback = callback;
|
||||
_loadSaveCallbackIsJs = isJsCallback;
|
||||
}
|
||||
|
||||
void UnregisterJSCallback()
|
||||
{
|
||||
// This is very hacky but for a silly confluence of reasons we have to clear the callback if it is a callback to
|
||||
// javascript plugin code, but not if it is to normal C++ code.
|
||||
// https://github.com/mrmbernardi/OpenRCT2/pull/11#issuecomment-3448197606
|
||||
if (_loadSaveCallbackIsJs)
|
||||
_loadSaveCallback = {};
|
||||
}
|
||||
|
||||
void InvokeCallback(ModalResult result, const utf8* path)
|
||||
|
||||
@@ -58,14 +58,16 @@ namespace OpenRCT2::Ui::FileBrowser
|
||||
u8string GetFilterPatternByType(LoadSaveType type, bool isSave, const TrackDesign* trackDesign = nullptr);
|
||||
u8string RemovePatternWildcard(u8string_view pattern);
|
||||
u8string GetDir(LoadSaveType type);
|
||||
void RegisterCallback(LoadSaveCallback callback);
|
||||
void RegisterCallback(LoadSaveCallback callback, bool isJsCallback);
|
||||
void UnregisterJSCallback();
|
||||
void InvokeCallback(ModalResult result, const utf8* path);
|
||||
void Select(const char* path, LoadSaveAction action, LoadSaveType type, TrackDesign* trackDesignPtr);
|
||||
StringId GetTitleStringId(LoadSaveType type, bool isSave);
|
||||
u8string OpenSystemFileBrowser(
|
||||
bool isSave, LoadSaveType type, u8string defaultDirectory, u8string defaultPath, const TrackDesign* trackDesign);
|
||||
WindowBase* OpenPreferred(
|
||||
LoadSaveAction action, LoadSaveType type, u8string defaultPath, LoadSaveCallback callback, TrackDesign* trackDesign);
|
||||
LoadSaveAction action, LoadSaveType type, u8string defaultPath, LoadSaveCallback callback, bool isJsCallback,
|
||||
TrackDesign* trackDesign);
|
||||
} // namespace OpenRCT2::Ui::FileBrowser
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
<ClInclude Include="scripting\ScUi.hpp" />
|
||||
<ClInclude Include="scripting\ScViewport.hpp" />
|
||||
<ClInclude Include="scripting\ScWidget.hpp" />
|
||||
<ClInclude Include="scripting\ScWindow.h" />
|
||||
<ClInclude Include="scripting\ScWindow.hpp" />
|
||||
<ClInclude Include="scripting\UiExtensions.h" />
|
||||
<ClInclude Include="ProvisionalElements.h" />
|
||||
@@ -155,6 +156,7 @@
|
||||
<ClCompile Include="scripting\CustomListView.cpp" />
|
||||
<ClCompile Include="scripting\CustomMenu.cpp" />
|
||||
<ClCompile Include="scripting\CustomWindow.cpp" />
|
||||
<ClCompile Include="scripting\ScWindow.cpp" />
|
||||
<ClCompile Include="scripting\UiExtensions.cpp" />
|
||||
<ClCompile Include="ProvisionalElements.cpp" />
|
||||
<ClCompile Include="SDLException.cpp" />
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <openrct2/drawing/ImageImporter.h>
|
||||
#include <openrct2/drawing/X8DrawingEngine.h>
|
||||
#include <openrct2/scripting/Plugin.h>
|
||||
#include <thirdparty/base64.hpp>
|
||||
|
||||
using namespace OpenRCT2::Drawing;
|
||||
|
||||
@@ -47,7 +48,7 @@ namespace OpenRCT2::Scripting
|
||||
int32_t Height;
|
||||
int32_t Stride;
|
||||
PixelDataPaletteKind Palette;
|
||||
DukValue Data;
|
||||
JSValue Data;
|
||||
};
|
||||
|
||||
struct AllocatedImageList
|
||||
@@ -141,37 +142,37 @@ namespace OpenRCT2::Scripting
|
||||
scriptEngine.SubscribeToPluginStoppedEvent([](std::shared_ptr<Plugin> plugin) -> void { FreeCustomImages(plugin); });
|
||||
}
|
||||
|
||||
DukValue DukGetImageInfo(duk_context* ctx, ImageIndex id)
|
||||
JSValue JSGetImageInfo(JSContext* ctx, ImageIndex id)
|
||||
{
|
||||
auto* g1 = GfxGetG1Element(id);
|
||||
if (g1 == nullptr)
|
||||
{
|
||||
return ToDuk(ctx, undefined);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
DukObject obj(ctx);
|
||||
obj.Set("id", id);
|
||||
obj.Set("offset", ToDuk<ScreenCoordsXY>(ctx, { g1->xOffset, g1->yOffset }));
|
||||
obj.Set("width", g1->width);
|
||||
obj.Set("height", g1->height);
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "id", JS_NewInt32(ctx, id));
|
||||
JS_SetPropertyStr(ctx, obj, "offset", ToJSValue(ctx, ScreenCoordsXY{ g1->xOffset, g1->yOffset }));
|
||||
JS_SetPropertyStr(ctx, obj, "width", JS_NewInt32(ctx, g1->width));
|
||||
JS_SetPropertyStr(ctx, obj, "height", JS_NewInt32(ctx, g1->height));
|
||||
|
||||
obj.Set("hasTransparent", g1->flags.has(G1Flag::hasTransparency));
|
||||
obj.Set("isRLE", g1->flags.has(G1Flag::hasRLECompression));
|
||||
obj.Set("isPalette", g1->flags.has(G1Flag::isPalette));
|
||||
obj.Set("noZoom", g1->flags.has(G1Flag::noZoomDraw));
|
||||
JS_SetPropertyStr(ctx, obj, "hasTransparent", JS_NewBool(ctx, g1->flags.has(G1Flag::hasTransparency)));
|
||||
JS_SetPropertyStr(ctx, obj, "isRLE", JS_NewBool(ctx, g1->flags.has(G1Flag::hasRLECompression)));
|
||||
JS_SetPropertyStr(ctx, obj, "isPalette", JS_NewBool(ctx, g1->flags.has(G1Flag::isPalette)));
|
||||
JS_SetPropertyStr(ctx, obj, "noZoom", JS_NewBool(ctx, g1->flags.has(G1Flag::noZoomDraw)));
|
||||
|
||||
if (g1->flags.has(G1Flag::hasZoomSprite))
|
||||
{
|
||||
obj.Set("nextZoomId", id - g1->zoomedOffset);
|
||||
JS_SetPropertyStr(ctx, obj, "nextZoomId", JS_NewInt32(ctx, id - g1->zoomedOffset));
|
||||
}
|
||||
else
|
||||
{
|
||||
obj.Set("nextZoomId", undefined);
|
||||
JS_SetPropertyStr(ctx, obj, "nextZoomId", JS_UNDEFINED);
|
||||
}
|
||||
return obj.Take();
|
||||
return obj;
|
||||
}
|
||||
|
||||
static const char* GetPixelDataTypeForG1(const G1Element& g1)
|
||||
static std::string_view GetPixelDataTypeForG1(const G1Element& g1)
|
||||
{
|
||||
if (g1.flags.has(G1Flag::hasRLECompression))
|
||||
return "rle";
|
||||
@@ -180,83 +181,55 @@ namespace OpenRCT2::Scripting
|
||||
return "raw";
|
||||
}
|
||||
|
||||
DukValue DukGetImagePixelData(duk_context* ctx, ImageIndex id)
|
||||
JSValue JSGetImagePixelData(JSContext* ctx, ImageIndex id)
|
||||
{
|
||||
auto* g1 = GfxGetG1Element(id);
|
||||
if (g1 == nullptr)
|
||||
{
|
||||
return ToDuk(ctx, undefined);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
auto dataSize = G1CalculateDataSize(g1);
|
||||
auto* type = GetPixelDataTypeForG1(*g1);
|
||||
auto type = GetPixelDataTypeForG1(*g1);
|
||||
|
||||
// Copy the G1 data to a JS buffer wrapped in a Uint8Array
|
||||
duk_push_fixed_buffer(ctx, dataSize);
|
||||
duk_size_t bufferSize{};
|
||||
auto* buffer = duk_get_buffer_data(ctx, -1, &bufferSize);
|
||||
if (buffer != nullptr && bufferSize == dataSize)
|
||||
{
|
||||
std::memcpy(buffer, g1->offset, dataSize);
|
||||
}
|
||||
duk_push_buffer_object(ctx, -1, 0, dataSize, DUK_BUFOBJ_UINT8ARRAY);
|
||||
duk_remove(ctx, -2);
|
||||
auto data = DukValue::take_from_stack(ctx, -1);
|
||||
JSValue data = JS_NewUint8ArrayCopy(ctx, g1->offset, dataSize);
|
||||
|
||||
DukObject obj(ctx);
|
||||
obj.Set("type", type);
|
||||
obj.Set("width", g1->width);
|
||||
obj.Set("height", g1->height);
|
||||
obj.Set("data", data);
|
||||
return obj.Take();
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "type", JSFromStdString(ctx, type));
|
||||
JS_SetPropertyStr(ctx, obj, "width", JS_NewInt32(ctx, g1->width));
|
||||
JS_SetPropertyStr(ctx, obj, "height", JS_NewInt32(ctx, g1->height));
|
||||
JS_SetPropertyStr(ctx, obj, "data", data);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> GetBufferFromDukStack(duk_context* ctx)
|
||||
static std::vector<uint8_t> GetDataFromBufferLikeObject(JSContext* ctx, JSValue data)
|
||||
{
|
||||
std::vector<uint8_t> result;
|
||||
duk_size_t bufferLen{};
|
||||
const auto* buffer = reinterpret_cast<uint8_t*>(duk_get_buffer_data(ctx, -1, &bufferLen));
|
||||
if (buffer != nullptr)
|
||||
{
|
||||
result.resize(bufferLen);
|
||||
std::memcpy(result.data(), buffer, bufferLen);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> DukGetDataFromBufferLikeObject(const DukValue& data)
|
||||
{
|
||||
std::vector<uint8_t> result;
|
||||
auto ctx = data.context();
|
||||
if (data.is_array())
|
||||
if (JS_IsArray(data))
|
||||
{
|
||||
// From array of numbers
|
||||
data.push();
|
||||
auto len = duk_get_length(ctx, -1);
|
||||
result.resize(len);
|
||||
for (duk_uarridx_t i = 0; i < len; i++)
|
||||
int64_t arrSz = 0;
|
||||
JS_GetLength(ctx, data, &arrSz);
|
||||
if (arrSz > 0)
|
||||
{
|
||||
if (duk_get_prop_index(ctx, -1, i))
|
||||
{
|
||||
result[i] = duk_get_int(ctx, -1) & 0xFF;
|
||||
duk_pop(ctx);
|
||||
}
|
||||
result.resize(arrSz);
|
||||
JSIterateArray(ctx, data, [&result](JSContext* ctx2, JSValue val) { result.push_back(JSToInt(ctx2, val)); });
|
||||
}
|
||||
duk_pop(ctx);
|
||||
}
|
||||
else if (data.type() == DukValue::Type::STRING)
|
||||
else if (JS_IsString(data))
|
||||
{
|
||||
// From base64 string
|
||||
data.push();
|
||||
duk_base64_decode(ctx, -1);
|
||||
result = GetBufferFromDukStack(ctx);
|
||||
duk_pop(ctx);
|
||||
std::string str = JSToStdString(ctx, data);
|
||||
result = base64::decode_into<std::vector<uint8_t>>(str);
|
||||
}
|
||||
else if (data.type() == DukValue::Type::OBJECT)
|
||||
else if (JS_IsArrayBuffer(data))
|
||||
{
|
||||
// From Uint8Array
|
||||
data.push();
|
||||
result = GetBufferFromDukStack(ctx);
|
||||
duk_pop(ctx);
|
||||
size_t sz = 0;
|
||||
uint8_t* arr = JS_GetUint8Array(ctx, &sz, data);
|
||||
if (arr)
|
||||
{
|
||||
result = std::vector<uint8_t>(arr, arr + sz);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -290,14 +263,14 @@ namespace OpenRCT2::Scripting
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> GetBufferFromPixelData(duk_context* ctx, PixelData& pixelData)
|
||||
static std::vector<uint8_t> GetBufferFromPixelData(JSContext* ctx, PixelData& pixelData)
|
||||
{
|
||||
std::vector<uint8_t> imageData;
|
||||
switch (pixelData.Type)
|
||||
{
|
||||
case PixelDataKind::Raw:
|
||||
{
|
||||
auto data = DukGetDataFromBufferLikeObject(pixelData.Data);
|
||||
auto data = GetDataFromBufferLikeObject(ctx, pixelData.Data);
|
||||
if (pixelData.Stride != pixelData.Width)
|
||||
{
|
||||
// Make sure data is expected size for RemovePadding
|
||||
@@ -312,7 +285,7 @@ namespace OpenRCT2::Scripting
|
||||
}
|
||||
case PixelDataKind::Rle:
|
||||
{
|
||||
imageData = DukGetDataFromBufferLikeObject(pixelData.Data);
|
||||
imageData = GetDataFromBufferLikeObject(ctx, pixelData.Data);
|
||||
break;
|
||||
}
|
||||
case PixelDataKind::Png:
|
||||
@@ -320,7 +293,7 @@ namespace OpenRCT2::Scripting
|
||||
auto imageFormat = pixelData.Palette == PixelDataPaletteKind::Keep ? ImageFormat::png : ImageFormat::png32;
|
||||
auto palette = pixelData.Palette == PixelDataPaletteKind::Keep ? Palette::KeepIndices : Palette::OpenRCT2;
|
||||
auto importMode = getImportModeFromPalette(pixelData.Palette);
|
||||
auto pngData = DukGetDataFromBufferLikeObject(pixelData.Data);
|
||||
auto pngData = GetDataFromBufferLikeObject(ctx, pixelData.Data);
|
||||
auto image = Imaging::ReadFromBuffer(pngData, imageFormat);
|
||||
constexpr ImportFlags flags = { ImportFlag::rle };
|
||||
ImageImportMeta meta = { { 0, 0 }, palette, flags, importMode };
|
||||
@@ -341,49 +314,40 @@ namespace OpenRCT2::Scripting
|
||||
return imageData;
|
||||
}
|
||||
|
||||
template<>
|
||||
PixelDataKind FromDuk(const DukValue& d)
|
||||
static PixelDataKind PixelDataKindFromJS(const std::string& s)
|
||||
{
|
||||
if (d.type() == DukValue::Type::STRING)
|
||||
{
|
||||
auto& s = d.as_string();
|
||||
if (s == "raw")
|
||||
return PixelDataKind::Raw;
|
||||
if (s == "rle")
|
||||
return PixelDataKind::Rle;
|
||||
if (s == "palette")
|
||||
return PixelDataKind::Palette;
|
||||
if (s == "png")
|
||||
return PixelDataKind::Png;
|
||||
}
|
||||
if (s == "raw")
|
||||
return PixelDataKind::Raw;
|
||||
if (s == "rle")
|
||||
return PixelDataKind::Rle;
|
||||
if (s == "palette")
|
||||
return PixelDataKind::Palette;
|
||||
if (s == "png")
|
||||
return PixelDataKind::Png;
|
||||
return PixelDataKind::Unknown;
|
||||
}
|
||||
|
||||
template<>
|
||||
PixelDataPaletteKind FromDuk(const DukValue& d)
|
||||
static PixelDataPaletteKind PixelDataPaletteKindFromJS(const std::string& s)
|
||||
{
|
||||
if (d.type() == DukValue::Type::STRING)
|
||||
{
|
||||
auto& s = d.as_string();
|
||||
if (s == "keep")
|
||||
return PixelDataPaletteKind::Keep;
|
||||
if (s == "closest")
|
||||
return PixelDataPaletteKind::Closest;
|
||||
if (s == "dither")
|
||||
return PixelDataPaletteKind::Dither;
|
||||
}
|
||||
if (s == "keep")
|
||||
return PixelDataPaletteKind::Keep;
|
||||
if (s == "closest")
|
||||
return PixelDataPaletteKind::Closest;
|
||||
if (s == "dither")
|
||||
return PixelDataPaletteKind::Dither;
|
||||
return PixelDataPaletteKind::None;
|
||||
}
|
||||
|
||||
static PixelData GetPixelDataFromDuk(const DukValue& dukPixelData)
|
||||
static PixelData GetPixelDataFromJS(JSContext* ctx, JSValue jsPixelData)
|
||||
{
|
||||
PixelData pixelData;
|
||||
pixelData.Type = FromDuk<PixelDataKind>(dukPixelData["type"]);
|
||||
pixelData.Palette = FromDuk<PixelDataPaletteKind>(dukPixelData["palette"]);
|
||||
pixelData.Width = AsOrDefault(dukPixelData["width"], 0);
|
||||
pixelData.Height = AsOrDefault(dukPixelData["height"], 0);
|
||||
pixelData.Stride = AsOrDefault(dukPixelData["stride"], pixelData.Width);
|
||||
pixelData.Data = dukPixelData["data"];
|
||||
pixelData.Type = PixelDataKindFromJS(JSToStdString(ctx, jsPixelData, "type"));
|
||||
pixelData.Palette = PixelDataPaletteKindFromJS(JSToStdString(ctx, jsPixelData, "palette"));
|
||||
pixelData.Width = AsOrDefault(ctx, jsPixelData, "width", static_cast<int32_t>(0));
|
||||
pixelData.Height = AsOrDefault(ctx, jsPixelData, "height", static_cast<int32_t>(0));
|
||||
pixelData.Stride = AsOrDefault(ctx, jsPixelData, "stride", static_cast<int32_t>(pixelData.Width));
|
||||
// Note: this must be JS_FreeValued
|
||||
pixelData.Data = JS_GetPropertyStr(ctx, jsPixelData, "data");
|
||||
return pixelData;
|
||||
}
|
||||
|
||||
@@ -414,21 +378,15 @@ namespace OpenRCT2::Scripting
|
||||
DrawingEngineInvalidateImage(id);
|
||||
}
|
||||
|
||||
void DukSetPixelData(duk_context* ctx, ImageIndex id, const DukValue& dukPixelData)
|
||||
void JSSetPixelData(JSContext* ctx, ImageIndex id, JSValue jsPixelData)
|
||||
{
|
||||
auto pixelData = GetPixelDataFromDuk(dukPixelData);
|
||||
try
|
||||
{
|
||||
auto newData = GetBufferFromPixelData(ctx, pixelData);
|
||||
ReplacePixelDataForImage(id, pixelData, std::move(newData));
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, e.what());
|
||||
}
|
||||
auto pixelData = GetPixelDataFromJS(ctx, jsPixelData);
|
||||
auto newData = GetBufferFromPixelData(ctx, pixelData);
|
||||
ReplacePixelDataForImage(id, pixelData, std::move(newData));
|
||||
JS_FreeValue(ctx, pixelData.Data);
|
||||
}
|
||||
|
||||
void DukDrawCustomImage(ScriptEngine& scriptEngine, ImageIndex id, ScreenSize size, const DukValue& callback)
|
||||
void JSDrawCustomImage(ScriptEngine& scriptEngine, ImageIndex id, ScreenSize size, const JSCallback& callback)
|
||||
{
|
||||
auto* ctx = scriptEngine.GetContext();
|
||||
auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
@@ -464,10 +422,12 @@ namespace OpenRCT2::Scripting
|
||||
rt.bits = reinterpret_cast<PaletteIndex*>(g1->offset);
|
||||
}
|
||||
|
||||
auto dukG = GetObjectAsDukValue(ctx, std::make_shared<ScGraphicsContext>(ctx, rt));
|
||||
drawingEngine->BeginDraw();
|
||||
scriptEngine.ExecutePluginCall(plugin, callback, { dukG }, false);
|
||||
drawingEngine->EndDraw();
|
||||
if (callback.IsValid())
|
||||
{
|
||||
drawingEngine->BeginDraw();
|
||||
scriptEngine.ExecutePluginCall(plugin, callback.callback, { gScGraphicsContext.New(ctx, rt) }, false);
|
||||
drawingEngine->EndDraw();
|
||||
}
|
||||
|
||||
if (createNewImage)
|
||||
{
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
|
||||
#include <memory>
|
||||
#include <openrct2/drawing/Image.h>
|
||||
#include <openrct2/drawing/ImageId.hpp>
|
||||
#include <openrct2/scripting/Duktape.hpp>
|
||||
#include <openrct2/scripting/Plugin.h>
|
||||
#include <openrct2/scripting/ScriptEngine.h>
|
||||
|
||||
@@ -24,10 +22,10 @@ namespace OpenRCT2::Scripting
|
||||
std::optional<ImageList> AllocateCustomImages(const std::shared_ptr<Plugin>& plugin, uint32_t count);
|
||||
bool FreeCustomImages(const std::shared_ptr<Plugin>& plugin, ImageList range);
|
||||
bool DoesPluginOwnImage(const std::shared_ptr<Plugin>& plugin, ImageIndex index);
|
||||
DukValue DukGetImageInfo(duk_context* ctx, ImageIndex id);
|
||||
DukValue DukGetImagePixelData(duk_context* ctx, ImageIndex id);
|
||||
void DukSetPixelData(duk_context* ctx, ImageIndex id, const DukValue& dukPixelData);
|
||||
void DukDrawCustomImage(ScriptEngine& scriptEngine, ImageIndex id, ScreenSize size, const DukValue& callback);
|
||||
JSValue JSGetImageInfo(JSContext* ctx, ImageIndex id);
|
||||
JSValue JSGetImagePixelData(JSContext* ctx, ImageIndex id);
|
||||
void JSSetPixelData(JSContext* ctx, ImageIndex id, JSValue jsPixelData);
|
||||
void JSDrawCustomImage(ScriptEngine& scriptEngine, ImageIndex id, ScreenSize size, const JSCallback& callback);
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
|
||||
@@ -34,12 +34,11 @@ namespace OpenRCT2::Scripting
|
||||
{
|
||||
constexpr size_t kColumnHeaderHeight = kListRowHeight + 1;
|
||||
|
||||
template<>
|
||||
ColumnSortOrder FromDuk(const DukValue& d)
|
||||
ColumnSortOrder ColumnSortOrderFromJS(JSContext* ctx, JSValue d)
|
||||
{
|
||||
if (d.type() == DukValue::Type::STRING)
|
||||
if (JS_IsString(d))
|
||||
{
|
||||
auto s = d.as_string();
|
||||
auto s = JSToStdString(ctx, d);
|
||||
if (s == "ascending")
|
||||
return ColumnSortOrder::Ascending;
|
||||
if (s == "descending")
|
||||
@@ -48,44 +47,36 @@ namespace OpenRCT2::Scripting
|
||||
return ColumnSortOrder::None;
|
||||
}
|
||||
|
||||
template<>
|
||||
DukValue ToDuk(duk_context* ctx, const ColumnSortOrder& value)
|
||||
static JSValue ColumnSortOrderToJS(JSContext* ctx, ColumnSortOrder value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case ColumnSortOrder::Ascending:
|
||||
return ToDuk(ctx, "ascending");
|
||||
return JSFromStdString(ctx, "ascending");
|
||||
case ColumnSortOrder::Descending:
|
||||
return ToDuk(ctx, "descending");
|
||||
return JSFromStdString(ctx, "descending");
|
||||
default:
|
||||
return ToDuk(ctx, "none");
|
||||
return JSFromStdString(ctx, "none");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
std::optional<int32_t> FromDuk(const DukValue& d)
|
||||
{
|
||||
if (d.type() == DukValue::Type::NUMBER)
|
||||
{
|
||||
return d.as_int();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<>
|
||||
ListViewColumn FromDuk(const DukValue& d)
|
||||
ListViewColumn ListViewColumnFromJS(JSContext* ctx, JSValue d)
|
||||
{
|
||||
ListViewColumn result;
|
||||
result.CanSort = AsOrDefault(d["canSort"], false);
|
||||
result.SortOrder = FromDuk<ColumnSortOrder>(d["sortOrder"]);
|
||||
result.Header = AsOrDefault(d["header"], "");
|
||||
result.HeaderTooltip = AsOrDefault(d["headerTooltip"], "");
|
||||
result.MinWidth = FromDuk<std::optional<int32_t>>(d["minWidth"]);
|
||||
result.MaxWidth = FromDuk<std::optional<int32_t>>(d["maxWidth"]);
|
||||
result.RatioWidth = FromDuk<std::optional<int32_t>>(d["ratioWidth"]);
|
||||
if (d["width"].type() == DukValue::Type::NUMBER)
|
||||
result.CanSort = AsOrDefault(ctx, d, "canSort", false);
|
||||
JSValue sortOrderVal = JS_GetPropertyStr(ctx, d, "sortOrder");
|
||||
result.SortOrder = ColumnSortOrderFromJS(ctx, sortOrderVal);
|
||||
JS_FreeValue(ctx, sortOrderVal);
|
||||
result.Header = AsOrDefault(ctx, d, "header", "");
|
||||
result.HeaderTooltip = AsOrDefault(ctx, d, "headerTooltip", "");
|
||||
result.MinWidth = JSToOptionalInt(ctx, d, "minWidth");
|
||||
result.MaxWidth = JSToOptionalInt(ctx, d, "maxWidth");
|
||||
result.RatioWidth = JSToOptionalInt(ctx, d, "ratioWidth");
|
||||
|
||||
auto width = JSToOptionalInt(ctx, d, "width");
|
||||
if (width.has_value())
|
||||
{
|
||||
result.MinWidth = d["width"].as_int();
|
||||
result.MinWidth = width.value();
|
||||
result.MaxWidth = result.MinWidth;
|
||||
result.RatioWidth = std::nullopt;
|
||||
}
|
||||
@@ -96,45 +87,43 @@ namespace OpenRCT2::Scripting
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
DukValue ToDuk(duk_context* ctx, const ListViewColumn& value)
|
||||
JSValue ListViewColumnToJS(JSContext* ctx, const ListViewColumn& value)
|
||||
{
|
||||
DukObject obj(ctx);
|
||||
obj.Set("canSort", value.CanSort);
|
||||
obj.Set("sortOrder", ToDuk(ctx, value.SortOrder));
|
||||
obj.Set("header", value.Header);
|
||||
obj.Set("headerTooltip", value.HeaderTooltip);
|
||||
obj.Set("minWidth", value.MinWidth);
|
||||
obj.Set("maxWidth", value.MaxWidth);
|
||||
obj.Set("ratioWidth", value.RatioWidth);
|
||||
obj.Set("width", value.Width);
|
||||
return obj.Take();
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "canSort", JS_NewBool(ctx, value.CanSort));
|
||||
JS_SetPropertyStr(ctx, obj, "sortOrder", ColumnSortOrderToJS(ctx, value.SortOrder));
|
||||
JS_SetPropertyStr(ctx, obj, "header", JSFromStdString(ctx, value.Header));
|
||||
JS_SetPropertyStr(ctx, obj, "headerTooltip", JSFromStdString(ctx, value.HeaderTooltip));
|
||||
JS_SetPropertyStr(
|
||||
ctx, obj, "minWidth", value.MinWidth.has_value() ? JS_NewInt32(ctx, value.MinWidth.value()) : JS_NULL);
|
||||
JS_SetPropertyStr(
|
||||
ctx, obj, "maxWidth", value.MaxWidth.has_value() ? JS_NewInt32(ctx, value.MaxWidth.value()) : JS_NULL);
|
||||
JS_SetPropertyStr(
|
||||
ctx, obj, "ratioWidth", value.RatioWidth.has_value() ? JS_NewInt32(ctx, value.RatioWidth.value()) : JS_NULL);
|
||||
JS_SetPropertyStr(ctx, obj, "width", JS_NewInt32(ctx, value.Width));
|
||||
return obj;
|
||||
}
|
||||
|
||||
template<>
|
||||
ListViewItem FromDuk(const DukValue& d)
|
||||
ListViewItem ListViewItemFromJS(JSContext* ctx, JSValue d)
|
||||
{
|
||||
ListViewItem result;
|
||||
if (d.type() == DukValue::Type::STRING)
|
||||
if (JS_IsString(d))
|
||||
{
|
||||
result = ListViewItem(ProcessString(d));
|
||||
result = ListViewItem(JSToStdString(ctx, d));
|
||||
}
|
||||
else if (d.is_array())
|
||||
else if (JS_IsArray(d))
|
||||
{
|
||||
std::vector<std::string> cells;
|
||||
for (const auto& dukCell : d.as_array())
|
||||
{
|
||||
cells.push_back(ProcessString(dukCell));
|
||||
}
|
||||
JSIterateArray(ctx, d, [&cells](JSContext* ctx2, JSValue x) { cells.push_back(JSToStdString(ctx2, x)); });
|
||||
result = ListViewItem(std::move(cells));
|
||||
}
|
||||
else if (d.type() == DukValue::Type::OBJECT)
|
||||
else if (JS_IsObject(d))
|
||||
{
|
||||
auto type = ProcessString(d["type"]);
|
||||
auto type = JSToStdString(ctx, d, "type");
|
||||
// This type was misspelt between 2020 and 2025.
|
||||
if (type == "separator" || type == "seperator")
|
||||
{
|
||||
auto text = ProcessString(d["text"]);
|
||||
auto text = JSToStdString(ctx, d, "text");
|
||||
result = ListViewItem(text);
|
||||
result.IsSeparator = true;
|
||||
}
|
||||
@@ -142,64 +131,51 @@ namespace OpenRCT2::Scripting
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::vector<ListViewColumn> FromDuk(const DukValue& d)
|
||||
std::vector<ListViewColumn> ListViewColumnVecFromJS(JSContext* ctx, JSValue d)
|
||||
{
|
||||
std::vector<ListViewColumn> result;
|
||||
if (d.is_array())
|
||||
if (JS_IsArray(d))
|
||||
{
|
||||
auto dukColumns = d.as_array();
|
||||
for (const auto& dukColumn : dukColumns)
|
||||
{
|
||||
result.push_back(FromDuk<ListViewColumn>(dukColumn));
|
||||
}
|
||||
JSIterateArray(ctx, d, [&result](JSContext* ctx2, JSValue x) { result.push_back(ListViewColumnFromJS(ctx2, x)); });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::vector<ListViewItem> FromDuk(const DukValue& d)
|
||||
std::vector<ListViewItem> ListViewItemVecFromJS(JSContext* ctx, JSValue d)
|
||||
{
|
||||
std::vector<ListViewItem> result;
|
||||
if (d.is_array())
|
||||
if (JS_IsArray(d))
|
||||
{
|
||||
auto dukItems = d.as_array();
|
||||
for (const auto& dukItem : dukItems)
|
||||
{
|
||||
result.push_back(FromDuk<ListViewItem>(dukItem));
|
||||
}
|
||||
JSIterateArray(ctx, d, [&result](JSContext* ctx2, JSValue x) { result.push_back(ListViewItemFromJS(ctx2, x)); });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::optional<RowColumn> FromDuk(const DukValue& d)
|
||||
std::optional<RowColumn> RowColumnFromJS(JSContext* ctx, JSValue d)
|
||||
{
|
||||
if (d.type() == DukValue::Type::OBJECT)
|
||||
if (JS_IsObject(d))
|
||||
{
|
||||
auto dukRow = d["row"];
|
||||
auto dukColumn = d["column"];
|
||||
if (dukRow.type() == DukValue::Type::NUMBER && dukColumn.type() == DukValue::Type::NUMBER)
|
||||
auto row = JSToOptionalInt(ctx, d, "row");
|
||||
auto column = JSToOptionalInt(ctx, d, "column");
|
||||
if (row.has_value() && column.has_value())
|
||||
{
|
||||
return RowColumn(dukRow.as_int(), dukColumn.as_int());
|
||||
return RowColumn(row.value(), column.value());
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<>
|
||||
DukValue ToDuk(duk_context* ctx, const RowColumn& value)
|
||||
JSValue RowColumnToJS(JSContext* ctx, const RowColumn value)
|
||||
{
|
||||
DukObject obj(ctx);
|
||||
obj.Set("row", value.Row);
|
||||
obj.Set("column", value.Column);
|
||||
return obj.Take();
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "row", JS_NewInt32(ctx, value.Row));
|
||||
JS_SetPropertyStr(ctx, obj, "column", JS_NewInt32(ctx, value.Column));
|
||||
return obj;
|
||||
}
|
||||
|
||||
template<>
|
||||
ScrollbarType FromDuk(const DukValue& d)
|
||||
ScrollbarType ScrollbarTypeFromJS(JSContext* ctx, JSValue d)
|
||||
{
|
||||
auto value = AsOrDefault(d, "");
|
||||
auto value = JSToStdString(ctx, d);
|
||||
if (value == "horizontal")
|
||||
return ScrollbarType::Horizontal;
|
||||
if (value == "vertical")
|
||||
@@ -209,20 +185,19 @@ namespace OpenRCT2::Scripting
|
||||
return ScrollbarType::None;
|
||||
}
|
||||
|
||||
template<>
|
||||
DukValue ToDuk(duk_context* ctx, const ScrollbarType& value)
|
||||
JSValue ScrollbarTypeToJS(JSContext* ctx, const ScrollbarType value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
default:
|
||||
case ScrollbarType::None:
|
||||
return ToDuk(ctx, "none");
|
||||
return JSFromStdString(ctx, "none");
|
||||
case ScrollbarType::Horizontal:
|
||||
return ToDuk(ctx, "horizontal");
|
||||
return JSFromStdString(ctx, "horizontal");
|
||||
case ScrollbarType::Vertical:
|
||||
return ToDuk(ctx, "vertical");
|
||||
return JSFromStdString(ctx, "vertical");
|
||||
case ScrollbarType::Both:
|
||||
return ToDuk(ctx, "both");
|
||||
return JSFromStdString(ctx, "both");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,15 +454,13 @@ void CustomListView::MouseOver(const ScreenCoordsXY& pos, bool isMouseDown)
|
||||
HighlightedCell = hitResult;
|
||||
if (HighlightedCell != LastHighlightedCell)
|
||||
{
|
||||
if (hitResult->Row != kHeaderRow && OnHighlight.context() != nullptr && OnHighlight.is_function())
|
||||
if (hitResult->Row != kHeaderRow && OnHighlight.context != nullptr && OnHighlight.IsValid())
|
||||
{
|
||||
auto ctx = OnHighlight.context();
|
||||
duk_push_int(ctx, static_cast<int32_t>(HighlightedCell->Row));
|
||||
auto dukRow = DukValue::take_from_stack(ctx, -1);
|
||||
duk_push_int(ctx, static_cast<int32_t>(HighlightedCell->Column));
|
||||
auto dukColumn = DukValue::take_from_stack(ctx, -1);
|
||||
auto ctx = OnHighlight.context;
|
||||
JSValue jsRow = JS_NewInt32(ctx, HighlightedCell->Row);
|
||||
JSValue jsColumn = JS_NewInt32(ctx, HighlightedCell->Column);
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
scriptEngine.ExecutePluginCall(Owner, OnHighlight, { dukRow, dukColumn }, false);
|
||||
scriptEngine.ExecutePluginCall(Owner, OnHighlight.callback, { jsRow, jsColumn }, false);
|
||||
}
|
||||
Invalidate();
|
||||
}
|
||||
@@ -526,15 +499,13 @@ void CustomListView::MouseDown(const ScreenCoordsXY& pos)
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
auto ctx = OnClick.context();
|
||||
if (ctx != nullptr && OnClick.is_function())
|
||||
auto ctx = OnClick.context;
|
||||
if (ctx != nullptr && OnClick.IsValid())
|
||||
{
|
||||
duk_push_int(ctx, static_cast<int32_t>(hitResult->Row));
|
||||
auto dukRow = DukValue::take_from_stack(ctx, -1);
|
||||
duk_push_int(ctx, static_cast<int32_t>(hitResult->Column));
|
||||
auto dukColumn = DukValue::take_from_stack(ctx, -1);
|
||||
JSValue jsRow = JS_NewInt32(ctx, hitResult->Row);
|
||||
JSValue jsColumn = JS_NewInt32(ctx, hitResult->Column);
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
scriptEngine.ExecutePluginCall(Owner, OnClick, { dukRow, dukColumn }, false);
|
||||
scriptEngine.ExecutePluginCall(Owner, OnClick.callback, { jsRow, jsColumn }, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <openrct2/scripting/Duktape.hpp>
|
||||
#include <openrct2/scripting/ScriptEngine.h>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@@ -119,8 +118,8 @@ namespace OpenRCT2::Ui::Windows
|
||||
bool IsMouseDown{};
|
||||
bool CanSelect{};
|
||||
|
||||
DukValue OnClick;
|
||||
DukValue OnHighlight;
|
||||
JSCallback OnClick;
|
||||
JSCallback OnHighlight;
|
||||
|
||||
CustomListView(WindowBase* parent, size_t scrollIndex);
|
||||
ScrollbarType GetScrollbars() const;
|
||||
@@ -155,44 +154,29 @@ namespace OpenRCT2::Ui::Windows
|
||||
};
|
||||
} // namespace OpenRCT2::Ui::Windows
|
||||
|
||||
class DukValue;
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
using namespace OpenRCT2::Ui::Windows;
|
||||
|
||||
template<>
|
||||
ColumnSortOrder FromDuk(const DukValue& d);
|
||||
ColumnSortOrder ColumnSortOrderFromJS(JSContext* ctx, JSValue d);
|
||||
|
||||
template<>
|
||||
std::optional<int32_t> FromDuk(const DukValue& d);
|
||||
ListViewColumn ListViewColumnFromJS(JSContext* ctx, JSValue d);
|
||||
|
||||
template<>
|
||||
ListViewColumn FromDuk(const DukValue& d);
|
||||
ListViewItem ListViewItemFromJS(JSContext* ctx, JSValue d);
|
||||
|
||||
template<>
|
||||
ListViewItem FromDuk(const DukValue& d);
|
||||
std::vector<ListViewColumn> ListViewColumnVecFromJS(JSContext* ctx, JSValue d);
|
||||
|
||||
template<>
|
||||
std::vector<ListViewColumn> FromDuk(const DukValue& d);
|
||||
std::vector<ListViewItem> ListViewItemVecFromJS(JSContext* ctx, JSValue d);
|
||||
|
||||
template<>
|
||||
std::vector<ListViewItem> FromDuk(const DukValue& d);
|
||||
std::optional<RowColumn> RowColumnFromJS(JSContext* ctx, JSValue d);
|
||||
|
||||
template<>
|
||||
std::optional<RowColumn> FromDuk(const DukValue& d);
|
||||
JSValue RowColumnToJS(JSContext* ctx, RowColumn value);
|
||||
|
||||
template<>
|
||||
DukValue ToDuk(duk_context* ctx, const RowColumn& value);
|
||||
JSValue ListViewColumnToJS(JSContext* ctx, const ListViewColumn& value);
|
||||
|
||||
template<>
|
||||
DukValue ToDuk(duk_context* ctx, const ListViewColumn& value);
|
||||
ScrollbarType ScrollbarTypeFromJS(JSContext* ctx, JSValue d);
|
||||
|
||||
template<>
|
||||
ScrollbarType FromDuk(const DukValue& d);
|
||||
|
||||
template<>
|
||||
DukValue ToDuk(duk_context* ctx, const ScrollbarType& value);
|
||||
JSValue ScrollbarTypeToJS(JSContext* ctx, ScrollbarType value);
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
#endif
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <openrct2-ui/UiContext.h>
|
||||
#include <openrct2-ui/input/ShortcutManager.h>
|
||||
#include <openrct2/Input.h>
|
||||
#include <openrct2/core/EnumMap.hpp>
|
||||
#include <openrct2/scripting/ScriptUtil.hpp>
|
||||
#include <openrct2/ui/UiContext.h>
|
||||
#include <openrct2/ui/WindowManager.h>
|
||||
#include <openrct2/world/Map.h>
|
||||
@@ -31,7 +33,7 @@ namespace OpenRCT2::Scripting
|
||||
|
||||
CustomShortcut::CustomShortcut(
|
||||
std::shared_ptr<Plugin> owner, std::string_view id, std::string_view text, const std::vector<std::string>& bindings,
|
||||
DukValue callback)
|
||||
JSCallback callback)
|
||||
: Owner(owner)
|
||||
, Id(id)
|
||||
, Text(text)
|
||||
@@ -59,7 +61,7 @@ namespace OpenRCT2::Scripting
|
||||
void CustomShortcut::Invoke() const
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
scriptEngine.ExecutePluginCall(Owner, Callback, {}, false);
|
||||
scriptEngine.ExecutePluginCall(Owner, Callback.callback, {}, false);
|
||||
}
|
||||
|
||||
static constexpr std::array<std::string_view, EnumValue(CursorID::Count)> CursorNames = {
|
||||
@@ -69,47 +71,57 @@ namespace OpenRCT2::Scripting
|
||||
"volcano_down", "walk_down", "paint_down", "entrance_down", "hand_open", "hand_closed",
|
||||
};
|
||||
|
||||
static const DukEnumMap<ViewportInteractionItem> ToolFilterMap(
|
||||
{
|
||||
{ "terrain", ViewportInteractionItem::terrain },
|
||||
{ "entity", ViewportInteractionItem::entity },
|
||||
{ "ride", ViewportInteractionItem::ride },
|
||||
{ "water", ViewportInteractionItem::water },
|
||||
{ "scenery", ViewportInteractionItem::scenery },
|
||||
{ "footpath", ViewportInteractionItem::footpath },
|
||||
{ "footpath_item", ViewportInteractionItem::pathAddition },
|
||||
{ "park_entrance", ViewportInteractionItem::parkEntrance },
|
||||
{ "wall", ViewportInteractionItem::wall },
|
||||
{ "large_scenery", ViewportInteractionItem::largeScenery },
|
||||
{ "label", ViewportInteractionItem::label },
|
||||
{ "banner", ViewportInteractionItem::banner },
|
||||
});
|
||||
|
||||
template<>
|
||||
DukValue ToDuk(duk_context* ctx, const CursorID& cursorId)
|
||||
JSValue CursorIDToJSValue(JSContext* ctx, CursorID id)
|
||||
{
|
||||
auto value = EnumValue(cursorId);
|
||||
if (value < std::size(CursorNames))
|
||||
auto idVal = EnumValue(id);
|
||||
if (idVal < CursorNames.size())
|
||||
{
|
||||
auto str = CursorNames[value];
|
||||
duk_push_lstring(ctx, str.data(), str.size());
|
||||
return DukValue::take_from_stack(ctx);
|
||||
return JSFromStdString(ctx, CursorNames[idVal]);
|
||||
}
|
||||
return ToDuk(ctx, undefined);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
template<>
|
||||
CursorID FromDuk(const DukValue& s)
|
||||
static CursorID CursorJSValToID(JSContext* ctx, JSValue value)
|
||||
{
|
||||
if (s.type() == DukValue::Type::STRING)
|
||||
if (JS_IsString(value))
|
||||
{
|
||||
auto it = std::find(std::begin(CursorNames), std::end(CursorNames), s.as_c_string());
|
||||
if (it != std::end(CursorNames))
|
||||
std::string valueStr = JSToStdString(ctx, value);
|
||||
for (uint8_t i = 0; i < EnumValue(CursorID::Count); i++)
|
||||
{
|
||||
return static_cast<CursorID>(std::distance(std::begin(CursorNames), it));
|
||||
if (CursorNames[i] == valueStr)
|
||||
{
|
||||
return static_cast<CursorID>(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return CursorID::Undefined;
|
||||
return CursorID::Arrow;
|
||||
}
|
||||
|
||||
static const EnumMap<ViewportInteractionItem> ToolFilterMap{
|
||||
{ "terrain", ViewportInteractionItem::terrain },
|
||||
{ "entity", ViewportInteractionItem::entity },
|
||||
{ "ride", ViewportInteractionItem::ride },
|
||||
{ "water", ViewportInteractionItem::water },
|
||||
{ "scenery", ViewportInteractionItem::scenery },
|
||||
{ "footpath", ViewportInteractionItem::footpath },
|
||||
{ "footpath_item", ViewportInteractionItem::pathAddition },
|
||||
{ "park_entrance", ViewportInteractionItem::parkEntrance },
|
||||
{ "wall", ViewportInteractionItem::wall },
|
||||
{ "large_scenery", ViewportInteractionItem::largeScenery },
|
||||
{ "label", ViewportInteractionItem::label },
|
||||
{ "banner", ViewportInteractionItem::banner },
|
||||
};
|
||||
|
||||
static ViewportInteractionItem FilterJSValToEnum(JSContext* ctx, JSValue value)
|
||||
{
|
||||
if (JS_IsString(value))
|
||||
{
|
||||
if (auto val = ToolFilterMap.TryGet(JSToStdString(ctx, value)); val.has_value())
|
||||
{
|
||||
return val.value();
|
||||
}
|
||||
}
|
||||
return ViewportInteractionItem::none;
|
||||
}
|
||||
|
||||
static void RemoveMenuItemsAndTool(std::shared_ptr<Plugin> owner)
|
||||
@@ -157,13 +169,13 @@ namespace OpenRCT2::Scripting
|
||||
|
||||
void CustomTool::OnUpdate(const ScreenCoordsXY& screenCoords)
|
||||
{
|
||||
InvokeEventHandler(onMove, screenCoords);
|
||||
InvokeEventHandler(onMove.callback, screenCoords);
|
||||
}
|
||||
|
||||
void CustomTool::OnDown(const ScreenCoordsXY& screenCoords)
|
||||
{
|
||||
MouseDown = true;
|
||||
InvokeEventHandler(onDown, screenCoords);
|
||||
InvokeEventHandler(onDown.callback, screenCoords);
|
||||
}
|
||||
|
||||
void CustomTool::OnDrag(const ScreenCoordsXY& screenCoords)
|
||||
@@ -173,36 +185,36 @@ namespace OpenRCT2::Scripting
|
||||
void CustomTool::Start()
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
scriptEngine.ExecutePluginCall(Owner, onStart, {}, false);
|
||||
scriptEngine.ExecutePluginCall(Owner, onStart.callback, {}, false);
|
||||
}
|
||||
|
||||
void CustomTool::OnUp(const ScreenCoordsXY& screenCoords)
|
||||
{
|
||||
MouseDown = false;
|
||||
InvokeEventHandler(onUp, screenCoords);
|
||||
InvokeEventHandler(onUp.callback, screenCoords);
|
||||
}
|
||||
|
||||
void CustomTool::OnAbort()
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
scriptEngine.ExecutePluginCall(Owner, onFinish, {}, false);
|
||||
scriptEngine.ExecutePluginCall(Owner, onFinish.callback, {}, false);
|
||||
}
|
||||
|
||||
void CustomTool::InvokeEventHandler(const DukValue& dukHandler, const ScreenCoordsXY& screenCoords)
|
||||
void CustomTool::InvokeEventHandler(JSValue handler, const ScreenCoordsXY& screenCoords)
|
||||
{
|
||||
if (dukHandler.is_function())
|
||||
JSContext* ctx = Owner->GetContext();
|
||||
if (JS_IsFunction(ctx, handler))
|
||||
{
|
||||
auto ctx = dukHandler.context();
|
||||
auto info = GetMapCoordinatesFromPos(screenCoords, Filter);
|
||||
|
||||
DukObject obj(dukHandler.context());
|
||||
obj.Set("isDown", MouseDown);
|
||||
obj.Set("screenCoords", ToDuk(ctx, screenCoords));
|
||||
obj.Set("mapCoords", ToDuk(ctx, info.Loc));
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "isDown", JS_NewBool(ctx, MouseDown));
|
||||
JS_SetPropertyStr(ctx, obj, "screenCoords", ToJSValue(ctx, screenCoords));
|
||||
JS_SetPropertyStr(ctx, obj, "mapCoords", ToJSValue(ctx, info.Loc));
|
||||
|
||||
if (info.interactionType == ViewportInteractionItem::entity && info.Entity != nullptr)
|
||||
{
|
||||
obj.Set("entityId", info.Entity->Id.ToUnderlying());
|
||||
JS_SetPropertyStr(ctx, obj, "entityId", JS_NewInt32(ctx, info.Entity->Id.ToUnderlying()));
|
||||
}
|
||||
else if (info.Element != nullptr)
|
||||
{
|
||||
@@ -214,7 +226,7 @@ namespace OpenRCT2::Scripting
|
||||
{
|
||||
if (el == info.Element)
|
||||
{
|
||||
obj.Set("tileElementIndex", index);
|
||||
JS_SetPropertyStr(ctx, obj, "tileElementIndex", JS_NewInt32(ctx, index));
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
@@ -223,71 +235,75 @@ namespace OpenRCT2::Scripting
|
||||
}
|
||||
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
std::vector<DukValue> args;
|
||||
args.emplace_back(obj.Take());
|
||||
scriptEngine.ExecutePluginCall(Owner, dukHandler, args, false);
|
||||
scriptEngine.ExecutePluginCall(Owner, handler, { obj }, false);
|
||||
}
|
||||
}
|
||||
|
||||
void InitialiseCustomTool(ScriptEngine& scriptEngine, const DukValue& dukValue)
|
||||
[[nodiscard]] JSValue InitialiseCustomTool(ScriptEngine& scriptEngine, JSContext* ctx, JSValue value)
|
||||
{
|
||||
try
|
||||
std::shared_ptr<Plugin> currentPlugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
|
||||
if (JS_IsObject(value))
|
||||
{
|
||||
if (dukValue.type() == DukValue::Type::OBJECT)
|
||||
JSValue idVal = JS_GetPropertyStr(ctx, value, "id");
|
||||
if (!JS_IsString(idVal))
|
||||
{
|
||||
CustomTool customTool;
|
||||
customTool.Owner = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
customTool.Id = dukValue["id"].as_string();
|
||||
customTool.Cursor = FromDuk<CursorID>(dukValue["cursor"]);
|
||||
if (customTool.Cursor == CursorID::Undefined)
|
||||
{
|
||||
customTool.Cursor = CursorID::Arrow;
|
||||
}
|
||||
JS_FreeValue(ctx, idVal);
|
||||
JS_ThrowTypeError(ctx, "Invalid id. Expected string");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
auto dukFilter = dukValue["filter"];
|
||||
if (dukFilter.is_array())
|
||||
{
|
||||
customTool.Filter = 0;
|
||||
auto dukItems = dukFilter.as_array();
|
||||
for (const auto& dukItem : dukItems)
|
||||
{
|
||||
if (dukItem.type() == DukValue::Type::STRING)
|
||||
{
|
||||
auto value = ToolFilterMap[dukItem.as_string()];
|
||||
customTool.Filter |= static_cast<uint32_t>(EnumToFlag(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
customTool.Filter = kViewportInteractionItemAll;
|
||||
}
|
||||
CustomTool customTool;
|
||||
customTool.Owner = currentPlugin;
|
||||
|
||||
customTool.onStart = dukValue["onStart"];
|
||||
customTool.onDown = dukValue["onDown"];
|
||||
customTool.onMove = dukValue["onMove"];
|
||||
customTool.onUp = dukValue["onUp"];
|
||||
customTool.onFinish = dukValue["onFinish"];
|
||||
customTool.Id = JSToStdString(ctx, idVal);
|
||||
JS_FreeValue(ctx, idVal);
|
||||
|
||||
auto* windowMgr = GetWindowManager();
|
||||
auto toolbarWindow = windowMgr->FindByClass(WindowClass::topToolbar);
|
||||
if (toolbarWindow != nullptr)
|
||||
JSValue cursorVal = JS_GetPropertyStr(ctx, value, "cursor");
|
||||
customTool.Cursor = CursorJSValToID(ctx, cursorVal);
|
||||
JS_FreeValue(ctx, cursorVal);
|
||||
|
||||
JSValue filter = JS_GetPropertyStr(ctx, value, "filter");
|
||||
if (JS_IsArray(filter))
|
||||
{
|
||||
customTool.Filter = 0;
|
||||
int64_t len = -1;
|
||||
JS_GetLength(ctx, filter, &len);
|
||||
for (int64_t i = 0; i < len; i++)
|
||||
{
|
||||
// Use a widget that does not exist on top toolbar but also make sure it isn't
|
||||
// kWidgetIndexNull, as that prevents abort from being called.
|
||||
// TODO: refactor this to not leech on the top toolbar.
|
||||
WidgetIndex widgetIndex = 0xFFFE;
|
||||
ToolCancel();
|
||||
ToolSet(*toolbarWindow, widgetIndex, static_cast<Tool>(customTool.Cursor));
|
||||
ActiveCustomTool = std::move(customTool);
|
||||
ActiveCustomTool->Start();
|
||||
JSValue curFilter = JS_GetPropertyInt64(ctx, filter, i);
|
||||
auto elem = FilterJSValToEnum(ctx, curFilter);
|
||||
customTool.Filter |= static_cast<uint32_t>(EnumToFlag(elem));
|
||||
JS_FreeValue(ctx, curFilter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
customTool.Filter = kViewportInteractionItemAll;
|
||||
}
|
||||
JS_FreeValue(ctx, filter);
|
||||
|
||||
customTool.onStart = JSToCallback(ctx, value, "onStart");
|
||||
customTool.onDown = JSToCallback(ctx, value, "onDown");
|
||||
customTool.onMove = JSToCallback(ctx, value, "onMove");
|
||||
customTool.onUp = JSToCallback(ctx, value, "onUp");
|
||||
customTool.onFinish = JSToCallback(ctx, value, "onFinish");
|
||||
|
||||
auto* windowMgr = GetWindowManager();
|
||||
auto toolbarWindow = windowMgr->FindByClass(WindowClass::topToolbar);
|
||||
if (toolbarWindow != nullptr)
|
||||
{
|
||||
// Use a widget that does not exist on top toolbar but also make sure it isn't
|
||||
// kWidgetIndexNull, as that prevents abort from being called.
|
||||
// TODO: refactor this to not leech on the top toolbar.
|
||||
WidgetIndex widgetIndex = 0xFFFE;
|
||||
ToolCancel();
|
||||
ToolSet(*toolbarWindow, widgetIndex, static_cast<Tool>(customTool.Cursor));
|
||||
ActiveCustomTool = std::move(customTool);
|
||||
ActiveCustomTool->Start();
|
||||
}
|
||||
}
|
||||
catch (const DukException&)
|
||||
{
|
||||
duk_error(scriptEngine.GetContext(), DUK_ERR_ERROR, "Invalid parameters.");
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <memory>
|
||||
#include <openrct2/Context.h>
|
||||
#include <openrct2/interface/Cursors.h>
|
||||
#include <openrct2/scripting/Duktape.hpp>
|
||||
#include <openrct2/scripting/ScriptEngine.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -35,10 +34,10 @@ namespace OpenRCT2::Scripting
|
||||
std::shared_ptr<Plugin> Owner;
|
||||
CustomToolbarMenuItemKind Kind;
|
||||
std::string Text;
|
||||
DukValue Callback;
|
||||
JSCallback Callback;
|
||||
|
||||
CustomToolbarMenuItem(
|
||||
std::shared_ptr<Plugin> owner, CustomToolbarMenuItemKind kind, const std::string& text, DukValue callback)
|
||||
std::shared_ptr<Plugin> owner, CustomToolbarMenuItemKind kind, const std::string& text, JSCallback callback)
|
||||
: Owner(owner)
|
||||
, Kind(kind)
|
||||
, Text(text)
|
||||
@@ -49,7 +48,7 @@ namespace OpenRCT2::Scripting
|
||||
void Invoke() const
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
scriptEngine.ExecutePluginCall(Owner, Callback, {}, false);
|
||||
scriptEngine.ExecutePluginCall(Owner, Callback.callback, {}, false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -60,11 +59,11 @@ namespace OpenRCT2::Scripting
|
||||
std::string Id;
|
||||
std::string Text;
|
||||
std::vector<std::string> Bindings;
|
||||
DukValue Callback;
|
||||
JSCallback Callback;
|
||||
|
||||
CustomShortcut(
|
||||
std::shared_ptr<Plugin> owner, std::string_view id, std::string_view text, const std::vector<std::string>& bindings,
|
||||
DukValue callback);
|
||||
JSCallback callback);
|
||||
CustomShortcut(CustomShortcut&&) = default;
|
||||
CustomShortcut(const CustomShortcut&) = delete;
|
||||
~CustomShortcut();
|
||||
@@ -84,11 +83,11 @@ namespace OpenRCT2::Scripting
|
||||
bool MouseDown{};
|
||||
|
||||
// Event handlers
|
||||
DukValue onStart;
|
||||
DukValue onDown;
|
||||
DukValue onMove;
|
||||
DukValue onUp;
|
||||
DukValue onFinish;
|
||||
JSCallback onStart;
|
||||
JSCallback onDown;
|
||||
JSCallback onMove;
|
||||
JSCallback onUp;
|
||||
JSCallback onFinish;
|
||||
|
||||
void Start();
|
||||
void OnUpdate(const ScreenCoordsXY& screenCoords);
|
||||
@@ -98,7 +97,7 @@ namespace OpenRCT2::Scripting
|
||||
void OnAbort();
|
||||
|
||||
private:
|
||||
void InvokeEventHandler(const DukValue& dukHandler, const ScreenCoordsXY& screenCoords);
|
||||
void InvokeEventHandler(JSValue handler, const ScreenCoordsXY& screenCoords);
|
||||
};
|
||||
|
||||
extern std::optional<CustomTool> ActiveCustomTool;
|
||||
@@ -106,13 +105,9 @@ namespace OpenRCT2::Scripting
|
||||
extern std::vector<std::unique_ptr<CustomShortcut>> CustomShortcuts;
|
||||
|
||||
void InitialiseCustomMenuItems(ScriptEngine& scriptEngine);
|
||||
void InitialiseCustomTool(ScriptEngine& scriptEngine, const DukValue& dukValue);
|
||||
|
||||
template<>
|
||||
DukValue ToDuk(duk_context* ctx, const CursorID& value);
|
||||
template<>
|
||||
CursorID FromDuk(const DukValue& s);
|
||||
JSValue InitialiseCustomTool(ScriptEngine& scriptEngine, JSContext* ctx, JSValue value);
|
||||
|
||||
JSValue CursorIDToJSValue(JSContext* ctx, CursorID id);
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,13 +17,14 @@
|
||||
#include "../windows/Windows.h"
|
||||
#include "CustomListView.h"
|
||||
#include "ScUi.hpp"
|
||||
#include "ScWindow.hpp"
|
||||
#include "ScWindow.h"
|
||||
|
||||
#include <limits>
|
||||
#include <openrct2/SpriteIds.h>
|
||||
#include <openrct2/config/Config.h>
|
||||
#include <openrct2/drawing/Drawing.h>
|
||||
#include <openrct2/interface/ColourWithFlags.h>
|
||||
#include <openrct2/interface/Viewport.h>
|
||||
#include <openrct2/interface/Window.h>
|
||||
#include <openrct2/scripting/Plugin.h>
|
||||
#include <optional>
|
||||
@@ -82,114 +83,118 @@ namespace OpenRCT2::Ui::Windows
|
||||
bool CanSelect{};
|
||||
|
||||
// Event handlers
|
||||
DukValue OnClick;
|
||||
DukValue OnChange;
|
||||
DukValue OnDraw;
|
||||
DukValue OnIncrement;
|
||||
DukValue OnDecrement;
|
||||
DukValue OnHighlight;
|
||||
JSCallback OnClick;
|
||||
JSCallback OnChange;
|
||||
JSCallback OnDraw;
|
||||
JSCallback OnIncrement;
|
||||
JSCallback OnDecrement;
|
||||
JSCallback OnHighlight;
|
||||
|
||||
static CustomWidgetDesc FromDukValue(DukValue desc)
|
||||
static CustomWidgetDesc FromJSValue(JSContext* ctx, JSValue desc)
|
||||
{
|
||||
CustomWidgetDesc result;
|
||||
result.Type = desc["type"].as_string();
|
||||
result.X = desc["x"].as_int();
|
||||
result.Y = desc["y"].as_int();
|
||||
result.Width = desc["width"].as_int();
|
||||
result.Height = desc["height"].as_int();
|
||||
result.IsDisabled = AsOrDefault(desc["isDisabled"], false);
|
||||
result.IsVisible = AsOrDefault(desc["isVisible"], true);
|
||||
result.Name = AsOrDefault(desc["name"], "");
|
||||
result.Tooltip = AsOrDefault(desc["tooltip"], "");
|
||||
result.Type = JSToStdString(ctx, desc, "type");
|
||||
result.X = JSToInt(ctx, desc, "x");
|
||||
result.Y = JSToInt(ctx, desc, "y");
|
||||
result.Width = JSToInt(ctx, desc, "width");
|
||||
result.Height = JSToInt(ctx, desc, "height");
|
||||
result.IsDisabled = AsOrDefault(ctx, desc, "isDisabled", false);
|
||||
result.IsVisible = AsOrDefault(ctx, desc, "isVisible", true);
|
||||
result.Name = AsOrDefault(ctx, desc, "name", "");
|
||||
result.Tooltip = AsOrDefault(ctx, desc, "tooltip", "");
|
||||
if (result.Type == "button")
|
||||
{
|
||||
auto dukImage = desc["image"];
|
||||
if (dukImage.type() == DukValue::Type::STRING || dukImage.type() == DukValue::Type::NUMBER)
|
||||
JSValue jsImage = JS_GetPropertyStr(ctx, desc, "image");
|
||||
if (JS_IsString(jsImage) || JS_IsNumber(jsImage))
|
||||
{
|
||||
result.Image = ImageId(ImageFromDuk(dukImage));
|
||||
result.Image = ImageId(ImageFromJSValue(ctx, jsImage));
|
||||
result.HasBorder = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Text = ProcessString(desc["text"]);
|
||||
result.Text = AsOrDefault(ctx, desc, "text", "");
|
||||
result.HasBorder = true;
|
||||
}
|
||||
result.IsPressed = AsOrDefault(desc["isPressed"], false);
|
||||
result.OnClick = desc["onClick"];
|
||||
JS_FreeValue(ctx, jsImage);
|
||||
result.IsPressed = AsOrDefault(ctx, desc, "isPressed", false);
|
||||
result.OnClick = JSToCallback(ctx, desc, "onClick");
|
||||
}
|
||||
else if (result.Type == "checkbox")
|
||||
{
|
||||
result.Text = ProcessString(desc["text"]);
|
||||
result.IsChecked = AsOrDefault(desc["isChecked"], false);
|
||||
result.OnChange = desc["onChange"];
|
||||
result.Text = AsOrDefault(ctx, desc, "text", "");
|
||||
result.IsChecked = AsOrDefault(ctx, desc, "isChecked", false);
|
||||
result.OnChange = JSToCallback(ctx, desc, "onChange");
|
||||
}
|
||||
else if (result.Type == "colourpicker")
|
||||
{
|
||||
auto colour = AsOrDefault(desc["colour"], 0);
|
||||
auto colour = AsOrDefault(ctx, desc, "colour", 0);
|
||||
if (colour < kColourNumTotal)
|
||||
{
|
||||
result.Colour = static_cast<Drawing::Colour>(colour);
|
||||
}
|
||||
result.OnChange = desc["onChange"];
|
||||
result.OnChange = JSToCallback(ctx, desc, "onChange");
|
||||
}
|
||||
else if (result.Type == "custom")
|
||||
{
|
||||
result.OnDraw = desc["onDraw"];
|
||||
result.OnDraw = JSToCallback(ctx, desc, "onDraw");
|
||||
}
|
||||
else if (result.Type == "dropdown")
|
||||
{
|
||||
if (desc["items"].is_array())
|
||||
{
|
||||
auto dukItems = desc["items"].as_array();
|
||||
for (const auto& dukItem : dukItems)
|
||||
{
|
||||
result.Items.push_back(ProcessString(dukItem));
|
||||
}
|
||||
}
|
||||
result.SelectedIndex = AsOrDefault(desc["selectedIndex"], 0);
|
||||
result.OnChange = desc["onChange"];
|
||||
JSIterateArray(ctx, desc, "items", [&result](JSContext* ctx2, JSValue val) {
|
||||
result.Items.push_back(JSToStdString(ctx2, val));
|
||||
});
|
||||
result.SelectedIndex = AsOrDefault(ctx, desc, "selectedIndex", 0);
|
||||
result.OnChange = JSToCallback(ctx, desc, "onChange");
|
||||
}
|
||||
else if (result.Type == "groupbox")
|
||||
{
|
||||
result.Text = ProcessString(desc["text"]);
|
||||
result.Text = AsOrDefault(ctx, desc, "text", "");
|
||||
}
|
||||
else if (result.Type == "label")
|
||||
{
|
||||
result.Text = ProcessString(desc["text"]);
|
||||
if (ProcessString(desc["textAlign"]) == "centred")
|
||||
result.Text = AsOrDefault(ctx, desc, "text", "");
|
||||
if (JSToStdString(ctx, desc, "textAlign") == "centred")
|
||||
{
|
||||
result.TextAlign = TextAlignment::centre;
|
||||
}
|
||||
}
|
||||
else if (result.Type == "listview")
|
||||
{
|
||||
result.ListViewColumns = FromDuk<std::vector<ListViewColumn>>(desc["columns"]);
|
||||
result.ListViewItems = FromDuk<std::vector<ListViewItem>>(desc["items"]);
|
||||
result.SelectedCell = FromDuk<std::optional<RowColumn>>(desc["selectedCell"]);
|
||||
result.ShowColumnHeaders = AsOrDefault(desc["showColumnHeaders"], false);
|
||||
result.IsStriped = AsOrDefault(desc["isStriped"], false);
|
||||
result.OnClick = desc["onClick"];
|
||||
result.OnHighlight = desc["onHighlight"];
|
||||
result.CanSelect = AsOrDefault(desc["canSelect"], false);
|
||||
if (desc["scrollbars"].type() == DukValue::UNDEFINED)
|
||||
JSValue cols = JS_GetPropertyStr(ctx, desc, "columns");
|
||||
JSValue items = JS_GetPropertyStr(ctx, desc, "items");
|
||||
JSValue selected = JS_GetPropertyStr(ctx, desc, "selectedCell");
|
||||
JSValue scrollbars = JS_GetPropertyStr(ctx, desc, "scrollbars");
|
||||
result.ListViewColumns = ListViewColumnVecFromJS(ctx, cols);
|
||||
result.ListViewItems = ListViewItemVecFromJS(ctx, items);
|
||||
result.SelectedCell = RowColumnFromJS(ctx, selected);
|
||||
result.ShowColumnHeaders = AsOrDefault(ctx, desc, "showColumnHeaders", false);
|
||||
result.IsStriped = AsOrDefault(ctx, desc, "isStriped", false);
|
||||
result.OnClick = JSToCallback(ctx, desc, "onClick");
|
||||
result.OnHighlight = JSToCallback(ctx, desc, "onHighlight");
|
||||
result.CanSelect = AsOrDefault(ctx, desc, "canSelect", false);
|
||||
if (JS_IsUndefined(scrollbars))
|
||||
result.Scrollbars = ScrollbarType::Vertical;
|
||||
else
|
||||
result.Scrollbars = FromDuk<ScrollbarType>(desc["scrollbars"]);
|
||||
result.Scrollbars = ScrollbarTypeFromJS(ctx, scrollbars);
|
||||
JS_FreeValue(ctx, cols);
|
||||
JS_FreeValue(ctx, items);
|
||||
JS_FreeValue(ctx, selected);
|
||||
JS_FreeValue(ctx, scrollbars);
|
||||
}
|
||||
else if (result.Type == "spinner")
|
||||
{
|
||||
result.Text = ProcessString(desc["text"]);
|
||||
result.OnIncrement = desc["onIncrement"];
|
||||
result.OnDecrement = desc["onDecrement"];
|
||||
result.OnClick = desc["onClick"];
|
||||
result.Text = AsOrDefault(ctx, desc, "text", "");
|
||||
result.OnIncrement = JSToCallback(ctx, desc, "onIncrement");
|
||||
result.OnDecrement = JSToCallback(ctx, desc, "onDecrement");
|
||||
result.OnClick = JSToCallback(ctx, desc, "onClick");
|
||||
}
|
||||
else if (result.Type == "textbox")
|
||||
{
|
||||
result.Text = ProcessString(desc["text"]);
|
||||
result.MaxLength = AsOrDefault(desc["maxLength"], 32);
|
||||
result.OnChange = desc["onChange"];
|
||||
result.Text = AsOrDefault(ctx, desc, "text", "");
|
||||
result.MaxLength = AsOrDefault(ctx, desc, "maxLength", 32);
|
||||
result.OnChange = JSToCallback(ctx, desc, "onChange");
|
||||
}
|
||||
result.HasBorder = AsOrDefault(desc["border"], result.HasBorder);
|
||||
result.HasBorder = AsOrDefault(ctx, desc, "border", result.HasBorder);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
@@ -202,53 +207,53 @@ namespace OpenRCT2::Ui::Windows
|
||||
ScreenCoordsXY offset;
|
||||
std::vector<CustomWidgetDesc> Widgets;
|
||||
|
||||
static CustomTabDesc FromDukValue(const DukValue& desc)
|
||||
static CustomTabDesc FromJSValue(JSContext* ctx, JSValue desc)
|
||||
{
|
||||
CustomTabDesc result;
|
||||
auto dukImage = desc["image"];
|
||||
if (dukImage.type() == DukValue::Type::STRING || dukImage.type() == DukValue::Type::NUMBER)
|
||||
JSValue jsImage = JS_GetPropertyStr(ctx, desc, "image");
|
||||
if (JS_IsString(jsImage) || JS_IsNumber(jsImage))
|
||||
{
|
||||
result.imageFrameBase = ImageId(ImageFromDuk(dukImage));
|
||||
result.imageFrameBase = ImageId(ImageFromJSValue(ctx, jsImage));
|
||||
result.imageFrameCount = 0;
|
||||
result.imageFrameDuration = 0;
|
||||
}
|
||||
else if (dukImage.type() == DukValue::Type::OBJECT)
|
||||
else if (JS_IsObject(jsImage))
|
||||
{
|
||||
result.imageFrameBase = ImageId(dukImage["frameBase"].as_uint());
|
||||
result.imageFrameCount = AsOrDefault(dukImage["frameCount"], 0);
|
||||
result.imageFrameDuration = AsOrDefault(dukImage["frameDuration"], 0);
|
||||
result.imageFrameBase = ImageId(JSToInt64(ctx, jsImage, "frameBase"));
|
||||
result.imageFrameCount = AsOrDefault(ctx, jsImage, "frameCount", 0);
|
||||
result.imageFrameDuration = AsOrDefault(ctx, jsImage, "frameDuration", 0);
|
||||
|
||||
if (dukImage["primaryColour"].type() == DukValue::Type::NUMBER)
|
||||
auto primaryCol = JSToOptionalInt64(ctx, jsImage, "primaryColour");
|
||||
if (primaryCol.has_value())
|
||||
{
|
||||
result.imageFrameBase = result.imageFrameBase.WithPrimary(
|
||||
static_cast<Colour>(dukImage["primaryColour"].as_uint()));
|
||||
result.imageFrameBase = result.imageFrameBase.WithPrimary(static_cast<Colour>(primaryCol.value()));
|
||||
|
||||
if (dukImage["secondaryColour"].type() == DukValue::Type::NUMBER)
|
||||
auto secondaryCol = JSToOptionalInt64(ctx, jsImage, "secondaryColour");
|
||||
if (secondaryCol.has_value())
|
||||
{
|
||||
result.imageFrameBase = result.imageFrameBase.WithSecondary(
|
||||
static_cast<Colour>(dukImage["secondaryColour"].as_uint()));
|
||||
result.imageFrameBase = result.imageFrameBase.WithSecondary(static_cast<Colour>(secondaryCol.value()));
|
||||
|
||||
if (dukImage["tertiaryColour"].type() == DukValue::Type::NUMBER)
|
||||
auto tertiaryCol = JSToOptionalInt64(ctx, jsImage, "tertiaryColour");
|
||||
if (tertiaryCol.has_value())
|
||||
{
|
||||
result.imageFrameBase = result.imageFrameBase.WithTertiary(
|
||||
static_cast<Colour>(dukImage["tertiaryColour"].as_uint()));
|
||||
static_cast<Colour>(tertiaryCol.value()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto dukCoord = dukImage["offset"];
|
||||
if (dukCoord.type() == DukValue::Type::OBJECT)
|
||||
JSValue jsCoord = JS_GetPropertyStr(ctx, jsImage, "offset");
|
||||
if (JS_IsObject(jsCoord))
|
||||
{
|
||||
result.offset = { AsOrDefault(dukCoord["x"], 0), AsOrDefault(dukCoord["y"], 0) };
|
||||
result.offset = { AsOrDefault(ctx, jsCoord, "x", 0), AsOrDefault(ctx, jsCoord, "y", 0) };
|
||||
}
|
||||
JS_FreeValue(ctx, jsCoord);
|
||||
}
|
||||
if (desc["widgets"].is_array())
|
||||
{
|
||||
auto dukWidgets = desc["widgets"].as_array();
|
||||
std::transform(dukWidgets.begin(), dukWidgets.end(), std::back_inserter(result.Widgets), [](const DukValue& w) {
|
||||
return CustomWidgetDesc::FromDukValue(w);
|
||||
});
|
||||
}
|
||||
JS_FreeValue(ctx, jsImage);
|
||||
|
||||
JSIterateArray(ctx, desc, "widgets", [&result](JSContext* ctx2, JSValue x) {
|
||||
result.Widgets.push_back(CustomWidgetDesc::FromJSValue(ctx2, x));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
@@ -271,9 +276,9 @@ namespace OpenRCT2::Ui::Windows
|
||||
std::optional<int32_t> TabIndex;
|
||||
|
||||
// Event handlers
|
||||
DukValue OnClose;
|
||||
DukValue OnUpdate;
|
||||
DukValue OnTabChange;
|
||||
JSCallback OnClose;
|
||||
JSCallback OnUpdate;
|
||||
JSCallback OnTabChange;
|
||||
|
||||
CustomWindowDesc() = default;
|
||||
|
||||
@@ -282,65 +287,49 @@ namespace OpenRCT2::Ui::Windows
|
||||
return MinWidth || MinHeight || MaxWidth || MaxHeight;
|
||||
}
|
||||
|
||||
static CustomWindowDesc FromDukValue(DukValue desc)
|
||||
static CustomWindowDesc FromJSValue(JSContext* ctx, JSValue desc)
|
||||
{
|
||||
CustomWindowDesc result;
|
||||
result.Classification = desc["classification"].as_string();
|
||||
result.X = GetOptionalInt(desc["x"]);
|
||||
result.Y = GetOptionalInt(desc["y"]);
|
||||
result.size.width = desc["width"].as_int();
|
||||
result.size.height = desc["height"].as_int();
|
||||
result.MinWidth = GetOptionalInt(desc["minWidth"]);
|
||||
result.MaxWidth = GetOptionalInt(desc["maxWidth"]);
|
||||
result.MinHeight = GetOptionalInt(desc["minHeight"]);
|
||||
result.MaxHeight = GetOptionalInt(desc["maxHeight"]);
|
||||
result.Title = desc["title"].as_string();
|
||||
result.Id = GetOptionalInt(desc["id"]);
|
||||
result.TabIndex = GetOptionalInt(desc["tabIndex"]);
|
||||
result.Classification = JSToStdString(ctx, desc, "classification");
|
||||
result.X = JSToOptionalInt(ctx, desc, "x");
|
||||
result.Y = JSToOptionalInt(ctx, desc, "y");
|
||||
result.size.width = JSToInt(ctx, desc, "width");
|
||||
result.size.height = JSToInt(ctx, desc, "height");
|
||||
result.MinWidth = JSToOptionalInt(ctx, desc, "minWidth");
|
||||
result.MaxWidth = JSToOptionalInt(ctx, desc, "maxWidth");
|
||||
result.MinHeight = JSToOptionalInt(ctx, desc, "minHeight");
|
||||
result.MaxHeight = JSToOptionalInt(ctx, desc, "maxHeight");
|
||||
result.Title = JSToStdString(ctx, desc, "title");
|
||||
result.Id = JSToOptionalInt(ctx, desc, "id");
|
||||
result.TabIndex = JSToOptionalInt(ctx, desc, "tabIndex");
|
||||
|
||||
if (desc["widgets"].is_array())
|
||||
{
|
||||
auto dukWidgets = desc["widgets"].as_array();
|
||||
std::transform(dukWidgets.begin(), dukWidgets.end(), std::back_inserter(result.Widgets), [](const DukValue& w) {
|
||||
return CustomWidgetDesc::FromDukValue(w);
|
||||
});
|
||||
}
|
||||
JSIterateArray(ctx, desc, "widgets", [&result](JSContext* ctx2, JSValue val) {
|
||||
result.Widgets.push_back(CustomWidgetDesc::FromJSValue(ctx2, val));
|
||||
});
|
||||
|
||||
if (desc["tabs"].is_array())
|
||||
{
|
||||
auto dukTabs = desc["tabs"].as_array();
|
||||
std::transform(dukTabs.begin(), dukTabs.end(), std::back_inserter(result.Tabs), [](const DukValue& w) {
|
||||
return CustomTabDesc::FromDukValue(w);
|
||||
});
|
||||
}
|
||||
JSIterateArray(ctx, desc, "tabs", [&result](JSContext* ctx2, JSValue x) {
|
||||
result.Tabs.push_back(CustomTabDesc::FromJSValue(ctx2, x));
|
||||
});
|
||||
|
||||
if (desc["colours"].is_array())
|
||||
{
|
||||
auto dukColours = desc["colours"].as_array();
|
||||
std::transform(dukColours.begin(), dukColours.end(), std::back_inserter(result.Colours), [](const DukValue& w) {
|
||||
ColourWithFlags c = { Colour::black };
|
||||
if (w.type() == DukValue::Type::NUMBER)
|
||||
{
|
||||
uint8_t colour = (w.as_uint() & ~kLegacyColourFlagTranslucent) % kColourNumTotal;
|
||||
bool isTranslucent = (w.as_uint() & kLegacyColourFlagTranslucent);
|
||||
c.colour = static_cast<Colour>(colour);
|
||||
c.flags.set(ColourFlag::translucent, isTranslucent);
|
||||
}
|
||||
return c;
|
||||
});
|
||||
}
|
||||
JSIterateArray(ctx, desc, "colours", [&result](JSContext* ctx2, JSValue x) {
|
||||
ColourWithFlags c = { Colour::black };
|
||||
if (JS_IsNumber(x))
|
||||
{
|
||||
int32_t xValue = JSToInt(ctx2, x);
|
||||
uint8_t colour = (xValue & ~kLegacyColourFlagTranslucent) % kColourNumTotal;
|
||||
bool isTranslucent = (xValue & kLegacyColourFlagTranslucent);
|
||||
c.colour = static_cast<Colour>(colour);
|
||||
c.flags.set(ColourFlag::translucent, isTranslucent);
|
||||
}
|
||||
result.Colours.push_back(c);
|
||||
});
|
||||
|
||||
result.OnClose = desc["onClose"];
|
||||
result.OnUpdate = desc["onUpdate"];
|
||||
result.OnTabChange = desc["onTabChange"];
|
||||
result.OnClose = JSToCallback(ctx, desc, "onClose");
|
||||
result.OnUpdate = JSToCallback(ctx, desc, "onUpdate");
|
||||
result.OnTabChange = JSToCallback(ctx, desc, "onTabChange");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::optional<int32_t> GetOptionalInt(DukValue input)
|
||||
{
|
||||
return input.type() == DukValue::Type::NUMBER ? std::make_optional(input.as_int()) : std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
class CustomWindowInfo
|
||||
@@ -392,9 +381,9 @@ namespace OpenRCT2::Ui::Windows
|
||||
|
||||
class CustomWindow;
|
||||
static CustomWindowInfo& GetInfo(CustomWindow* w);
|
||||
static void InvokeEventHandler(const std::shared_ptr<Plugin>& owner, const DukValue& dukHandler);
|
||||
static void InvokeEventHandler(const std::shared_ptr<Plugin>& owner, const JSCallback& jsCallback);
|
||||
static void InvokeEventHandler(
|
||||
const std::shared_ptr<Plugin>& owner, const DukValue& dukHandler, const std::vector<DukValue>& args);
|
||||
const std::shared_ptr<Plugin>& owner, const JSCallback& jsCallback, const std::vector<JSValue>& args);
|
||||
|
||||
class CustomWindow final : public Window
|
||||
{
|
||||
@@ -559,18 +548,19 @@ namespace OpenRCT2::Ui::Windows
|
||||
if (widgetDesc != nullptr && widgetDesc->Type == "custom")
|
||||
{
|
||||
auto& onDraw = widgetDesc->OnDraw;
|
||||
if (onDraw.is_function())
|
||||
if (onDraw.IsValid())
|
||||
{
|
||||
RenderTarget widgetRT;
|
||||
if (ClipRenderTarget(
|
||||
widgetRT, rt, { windowPos.x + widget.left, windowPos.y + widget.top }, widget.width() - 1,
|
||||
widget.height() - 1))
|
||||
{
|
||||
auto ctx = onDraw.context();
|
||||
auto dukWidget = ScWidget::ToDukValue(ctx, this, widgetIndex);
|
||||
auto dukG = GetObjectAsDukValue(ctx, std::make_shared<ScGraphicsContext>(ctx, widgetRT));
|
||||
auto ctx = onDraw.context;
|
||||
auto jsWidget = gScWidget.New(ctx, this, widgetIndex);
|
||||
auto gfx = gScGraphicsContext.New(ctx, widgetRT);
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
scriptEngine.ExecutePluginCall(_info.Owner, widgetDesc->OnDraw, dukWidget, { dukG }, false);
|
||||
scriptEngine.ExecutePluginCall(_info.Owner, onDraw.callback, jsWidget, { gfx }, false);
|
||||
JS_FreeValue(ctx, jsWidget);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -614,10 +604,8 @@ namespace OpenRCT2::Ui::Windows
|
||||
|
||||
widgetSetCheckboxValue(*this, widgetIndex, isChecked);
|
||||
|
||||
std::vector<DukValue> args;
|
||||
auto ctx = widgetDesc->OnChange.context();
|
||||
duk_push_boolean(ctx, isChecked);
|
||||
args.push_back(DukValue::take_from_stack(ctx));
|
||||
std::vector<JSValue> args;
|
||||
args.push_back(JS_NewBool(widgetDesc->OnChange.context, isChecked));
|
||||
InvokeEventHandler(_info.Owner, widgetDesc->OnChange, args);
|
||||
}
|
||||
else if (widgetDesc->Type == "spinner")
|
||||
@@ -707,10 +695,9 @@ namespace OpenRCT2::Ui::Windows
|
||||
{
|
||||
UpdateWidgetText(this, widgetIndex, text);
|
||||
|
||||
std::vector<DukValue> args;
|
||||
auto ctx = widgetDesc->OnChange.context();
|
||||
duk_push_lstring(ctx, text.data(), text.size());
|
||||
args.push_back(DukValue::take_from_stack(ctx));
|
||||
std::vector<JSValue> args;
|
||||
std::string textStr(text);
|
||||
args.push_back(JSFromStdString(widgetDesc->OnChange.context, textStr));
|
||||
InvokeEventHandler(_info.Owner, widgetDesc->OnChange, args);
|
||||
}
|
||||
}
|
||||
@@ -1127,9 +1114,9 @@ namespace OpenRCT2::Ui::Windows
|
||||
|
||||
WindowNumber CustomWindow::_nextWindowNumber;
|
||||
|
||||
WindowBase* WindowCustomOpen(std::shared_ptr<Plugin> owner, DukValue dukDesc)
|
||||
WindowBase* WindowCustomOpen(JSContext* ctx, std::shared_ptr<Plugin> owner, JSValue descVal)
|
||||
{
|
||||
auto desc = CustomWindowDesc::FromDukValue(dukDesc);
|
||||
auto desc = CustomWindowDesc::FromJSValue(ctx, descVal);
|
||||
WindowFlags windowFlags = { WindowFlag::resizable, WindowFlag::transparent };
|
||||
auto* windowMgr = GetWindowManager();
|
||||
|
||||
@@ -1151,17 +1138,17 @@ namespace OpenRCT2::Ui::Windows
|
||||
return w->getInfo();
|
||||
}
|
||||
|
||||
static void InvokeEventHandler(const std::shared_ptr<Plugin>& owner, const DukValue& dukHandler)
|
||||
static void InvokeEventHandler(const std::shared_ptr<Plugin>& owner, const JSCallback& jsCallback)
|
||||
{
|
||||
std::vector<DukValue> args;
|
||||
InvokeEventHandler(owner, dukHandler, args);
|
||||
std::vector<JSValue> args;
|
||||
InvokeEventHandler(owner, jsCallback, args);
|
||||
}
|
||||
|
||||
static void InvokeEventHandler(
|
||||
const std::shared_ptr<Plugin>& owner, const DukValue& dukHandler, const std::vector<DukValue>& args)
|
||||
const std::shared_ptr<Plugin>& owner, const JSCallback& jsCallback, const std::vector<JSValue>& args)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
scriptEngine.ExecutePluginCall(owner, dukHandler, args, false);
|
||||
scriptEngine.ExecutePluginCall(owner, jsCallback.callback, args, false);
|
||||
}
|
||||
|
||||
std::string GetWindowTitle(WindowBase* w)
|
||||
@@ -1245,10 +1232,8 @@ namespace OpenRCT2::Ui::Windows
|
||||
auto* windowMgr = GetWindowManager();
|
||||
windowMgr->InvalidateWidget(*w, widgetIndex);
|
||||
|
||||
std::vector<DukValue> args;
|
||||
auto ctx = customWidgetInfo->OnChange.context();
|
||||
duk_push_int(ctx, EnumValue(colour));
|
||||
args.push_back(DukValue::take_from_stack(ctx));
|
||||
std::vector<JSValue> args;
|
||||
args.push_back(JS_NewInt32(customWidgetInfo->OnChange.context, EnumValue(colour)));
|
||||
InvokeEventHandler(customInfo.Owner, customWidgetInfo->OnChange, args);
|
||||
}
|
||||
}
|
||||
@@ -1293,10 +1278,8 @@ namespace OpenRCT2::Ui::Windows
|
||||
|
||||
if (lastSelectedIndex != selectedIndex)
|
||||
{
|
||||
std::vector<DukValue> args;
|
||||
auto ctx = customWidgetInfo->OnChange.context();
|
||||
duk_push_int(ctx, selectedIndex);
|
||||
args.push_back(DukValue::take_from_stack(ctx));
|
||||
std::vector<JSValue> args;
|
||||
args.push_back(JS_NewInt32(customWidgetInfo->OnChange.context, selectedIndex));
|
||||
InvokeEventHandler(customInfo.Owner, customWidgetInfo->OnChange, args);
|
||||
}
|
||||
}
|
||||
@@ -1499,7 +1482,6 @@ namespace OpenRCT2::Ui::Windows
|
||||
windowMgr->Close(*window);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OpenRCT2::Ui::Windows
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "../interface/Window.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
@@ -40,7 +38,8 @@ namespace OpenRCT2::Ui::Windows
|
||||
CustomListView* GetCustomListView(WindowBase* w, WidgetIndex widgetIndex);
|
||||
int32_t GetWidgetMaxLength(WindowBase* w, WidgetIndex widgetIndex);
|
||||
void SetWidgetMaxLength(WindowBase* w, WidgetIndex widgetIndex, int32_t value);
|
||||
void CloseWindowsOwnedByPlugin(std::shared_ptr<Plugin> plugin);
|
||||
void CloseWindowsOwnedByPlugin(std::shared_ptr<Scripting::Plugin> plugin);
|
||||
WindowBase* WindowCustomOpen(JSContext* ctx, std::shared_ptr<Scripting::Plugin> owner, JSValue descVal);
|
||||
} // namespace OpenRCT2::Ui::Windows
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,230 +18,321 @@
|
||||
#include <openrct2/drawing/Rectangle.h>
|
||||
#include <openrct2/drawing/RenderTarget.h>
|
||||
#include <openrct2/drawing/Text.h>
|
||||
#include <openrct2/scripting/Duktape.hpp>
|
||||
#include <openrct2/scripting/ScriptEngine.h>
|
||||
#include <quickjs.h>
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScGraphicsContext
|
||||
class ScGraphicsContext;
|
||||
extern ScGraphicsContext gScGraphicsContext;
|
||||
class ScGraphicsContext final : public ScBase
|
||||
{
|
||||
private:
|
||||
duk_context* _ctx{};
|
||||
Drawing::RenderTarget _rt{};
|
||||
struct GraphicsData
|
||||
{
|
||||
Drawing::RenderTarget _rt{};
|
||||
|
||||
std::optional<uint8_t> _colour{};
|
||||
std::optional<uint8_t> _secondaryColour{};
|
||||
std::optional<uint8_t> _tertiaryColour{};
|
||||
std::optional<uint8_t> _paletteId{};
|
||||
Drawing::PaletteIndex _stroke{};
|
||||
Drawing::PaletteIndex _fill{};
|
||||
std::optional<uint8_t> _colour{};
|
||||
std::optional<uint8_t> _secondaryColour{};
|
||||
std::optional<uint8_t> _tertiaryColour{};
|
||||
std::optional<uint8_t> _paletteId{};
|
||||
Drawing::PaletteIndex _stroke{};
|
||||
Drawing::PaletteIndex _fill{};
|
||||
};
|
||||
|
||||
public:
|
||||
ScGraphicsContext(duk_context* ctx, const Drawing::RenderTarget& rt)
|
||||
: _ctx(ctx)
|
||||
, _rt(rt)
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "GraphicsContext", Finalize);
|
||||
}
|
||||
|
||||
static void Register(duk_context* ctx)
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScGraphicsContext::colour_get, &ScGraphicsContext::colour_set, "colour");
|
||||
dukglue_register_property(
|
||||
ctx, &ScGraphicsContext::secondaryColour_get, &ScGraphicsContext::secondaryColour_set, "secondaryColour");
|
||||
dukglue_register_property(
|
||||
ctx, &ScGraphicsContext::tertiaryColour_get, &ScGraphicsContext::tertiaryColour_set, "ternaryColour");
|
||||
dukglue_register_property(
|
||||
ctx, &ScGraphicsContext::tertiaryColour_get, &ScGraphicsContext::tertiaryColour_set, "tertiaryColour");
|
||||
dukglue_register_property(ctx, &ScGraphicsContext::paletteId_get, &ScGraphicsContext::paletteId_set, "paletteId");
|
||||
dukglue_register_property(ctx, &ScGraphicsContext::fill_get, &ScGraphicsContext::fill_set, "fill");
|
||||
dukglue_register_property(ctx, &ScGraphicsContext::stroke_get, &ScGraphicsContext::stroke_set, "stroke");
|
||||
dukglue_register_property(ctx, &ScGraphicsContext::width_get, nullptr, "width");
|
||||
dukglue_register_property(ctx, &ScGraphicsContext::height_get, nullptr, "height");
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
if (data)
|
||||
delete data;
|
||||
}
|
||||
|
||||
dukglue_register_method(ctx, &ScGraphicsContext::getImage, "getImage");
|
||||
dukglue_register_method(ctx, &ScGraphicsContext::measureText, "measureText");
|
||||
JSValue New(JSContext* ctx, const Drawing::RenderTarget& rt)
|
||||
{
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("colour", ScGraphicsContext::colour_get, ScGraphicsContext::colour_set),
|
||||
JS_CGETSET_DEF(
|
||||
"secondaryColour", ScGraphicsContext::secondaryColour_get, ScGraphicsContext::secondaryColour_set),
|
||||
JS_CGETSET_DEF("ternaryColour", ScGraphicsContext::tertiaryColour_get, ScGraphicsContext::tertiaryColour_set),
|
||||
JS_CGETSET_DEF("tertiaryColour", ScGraphicsContext::tertiaryColour_get, ScGraphicsContext::tertiaryColour_set),
|
||||
JS_CGETSET_DEF("paletteId", ScGraphicsContext::paletteId_get, ScGraphicsContext::paletteId_set),
|
||||
JS_CGETSET_DEF("fill", ScGraphicsContext::fill_get, ScGraphicsContext::fill_set),
|
||||
JS_CGETSET_DEF("stroke", ScGraphicsContext::stroke_get, ScGraphicsContext::stroke_set),
|
||||
JS_CGETSET_DEF("width", ScGraphicsContext::width_get, nullptr),
|
||||
JS_CGETSET_DEF("height", ScGraphicsContext::height_get, nullptr),
|
||||
|
||||
dukglue_register_method(ctx, &ScGraphicsContext::box, "box");
|
||||
dukglue_register_method(ctx, &ScGraphicsContext::clear, "clear");
|
||||
dukglue_register_method(ctx, &ScGraphicsContext::clip, "clip");
|
||||
dukglue_register_method(ctx, &ScGraphicsContext::image, "image");
|
||||
dukglue_register_method(ctx, &ScGraphicsContext::line, "line");
|
||||
dukglue_register_method(ctx, &ScGraphicsContext::rect, "rect");
|
||||
dukglue_register_method(ctx, &ScGraphicsContext::text, "text");
|
||||
dukglue_register_method(ctx, &ScGraphicsContext::well, "well");
|
||||
JS_CFUNC_DEF("getImage", 1, ScGraphicsContext::getImage),
|
||||
JS_CFUNC_DEF("measureText", 1, ScGraphicsContext::measureText),
|
||||
|
||||
JS_CFUNC_DEF("box", 4, ScGraphicsContext::box),
|
||||
JS_CFUNC_DEF("clear", 0, ScGraphicsContext::clear),
|
||||
JS_CFUNC_DEF("clip", 4, ScGraphicsContext::clip),
|
||||
JS_CFUNC_DEF("image", 3, ScGraphicsContext::image),
|
||||
JS_CFUNC_DEF("line", 4, ScGraphicsContext::lineJS),
|
||||
JS_CFUNC_DEF("rect", 4, ScGraphicsContext::rect),
|
||||
JS_CFUNC_DEF("text", 3, ScGraphicsContext::text),
|
||||
JS_CFUNC_DEF("well", 4, ScGraphicsContext::well),
|
||||
};
|
||||
return MakeWithOpaque(ctx, funcs, new GraphicsData{ rt });
|
||||
}
|
||||
|
||||
private:
|
||||
DukValue colour_get() const
|
||||
static JSValue colour_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return ToDuk(_ctx, _colour);
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
return ToJSValue(ctx, data->_colour);
|
||||
}
|
||||
|
||||
void colour_set(DukValue value)
|
||||
static JSValue colour_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
if (value.type() == DukValue::NUMBER)
|
||||
_colour = static_cast<uint8_t>(value.as_uint());
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
if (JS_IsNumber(value))
|
||||
data->_colour = static_cast<uint8_t>(JSToInt(ctx, value));
|
||||
else
|
||||
_colour = {};
|
||||
data->_colour = {};
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
DukValue secondaryColour_get() const
|
||||
static JSValue secondaryColour_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return ToDuk(_ctx, _secondaryColour);
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
return ToJSValue(ctx, data->_secondaryColour);
|
||||
}
|
||||
|
||||
void secondaryColour_set(DukValue value)
|
||||
static JSValue secondaryColour_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
if (value.type() == DukValue::NUMBER)
|
||||
_secondaryColour = static_cast<uint8_t>(value.as_uint());
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
if (JS_IsNumber(value))
|
||||
data->_secondaryColour = static_cast<uint8_t>(JSToInt(ctx, value));
|
||||
else
|
||||
_secondaryColour = {};
|
||||
data->_secondaryColour = {};
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
DukValue tertiaryColour_get() const
|
||||
static JSValue tertiaryColour_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return ToDuk(_ctx, _tertiaryColour);
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
return ToJSValue(ctx, data->_tertiaryColour);
|
||||
}
|
||||
|
||||
void tertiaryColour_set(DukValue value)
|
||||
static JSValue tertiaryColour_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
if (value.type() == DukValue::NUMBER)
|
||||
_tertiaryColour = static_cast<uint8_t>(value.as_uint());
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
if (JS_IsNumber(value))
|
||||
data->_tertiaryColour = static_cast<uint8_t>(JSToInt(ctx, value));
|
||||
else
|
||||
_tertiaryColour = {};
|
||||
data->_tertiaryColour = {};
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
DukValue paletteId_get() const
|
||||
static JSValue paletteId_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return ToDuk(_ctx, _paletteId);
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
return ToJSValue(ctx, data->_paletteId);
|
||||
}
|
||||
|
||||
void paletteId_set(DukValue value)
|
||||
static JSValue paletteId_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
if (value.type() == DukValue::NUMBER)
|
||||
_paletteId = static_cast<uint8_t>(value.as_uint());
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
if (JS_IsNumber(value))
|
||||
data->_paletteId = static_cast<uint8_t>(JSToInt(ctx, value));
|
||||
else
|
||||
_paletteId = {};
|
||||
data->_paletteId = {};
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint8_t fill_get() const
|
||||
static JSValue fill_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return EnumValue(_fill);
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
return JS_NewInt32(ctx, EnumValue(data->_fill));
|
||||
}
|
||||
|
||||
void fill_set(uint8_t value)
|
||||
static JSValue fill_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
_fill = static_cast<Drawing::PaletteIndex>(value);
|
||||
JS_UNPACK_INT32(valueInt, ctx, value)
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
data->_fill = static_cast<Drawing::PaletteIndex>(valueInt);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint8_t stroke_get() const
|
||||
static JSValue stroke_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return EnumValue(_stroke);
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
return JS_NewInt32(ctx, EnumValue(data->_stroke));
|
||||
}
|
||||
|
||||
void stroke_set(uint8_t value)
|
||||
static JSValue stroke_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
_stroke = static_cast<Drawing::PaletteIndex>(value);
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
data->_stroke = static_cast<Drawing::PaletteIndex>(valueInt);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
int32_t width_get() const
|
||||
static JSValue width_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return _rt.width;
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
return JS_NewInt32(ctx, data->_rt.width);
|
||||
}
|
||||
|
||||
int32_t height_get() const
|
||||
static JSValue height_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return _rt.height;
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
return JS_NewInt32(ctx, data->_rt.height);
|
||||
}
|
||||
|
||||
DukValue getImage(uint32_t id)
|
||||
static JSValue getImage(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
return DukGetImageInfo(_ctx, id);
|
||||
JS_UNPACK_UINT32(id, ctx, argv[0]);
|
||||
|
||||
return JSGetImageInfo(ctx, id);
|
||||
}
|
||||
|
||||
DukValue measureText(const std::string& text)
|
||||
static JSValue measureText(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_STR(text, ctx, argv[0])
|
||||
|
||||
auto width = Drawing::getStringWidth(text, FontStyle::medium);
|
||||
auto height = Drawing::getStringHeightRaw(text.c_str(), FontStyle::medium);
|
||||
return ToDuk<ScreenSize>(_ctx, { width, height });
|
||||
return ToJSValue(ctx, ScreenSize{ width, height });
|
||||
}
|
||||
|
||||
void box(int32_t x, int32_t y, int32_t width, int32_t height)
|
||||
static JSValue box(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_INT32(x, ctx, argv[0]);
|
||||
JS_UNPACK_INT32(y, ctx, argv[1]);
|
||||
JS_UNPACK_INT32(width, ctx, argv[2]);
|
||||
JS_UNPACK_INT32(height, ctx, argv[3]);
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
|
||||
Drawing::Rectangle::fillInset(
|
||||
_rt, { x, y, x + width - 1, y + height - 1 }, { static_cast<Drawing::Colour>(_colour.value_or(0)) });
|
||||
data->_rt, { x, y, x + width - 1, y + height - 1 }, { static_cast<Drawing::Colour>(data->_colour.value_or(0)) });
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void well(int32_t x, int32_t y, int32_t width, int32_t height)
|
||||
static JSValue well(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_INT32(x, ctx, argv[0]);
|
||||
JS_UNPACK_INT32(y, ctx, argv[1]);
|
||||
JS_UNPACK_INT32(width, ctx, argv[2]);
|
||||
JS_UNPACK_INT32(height, ctx, argv[3]);
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
|
||||
Drawing::Rectangle::fillInset(
|
||||
_rt, { x, y, x + width - 1, y + height - 1 }, { static_cast<Drawing::Colour>(_colour.value_or(0)) },
|
||||
data->_rt, { x, y, x + width - 1, y + height - 1 }, { static_cast<Drawing::Colour>(data->_colour.value_or(0)) },
|
||||
Drawing::Rectangle::BorderStyle::inset, Drawing::Rectangle::FillBrightness::light,
|
||||
Drawing::Rectangle::FillMode::dontLightenWhenInset);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void clear()
|
||||
static JSValue clear(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
GfxClear(_rt, _fill);
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
GfxClear(data->_rt, data->_fill);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void clip(int32_t x, int32_t y, int32_t width, int32_t height)
|
||||
static JSValue clip(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_INT32(x, ctx, argv[0]);
|
||||
JS_UNPACK_INT32(y, ctx, argv[1]);
|
||||
JS_UNPACK_INT32(width, ctx, argv[2]);
|
||||
JS_UNPACK_INT32(height, ctx, argv[3]);
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
|
||||
Drawing::RenderTarget newRT;
|
||||
ClipRenderTarget(newRT, _rt, { x, y }, width, height);
|
||||
_rt = newRT;
|
||||
ClipRenderTarget(newRT, data->_rt, { x, y }, width, height);
|
||||
data->_rt = newRT;
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void image(uint32_t id, int32_t x, int32_t y)
|
||||
static JSValue image(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_UINT32(id, ctx, argv[0]);
|
||||
JS_UNPACK_INT32(x, ctx, argv[1]);
|
||||
JS_UNPACK_INT32(y, ctx, argv[2]);
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
|
||||
ImageId img;
|
||||
img = img.WithIndex(id);
|
||||
if (_paletteId)
|
||||
if (data->_paletteId)
|
||||
{
|
||||
img = img.WithRemap(*_paletteId);
|
||||
img = img.WithRemap(*data->_paletteId);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_colour)
|
||||
if (data->_colour)
|
||||
{
|
||||
img = img.WithPrimary(static_cast<Drawing::Colour>(*_colour));
|
||||
img = img.WithPrimary(static_cast<Drawing::Colour>(*data->_colour));
|
||||
}
|
||||
if (_secondaryColour)
|
||||
if (data->_secondaryColour)
|
||||
{
|
||||
img = img.WithSecondary(static_cast<Drawing::Colour>(*_secondaryColour));
|
||||
img = img.WithSecondary(static_cast<Drawing::Colour>(*data->_secondaryColour));
|
||||
}
|
||||
}
|
||||
|
||||
GfxDrawSprite(_rt, img.WithTertiary(static_cast<Drawing::Colour>(_tertiaryColour.value_or(0))), { x, y });
|
||||
GfxDrawSprite(
|
||||
data->_rt, img.WithTertiary(static_cast<Drawing::Colour>(data->_tertiaryColour.value_or(0))), { x, y });
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void line(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
|
||||
static void line(GraphicsData* data, int32_t x1, int32_t y1, int32_t x2, int32_t y2)
|
||||
{
|
||||
GfxDrawLine(_rt, { { x1, y1 }, { x2, y2 } }, _stroke);
|
||||
GfxDrawLine(data->_rt, { { x1, y1 }, { x2, y2 } }, data->_stroke);
|
||||
}
|
||||
|
||||
void rect(int32_t x, int32_t y, int32_t width, int32_t height)
|
||||
static JSValue lineJS(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
if (_stroke != Drawing::PaletteIndex::transparent)
|
||||
JS_UNPACK_INT32(x1, ctx, argv[0]);
|
||||
JS_UNPACK_INT32(y1, ctx, argv[1]);
|
||||
JS_UNPACK_INT32(x2, ctx, argv[2]);
|
||||
JS_UNPACK_INT32(y2, ctx, argv[3]);
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
line(data, x1, y1, x2, y2);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue rect(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_INT32(x, ctx, argv[0]);
|
||||
JS_UNPACK_INT32(y, ctx, argv[1]);
|
||||
JS_UNPACK_INT32(width, ctx, argv[2]);
|
||||
JS_UNPACK_INT32(height, ctx, argv[3]);
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
|
||||
if (data->_stroke != Drawing::PaletteIndex::transparent)
|
||||
{
|
||||
line(x, y, x + width, y);
|
||||
line(x + width - 1, y + 1, x + width - 1, y + height - 1);
|
||||
line(x, y + height - 1, x + width, y + height - 1);
|
||||
line(x, y + 1, x, y + height - 1);
|
||||
line(data, x, y, x + width, y);
|
||||
line(data, x + width - 1, y + 1, x + width - 1, y + height - 1);
|
||||
line(data, x, y + height - 1, x + width, y + height - 1);
|
||||
line(data, x, y + 1, x, y + height - 1);
|
||||
|
||||
x++;
|
||||
y++;
|
||||
width -= 2;
|
||||
height -= 2;
|
||||
}
|
||||
if (_fill != Drawing::PaletteIndex::transparent)
|
||||
if (data->_fill != Drawing::PaletteIndex::transparent)
|
||||
{
|
||||
Drawing::Rectangle::fill(_rt, { x, y, x + width - 1, y + height - 1 }, _fill);
|
||||
Drawing::Rectangle::fill(data->_rt, { x, y, x + width - 1, y + height - 1 }, data->_fill);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void text(const std::string& text, int32_t x, int32_t y)
|
||||
static JSValue text(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
drawText(_rt, { x, y }, text, { static_cast<Drawing::Colour>(_colour.value_or(0)) });
|
||||
JS_UNPACK_STR(text, ctx, argv[0]);
|
||||
JS_UNPACK_INT32(x, ctx, argv[1]);
|
||||
JS_UNPACK_INT32(y, ctx, argv[2]);
|
||||
GraphicsData* data = gScGraphicsContext.GetOpaque<GraphicsData*>(thisVal);
|
||||
drawText(data->_rt, { x, y }, text, { static_cast<Drawing::Colour>(data->_colour.value_or(0)) });
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -16,147 +16,185 @@
|
||||
#include <openrct2/Context.h>
|
||||
#include <openrct2/SpriteIds.h>
|
||||
#include <openrct2/drawing/Image.h>
|
||||
#include <openrct2/scripting/Duktape.hpp>
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScImageManager
|
||||
class ScImageManager;
|
||||
extern ScImageManager gScImageManager;
|
||||
|
||||
class ScImageManager final : public ScBase
|
||||
{
|
||||
private:
|
||||
duk_context* _ctx{};
|
||||
|
||||
public:
|
||||
ScImageManager(duk_context* ctx)
|
||||
: _ctx(ctx)
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "ImageManager");
|
||||
}
|
||||
|
||||
static void Register(duk_context* ctx)
|
||||
JSValue New(JSContext* ctx)
|
||||
{
|
||||
dukglue_register_method(ctx, &ScImageManager::getPredefinedRange, "getPredefinedRange");
|
||||
dukglue_register_method(ctx, &ScImageManager::getAvailableAllocationRanges, "getAvailableAllocationRanges");
|
||||
dukglue_register_method(ctx, &ScImageManager::allocate, "allocate");
|
||||
dukglue_register_method(ctx, &ScImageManager::free, "free");
|
||||
dukglue_register_method(ctx, &ScImageManager::getImageInfo, "getImageInfo");
|
||||
dukglue_register_method(ctx, &ScImageManager::getPixelData, "getPixelData");
|
||||
dukglue_register_method(ctx, &ScImageManager::setPixelData, "setPixelData");
|
||||
dukglue_register_method(ctx, &ScImageManager::draw, "draw");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CFUNC_DEF("getPredefinedRange", 1, ScImageManager::getPredefinedRange),
|
||||
JS_CFUNC_DEF("getAvailableAllocationRanges", 0, ScImageManager::getAvailableAllocationRanges),
|
||||
JS_CFUNC_DEF("allocate", 1, ScImageManager::allocate),
|
||||
JS_CFUNC_DEF("free", 1, ScImageManager::free),
|
||||
JS_CFUNC_DEF("getImageInfo", 1, ScImageManager::getImageInfo),
|
||||
JS_CFUNC_DEF("getPixelData", 1, ScImageManager::getPixelData),
|
||||
JS_CFUNC_DEF("setPixelData", 2, ScImageManager::setPixelData),
|
||||
JS_CFUNC_DEF("draw", 3, ScImageManager::draw)
|
||||
};
|
||||
return MakeWithOpaque(ctx, funcs, nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
DukValue getPredefinedRange(const std::string& name) const
|
||||
static JSValue getPredefinedRange(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_STR(name, ctx, argv[0]);
|
||||
if (name == "g1")
|
||||
{
|
||||
return CreateImageIndexRange(0, SPR_G1_END);
|
||||
return CreateImageIndexRange(ctx, 0, SPR_G1_END);
|
||||
}
|
||||
else if (name == "g2")
|
||||
{
|
||||
return CreateImageIndexRange(SPR_G2_BEGIN, SPR_G2_END - SPR_G2_BEGIN);
|
||||
return CreateImageIndexRange(ctx, SPR_G2_BEGIN, SPR_G2_END - SPR_G2_BEGIN);
|
||||
}
|
||||
else if (name == "fonts")
|
||||
{
|
||||
return CreateImageIndexRange(SPR_FONTS_BEGIN, SPR_FONTS_END - SPR_FONTS_BEGIN);
|
||||
return CreateImageIndexRange(ctx, SPR_FONTS_BEGIN, SPR_FONTS_END - SPR_FONTS_BEGIN);
|
||||
}
|
||||
else if (name == "tracks")
|
||||
{
|
||||
return CreateImageIndexRange(SPR_TRACKS_BEGIN, SPR_TRACKS_END - SPR_TRACKS_BEGIN);
|
||||
return CreateImageIndexRange(ctx, SPR_TRACKS_BEGIN, SPR_TRACKS_END - SPR_TRACKS_BEGIN);
|
||||
}
|
||||
else if (name == "csg")
|
||||
{
|
||||
return CreateImageIndexRange(SPR_CSG_BEGIN, SPR_CSG_END - SPR_CSG_BEGIN);
|
||||
return CreateImageIndexRange(ctx, SPR_CSG_BEGIN, SPR_CSG_END - SPR_CSG_BEGIN);
|
||||
}
|
||||
else if (name == "allocated")
|
||||
{
|
||||
return CreateImageIndexRange(SPR_IMAGE_LIST_BEGIN, SPR_IMAGE_LIST_END - SPR_IMAGE_LIST_BEGIN);
|
||||
return CreateImageIndexRange(ctx, SPR_IMAGE_LIST_BEGIN, SPR_IMAGE_LIST_END - SPR_IMAGE_LIST_BEGIN);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ToDuk(_ctx, nullptr);
|
||||
return JS_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
DukValue getAvailableAllocationRanges() const
|
||||
static JSValue getAvailableAllocationRanges(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto ranges = GetAvailableAllocationRanges();
|
||||
duk_push_array(_ctx);
|
||||
duk_uarridx_t index = 0;
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
int64_t index = 0;
|
||||
for (const auto& range : ranges)
|
||||
{
|
||||
auto value = CreateImageIndexRange(range.BaseId, range.Count);
|
||||
value.push();
|
||||
duk_put_prop_index(_ctx, /* duk stack index */ -2, index);
|
||||
JS_SetPropertyInt64(ctx, arr, index, CreateImageIndexRange(ctx, range.BaseId, range.Count));
|
||||
index++;
|
||||
}
|
||||
return DukValue::take_from_stack(_ctx);
|
||||
return arr;
|
||||
}
|
||||
|
||||
DukValue allocate(int32_t count)
|
||||
static JSValue allocate(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_INT32(count, ctx, argv[0])
|
||||
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
auto range = AllocateCustomImages(plugin, count);
|
||||
return range ? CreateImageIndexRange(range->BaseId, range->Count) : ToDuk(_ctx, undefined);
|
||||
return range ? CreateImageIndexRange(ctx, range->BaseId, range->Count) : JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void free(const DukValue& dukRange)
|
||||
static JSValue free(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto start = dukRange["start"].as_uint();
|
||||
auto count = dukRange["count"].as_uint();
|
||||
auto start = JSToOptionalInt64(ctx, argv[0], "start");
|
||||
auto count = JSToOptionalInt64(ctx, argv[0], "count");
|
||||
if (!start.has_value() || !count.has_value())
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "Invalid argument");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
ImageList range(start, count);
|
||||
ImageList range(start.value(), count.value());
|
||||
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
if (!FreeCustomImages(plugin, range))
|
||||
{
|
||||
duk_error(_ctx, DUK_ERR_ERROR, "This plugin did not allocate the specified image range.");
|
||||
}
|
||||
}
|
||||
|
||||
DukValue getImageInfo(int32_t id)
|
||||
{
|
||||
return DukGetImageInfo(_ctx, id);
|
||||
}
|
||||
|
||||
DukValue getPixelData(int32_t id)
|
||||
{
|
||||
return DukGetImagePixelData(_ctx, id);
|
||||
}
|
||||
|
||||
void setPixelData(int32_t id, const DukValue& pixelData)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
if (!DoesPluginOwnImage(plugin, id))
|
||||
{
|
||||
duk_error(_ctx, DUK_ERR_ERROR, "This plugin did not allocate the specified image.");
|
||||
JS_ThrowPlainError(ctx, "This plugin did not allocate the specified image range.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
DukSetPixelData(_ctx, id, pixelData);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void draw(int32_t id, const DukValue& dukSize, const DukValue& callback)
|
||||
static JSValue getImageInfo(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto width = dukSize["width"].as_int();
|
||||
auto height = dukSize["height"].as_int();
|
||||
JS_UNPACK_INT32(id, ctx, argv[0]);
|
||||
|
||||
return JSGetImageInfo(ctx, id);
|
||||
}
|
||||
|
||||
static JSValue getPixelData(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_INT32(id, ctx, argv[0]);
|
||||
|
||||
return JSGetImagePixelData(ctx, id);
|
||||
}
|
||||
|
||||
static JSValue setPixelData(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_INT32(id, ctx, argv[0]);
|
||||
JSValue pixelData = argv[1];
|
||||
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
if (!DoesPluginOwnImage(plugin, id))
|
||||
{
|
||||
duk_error(_ctx, DUK_ERR_ERROR, "This plugin did not allocate the specified image.");
|
||||
JS_ThrowPlainError(ctx, "This plugin did not allocate the specified image range.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
DukDrawCustomImage(scriptEngine, id, { width, height }, callback);
|
||||
try
|
||||
{
|
||||
JSSetPixelData(ctx, id, pixelData);
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
JS_ThrowInternalError(ctx, "%s", e.what());
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
DukValue CreateImageIndexRange(size_t start, size_t count) const
|
||||
static JSValue draw(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
DukObject obj(_ctx);
|
||||
obj.Set("start", static_cast<uint32_t>(start));
|
||||
obj.Set("count", static_cast<uint32_t>(count));
|
||||
return obj.Take();
|
||||
JS_UNPACK_INT32(id, ctx, argv[0])
|
||||
auto width = JSToOptionalInt64(ctx, argv[1], "width");
|
||||
auto height = JSToOptionalInt64(ctx, argv[1], "height");
|
||||
if (!width.has_value() || !height.has_value())
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "Invalid size argument");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
JSCallback callback(ctx, argv[2]);
|
||||
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
if (!DoesPluginOwnImage(plugin, id))
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "This plugin did not allocate the specified image range.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
JSDrawCustomImage(
|
||||
scriptEngine, id, { static_cast<int32_t>(width.value()), static_cast<int32_t>(height.value()) }, callback);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue CreateImageIndexRange(JSContext* ctx, size_t start, size_t count)
|
||||
{
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "start", JS_NewInt64(ctx, static_cast<uint32_t>(start)));
|
||||
JS_SetPropertyStr(ctx, obj, "count", JS_NewInt64(ctx, static_cast<uint32_t>(count)));
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -11,49 +11,56 @@
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include <openrct2/scripting/Duktape.hpp>
|
||||
#include <openrct2/world/MapSelection.h>
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScTileSelection
|
||||
{
|
||||
private:
|
||||
duk_context* _ctx{};
|
||||
class ScTileSelection;
|
||||
extern ScTileSelection gScTileSelection;
|
||||
|
||||
class ScTileSelection final : public ScBase
|
||||
{
|
||||
public:
|
||||
ScTileSelection(duk_context* ctx)
|
||||
: _ctx(ctx)
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "TileSelection");
|
||||
}
|
||||
|
||||
DukValue range_get() const
|
||||
JSValue New(JSContext* ctx)
|
||||
{
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("range", ScTileSelection::range_get, ScTileSelection::range_set),
|
||||
JS_CGETSET_DEF("tiles", ScTileSelection::tiles_get, ScTileSelection::tiles_set),
|
||||
};
|
||||
return MakeWithOpaque(ctx, funcs, nullptr);
|
||||
}
|
||||
|
||||
static JSValue range_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
if (gMapSelectFlags.has(MapSelectFlag::enable))
|
||||
{
|
||||
DukObject range(_ctx);
|
||||
JSValue range = JS_NewObject(ctx);
|
||||
|
||||
DukObject leftTop(_ctx);
|
||||
leftTop.Set("x", gMapSelectPositionA.x);
|
||||
leftTop.Set("y", gMapSelectPositionA.y);
|
||||
range.Set("leftTop", leftTop.Take());
|
||||
JSValue leftTop = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, leftTop, "x", JS_NewInt32(ctx, gMapSelectPositionA.x));
|
||||
JS_SetPropertyStr(ctx, leftTop, "y", JS_NewInt32(ctx, gMapSelectPositionA.y));
|
||||
JS_SetPropertyStr(ctx, range, "leftTop", leftTop);
|
||||
|
||||
DukObject rightBottom(_ctx);
|
||||
rightBottom.Set("x", gMapSelectPositionB.x);
|
||||
rightBottom.Set("y", gMapSelectPositionB.y);
|
||||
range.Set("rightBottom", rightBottom.Take());
|
||||
return range.Take();
|
||||
JSValue rightBottom = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, rightBottom, "x", JS_NewInt32(ctx, gMapSelectPositionB.x));
|
||||
JS_SetPropertyStr(ctx, rightBottom, "y", JS_NewInt32(ctx, gMapSelectPositionB.y));
|
||||
JS_SetPropertyStr(ctx, range, "rightBottom", rightBottom);
|
||||
return range;
|
||||
}
|
||||
|
||||
duk_push_null(_ctx);
|
||||
return DukValue::take_from_stack(_ctx);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
void range_set(DukValue value)
|
||||
static JSValue range_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
if (value.type() == DukValue::Type::OBJECT)
|
||||
if (JS_IsObject(value))
|
||||
{
|
||||
auto range = GetMapRange(value);
|
||||
auto range = GetMapRange(ctx, value);
|
||||
if (range)
|
||||
{
|
||||
gMapSelectPositionA.x = range->GetX1();
|
||||
@@ -68,48 +75,40 @@ namespace OpenRCT2::Scripting
|
||||
{
|
||||
gMapSelectFlags.unset(MapSelectFlag::enable);
|
||||
}
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
DukValue tiles_get() const
|
||||
static JSValue tiles_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
duk_push_array(_ctx);
|
||||
JSValue tiles = JS_NewArray(ctx);
|
||||
if (gMapSelectFlags.has(MapSelectFlag::enableConstruct))
|
||||
{
|
||||
duk_uarridx_t index = 0;
|
||||
int64_t index = 0;
|
||||
for (const auto& tile : MapSelection::getSelectedTiles())
|
||||
{
|
||||
duk_push_object(_ctx);
|
||||
duk_push_int(_ctx, tile.x);
|
||||
duk_put_prop_string(_ctx, -2, "x");
|
||||
duk_push_int(_ctx, tile.y);
|
||||
duk_put_prop_string(_ctx, -2, "y");
|
||||
duk_put_prop_index(_ctx, -2, index);
|
||||
JSValue val = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, val, "x", JS_NewInt32(ctx, tile.x));
|
||||
JS_SetPropertyStr(ctx, val, "y", JS_NewInt32(ctx, tile.y));
|
||||
JS_SetPropertyInt64(ctx, tiles, index, val);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return DukValue::take_from_stack(_ctx);
|
||||
return tiles;
|
||||
}
|
||||
|
||||
void tiles_set(DukValue value)
|
||||
static JSValue tiles_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
MapSelection::clearSelectedTiles();
|
||||
if (value.is_array())
|
||||
if (JS_IsArray(value))
|
||||
{
|
||||
value.push();
|
||||
auto arrayLen = duk_get_length(_ctx, -1);
|
||||
for (duk_uarridx_t i = 0; i < arrayLen; i++)
|
||||
{
|
||||
if (duk_get_prop_index(_ctx, -1, i))
|
||||
JSIterateArray(ctx, value, [](JSContext* ctx2, JSValue v) {
|
||||
auto coords = GetCoordsXY(ctx2, v);
|
||||
if (coords.has_value())
|
||||
{
|
||||
auto dukElement = DukValue::take_from_stack(_ctx);
|
||||
auto coords = GetCoordsXY(dukElement);
|
||||
if (coords)
|
||||
{
|
||||
MapSelection::addSelectedTile(*coords);
|
||||
}
|
||||
MapSelection::addSelectedTile(coords.value());
|
||||
}
|
||||
}
|
||||
duk_pop(_ctx);
|
||||
});
|
||||
}
|
||||
|
||||
if (MapSelection::getSelectedTiles().empty())
|
||||
@@ -121,40 +120,37 @@ namespace OpenRCT2::Scripting
|
||||
{
|
||||
gMapSelectFlags.set(MapSelectFlag::enableConstruct);
|
||||
}
|
||||
}
|
||||
|
||||
static void Register(duk_context* ctx)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScTileSelection::range_get, &ScTileSelection::range_set, "range");
|
||||
dukglue_register_property(ctx, &ScTileSelection::tiles_get, &ScTileSelection::tiles_set, "tiles");
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::optional<CoordsXY> GetCoordsXY(const DukValue& dukCoords)
|
||||
static std::optional<CoordsXY> GetCoordsXY(JSContext* ctx, JSValue jsCoords)
|
||||
{
|
||||
if (dukCoords.type() == DukValue::Type::OBJECT)
|
||||
if (JS_IsObject(jsCoords))
|
||||
{
|
||||
auto dukX = dukCoords["x"];
|
||||
if (dukX.type() == DukValue::Type::NUMBER)
|
||||
auto x = JSToOptionalInt(ctx, jsCoords, "x");
|
||||
auto y = JSToOptionalInt(ctx, jsCoords, "y");
|
||||
if (x.has_value() && y.has_value())
|
||||
{
|
||||
auto dukY = dukCoords["y"];
|
||||
if (dukY.type() == DukValue::Type::NUMBER)
|
||||
{
|
||||
return CoordsXY(dukX.as_int(), dukY.as_int());
|
||||
}
|
||||
return CoordsXY(x.value(), y.value());
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static std::optional<MapRange> GetMapRange(const DukValue& dukMapRange)
|
||||
static std::optional<MapRange> GetMapRange(JSContext* ctx, JSValue jsMapRange)
|
||||
{
|
||||
if (dukMapRange.type() == DukValue::Type::OBJECT)
|
||||
if (JS_IsObject(jsMapRange))
|
||||
{
|
||||
auto leftTop = GetCoordsXY(dukMapRange["leftTop"]);
|
||||
JSValue jsLeftTop = JS_GetPropertyStr(ctx, jsMapRange, "leftTop");
|
||||
auto leftTop = GetCoordsXY(ctx, jsLeftTop);
|
||||
JS_FreeValue(ctx, jsLeftTop);
|
||||
if (leftTop.has_value())
|
||||
{
|
||||
auto rightBottom = GetCoordsXY(dukMapRange["rightBottom"]);
|
||||
JSValue jsRightBottom = JS_GetPropertyStr(ctx, jsMapRange, "rightBottom");
|
||||
auto rightBottom = GetCoordsXY(ctx, jsRightBottom);
|
||||
JS_FreeValue(ctx, jsRightBottom);
|
||||
if (rightBottom.has_value())
|
||||
{
|
||||
return MapRange(leftTop->x, leftTop->y, rightBottom->x, rightBottom->y);
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
#include <openrct2/GameState.h>
|
||||
#include <openrct2/OpenRCT2.h>
|
||||
#include <openrct2/ParkImporter.h>
|
||||
#include <openrct2/core/EnumMap.hpp>
|
||||
#include <openrct2/core/String.hpp>
|
||||
#include <openrct2/entity/EntityRegistry.h>
|
||||
#include <openrct2/object/ObjectManager.h>
|
||||
#include <openrct2/scenario/Scenario.h>
|
||||
#include <openrct2/scenes/title/TitleScene.h>
|
||||
@@ -48,7 +48,7 @@ namespace OpenRCT2::Scripting
|
||||
LoadSc,
|
||||
};
|
||||
|
||||
static const DukEnumMap<TitleScript> TitleScriptMap(
|
||||
static const EnumMap<TitleScript> TitleScriptMap(
|
||||
{
|
||||
{ Title::LoadParkCommand::ScriptingName, TitleScript::Load },
|
||||
{ Title::SetLocationCommand::ScriptingName, TitleScript::Location },
|
||||
@@ -62,193 +62,224 @@ namespace OpenRCT2::Scripting
|
||||
{ Title::EndCommand::ScriptingName, TitleScript::End },
|
||||
});
|
||||
|
||||
template<>
|
||||
DukValue ToDuk(duk_context* ctx, const TitleScript& value)
|
||||
inline JSValue TitleScriptToJS(JSContext* ctx, TitleScript value)
|
||||
{
|
||||
return ToDuk(ctx, TitleScriptMap[value]);
|
||||
return JSFromStdString(ctx, TitleScriptMap[value]);
|
||||
}
|
||||
|
||||
template<>
|
||||
DukValue ToDuk(duk_context* ctx, const Title::TitleCommand& value)
|
||||
inline JSValue TitleCommandToJS(JSContext* ctx, const Title::TitleCommand& value)
|
||||
{
|
||||
using namespace OpenRCT2::Title;
|
||||
DukObject obj(ctx);
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
std::visit(
|
||||
[&obj](auto&& command) {
|
||||
[ctx, &obj](auto&& command) {
|
||||
using T = std::decay_t<decltype(command)>;
|
||||
obj.Set("type", T::ScriptingName);
|
||||
JS_SetPropertyStr(ctx, obj, "type", JSFromStdString(ctx, T::ScriptingName));
|
||||
if constexpr (std::is_same_v<T, LoadParkCommand>)
|
||||
{
|
||||
obj.Set("index", command.SaveIndex);
|
||||
JS_SetPropertyStr(ctx, obj, "index", JS_NewInt32(ctx, command.SaveIndex));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, SetLocationCommand>)
|
||||
{
|
||||
obj.Set("x", command.Location.X);
|
||||
obj.Set("y", command.Location.Y);
|
||||
JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, command.Location.X));
|
||||
JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, command.Location.Y));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, RotateViewCommand>)
|
||||
{
|
||||
obj.Set("rotations", command.Rotations);
|
||||
JS_SetPropertyStr(ctx, obj, "rotations", JS_NewInt32(ctx, command.Rotations));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, SetZoomCommand>)
|
||||
{
|
||||
obj.Set("zoom", command.Zoom);
|
||||
JS_SetPropertyStr(ctx, obj, "zoom", JS_NewInt32(ctx, command.Zoom));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, FollowEntityCommand>)
|
||||
{
|
||||
if (command.Follow.SpriteIndex.IsNull())
|
||||
obj.Set("id", nullptr);
|
||||
JS_SetPropertyStr(ctx, obj, "id", JS_NULL);
|
||||
else
|
||||
obj.Set("id", command.Follow.SpriteIndex.ToUnderlying());
|
||||
JS_SetPropertyStr(ctx, obj, "id", JS_NewInt32(ctx, command.Follow.SpriteIndex.ToUnderlying()));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, SetSpeedCommand>)
|
||||
{
|
||||
obj.Set("speed", command.Speed);
|
||||
JS_SetPropertyStr(ctx, obj, "speed", JS_NewInt32(ctx, command.Speed));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, WaitCommand>)
|
||||
{
|
||||
obj.Set("duration", command.Milliseconds);
|
||||
JS_SetPropertyStr(ctx, obj, "duration", JS_NewInt32(ctx, command.Milliseconds));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, LoadScenarioCommand>)
|
||||
{
|
||||
obj.Set("scenario", String::toStringView(command.Scenario, sizeof(command.Scenario)));
|
||||
JS_SetPropertyStr(ctx, obj, "scenario", JSFromStdString(ctx, command.Scenario));
|
||||
}
|
||||
},
|
||||
value);
|
||||
return obj.Take();
|
||||
return obj;
|
||||
}
|
||||
|
||||
template<>
|
||||
TitleScript FromDuk(const DukValue& value)
|
||||
{
|
||||
if (value.type() == DukValue::Type::STRING)
|
||||
return TitleScriptMap[value.as_string()];
|
||||
throw DukException() << "Invalid title command id";
|
||||
}
|
||||
|
||||
template<>
|
||||
Title::TitleCommand FromDuk(const DukValue& value)
|
||||
inline std::optional<Title::TitleCommand> TitleCommandFromJS(JSContext* ctx, JSValue value)
|
||||
{
|
||||
using namespace OpenRCT2::Title;
|
||||
auto type = FromDuk<TitleScript>(value["type"]);
|
||||
TitleCommand command{};
|
||||
switch (type)
|
||||
auto type = TitleScriptMap.TryGet(JSToStdString(ctx, value, "type"));
|
||||
if (!type.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
switch (type.value())
|
||||
{
|
||||
case TitleScript::Load:
|
||||
command = LoadParkCommand{ static_cast<uint8_t>(value["index"].as_uint()) };
|
||||
break;
|
||||
case TitleScript::Location:
|
||||
command = SetLocationCommand{
|
||||
static_cast<uint8_t>(value["x"].as_uint()),
|
||||
static_cast<uint8_t>(value["y"].as_uint()),
|
||||
};
|
||||
break;
|
||||
case TitleScript::Rotate:
|
||||
command = RotateViewCommand{ static_cast<uint8_t>(value["rotations"].as_uint()) };
|
||||
break;
|
||||
case TitleScript::Zoom:
|
||||
command = SetZoomCommand{ static_cast<uint8_t>(value["zoom"].as_uint()) };
|
||||
break;
|
||||
case TitleScript::Follow:
|
||||
{
|
||||
auto dukId = value["id"];
|
||||
if (dukId.type() == DukValue::Type::NUMBER)
|
||||
auto index = JSToOptionalInt(ctx, value, "index");
|
||||
if (!index.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
return LoadParkCommand{ static_cast<uint8_t>(index.value()) };
|
||||
}
|
||||
case TitleScript::Location:
|
||||
{
|
||||
auto x = JSToOptionalInt(ctx, value, "x");
|
||||
auto y = JSToOptionalInt(ctx, value, "y");
|
||||
if (!x.has_value() || !y.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
return SetLocationCommand{
|
||||
static_cast<uint8_t>(x.value()),
|
||||
static_cast<uint8_t>(y.value()),
|
||||
};
|
||||
}
|
||||
case TitleScript::Rotate:
|
||||
{
|
||||
auto rotations = JSToOptionalInt(ctx, value, "rotations");
|
||||
if (!rotations.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
return RotateViewCommand{ static_cast<uint8_t>(rotations.value()) };
|
||||
}
|
||||
case TitleScript::Zoom:
|
||||
{
|
||||
auto zoom = JSToOptionalInt(ctx, value, "zoom");
|
||||
if (!zoom.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
return SetZoomCommand{ static_cast<uint8_t>(zoom.value()) };
|
||||
}
|
||||
case TitleScript::Follow:
|
||||
if (auto id = JSToOptionalInt(ctx, value, "id"); id.has_value())
|
||||
{
|
||||
command = FollowEntityCommand{ EntityId::FromUnderlying(dukId.as_uint()) };
|
||||
return FollowEntityCommand{ EntityId::FromUnderlying(id.value()) };
|
||||
}
|
||||
else
|
||||
{
|
||||
command = FollowEntityCommand{ EntityId::GetNull() };
|
||||
return FollowEntityCommand{ EntityId::GetNull() };
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TitleScript::Speed:
|
||||
command = SetSpeedCommand{ static_cast<uint8_t>(value["speed"].as_uint()) };
|
||||
break;
|
||||
{
|
||||
auto speed = JSToOptionalInt(ctx, value, "speed");
|
||||
if (!speed.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
return SetSpeedCommand{ static_cast<uint8_t>(speed.value()) };
|
||||
}
|
||||
case TitleScript::Wait:
|
||||
command = WaitCommand{ static_cast<uint16_t>(value["duration"].as_uint()) };
|
||||
break;
|
||||
{
|
||||
auto duration = JSToOptionalInt(ctx, value, "duration");
|
||||
if (!duration.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
return WaitCommand{ static_cast<uint16_t>(duration.value()) };
|
||||
}
|
||||
case TitleScript::LoadSc:
|
||||
{
|
||||
auto scenario = JSToOptionalStdString(ctx, value, "scenario");
|
||||
if (!scenario.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
auto loadScenarioCommand = LoadScenarioCommand{};
|
||||
String::set(
|
||||
loadScenarioCommand.Scenario, sizeof(loadScenarioCommand.Scenario), value["scenario"].as_c_string());
|
||||
command = loadScenarioCommand;
|
||||
break;
|
||||
String::set(loadScenarioCommand.Scenario, sizeof(loadScenarioCommand.Scenario), scenario.value().c_str());
|
||||
return loadScenarioCommand;
|
||||
}
|
||||
case TitleScript::Restart:
|
||||
command = RestartCommand{};
|
||||
break;
|
||||
return RestartCommand{};
|
||||
case TitleScript::End:
|
||||
command = EndCommand{};
|
||||
break;
|
||||
return EndCommand{};
|
||||
default:
|
||||
break;
|
||||
return std::nullopt;
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
class ScTitleSequencePark
|
||||
class ScTitleSequencePark;
|
||||
extern ScTitleSequencePark gScTitleSequencePark;
|
||||
class ScTitleSequencePark : public ScBase
|
||||
{
|
||||
private:
|
||||
std::string _titleSequencePath;
|
||||
std::string _fileName;
|
||||
|
||||
public:
|
||||
ScTitleSequencePark(std::string_view path, std::string_view fileName)
|
||||
: _titleSequencePath(path)
|
||||
, _fileName(fileName)
|
||||
struct OpaqueData
|
||||
{
|
||||
std::string _titleSequencePath{};
|
||||
std::string _fileName{};
|
||||
};
|
||||
|
||||
static JSValue fileName_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
OpaqueData* data = gScTitleSequencePark.GetOpaque<OpaqueData*>(thisVal);
|
||||
if (!data)
|
||||
return JS_UNDEFINED;
|
||||
return JSFromStdString(ctx, data->_fileName);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string fileName_get() const
|
||||
static JSValue fileName_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
return _fileName;
|
||||
}
|
||||
JS_UNPACK_STR(valueStr, ctx, value);
|
||||
OpaqueData* data = gScTitleSequencePark.GetOpaque<OpaqueData*>(thisVal);
|
||||
if (!data)
|
||||
return JS_UNDEFINED;
|
||||
|
||||
void fileName_set(const std::string& value)
|
||||
{
|
||||
if (value == _fileName)
|
||||
return;
|
||||
if (valueStr == data->_fileName)
|
||||
return JS_UNDEFINED;
|
||||
|
||||
auto seq = Title::LoadTitleSequence(_titleSequencePath);
|
||||
auto seq = Title::LoadTitleSequence(data->_titleSequencePath);
|
||||
if (seq != nullptr)
|
||||
{
|
||||
// Check if name already in use
|
||||
auto index = GetIndex(*seq, value);
|
||||
auto index = GetIndex(*seq, valueStr);
|
||||
if (!index)
|
||||
{
|
||||
index = GetIndex(*seq, _fileName);
|
||||
index = GetIndex(*seq, data->_fileName);
|
||||
if (index)
|
||||
{
|
||||
TitleSequenceRenamePark(*seq, *index, value.c_str());
|
||||
TitleSequenceRenamePark(*seq, *index, valueStr.c_str());
|
||||
TitleSequenceSave(*seq);
|
||||
}
|
||||
}
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void delete_()
|
||||
static JSValue delete_(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto seq = Title::LoadTitleSequence(_titleSequencePath);
|
||||
OpaqueData* data = gScTitleSequencePark.GetOpaque<OpaqueData*>(thisVal);
|
||||
if (!data)
|
||||
return JS_UNDEFINED;
|
||||
|
||||
auto seq = Title::LoadTitleSequence(data->_titleSequencePath);
|
||||
if (seq != nullptr)
|
||||
{
|
||||
auto index = GetIndex(*seq, _fileName);
|
||||
auto index = GetIndex(*seq, data->_fileName);
|
||||
if (index)
|
||||
{
|
||||
OpenRCT2::Title::TitleSequenceRemovePark(*seq, *index);
|
||||
OpenRCT2::Title::TitleSequenceSave(*seq);
|
||||
}
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void load()
|
||||
static JSValue load(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto seq = Title::LoadTitleSequence(_titleSequencePath);
|
||||
OpaqueData* data = gScTitleSequencePark.GetOpaque<OpaqueData*>(thisVal);
|
||||
if (!data)
|
||||
return JS_UNDEFINED;
|
||||
|
||||
auto seq = Title::LoadTitleSequence(data->_titleSequencePath);
|
||||
if (seq != nullptr)
|
||||
{
|
||||
auto index = GetIndex(*seq, _fileName);
|
||||
auto index = GetIndex(*seq, data->_fileName);
|
||||
if (index)
|
||||
{
|
||||
auto handle = OpenRCT2::Title::TitleSequenceGetParkHandle(*seq, *index);
|
||||
@@ -281,22 +312,39 @@ namespace OpenRCT2::Scripting
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Unable to load park.");
|
||||
JS_ThrowPlainError(ctx, "Unable to load park.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
}
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
public:
|
||||
static void Register(duk_context* ctx)
|
||||
JSValue New(JSContext* ctx, std::string_view path, std::string_view fileName)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScTitleSequencePark::fileName_get, &ScTitleSequencePark::fileName_set, "fileName");
|
||||
dukglue_register_method(ctx, &ScTitleSequencePark::delete_, "delete");
|
||||
dukglue_register_method(ctx, &ScTitleSequencePark::load, "load");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("fileName", ScTitleSequencePark::fileName_get, ScTitleSequencePark::fileName_set),
|
||||
JS_CFUNC_DEF("delete", 0, ScTitleSequencePark::delete_),
|
||||
JS_CFUNC_DEF("load", 0, ScTitleSequencePark::load),
|
||||
};
|
||||
|
||||
return MakeWithOpaque(ctx, funcs, new OpaqueData{ std::string(path), std::string(fileName) });
|
||||
}
|
||||
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "TitleSequencePark", Finalize);
|
||||
}
|
||||
|
||||
private:
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
OpaqueData* data = gScTitleSequencePark.GetOpaque<OpaqueData*>(thisVal);
|
||||
if (data)
|
||||
delete data;
|
||||
}
|
||||
|
||||
static std::optional<size_t> GetIndex(const Title::TitleSequence& seq, const std::string_view needle)
|
||||
{
|
||||
for (size_t i = 0; i < seq.Saves.size(); i++)
|
||||
@@ -310,189 +358,236 @@ namespace OpenRCT2::Scripting
|
||||
}
|
||||
};
|
||||
|
||||
class ScTitleSequence
|
||||
class ScTitleSequence;
|
||||
extern ScTitleSequence gScTitleSequence;
|
||||
class ScTitleSequence : public ScBase
|
||||
{
|
||||
private:
|
||||
std::string _path;
|
||||
|
||||
public:
|
||||
ScTitleSequence(const std::string& path)
|
||||
struct OpaqueData
|
||||
{
|
||||
_path = path;
|
||||
}
|
||||
std::string _path;
|
||||
};
|
||||
|
||||
private:
|
||||
std::string name_get() const
|
||||
static JSValue name_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto* item = GetItem();
|
||||
const auto* item = GetItem(ctx, thisVal);
|
||||
if (item != nullptr)
|
||||
{
|
||||
return item->Name;
|
||||
return JSFromStdString(ctx, item->Name);
|
||||
}
|
||||
return {};
|
||||
return JSFromStdString(ctx, {});
|
||||
}
|
||||
|
||||
void name_set(const std::string& value)
|
||||
static JSValue name_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
auto index = GetManagerIndex();
|
||||
JS_UNPACK_STR(valueStr, ctx, value)
|
||||
OpaqueData* data = gScTitleSequence.GetOpaque<OpaqueData*>(thisVal);
|
||||
if (!data)
|
||||
return JS_UNDEFINED;
|
||||
|
||||
auto index = GetManagerIndex(ctx, thisVal);
|
||||
if (index)
|
||||
{
|
||||
auto newIndex = TitleSequenceManager::RenameItem(*index, value.c_str());
|
||||
auto newIndex = TitleSequenceManager::RenameItem(*index, valueStr.c_str());
|
||||
|
||||
// Update path to new value
|
||||
auto newItem = TitleSequenceManager::GetItem(newIndex);
|
||||
_path = newItem != nullptr ? newItem->Path : std::string();
|
||||
data->_path = newItem != nullptr ? newItem->Path : std::string();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
std::string path_get() const
|
||||
static JSValue path_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto* item = GetItem();
|
||||
const auto* item = GetItem(ctx, thisVal);
|
||||
if (item != nullptr)
|
||||
{
|
||||
return item->Path;
|
||||
return JSFromStdString(ctx, item->Path);
|
||||
}
|
||||
return {};
|
||||
return JSFromStdString(ctx, {});
|
||||
}
|
||||
|
||||
bool isDirectory_get() const
|
||||
static JSValue isDirectory_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto* item = GetItem();
|
||||
const auto* item = GetItem(ctx, thisVal);
|
||||
if (item != nullptr)
|
||||
{
|
||||
return !item->IsZip;
|
||||
return JS_NewBool(ctx, !item->IsZip);
|
||||
}
|
||||
return {};
|
||||
return JS_NewBool(ctx, {});
|
||||
}
|
||||
|
||||
bool isReadOnly_get() const
|
||||
static JSValue isReadOnly_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto* item = GetItem();
|
||||
const auto* item = GetItem(ctx, thisVal);
|
||||
if (item != nullptr)
|
||||
{
|
||||
return item->PredefinedIndex != TitleSequenceManager::kPredefinedIndexCustom;
|
||||
return JS_NewBool(ctx, item->PredefinedIndex != TitleSequenceManager::kPredefinedIndexCustom);
|
||||
}
|
||||
return {};
|
||||
return JS_NewBool(ctx, {});
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<ScTitleSequencePark>> parks_get() const
|
||||
static JSValue parks_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
std::vector<std::shared_ptr<ScTitleSequencePark>> result;
|
||||
auto titleSeq = Title::LoadTitleSequence(_path);
|
||||
JSValue result = JS_NewArray(ctx);
|
||||
OpaqueData* data = gScTitleSequence.GetOpaque<OpaqueData*>(thisVal);
|
||||
if (!data)
|
||||
return result;
|
||||
|
||||
auto titleSeq = Title::LoadTitleSequence(data->_path);
|
||||
if (titleSeq != nullptr)
|
||||
{
|
||||
for (size_t i = 0; i < titleSeq->Saves.size(); i++)
|
||||
{
|
||||
result.push_back(std::make_shared<ScTitleSequencePark>(_path, titleSeq->Saves[i]));
|
||||
JS_SetPropertyInt64(ctx, result, i, gScTitleSequencePark.New(ctx, data->_path, titleSeq->Saves[i]));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<DukValue> commands_get() const
|
||||
static JSValue commands_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto ctx = scriptEngine.GetContext();
|
||||
JSValue result = JS_NewArray(ctx);
|
||||
OpaqueData* data = gScTitleSequence.GetOpaque<OpaqueData*>(thisVal);
|
||||
if (!data)
|
||||
return result;
|
||||
|
||||
std::vector<DukValue> result;
|
||||
auto titleSeq = Title::LoadTitleSequence(_path);
|
||||
auto titleSeq = Title::LoadTitleSequence(data->_path);
|
||||
if (titleSeq != nullptr)
|
||||
{
|
||||
int64_t idx = 0;
|
||||
for (const auto& command : titleSeq->Commands)
|
||||
{
|
||||
result.push_back(ToDuk(ctx, command));
|
||||
JS_SetPropertyInt64(ctx, result, idx++, TitleCommandToJS(ctx, command));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void commands_set(const std::vector<DukValue>& value)
|
||||
static JSValue commands_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
JS_UNPACK_ARRAY(array, ctx, value);
|
||||
OpaqueData* data = gScTitleSequence.GetOpaque<OpaqueData*>(thisVal);
|
||||
if (!data)
|
||||
return JS_UNDEFINED;
|
||||
|
||||
int64_t length;
|
||||
if (JS_GetLength(ctx, array, &length) != 0)
|
||||
return JS_UNDEFINED;
|
||||
std::vector<Title::TitleCommand> commands;
|
||||
for (const auto& v : value)
|
||||
commands.reserve(length);
|
||||
|
||||
for (int64_t i = 0; i < length; i++)
|
||||
{
|
||||
auto command = FromDuk<Title::TitleCommand>(v);
|
||||
commands.push_back(std::move(command));
|
||||
JSValue element = JS_GetPropertyInt64(ctx, array, i);
|
||||
auto command = TitleCommandFromJS(ctx, element);
|
||||
JS_FreeValue(ctx, element);
|
||||
if (!command.has_value())
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "Invalid command");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
commands.push_back(command.value());
|
||||
}
|
||||
|
||||
auto titleSeq = Title::LoadTitleSequence(_path);
|
||||
auto titleSeq = Title::LoadTitleSequence(data->_path);
|
||||
titleSeq->Commands = commands;
|
||||
OpenRCT2::Title::TitleSequenceSave(*titleSeq);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void addPark(const std::string& path, const std::string& fileName)
|
||||
static JSValue addPark(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto titleSeq = Title::LoadTitleSequence(_path);
|
||||
JS_UNPACK_STR(path, ctx, argv[0]);
|
||||
JS_UNPACK_STR(fileName, ctx, argv[1]);
|
||||
OpaqueData* data = gScTitleSequence.GetOpaque<OpaqueData*>(thisVal);
|
||||
if (!data)
|
||||
return JS_UNDEFINED;
|
||||
|
||||
auto titleSeq = Title::LoadTitleSequence(data->_path);
|
||||
OpenRCT2::Title::TitleSequenceAddPark(*titleSeq, path.c_str(), fileName.c_str());
|
||||
OpenRCT2::Title::TitleSequenceSave(*titleSeq);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
std::shared_ptr<ScTitleSequence> clone(const std::string& name) const
|
||||
static JSValue clone(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto copyIndex = GetManagerIndex();
|
||||
JS_UNPACK_STR(name, ctx, argv[0]);
|
||||
|
||||
auto copyIndex = GetManagerIndex(ctx, thisVal);
|
||||
if (copyIndex)
|
||||
{
|
||||
auto index = TitleSequenceManager::DuplicateItem(*copyIndex, name.c_str());
|
||||
auto* item = TitleSequenceManager::GetItem(index);
|
||||
if (item != nullptr)
|
||||
{
|
||||
return std::make_shared<ScTitleSequence>(item->Path);
|
||||
return gScTitleSequence.New(ctx, item->Path);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
void delete_()
|
||||
static JSValue delete_(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto index = GetManagerIndex();
|
||||
OpaqueData* data = gScTitleSequence.GetOpaque<OpaqueData*>(thisVal);
|
||||
if (!data)
|
||||
return JS_UNDEFINED;
|
||||
|
||||
auto index = GetManagerIndex(ctx, thisVal);
|
||||
if (index)
|
||||
{
|
||||
TitleSequenceManager::DeleteItem(*index);
|
||||
}
|
||||
_path = {};
|
||||
data->_path = {};
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool isPlaying_get() const
|
||||
static bool IsPlaying(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto index = GetManagerIndex();
|
||||
auto index = GetManagerIndex(ctx, thisVal);
|
||||
return index && TitleIsPreviewingSequence() && *index == TitleGetCurrentSequence();
|
||||
}
|
||||
|
||||
DukValue position_get() const
|
||||
static JSValue isPlaying_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
if (isPlaying_get())
|
||||
return JS_NewBool(ctx, IsPlaying(ctx, thisVal));
|
||||
}
|
||||
|
||||
static JSValue position_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
if (IsPlaying(ctx, thisVal))
|
||||
{
|
||||
auto* player = static_cast<ITitleSequencePlayer*>(TitleGetSequencePlayer());
|
||||
if (player != nullptr)
|
||||
{
|
||||
return ToDuk(ctx, player->GetCurrentPosition());
|
||||
return JS_NewInt32(ctx, player->GetCurrentPosition());
|
||||
}
|
||||
}
|
||||
return ToDuk(ctx, nullptr);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
void play()
|
||||
static JSValue play(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto index = GetManagerIndex();
|
||||
auto index = GetManagerIndex(ctx, thisVal);
|
||||
if (index && (!TitleIsPreviewingSequence() || *index != TitleGetCurrentSequence()))
|
||||
{
|
||||
if (!TitlePreviewSequence(*index))
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Failed to load title sequence");
|
||||
JS_ThrowPlainError(ctx, "Failed to load title sequence");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
else if (gLegacyScene != LegacyScene::titleSequence)
|
||||
{
|
||||
gPreviewingTitleSequenceInGame = true;
|
||||
}
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void seek(int32_t position)
|
||||
static JSValue seek(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
if (isPlaying_get())
|
||||
JS_UNPACK_INT32(position, ctx, argv[0]);
|
||||
|
||||
if (IsPlaying(ctx, thisVal))
|
||||
{
|
||||
auto* player = static_cast<ITitleSequencePlayer*>(TitleGetSequencePlayer());
|
||||
try
|
||||
@@ -502,47 +597,71 @@ namespace OpenRCT2::Scripting
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Failed to seek");
|
||||
JS_ThrowPlainError(ctx, "Failed to seek");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void stop()
|
||||
static JSValue stop(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
if (isPlaying_get())
|
||||
if (IsPlaying(ctx, thisVal))
|
||||
{
|
||||
TitleStopPreviewingSequence();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
public:
|
||||
static void Register(duk_context* ctx)
|
||||
JSValue New(JSContext* ctx, std::string_view path)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScTitleSequence::name_get, &ScTitleSequence::name_set, "name");
|
||||
dukglue_register_property(ctx, &ScTitleSequence::path_get, nullptr, "path");
|
||||
dukglue_register_property(ctx, &ScTitleSequence::isDirectory_get, nullptr, "isDirectory");
|
||||
dukglue_register_property(ctx, &ScTitleSequence::isReadOnly_get, nullptr, "isReadOnly");
|
||||
dukglue_register_property(ctx, &ScTitleSequence::parks_get, nullptr, "parks");
|
||||
dukglue_register_property(ctx, &ScTitleSequence::commands_get, &ScTitleSequence::commands_set, "commands");
|
||||
dukglue_register_property(ctx, &ScTitleSequence::isPlaying_get, nullptr, "isPlaying");
|
||||
dukglue_register_property(ctx, &ScTitleSequence::position_get, nullptr, "position");
|
||||
dukglue_register_method(ctx, &ScTitleSequence::addPark, "addPark");
|
||||
dukglue_register_method(ctx, &ScTitleSequence::clone, "clone");
|
||||
dukglue_register_method(ctx, &ScTitleSequence::delete_, "delete");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("name", ScTitleSequence::name_get, ScTitleSequence::name_set),
|
||||
JS_CGETSET_DEF("path", ScTitleSequence::path_get, nullptr),
|
||||
JS_CGETSET_DEF("isDirectory", ScTitleSequence::isDirectory_get, nullptr),
|
||||
JS_CGETSET_DEF("isReadOnly", ScTitleSequence::isReadOnly_get, nullptr),
|
||||
JS_CGETSET_DEF("parks", ScTitleSequence::parks_get, nullptr),
|
||||
JS_CGETSET_DEF("commands", ScTitleSequence::commands_get, ScTitleSequence::commands_set),
|
||||
JS_CGETSET_DEF("isPlaying", ScTitleSequence::isPlaying_get, nullptr),
|
||||
JS_CGETSET_DEF("position", ScTitleSequence::position_get, nullptr),
|
||||
|
||||
dukglue_register_method(ctx, &ScTitleSequence::play, "play");
|
||||
dukglue_register_method(ctx, &ScTitleSequence::seek, "seek");
|
||||
dukglue_register_method(ctx, &ScTitleSequence::stop, "stop");
|
||||
JS_CFUNC_DEF("addPark", 2, ScTitleSequence::addPark),
|
||||
JS_CFUNC_DEF("clone", 1, ScTitleSequence::clone),
|
||||
JS_CFUNC_DEF("delete", 0, ScTitleSequence::delete_),
|
||||
|
||||
JS_CFUNC_DEF("play", 0, ScTitleSequence::play),
|
||||
JS_CFUNC_DEF("seek", 1, ScTitleSequence::seek),
|
||||
JS_CFUNC_DEF("stop", 0, ScTitleSequence::stop),
|
||||
};
|
||||
|
||||
return MakeWithOpaque(ctx, funcs, new OpaqueData{ std::string(path) });
|
||||
}
|
||||
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "TitleSequence", Finalize);
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<size_t> GetManagerIndex() const
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
OpaqueData* data = gScTitleSequence.GetOpaque<OpaqueData*>(thisVal);
|
||||
if (data)
|
||||
delete data;
|
||||
}
|
||||
|
||||
static std::optional<size_t> GetManagerIndex(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
OpaqueData* data = gScTitleSequence.GetOpaque<OpaqueData*>(thisVal);
|
||||
if (!data)
|
||||
return std::nullopt;
|
||||
|
||||
auto count = TitleSequenceManager::GetCount();
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
auto item = TitleSequenceManager::GetItem(i);
|
||||
if (item != nullptr && item->Path == _path)
|
||||
if (item != nullptr && item->Path == data->_path)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
@@ -550,9 +669,9 @@ namespace OpenRCT2::Scripting
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const TitleSequenceManager::Item* GetItem() const
|
||||
static const TitleSequenceManager::Item* GetItem(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto index = GetManagerIndex();
|
||||
auto index = GetManagerIndex(ctx, thisVal);
|
||||
if (index)
|
||||
{
|
||||
return TitleSequenceManager::GetItem(*index);
|
||||
@@ -561,37 +680,48 @@ namespace OpenRCT2::Scripting
|
||||
}
|
||||
};
|
||||
|
||||
class ScTitleSequenceManager
|
||||
class ScTitleSequenceManager;
|
||||
extern ScTitleSequenceManager gScTitleSequenceManager;
|
||||
class ScTitleSequenceManager : public ScBase
|
||||
{
|
||||
private:
|
||||
std::vector<std::shared_ptr<ScTitleSequence>> titleSequences_get() const
|
||||
static JSValue titleSequences_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
std::vector<std::shared_ptr<ScTitleSequence>> result;
|
||||
JSValue result = JS_NewArray(ctx);
|
||||
auto count = TitleSequenceManager::GetCount();
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
const auto& path = TitleSequenceManager::GetItem(i)->Path;
|
||||
result.push_back(std::make_shared<ScTitleSequence>(path));
|
||||
JS_SetPropertyInt64(ctx, result, i, gScTitleSequence.New(ctx, path));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<ScTitleSequence> create(const std::string& name)
|
||||
static JSValue create(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_STR(name, ctx, argv[0])
|
||||
|
||||
auto index = TitleSequenceManager::CreateItem(name.c_str());
|
||||
auto* item = TitleSequenceManager::GetItem(index);
|
||||
if (item != nullptr)
|
||||
{
|
||||
return std::make_shared<ScTitleSequence>(item->Path);
|
||||
return gScTitleSequence.New(ctx, item->Path);
|
||||
}
|
||||
return nullptr;
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
static void Register(duk_context* ctx)
|
||||
JSValue New(JSContext* ctx)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScTitleSequenceManager::titleSequences_get, nullptr, "titleSequences");
|
||||
dukglue_register_method(ctx, &ScTitleSequenceManager::create, "create");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("titleSequences", ScTitleSequenceManager::titleSequences_get, nullptr),
|
||||
JS_CFUNC_DEF("create", 1, ScTitleSequenceManager::create),
|
||||
};
|
||||
return MakeWithOpaque(ctx, funcs, nullptr);
|
||||
}
|
||||
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "TitleSequenceManager");
|
||||
}
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
+321
-240
@@ -11,12 +11,14 @@
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "../interface/Window.h"
|
||||
#include "../windows/Windows.h"
|
||||
#include "CustomMenu.h"
|
||||
#include "CustomWindow.h"
|
||||
#include "ScImageManager.hpp"
|
||||
#include "ScTileSelection.hpp"
|
||||
#include "ScViewport.hpp"
|
||||
#include "ScWindow.hpp"
|
||||
#include "ScWindow.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
@@ -24,416 +26,495 @@
|
||||
#include <openrct2/Input.h>
|
||||
#include <openrct2/scenario/ScenarioCategory.h>
|
||||
#include <openrct2/scenario/ScenarioRepository.h>
|
||||
#include <openrct2/scripting/Duktape.hpp>
|
||||
#include <openrct2/scripting/ScriptEngine.h>
|
||||
#include <string>
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class Plugin;
|
||||
}
|
||||
class ScUi;
|
||||
extern ScUi gScUi;
|
||||
class ScTool;
|
||||
extern ScTool gScTool;
|
||||
|
||||
namespace OpenRCT2::Ui::Windows
|
||||
{
|
||||
WindowBase* WindowCustomOpen(std::shared_ptr<Scripting::Plugin> owner, DukValue dukDesc);
|
||||
}
|
||||
static const EnumMap<Scenario::Category> ScenarioCategoryMap{ {
|
||||
{ "beginner", Scenario::Category::beginner },
|
||||
{ "challenging", Scenario::Category::challenging },
|
||||
{ "expert", Scenario::Category::expert },
|
||||
{ "real", Scenario::Category::real },
|
||||
{ "other", Scenario::Category::other },
|
||||
{ "dlc", Scenario::Category::dlc },
|
||||
{ "build_your_own", Scenario::Category::buildYourOwn },
|
||||
{ "competitions", Scenario::Category::competitions },
|
||||
} };
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
static const DukEnumMap<Scenario::Category> ScenarioCategoryMap(
|
||||
{
|
||||
{ "beginner", Scenario::Category::beginner },
|
||||
{ "challenging", Scenario::Category::challenging },
|
||||
{ "expert", Scenario::Category::expert },
|
||||
{ "real", Scenario::Category::real },
|
||||
{ "other", Scenario::Category::other },
|
||||
{ "dlc", Scenario::Category::dlc },
|
||||
{ "build_your_own", Scenario::Category::buildYourOwn },
|
||||
{ "competitions", Scenario::Category::competitions },
|
||||
});
|
||||
static const EnumMap<ScenarioSource> ScenarioSourceMap{
|
||||
{ "rct1", ScenarioSource::RCT1 }, { "rct1_aa", ScenarioSource::RCT1_AA }, { "rct1_ll", ScenarioSource::RCT1_LL },
|
||||
{ "rct2", ScenarioSource::RCT2 }, { "rct2_ww", ScenarioSource::RCT2_WW }, { "rct2_tt", ScenarioSource::RCT2_TT },
|
||||
{ "real", ScenarioSource::Real }, { "extras", ScenarioSource::Extras }, { "other", ScenarioSource::Other },
|
||||
};
|
||||
|
||||
static const DukEnumMap<ScenarioSource> ScenarioSourceMap(
|
||||
{
|
||||
{ "rct1", ScenarioSource::RCT1 },
|
||||
{ "rct1_aa", ScenarioSource::RCT1_AA },
|
||||
{ "rct1_ll", ScenarioSource::RCT1_LL },
|
||||
{ "rct2", ScenarioSource::RCT2 },
|
||||
{ "rct2_ww", ScenarioSource::RCT2_WW },
|
||||
{ "rct2_tt", ScenarioSource::RCT2_TT },
|
||||
{ "real", ScenarioSource::Real },
|
||||
{ "extras", ScenarioSource::Extras },
|
||||
{ "other", ScenarioSource::Other },
|
||||
});
|
||||
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const Scenario::Category& value)
|
||||
inline JSValue ScenarioCategoryToJS(JSContext* ctx, Scenario::Category value)
|
||||
{
|
||||
const auto& entry = ScenarioCategoryMap.find(value);
|
||||
if (entry != ScenarioCategoryMap.end())
|
||||
return ToDuk(ctx, entry->first);
|
||||
return ToDuk(ctx, ScenarioCategoryMap[Scenario::Category::other]);
|
||||
return JSFromStdString(ctx, entry->first);
|
||||
return JSFromStdString(ctx, ScenarioCategoryMap[Scenario::Category::other]);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const ScenarioSource& value)
|
||||
inline JSValue ScenarioSourceToJS(JSContext* ctx, ScenarioSource value)
|
||||
{
|
||||
const auto& entry = ScenarioSourceMap.find(value);
|
||||
if (entry != ScenarioSourceMap.end())
|
||||
return ToDuk(ctx, entry->first);
|
||||
return ToDuk(ctx, ScenarioSourceMap[ScenarioSource::Other]);
|
||||
return JSFromStdString(ctx, entry->first);
|
||||
return JSFromStdString(ctx, ScenarioSourceMap[ScenarioSource::Other]);
|
||||
}
|
||||
|
||||
class ScTool
|
||||
class ScTool final : public ScBase
|
||||
{
|
||||
private:
|
||||
duk_context* _ctx{};
|
||||
|
||||
public:
|
||||
ScTool(duk_context* ctx)
|
||||
: _ctx(ctx)
|
||||
static JSValue id_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return JSFromStdString(ctx, ActiveCustomTool ? ActiveCustomTool->Id : "");
|
||||
}
|
||||
|
||||
static void Register(duk_context* ctx)
|
||||
static JSValue cursor_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScTool::id_get, nullptr, "id");
|
||||
dukglue_register_property(ctx, &ScTool::cursor_get, nullptr, "cursor");
|
||||
dukglue_register_method(ctx, &ScTool::cancel, "cancel");
|
||||
return CursorIDToJSValue(ctx, static_cast<CursorID>(gCurrentToolId));
|
||||
}
|
||||
|
||||
private:
|
||||
std::string id_get() const
|
||||
{
|
||||
return ActiveCustomTool ? ActiveCustomTool->Id : "";
|
||||
}
|
||||
|
||||
DukValue cursor_get() const
|
||||
{
|
||||
return ToDuk(_ctx, static_cast<CursorID>(gCurrentToolId));
|
||||
}
|
||||
|
||||
void cancel()
|
||||
static JSValue cancel(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
ToolCancel();
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("id", ScTool::id_get, nullptr),
|
||||
JS_CGETSET_DEF("cursor", ScTool::cursor_get, nullptr),
|
||||
JS_CFUNC_DEF("cancel", 0, ScTool::cancel),
|
||||
};
|
||||
|
||||
public:
|
||||
JSValue New(JSContext* ctx)
|
||||
{
|
||||
return MakeWithOpaque(ctx, funcs, nullptr);
|
||||
}
|
||||
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "Tool");
|
||||
}
|
||||
};
|
||||
|
||||
class ScUi
|
||||
class ScUi final : public ScBase
|
||||
{
|
||||
private:
|
||||
ScriptEngine& _scriptEngine;
|
||||
|
||||
public:
|
||||
ScUi(ScriptEngine& scriptEngine)
|
||||
: _scriptEngine(scriptEngine)
|
||||
static JSValue width_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return JS_NewInt32(ctx, ContextGetWidth());
|
||||
}
|
||||
static JSValue height_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return JS_NewInt32(ctx, ContextGetHeight());
|
||||
}
|
||||
static JSValue windows_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return JS_NewInt32(ctx, static_cast<int32_t>(gWindowList.size()));
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t width_get() const
|
||||
static JSValue mainViewport_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return ContextGetWidth();
|
||||
}
|
||||
int32_t height_get() const
|
||||
{
|
||||
return ContextGetHeight();
|
||||
}
|
||||
int32_t windows_get() const
|
||||
{
|
||||
return static_cast<int32_t>(gWindowList.size());
|
||||
return gScViewport.New(ctx, WindowClass::mainWindow);
|
||||
}
|
||||
|
||||
std::shared_ptr<ScViewport> mainViewport_get() const
|
||||
static JSValue tileSelection_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return std::make_shared<ScViewport>(WindowClass::mainWindow);
|
||||
return gScTileSelection.New(ctx);
|
||||
}
|
||||
|
||||
std::shared_ptr<ScTileSelection> tileSelection_get() const
|
||||
{
|
||||
return std::make_shared<ScTileSelection>(_scriptEngine.GetContext());
|
||||
}
|
||||
|
||||
std::shared_ptr<ScTool> tool_get() const
|
||||
static JSValue tool_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
if (gInputFlags.has(InputFlag::toolActive))
|
||||
{
|
||||
return std::make_shared<ScTool>(_scriptEngine.GetContext());
|
||||
return gScTool.New(ctx);
|
||||
}
|
||||
return {};
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
std::shared_ptr<ScImageManager> imageManager_get() const
|
||||
static JSValue imageManager_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return std::make_shared<ScImageManager>(_scriptEngine.GetContext());
|
||||
return gScImageManager.New(ctx);
|
||||
}
|
||||
|
||||
std::shared_ptr<ScWindow> openWindow(DukValue desc)
|
||||
static JSValue openWindow(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
using namespace OpenRCT2::Ui::Windows;
|
||||
|
||||
auto& execInfo = _scriptEngine.GetExecInfo();
|
||||
ScriptEngine* scriptEngine = gScUi.GetOpaque<ScriptEngine*>(thisVal);
|
||||
if (!scriptEngine)
|
||||
{
|
||||
JS_ThrowInternalError(ctx, "No script engine");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
auto& execInfo = scriptEngine->GetExecInfo();
|
||||
auto owner = execInfo.GetCurrentPlugin();
|
||||
|
||||
owner->ThrowIfStopping();
|
||||
if (owner->IsStopping())
|
||||
{
|
||||
JS_ThrowInternalError(ctx, "Plugin is stopping.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
std::shared_ptr<ScWindow> scWindow = nullptr;
|
||||
auto w = WindowCustomOpen(owner, desc);
|
||||
auto w = WindowCustomOpen(ctx, owner, argv[0]);
|
||||
if (w != nullptr)
|
||||
{
|
||||
scWindow = std::make_shared<ScWindow>(w);
|
||||
return gScWindow.New(ctx, w);
|
||||
}
|
||||
return scWindow;
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
void closeWindows(std::string classification, DukValue id)
|
||||
static JSValue closeWindows(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_STR(classification, ctx, argv[0]);
|
||||
JSValue id = argv[1];
|
||||
|
||||
auto* windowMgr = Ui::GetWindowManager();
|
||||
auto cls = GetClassification(classification);
|
||||
if (cls != WindowClass::null)
|
||||
{
|
||||
if (id.type() == DukValue::Type::NUMBER)
|
||||
if (JS_IsNumber(id))
|
||||
{
|
||||
windowMgr->CloseByNumber(cls, id.as_uint());
|
||||
windowMgr->CloseByNumber(cls, static_cast<WindowNumber>(JSToInt(ctx, id)));
|
||||
}
|
||||
else
|
||||
{
|
||||
windowMgr->CloseByClass(cls);
|
||||
}
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void closeAllWindows()
|
||||
static JSValue closeAllWindows(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto* windowMgr = Ui::GetWindowManager();
|
||||
windowMgr->CloseAll();
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
std::shared_ptr<ScWindow> getWindow(DukValue a) const
|
||||
static JSValue getWindow(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
if (a.type() == DukValue::Type::NUMBER)
|
||||
if (JS_IsNumber(argv[0]))
|
||||
{
|
||||
auto index = a.as_uint();
|
||||
size_t i = 0;
|
||||
int32_t index = -1;
|
||||
JS_ToInt32(ctx, &index, argv[0]);
|
||||
int32_t i = 0;
|
||||
for (const auto& w : gWindowList)
|
||||
{
|
||||
if (i == index)
|
||||
{
|
||||
return std::make_shared<ScWindow>(w.get());
|
||||
return gScWindow.New(ctx, w.get());
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if (a.type() == DukValue::Type::STRING)
|
||||
else if (JS_IsString(argv[0]))
|
||||
{
|
||||
const auto& classification = a.as_string();
|
||||
auto w = FindCustomWindowByClassification(classification);
|
||||
std::string classification = JSToStdString(ctx, argv[0]);
|
||||
auto w = Ui::Windows::FindCustomWindowByClassification(classification);
|
||||
if (w != nullptr)
|
||||
{
|
||||
return std::make_shared<ScWindow>(w);
|
||||
return gScWindow.New(ctx, w);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
void showError(const std::string& title, const std::string& message)
|
||||
static JSValue showError(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
ErrorOpen(title, message);
|
||||
if (argc < 2 || !JS_IsString(argv[0]) || !JS_IsString(argv[1]))
|
||||
{
|
||||
JS_ThrowTypeError(ctx, "Invalid arguments");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
std::string title = JSToStdString(ctx, argv[0]);
|
||||
std::string message = JSToStdString(ctx, argv[1]);
|
||||
Ui::Windows::ErrorOpen(title, message);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void showTextInput(const DukValue& desc)
|
||||
static JSValue showTextInput(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
try
|
||||
JSValue desc = argv[0];
|
||||
if (!JS_IsObject(desc))
|
||||
{
|
||||
constexpr int32_t kMaxLengthAllowed = 4096;
|
||||
auto plugin = _scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
auto title = desc["title"].as_string();
|
||||
auto description = desc["description"].as_string();
|
||||
auto initialValue = AsOrDefault(desc["initialValue"], "");
|
||||
auto maxLength = AsOrDefault(desc["maxLength"], kMaxLengthAllowed);
|
||||
auto callback = desc["callback"];
|
||||
WindowTextInputOpen(
|
||||
title, description, initialValue, std::clamp(maxLength, 0, kMaxLengthAllowed),
|
||||
[this, plugin, callback](std::string_view value) {
|
||||
auto dukValue = ToDuk(_scriptEngine.GetContext(), value);
|
||||
_scriptEngine.ExecutePluginCall(plugin, callback, { dukValue }, false);
|
||||
},
|
||||
{});
|
||||
JS_ThrowInternalError(ctx, "Invalid parameters.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
catch (const DukException&)
|
||||
|
||||
ScriptEngine* scriptEngine = gScUi.GetOpaque<ScriptEngine*>(thisVal);
|
||||
if (!scriptEngine)
|
||||
{
|
||||
duk_error(_scriptEngine.GetContext(), DUK_ERR_ERROR, "Invalid parameters.");
|
||||
JS_ThrowInternalError(ctx, "No script engine");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
constexpr int32_t kMaxLengthAllowed = 4096;
|
||||
auto plugin = scriptEngine->GetExecInfo().GetCurrentPlugin();
|
||||
auto title = JSToStdString(ctx, desc, "title");
|
||||
auto description = JSToStdString(ctx, desc, "description");
|
||||
auto initialValue = AsOrDefault(ctx, desc, "initialValue", "");
|
||||
auto maxLength = AsOrDefault(ctx, desc, "maxLength", kMaxLengthAllowed);
|
||||
auto callback = JSToCallback(ctx, desc, "callback");
|
||||
Ui::Windows::WindowTextInputOpen(
|
||||
title, description, initialValue, std::clamp(maxLength, 0, kMaxLengthAllowed),
|
||||
[scriptEngine, ctx, plugin, callback](std::string_view value) {
|
||||
scriptEngine->ExecutePluginCall(plugin, callback.callback, { JSFromStdString(ctx, value) }, false);
|
||||
},
|
||||
{});
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void showFileBrowse(const DukValue& desc)
|
||||
static JSValue showFileBrowse(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
try
|
||||
JSValue desc = argv[0];
|
||||
if (!JS_IsObject(desc))
|
||||
{
|
||||
auto plugin = _scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
auto type = desc["type"].as_string();
|
||||
auto fileType = desc["fileType"].as_string();
|
||||
auto defaultPath = AsOrDefault(desc["defaultPath"], "");
|
||||
auto callback = desc["callback"];
|
||||
|
||||
auto loadSaveAction = LoadSaveAction::load;
|
||||
if (type == "load")
|
||||
loadSaveAction = LoadSaveAction::load;
|
||||
else
|
||||
throw DukException();
|
||||
|
||||
LoadSaveType loadSaveType;
|
||||
if (fileType == "game")
|
||||
loadSaveType = LoadSaveType::park;
|
||||
else if (fileType == "heightmap")
|
||||
loadSaveType = LoadSaveType::heightmap;
|
||||
else
|
||||
throw DukException();
|
||||
|
||||
LoadsaveOpen(
|
||||
loadSaveAction, loadSaveType, defaultPath,
|
||||
[this, plugin, callback](ModalResult result, std::string_view path) {
|
||||
if (result == ModalResult::ok)
|
||||
{
|
||||
auto dukValue = ToDuk(_scriptEngine.GetContext(), path);
|
||||
_scriptEngine.ExecutePluginCall(plugin, callback, { dukValue }, false);
|
||||
}
|
||||
},
|
||||
nullptr);
|
||||
JS_ThrowInternalError(ctx, "Invalid parameters.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
catch (const DukException&)
|
||||
|
||||
ScriptEngine* scriptEngine = gScUi.GetOpaque<ScriptEngine*>(thisVal);
|
||||
if (!scriptEngine)
|
||||
{
|
||||
duk_error(_scriptEngine.GetContext(), DUK_ERR_ERROR, "Invalid parameters.");
|
||||
JS_ThrowInternalError(ctx, "No script engine");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
auto plugin = scriptEngine->GetExecInfo().GetCurrentPlugin();
|
||||
auto type = JSToStdString(ctx, desc, "type");
|
||||
auto fileType = JSToStdString(ctx, desc, "fileType");
|
||||
auto defaultPath = AsOrDefault(ctx, desc, "defaultPath", "");
|
||||
auto callback = JSToCallback(ctx, desc, "callback");
|
||||
|
||||
auto loadSaveAction = LoadSaveAction::load;
|
||||
if (type == "load")
|
||||
loadSaveAction = LoadSaveAction::load;
|
||||
else
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "Invalid type parameter");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
LoadSaveType loadSaveType;
|
||||
if (fileType == "game")
|
||||
loadSaveType = LoadSaveType::park;
|
||||
else if (fileType == "heightmap")
|
||||
loadSaveType = LoadSaveType::heightmap;
|
||||
else
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "Invalid fileType parameter");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
Ui::Windows::LoadsaveOpen(
|
||||
loadSaveAction, loadSaveType, defaultPath,
|
||||
[scriptEngine, ctx, plugin, callback](ModalResult result, std::string_view path) {
|
||||
if (result == ModalResult::ok)
|
||||
{
|
||||
scriptEngine->ExecutePluginCall(plugin, callback.callback, { JSFromStdString(ctx, path) }, false);
|
||||
}
|
||||
},
|
||||
true, nullptr);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void showScenarioSelect(const DukValue& desc)
|
||||
static JSValue showScenarioSelect(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto plugin = _scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
auto callback = desc["callback"];
|
||||
JSValue desc = argv[0];
|
||||
if (!JS_IsObject(desc))
|
||||
{
|
||||
JS_ThrowInternalError(ctx, "Invalid parameters.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
ScenarioselectOpen([this, plugin, callback](std::string_view path) {
|
||||
auto dukValue = GetScenarioFile(path);
|
||||
_scriptEngine.ExecutePluginCall(plugin, callback, { dukValue }, false);
|
||||
ScriptEngine* scriptEngine = gScUi.GetOpaque<ScriptEngine*>(thisVal);
|
||||
if (!scriptEngine)
|
||||
{
|
||||
JS_ThrowInternalError(ctx, "No script engine");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
auto plugin = scriptEngine->GetExecInfo().GetCurrentPlugin();
|
||||
auto callback = JSToCallback(ctx, desc, "callback");
|
||||
|
||||
Ui::Windows::ScenarioselectOpen([scriptEngine, ctx, plugin, callback](std::string_view path) {
|
||||
scriptEngine->ExecutePluginCall(plugin, callback.callback, { GetScenarioFile(ctx, path) }, false);
|
||||
});
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void activateTool(const DukValue& desc)
|
||||
static JSValue activateTool(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
InitialiseCustomTool(_scriptEngine, desc);
|
||||
ScriptEngine* scriptEngine = gScUi.GetOpaque<ScriptEngine*>(thisVal);
|
||||
if (!scriptEngine)
|
||||
{
|
||||
JS_ThrowInternalError(ctx, "No script engine");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
return InitialiseCustomTool(*scriptEngine, ctx, argv[0]);
|
||||
}
|
||||
|
||||
void registerMenuItem(std::string text, DukValue callback)
|
||||
static JSValue registerMenuItem(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto& execInfo = _scriptEngine.GetExecInfo();
|
||||
ScriptEngine* scriptEngine = gScUi.GetOpaque<ScriptEngine*>(thisVal);
|
||||
if (!scriptEngine)
|
||||
{
|
||||
JS_ThrowInternalError(ctx, "No script engine");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
if (argc < 2 || !JS_IsString(argv[0]) || !JS_IsFunction(ctx, argv[1]))
|
||||
{
|
||||
JS_ThrowTypeError(ctx, "Invalid arguments");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
auto& execInfo = scriptEngine->GetExecInfo();
|
||||
auto owner = execInfo.GetCurrentPlugin();
|
||||
CustomMenuItems.emplace_back(owner, CustomToolbarMenuItemKind::Standard, text, callback);
|
||||
std::string text = JSToStdString(ctx, argv[0]);
|
||||
assert(owner->GetContext() == ctx);
|
||||
CustomMenuItems.emplace_back(owner, CustomToolbarMenuItemKind::Standard, text, JSCallback(ctx, argv[1]));
|
||||
std::ranges::sort(CustomMenuItems, [](auto&& a, auto&& b) { return a.Text < b.Text; });
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void registerToolboxMenuItem(const std::string& text, DukValue callback)
|
||||
static JSValue registerToolboxMenuItem(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto& execInfo = _scriptEngine.GetExecInfo();
|
||||
ScriptEngine* scriptEngine = gScUi.GetOpaque<ScriptEngine*>(thisVal);
|
||||
if (!scriptEngine)
|
||||
{
|
||||
JS_ThrowInternalError(ctx, "No script engine");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
if (argc < 2 || !JS_IsString(argv[0]) || !JS_IsFunction(ctx, argv[1]))
|
||||
{
|
||||
JS_ThrowTypeError(ctx, "Invalid arguments");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
auto& execInfo = scriptEngine->GetExecInfo();
|
||||
auto owner = execInfo.GetCurrentPlugin();
|
||||
if (owner->GetMetadata().Type == PluginType::Intransient)
|
||||
{
|
||||
CustomMenuItems.emplace_back(owner, CustomToolbarMenuItemKind::Toolbox, text, callback);
|
||||
CustomMenuItems.emplace_back(
|
||||
owner, CustomToolbarMenuItemKind::Toolbox, JSToStdString(ctx, argv[0]), JSCallback(ctx, argv[1]));
|
||||
std::ranges::sort(CustomMenuItems, [](auto&& a, auto&& b) { return a.Text < b.Text; });
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_error(_scriptEngine.GetContext(), DUK_ERR_ERROR, "Plugin must be intransient.");
|
||||
JS_ThrowPlainError(ctx, "Plugin must be intransient.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void registerShortcut(DukValue desc)
|
||||
static JSValue registerShortcut(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
try
|
||||
JSValue desc = argv[0];
|
||||
if (!JS_IsObject(desc))
|
||||
{
|
||||
auto& execInfo = _scriptEngine.GetExecInfo();
|
||||
auto owner = execInfo.GetCurrentPlugin();
|
||||
auto id = desc["id"].as_string();
|
||||
auto text = desc["text"].as_string();
|
||||
|
||||
std::vector<std::string> bindings;
|
||||
auto dukBindings = desc["bindings"];
|
||||
if (dukBindings.is_array())
|
||||
{
|
||||
for (auto binding : dukBindings.as_array())
|
||||
{
|
||||
bindings.push_back(binding.as_string());
|
||||
}
|
||||
}
|
||||
|
||||
auto callback = desc["callback"];
|
||||
CustomShortcuts.emplace_back(std::make_unique<CustomShortcut>(owner, id, text, bindings, callback));
|
||||
JS_ThrowInternalError(ctx, "Invalid parameters.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
catch (const DukException&)
|
||||
|
||||
ScriptEngine* scriptEngine = gScUi.GetOpaque<ScriptEngine*>(thisVal);
|
||||
if (!scriptEngine)
|
||||
{
|
||||
duk_error(_scriptEngine.GetContext(), DUK_ERR_ERROR, "Invalid parameters.");
|
||||
JS_ThrowInternalError(ctx, "No script engine");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
auto owner = scriptEngine->GetExecInfo().GetCurrentPlugin();
|
||||
auto id = JSToStdString(ctx, desc, "id");
|
||||
auto text = JSToStdString(ctx, desc, "text");
|
||||
|
||||
std::vector<std::string> bindings;
|
||||
JSIterateArray(
|
||||
ctx, desc, "bindings", [&bindings](JSContext* ctx2, JSValue x) { bindings.push_back(JSToStdString(ctx2, x)); });
|
||||
|
||||
auto callback = JSToCallback(ctx, desc, "callback");
|
||||
CustomShortcuts.emplace_back(std::make_unique<CustomShortcut>(owner, id, text, bindings, callback));
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("height", ScUi::height_get, nullptr),
|
||||
JS_CGETSET_DEF("width", ScUi::width_get, nullptr),
|
||||
JS_CGETSET_DEF("windows", ScUi::windows_get, nullptr),
|
||||
JS_CGETSET_DEF("mainViewport", ScUi::mainViewport_get, nullptr),
|
||||
JS_CGETSET_DEF("tileSelection", ScUi::tileSelection_get, nullptr),
|
||||
JS_CGETSET_DEF("tool", ScUi::tool_get, nullptr),
|
||||
JS_CGETSET_DEF("imageManager", ScUi::imageManager_get, nullptr),
|
||||
|
||||
JS_CFUNC_DEF("openWindow", 1, ScUi::openWindow),
|
||||
JS_CFUNC_DEF("closeWindows", 2, ScUi::closeWindows),
|
||||
JS_CFUNC_DEF("closeAllWindows", 0, ScUi::closeAllWindows),
|
||||
JS_CFUNC_DEF("getWindow", 1, ScUi::getWindow),
|
||||
JS_CFUNC_DEF("showError", 2, ScUi::showError),
|
||||
JS_CFUNC_DEF("showTextInput", 1, ScUi::showTextInput),
|
||||
JS_CFUNC_DEF("showFileBrowse", 1, ScUi::showFileBrowse),
|
||||
JS_CFUNC_DEF("showScenarioSelect", 1, ScUi::showScenarioSelect),
|
||||
JS_CFUNC_DEF("activateTool", 1, ScUi::activateTool),
|
||||
JS_CFUNC_DEF("registerMenuItem", 2, ScUi::registerMenuItem),
|
||||
JS_CFUNC_DEF("registerToolboxMenuItem", 2, ScUi::registerToolboxMenuItem),
|
||||
JS_CFUNC_DEF("registerShortcut", 1, ScUi::registerShortcut),
|
||||
};
|
||||
|
||||
public:
|
||||
static void Register(duk_context* ctx)
|
||||
JSValue New(JSContext* ctx, ScriptEngine* scriptEngine)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScUi::height_get, nullptr, "height");
|
||||
dukglue_register_property(ctx, &ScUi::width_get, nullptr, "width");
|
||||
dukglue_register_property(ctx, &ScUi::windows_get, nullptr, "windows");
|
||||
dukglue_register_property(ctx, &ScUi::mainViewport_get, nullptr, "mainViewport");
|
||||
dukglue_register_property(ctx, &ScUi::tileSelection_get, nullptr, "tileSelection");
|
||||
dukglue_register_property(ctx, &ScUi::tool_get, nullptr, "tool");
|
||||
dukglue_register_property(ctx, &ScUi::imageManager_get, nullptr, "imageManager");
|
||||
dukglue_register_method(ctx, &ScUi::openWindow, "openWindow");
|
||||
dukglue_register_method(ctx, &ScUi::closeWindows, "closeWindows");
|
||||
dukglue_register_method(ctx, &ScUi::closeAllWindows, "closeAllWindows");
|
||||
dukglue_register_method(ctx, &ScUi::getWindow, "getWindow");
|
||||
dukglue_register_method(ctx, &ScUi::showError, "showError");
|
||||
dukglue_register_method(ctx, &ScUi::showTextInput, "showTextInput");
|
||||
dukglue_register_method(ctx, &ScUi::showFileBrowse, "showFileBrowse");
|
||||
dukglue_register_method(ctx, &ScUi::showScenarioSelect, "showScenarioSelect");
|
||||
dukglue_register_method(ctx, &ScUi::activateTool, "activateTool");
|
||||
dukglue_register_method(ctx, &ScUi::registerMenuItem, "registerMenuItem");
|
||||
dukglue_register_method(ctx, &ScUi::registerToolboxMenuItem, "registerToolboxMenuItem");
|
||||
dukglue_register_method(ctx, &ScUi::registerShortcut, "registerShortcut");
|
||||
return MakeWithOpaque(ctx, funcs, scriptEngine);
|
||||
}
|
||||
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "Ui");
|
||||
}
|
||||
|
||||
private:
|
||||
WindowClass GetClassification(const std::string& key) const
|
||||
static WindowClass GetClassification(const std::string& key)
|
||||
{
|
||||
return WindowClass::null;
|
||||
}
|
||||
|
||||
DukValue GetScenarioFile(std::string_view path)
|
||||
static JSValue GetScenarioFile(JSContext* ctx, std::string_view path)
|
||||
{
|
||||
auto ctx = _scriptEngine.GetContext();
|
||||
DukObject obj(ctx);
|
||||
obj.Set("path", path);
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "path", JSFromStdString(ctx, path));
|
||||
|
||||
auto* scenarioRepo = GetScenarioRepository();
|
||||
auto entry = scenarioRepo->GetByPath(std::string(path).c_str());
|
||||
if (entry != nullptr)
|
||||
{
|
||||
obj.Set("id", entry->ScenarioId);
|
||||
obj.Set("category", ToDuk(ctx, entry->Category));
|
||||
obj.Set("sourceGame", ToDuk(ctx, entry->SourceGame));
|
||||
obj.Set("internalName", entry->InternalName);
|
||||
obj.Set("name", entry->Name);
|
||||
obj.Set("details", entry->Details);
|
||||
JS_SetPropertyStr(ctx, obj, "id", JS_NewInt32(ctx, entry->ScenarioId));
|
||||
JS_SetPropertyStr(ctx, obj, "category", ScenarioCategoryToJS(ctx, entry->Category));
|
||||
JS_SetPropertyStr(ctx, obj, "sourceGame", ScenarioSourceToJS(ctx, entry->SourceGame));
|
||||
JS_SetPropertyStr(ctx, obj, "internalName", JSFromStdString(ctx, entry->InternalName));
|
||||
JS_SetPropertyStr(ctx, obj, "name", JSFromStdString(ctx, entry->Name));
|
||||
JS_SetPropertyStr(ctx, obj, "details", JSFromStdString(ctx, entry->Details));
|
||||
|
||||
auto* highscore = entry->Highscore;
|
||||
if (highscore == nullptr)
|
||||
{
|
||||
obj.Set("highscore", nullptr);
|
||||
JS_SetPropertyStr(ctx, obj, "highscore", JS_NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
DukObject dukHighscore(ctx);
|
||||
dukHighscore.Set("name", highscore->name);
|
||||
dukHighscore.Set("companyValue", highscore->company_value);
|
||||
obj.Set("highscore", dukHighscore.Take());
|
||||
JSValue jsHighscore = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, jsHighscore, "name", JSFromStdString(ctx, highscore->name));
|
||||
JS_SetPropertyStr(ctx, jsHighscore, "companyValue", JS_NewInt64(ctx, highscore->company_value));
|
||||
JS_SetPropertyStr(ctx, obj, "highscore", jsHighscore);
|
||||
}
|
||||
}
|
||||
return obj.Take();
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -13,252 +13,287 @@
|
||||
|
||||
#include "../interface/Window.h"
|
||||
|
||||
#include <memory>
|
||||
#include <openrct2/Context.h>
|
||||
#include <openrct2/interface/Viewport.h>
|
||||
#include <openrct2/scripting/Duktape.hpp>
|
||||
#include <openrct2/scripting/ScriptEngine.h>
|
||||
#include <openrct2/ui/WindowManager.h>
|
||||
#include <openrct2/world/Map.h>
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScViewport
|
||||
class ScViewport;
|
||||
extern ScViewport gScViewport;
|
||||
class ScViewport final : public ScBase
|
||||
{
|
||||
private:
|
||||
WindowClass _class{};
|
||||
WindowNumber _number{};
|
||||
|
||||
public:
|
||||
ScViewport(WindowClass c, WindowNumber n = 0)
|
||||
: _class(c)
|
||||
, _number(n)
|
||||
struct OpaqueWindowData
|
||||
{
|
||||
WindowClass _class{};
|
||||
WindowNumber _number{};
|
||||
};
|
||||
|
||||
static JSValue left_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto viewport = GetViewport(thisVal);
|
||||
if (viewport != nullptr)
|
||||
{
|
||||
return JS_NewInt32(ctx, viewport->viewPos.x);
|
||||
}
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
static JSValue left_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
|
||||
WindowBase* w;
|
||||
auto viewport = GetViewport(thisVal, &w);
|
||||
if (w != nullptr && viewport != nullptr)
|
||||
{
|
||||
SetViewLeftTop(w, viewport, valueInt, viewport->viewPos.y);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t left_get() const
|
||||
static JSValue top_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto viewport = GetViewport();
|
||||
auto viewport = GetViewport(thisVal);
|
||||
if (viewport != nullptr)
|
||||
{
|
||||
return viewport->viewPos.x;
|
||||
return JS_NewInt32(ctx, viewport->viewPos.y);
|
||||
}
|
||||
return 0;
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
void left_set(int32_t value)
|
||||
static JSValue top_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
auto viewport = GetViewport();
|
||||
if (viewport != nullptr)
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
|
||||
WindowBase* w;
|
||||
auto viewport = GetViewport(thisVal, &w);
|
||||
if (w != nullptr && viewport != nullptr)
|
||||
{
|
||||
SetViewLeftTop(value, viewport->viewPos.y);
|
||||
SetViewLeftTop(w, viewport, viewport->viewPos.x, valueInt);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
int32_t top_get() const
|
||||
static JSValue right_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto viewport = GetViewport();
|
||||
auto viewport = GetViewport(thisVal);
|
||||
if (viewport != nullptr)
|
||||
{
|
||||
return viewport->viewPos.y;
|
||||
return JS_NewInt32(ctx, viewport->viewPos.x + viewport->ViewWidth());
|
||||
}
|
||||
return 0;
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
void top_set(int32_t value)
|
||||
static JSValue right_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
auto viewport = GetViewport();
|
||||
if (viewport != nullptr)
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
|
||||
WindowBase* w;
|
||||
auto viewport = GetViewport(thisVal, &w);
|
||||
if (w != nullptr && viewport != nullptr)
|
||||
{
|
||||
SetViewLeftTop(viewport->viewPos.x, value);
|
||||
SetViewLeftTop(w, viewport, valueInt - viewport->ViewWidth(), viewport->viewPos.y);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
int32_t right_get() const
|
||||
static JSValue bottom_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto viewport = GetViewport();
|
||||
auto viewport = GetViewport(thisVal);
|
||||
if (viewport != nullptr)
|
||||
{
|
||||
return viewport->viewPos.x + viewport->ViewWidth();
|
||||
return JS_NewInt32(ctx, viewport->viewPos.y + viewport->ViewHeight());
|
||||
}
|
||||
return 0;
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
void right_set(int32_t value)
|
||||
static JSValue bottom_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
auto viewport = GetViewport();
|
||||
if (viewport != nullptr)
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
|
||||
WindowBase* w;
|
||||
auto viewport = GetViewport(thisVal, &w);
|
||||
if (w != nullptr && viewport != nullptr)
|
||||
{
|
||||
SetViewLeftTop(value - viewport->ViewWidth(), viewport->viewPos.y);
|
||||
SetViewLeftTop(w, viewport, viewport->viewPos.x, valueInt - viewport->ViewHeight());
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
int32_t bottom_get() const
|
||||
static JSValue rotation_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto viewport = GetViewport();
|
||||
auto viewport = GetViewport(thisVal);
|
||||
if (viewport != nullptr)
|
||||
{
|
||||
return viewport->viewPos.y + viewport->ViewHeight();
|
||||
return JS_NewInt32(ctx, viewport->rotation);
|
||||
}
|
||||
return 0;
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
void bottom_set(int32_t value)
|
||||
static JSValue rotation_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
auto viewport = GetViewport();
|
||||
if (viewport != nullptr)
|
||||
{
|
||||
SetViewLeftTop(viewport->viewPos.x, value - viewport->ViewHeight());
|
||||
}
|
||||
}
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
|
||||
int32_t rotation_get() const
|
||||
{
|
||||
auto viewport = GetViewport();
|
||||
if (viewport != nullptr)
|
||||
if (valueInt >= 0 && valueInt < 4)
|
||||
{
|
||||
return viewport->rotation;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void rotation_set(int32_t value)
|
||||
{
|
||||
if (value >= 0 && value < 4)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
while (w->viewport->rotation != value)
|
||||
while (w->viewport->rotation != valueInt)
|
||||
{
|
||||
ViewportRotateSingle(w, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
int32_t zoom_get() const
|
||||
static JSValue zoom_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto viewport = GetViewport();
|
||||
auto viewport = GetViewport(thisVal);
|
||||
if (viewport != nullptr)
|
||||
{
|
||||
return static_cast<int8_t>(viewport->zoom);
|
||||
return JS_NewInt32(ctx, static_cast<int8_t>(viewport->zoom));
|
||||
}
|
||||
return 0;
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
void zoom_set(int32_t value)
|
||||
static JSValue zoom_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
auto i8Value = static_cast<int8_t>(value);
|
||||
auto i8Value = static_cast<int8_t>(valueInt);
|
||||
WindowZoomSet(*w, ZoomLevel{ i8Value }, false);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint32_t visibilityFlags_get() const
|
||||
static JSValue visibilityFlags_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto viewport = GetViewport();
|
||||
auto viewport = GetViewport(thisVal);
|
||||
if (viewport != nullptr)
|
||||
{
|
||||
return viewport->flags;
|
||||
return JS_NewInt64(ctx, viewport->flags);
|
||||
}
|
||||
return 0;
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
void visibilityFlags_set(uint32_t value)
|
||||
static JSValue visibilityFlags_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
JS_UNPACK_UINT32(valueUint, ctx, value)
|
||||
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
auto viewport = w->viewport;
|
||||
if (viewport != nullptr)
|
||||
{
|
||||
if (viewport->flags != value)
|
||||
if (viewport->flags != valueUint)
|
||||
{
|
||||
viewport->flags = value;
|
||||
viewport->flags = valueUint;
|
||||
w->invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
DukValue getCentrePosition() const
|
||||
static JSValue getCentrePosition(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto viewport = GetViewport();
|
||||
auto viewport = GetViewport(thisVal);
|
||||
if (viewport != nullptr)
|
||||
{
|
||||
auto centre = viewport->viewPos + ScreenCoordsXY{ viewport->ViewWidth() / 2, viewport->ViewHeight() / 2 };
|
||||
auto coords = ViewportPosToMapPos(centre, 24, viewport->rotation);
|
||||
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto obj = duk_push_object(ctx);
|
||||
duk_push_number(ctx, coords.x);
|
||||
duk_put_prop_string(ctx, obj, "x");
|
||||
duk_push_number(ctx, coords.y);
|
||||
duk_put_prop_string(ctx, obj, "y");
|
||||
return DukValue::take_from_stack(ctx);
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, coords.x));
|
||||
JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, coords.y));
|
||||
return obj;
|
||||
}
|
||||
return {};
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void moveTo(DukValue position)
|
||||
static JSValue moveTo(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
auto viewport = w->viewport;
|
||||
if (viewport != nullptr)
|
||||
{
|
||||
auto coords = GetCoordsFromObject(position);
|
||||
auto coords = GetCoordsFromObject(ctx, argv[0]);
|
||||
if (coords)
|
||||
{
|
||||
auto screenCoords = Translate3DTo2DWithZ(viewport->rotation, *coords);
|
||||
auto left = screenCoords.x - (viewport->ViewWidth() / 2);
|
||||
auto top = screenCoords.y - (viewport->ViewHeight() / 2);
|
||||
SetViewLeftTop(left, top);
|
||||
SetViewLeftTop(w, viewport, left, top);
|
||||
}
|
||||
}
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void scrollTo(DukValue position)
|
||||
static JSValue scrollTo(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
auto coords = GetCoordsFromObject(position);
|
||||
auto coords = GetCoordsFromObject(ctx, argv[0]);
|
||||
if (coords)
|
||||
{
|
||||
WindowScrollToLocation(*w, *coords);
|
||||
}
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
public:
|
||||
static void Register(duk_context* ctx)
|
||||
JSValue New(JSContext* ctx, WindowClass wClass, WindowNumber wNum = 0)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScViewport::left_get, &ScViewport::left_set, "left");
|
||||
dukglue_register_property(ctx, &ScViewport::top_get, &ScViewport::top_set, "top");
|
||||
dukglue_register_property(ctx, &ScViewport::right_get, &ScViewport::right_set, "right");
|
||||
dukglue_register_property(ctx, &ScViewport::bottom_get, &ScViewport::bottom_set, "bottom");
|
||||
dukglue_register_property(ctx, &ScViewport::rotation_get, &ScViewport::rotation_set, "rotation");
|
||||
dukglue_register_property(ctx, &ScViewport::zoom_get, &ScViewport::zoom_set, "zoom");
|
||||
dukglue_register_property(
|
||||
ctx, &ScViewport::visibilityFlags_get, &ScViewport::visibilityFlags_set, "visibilityFlags");
|
||||
dukglue_register_method(ctx, &ScViewport::getCentrePosition, "getCentrePosition");
|
||||
dukglue_register_method(ctx, &ScViewport::moveTo, "moveTo");
|
||||
dukglue_register_method(ctx, &ScViewport::scrollTo, "scrollTo");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("left", ScViewport::left_get, ScViewport::left_set),
|
||||
JS_CGETSET_DEF("top", ScViewport::top_get, ScViewport::top_set),
|
||||
JS_CGETSET_DEF("right", ScViewport::right_get, ScViewport::right_set),
|
||||
JS_CGETSET_DEF("bottom", ScViewport::bottom_get, ScViewport::bottom_set),
|
||||
JS_CGETSET_DEF("rotation", ScViewport::rotation_get, ScViewport::rotation_set),
|
||||
JS_CGETSET_DEF("zoom", ScViewport::zoom_get, ScViewport::zoom_set),
|
||||
JS_CGETSET_DEF("visibilityFlags", ScViewport::visibilityFlags_get, ScViewport::visibilityFlags_set),
|
||||
JS_CFUNC_DEF("getCentrePosition", 0, ScViewport::getCentrePosition),
|
||||
JS_CFUNC_DEF("moveTo", 1, ScViewport::moveTo),
|
||||
JS_CFUNC_DEF("scrollTo", 1, ScViewport::scrollTo),
|
||||
};
|
||||
return MakeWithOpaque(ctx, funcs, new OpaqueWindowData{ wClass, wNum });
|
||||
}
|
||||
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "Viewport", Finalize);
|
||||
}
|
||||
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
OpaqueWindowData* data = gScViewport.GetOpaque<OpaqueWindowData*>(thisVal);
|
||||
if (data)
|
||||
delete data;
|
||||
}
|
||||
|
||||
private:
|
||||
WindowBase* GetWindow() const
|
||||
static WindowBase* GetWindow(JSValue thisVal)
|
||||
{
|
||||
if (_class == WindowClass::mainWindow)
|
||||
return WindowGetMain();
|
||||
|
||||
OpaqueWindowData* data = gScViewport.GetOpaque<OpaqueWindowData*>(thisVal);
|
||||
if (!data)
|
||||
return nullptr;
|
||||
auto* windowMgr = Ui::GetWindowManager();
|
||||
return windowMgr->FindByNumber(_class, _number);
|
||||
return windowMgr->FindByNumber(data->_class, data->_number);
|
||||
}
|
||||
|
||||
Viewport* GetViewport() const
|
||||
static Viewport* GetViewport(JSValue thisVal, WindowBase** wOut = nullptr)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
auto w = GetWindow(thisVal);
|
||||
if (wOut)
|
||||
*wOut = w;
|
||||
if (w != nullptr)
|
||||
{
|
||||
return w->viewport;
|
||||
@@ -266,42 +301,35 @@ namespace OpenRCT2::Scripting
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SetViewLeftTop(int32_t left, int32_t top)
|
||||
static void SetViewLeftTop(WindowBase* w, Viewport* viewport, int32_t left, int32_t top)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
if (viewport->viewPos != ScreenCoordsXY(left, top))
|
||||
{
|
||||
auto viewport = w->viewport;
|
||||
if (viewport != nullptr && viewport->viewPos != ScreenCoordsXY(left, top))
|
||||
{
|
||||
viewport->viewPos.x = left;
|
||||
viewport->viewPos.y = top;
|
||||
w->flags.unset(WindowFlag::scrollingToLocation);
|
||||
w->savedViewPos.x = viewport->viewPos.x;
|
||||
w->savedViewPos.y = viewport->viewPos.y;
|
||||
viewport->Invalidate();
|
||||
}
|
||||
viewport->viewPos.x = left;
|
||||
viewport->viewPos.y = top;
|
||||
w->flags.unset(WindowFlag::scrollingToLocation);
|
||||
w->savedViewPos.x = viewport->viewPos.x;
|
||||
w->savedViewPos.y = viewport->viewPos.y;
|
||||
viewport->Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<CoordsXYZ> GetCoordsFromObject(DukValue position) const
|
||||
static std::optional<CoordsXYZ> GetCoordsFromObject(JSContext* ctx, JSValue position)
|
||||
{
|
||||
if (position.type() == DukValue::Type::OBJECT)
|
||||
if (JS_IsObject(position))
|
||||
{
|
||||
auto dukX = position["x"];
|
||||
auto dukY = position["y"];
|
||||
auto dukZ = position["z"];
|
||||
if (dukX.type() == DukValue::Type::NUMBER && dukY.type() == DukValue::Type::NUMBER)
|
||||
auto x = JSToOptionalInt(ctx, position, "x");
|
||||
auto y = JSToOptionalInt(ctx, position, "y");
|
||||
auto z = JSToOptionalInt(ctx, position, "z");
|
||||
if (x.has_value() && y.has_value())
|
||||
{
|
||||
auto x = dukX.as_int();
|
||||
auto y = dukY.as_int();
|
||||
if (dukZ.type() == DukValue::Type::NUMBER)
|
||||
if (z.has_value())
|
||||
{
|
||||
return CoordsXYZ(x, y, dukZ.as_int());
|
||||
return CoordsXYZ(x.value(), y.value(), z.value());
|
||||
}
|
||||
|
||||
auto z = TileElementHeight(CoordsXY(x, y));
|
||||
return CoordsXYZ(x, y, z);
|
||||
auto zTile = TileElementHeight(CoordsXY(x.value(), y.value()));
|
||||
return CoordsXYZ(x.value(), y.value(), zTile);
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,406 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2026 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 "ScWindow.h"
|
||||
|
||||
#include "ScWidget.hpp"
|
||||
|
||||
#include <openrct2/interface/ColourWithFlags.h>
|
||||
#include <openrct2/ui/WindowManager.h>
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
using namespace OpenRCT2::Ui::Windows;
|
||||
|
||||
extern ScWindow gScWindow;
|
||||
|
||||
using OpaqueWindowData = struct
|
||||
{
|
||||
WindowClass _class;
|
||||
WindowNumber _number;
|
||||
};
|
||||
|
||||
JSValue ScWindow::classification_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
OpaqueWindowData* data = gScWindow.GetOpaque<OpaqueWindowData*>(thisVal);
|
||||
return JS_NewInt32(ctx, static_cast<int32_t>(data->_class));
|
||||
}
|
||||
|
||||
JSValue ScWindow::number_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
OpaqueWindowData* data = gScWindow.GetOpaque<OpaqueWindowData*>(thisVal);
|
||||
return JS_NewInt32(ctx, data->_number);
|
||||
}
|
||||
|
||||
JSValue ScWindow::x_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
return JS_NewInt32(ctx, w->windowPos.x);
|
||||
}
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
JSValue ScWindow::x_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
WindowSetPosition(*w, { valueInt, w->windowPos.y });
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
JSValue ScWindow::y_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
return JS_NewInt32(ctx, w->windowPos.y);
|
||||
}
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
JSValue ScWindow::y_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
WindowSetPosition(*w, { w->windowPos.x, valueInt });
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
JSValue ScWindow::width_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
return JS_NewInt32(ctx, w->width);
|
||||
}
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
JSValue ScWindow::width_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
if (w->canBeResized())
|
||||
{
|
||||
WindowResizeByDelta(*w, valueInt - w->width, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
WindowSetResize(*w, { valueInt, w->minHeight }, { valueInt, w->maxHeight });
|
||||
}
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
JSValue ScWindow::height_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
return JS_NewInt32(ctx, w->height - w->getTitleBarDiffNormal());
|
||||
}
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
JSValue ScWindow::height_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
valueInt += w->getTitleBarDiffNormal();
|
||||
if (w->canBeResized())
|
||||
{
|
||||
WindowResizeByDelta(*w, 0, valueInt - w->height);
|
||||
}
|
||||
else
|
||||
{
|
||||
WindowSetResize(*w, { w->minWidth, valueInt }, { w->maxWidth, valueInt });
|
||||
}
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
JSValue ScWindow::minWidth_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
return JS_NewInt32(ctx, w->minWidth);
|
||||
}
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
JSValue ScWindow::minWidth_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
WindowSetResize(*w, { valueInt, w->minHeight }, { w->maxWidth, w->maxHeight });
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
JSValue ScWindow::maxWidth_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
return JS_NewInt32(ctx, w->maxWidth);
|
||||
}
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
JSValue ScWindow::maxWidth_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
WindowSetResize(*w, { w->minWidth, w->minHeight }, { valueInt, w->maxHeight });
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
JSValue ScWindow::minHeight_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
return JS_NewInt32(ctx, w->minHeight - w->getTitleBarDiffNormal());
|
||||
}
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
JSValue ScWindow::minHeight_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
valueInt += w->getTitleBarDiffNormal();
|
||||
WindowSetResize(*w, { w->minWidth, valueInt }, { w->maxWidth, w->maxHeight });
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
JSValue ScWindow::maxHeight_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
return JS_NewInt32(ctx, w->maxHeight - w->getTitleBarDiffNormal());
|
||||
}
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
JSValue ScWindow::maxHeight_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
valueInt += w->getTitleBarDiffNormal();
|
||||
WindowSetResize(*w, { w->minWidth, w->minHeight }, { w->maxWidth, valueInt });
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
JSValue ScWindow::isSticky_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
return JS_NewBool(ctx, w->flags.hasAny(WindowFlag::stickToBack, WindowFlag::stickToFront) != 0);
|
||||
}
|
||||
return JS_NewBool(ctx, false);
|
||||
}
|
||||
|
||||
JSValue ScWindow::widgets_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
JSValue result = JS_NewArray(ctx);
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
for (WidgetIndex widgetIndex = 0; widgetIndex < w->widgets.size(); widgetIndex++)
|
||||
{
|
||||
JS_SetPropertyInt64(ctx, result, widgetIndex, gScWidget.New(ctx, w, widgetIndex));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
JSValue ScWindow::colours_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
JSValue result = JS_NewArray(ctx);
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
int64_t i = 0;
|
||||
for (auto c : w->colours)
|
||||
{
|
||||
auto colour = EnumValue(c.colour);
|
||||
if (c.flags.has(ColourFlag::translucent))
|
||||
colour |= kLegacyColourFlagTranslucent;
|
||||
|
||||
JS_SetPropertyInt64(ctx, result, i++, JS_NewInt32(ctx, colour));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
JSValue ScWindow::colours_set(JSContext* ctx, JSValue thisVal, JSValue colours)
|
||||
{
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr && JS_IsArray(colours))
|
||||
{
|
||||
int64_t coloursLen = -1;
|
||||
JS_GetLength(ctx, colours, &coloursLen);
|
||||
for (int64_t i = 0; i < std::ssize(w->colours); i++)
|
||||
{
|
||||
auto c = ColourWithFlags{ Drawing::Colour::black };
|
||||
if (i < coloursLen)
|
||||
{
|
||||
JSValue elem = JS_GetPropertyInt64(ctx, colours, i);
|
||||
if (JS_IsNumber(elem))
|
||||
{
|
||||
int32_t colorInt = -1;
|
||||
JS_ToInt32(ctx, &colorInt, elem);
|
||||
uint8_t colour = (colorInt & ~kLegacyColourFlagTranslucent) % Drawing::kColourNumTotal;
|
||||
bool isTranslucent = (colorInt & kLegacyColourFlagTranslucent);
|
||||
c.colour = static_cast<Drawing::Colour>(colour);
|
||||
c.flags.set(ColourFlag::translucent, isTranslucent);
|
||||
}
|
||||
JS_FreeValue(ctx, elem);
|
||||
}
|
||||
w->colours[i] = c;
|
||||
}
|
||||
w->invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue ScWindow::title_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr && w->classification == WindowClass::custom)
|
||||
{
|
||||
return JSFromStdString(ctx, GetWindowTitle(w));
|
||||
}
|
||||
return JSFromStdString(ctx, {});
|
||||
}
|
||||
JSValue ScWindow::title_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
JS_UNPACK_STR(valueStr, ctx, value);
|
||||
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr && w->classification == WindowClass::custom)
|
||||
{
|
||||
UpdateWindowTitle(w, valueStr);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue ScWindow::tabIndex_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr && w->classification == WindowClass::custom)
|
||||
{
|
||||
return JS_NewInt32(ctx, w->page);
|
||||
}
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
JSValue ScWindow::tabIndex_set(JSContext* ctx, JSValue thisVal, JSValue tab)
|
||||
{
|
||||
JS_UNPACK_INT32(tabNumber, ctx, tab);
|
||||
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr && w->classification == WindowClass::custom)
|
||||
{
|
||||
UpdateWindowTab(w, tabNumber);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue ScWindow::close(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
auto* windowMgr = Ui::GetWindowManager();
|
||||
windowMgr->Close(*w);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue ScWindow::findWidget(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_STR(name, ctx, argv[0]);
|
||||
|
||||
auto w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
auto widgetIndex = FindWidgetIndexByName(w, name);
|
||||
if (widgetIndex)
|
||||
{
|
||||
return gScWidget.New(ctx, w, *widgetIndex);
|
||||
}
|
||||
}
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
JSValue ScWindow::bringToFront(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto* w = GetWindow(thisVal);
|
||||
if (w != nullptr)
|
||||
{
|
||||
auto* windowMgr = Ui::GetWindowManager();
|
||||
w = windowMgr->BringToFront(*w);
|
||||
w->flash();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue ScWindow::New(JSContext* ctx, WindowBase* w)
|
||||
{
|
||||
return New(ctx, w->classification, w->number);
|
||||
}
|
||||
|
||||
JSValue ScWindow::New(JSContext* ctx, WindowClass wClass, WindowNumber wNum)
|
||||
{
|
||||
return MakeWithOpaque(ctx, funcs, new OpaqueWindowData{ wClass, wNum });
|
||||
}
|
||||
|
||||
void ScWindow::Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "Window", Finalize);
|
||||
}
|
||||
|
||||
void ScWindow::Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
OpaqueWindowData* data = gScWindow.GetOpaque<OpaqueWindowData*>(thisVal);
|
||||
if (data)
|
||||
delete data;
|
||||
}
|
||||
|
||||
WindowBase* ScWindow::GetWindow(JSValue thisVal)
|
||||
{
|
||||
OpaqueWindowData* data = gScWindow.GetOpaque<OpaqueWindowData*>(thisVal);
|
||||
if (!data)
|
||||
return nullptr;
|
||||
auto* windowMgr = Ui::GetWindowManager();
|
||||
return windowMgr->FindByNumber(data->_class, data->_number);
|
||||
}
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,108 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2026 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 <openrct2/interface/WindowBase.h>
|
||||
#include <openrct2/scripting/ScriptEngine.h>
|
||||
#include <quickjs.h>
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScWindow;
|
||||
extern ScWindow gScWindow;
|
||||
|
||||
class ScWindow final : public ScBase
|
||||
{
|
||||
private:
|
||||
static JSValue classification_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
static JSValue number_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
static JSValue x_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue x_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
static JSValue y_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue y_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
static JSValue width_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue width_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
static JSValue height_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue height_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
static JSValue minWidth_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue minWidth_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
static JSValue maxWidth_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue maxWidth_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
static JSValue minHeight_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue minHeight_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
static JSValue maxHeight_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue maxHeight_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
static JSValue isSticky_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
static JSValue widgets_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
static JSValue colours_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue colours_set(JSContext* ctx, JSValue thisVal, JSValue colours);
|
||||
|
||||
static JSValue title_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue title_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
static JSValue tabIndex_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue tabIndex_set(JSContext* ctx, JSValue thisVal, JSValue tab);
|
||||
|
||||
static JSValue close(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
|
||||
static JSValue findWidget(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
|
||||
static JSValue bringToFront(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
|
||||
public:
|
||||
JSValue New(JSContext* ctx, WindowBase* w);
|
||||
JSValue New(JSContext* ctx, WindowClass wClass, WindowNumber wNum);
|
||||
|
||||
void Register(JSContext* ctx);
|
||||
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("classification", ScWindow::classification_get, nullptr),
|
||||
JS_CGETSET_DEF("number", ScWindow::number_get, nullptr),
|
||||
JS_CGETSET_DEF("x", ScWindow::x_get, ScWindow::x_set),
|
||||
JS_CGETSET_DEF("y", ScWindow::y_get, ScWindow::y_set),
|
||||
JS_CGETSET_DEF("width", ScWindow::width_get, ScWindow::width_set),
|
||||
JS_CGETSET_DEF("height", ScWindow::height_get, ScWindow::height_set),
|
||||
JS_CGETSET_DEF("minWidth", ScWindow::minWidth_get, ScWindow::minWidth_set),
|
||||
JS_CGETSET_DEF("maxWidth", ScWindow::maxWidth_get, ScWindow::maxWidth_set),
|
||||
JS_CGETSET_DEF("minHeight", ScWindow::minHeight_get, ScWindow::minHeight_set),
|
||||
JS_CGETSET_DEF("maxHeight", ScWindow::maxHeight_get, ScWindow::maxHeight_set),
|
||||
JS_CGETSET_DEF("isSticky", ScWindow::isSticky_get, nullptr),
|
||||
JS_CGETSET_DEF("widgets", ScWindow::widgets_get, nullptr),
|
||||
JS_CGETSET_DEF("colours", ScWindow::colours_get, ScWindow::colours_set),
|
||||
JS_CGETSET_DEF("title", ScWindow::title_get, ScWindow::title_set),
|
||||
JS_CGETSET_DEF("tabIndex", ScWindow::tabIndex_get, ScWindow::tabIndex_set),
|
||||
|
||||
JS_CFUNC_DEF("close", 0, ScWindow::close),
|
||||
JS_CFUNC_DEF("findWidget", 1, ScWindow::findWidget),
|
||||
JS_CFUNC_DEF("bringToFront", 0, ScWindow::bringToFront)
|
||||
};
|
||||
|
||||
private:
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal);
|
||||
|
||||
static WindowBase* GetWindow(JSValue thisVal);
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
#endif
|
||||
@@ -1,374 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2026 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 "ScWidget.hpp"
|
||||
|
||||
#include <openrct2/interface/ColourWithFlags.h>
|
||||
#include <openrct2/interface/Window.h>
|
||||
#include <openrct2/interface/WindowBase.h>
|
||||
#include <openrct2/scripting/Duktape.hpp>
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
using namespace OpenRCT2::Ui::Windows;
|
||||
|
||||
class ScWindow
|
||||
{
|
||||
private:
|
||||
WindowClass _class;
|
||||
WindowNumber _number;
|
||||
|
||||
public:
|
||||
ScWindow(WindowBase* w)
|
||||
: ScWindow(w->classification, w->number)
|
||||
{
|
||||
}
|
||||
|
||||
ScWindow(WindowClass c, WindowNumber n)
|
||||
: _class(c)
|
||||
, _number(n)
|
||||
{
|
||||
}
|
||||
|
||||
int32_t classification_get() const
|
||||
{
|
||||
return static_cast<int32_t>(_class);
|
||||
}
|
||||
|
||||
int32_t number_get() const
|
||||
{
|
||||
return static_cast<int32_t>(_number);
|
||||
}
|
||||
|
||||
int32_t x_get() const
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
return w->windowPos.x;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void x_set(int32_t value)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
WindowSetPosition(*w, { value, w->windowPos.y });
|
||||
}
|
||||
}
|
||||
int32_t y_get() const
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
return w->windowPos.y;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void y_set(int32_t value)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
WindowSetPosition(*w, { w->windowPos.x, value });
|
||||
}
|
||||
}
|
||||
int32_t width_get() const
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
return w->width;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void width_set(int32_t value)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
if (w->canBeResized())
|
||||
{
|
||||
WindowResizeByDelta(*w, value - w->width, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
WindowSetResize(*w, { value, w->minHeight }, { value, w->maxHeight });
|
||||
}
|
||||
}
|
||||
}
|
||||
int32_t height_get() const
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
return w->height - w->getTitleBarDiffNormal();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void height_set(int32_t value)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
value += w->getTitleBarDiffNormal();
|
||||
if (w->canBeResized())
|
||||
{
|
||||
WindowResizeByDelta(*w, 0, value - w->height);
|
||||
}
|
||||
else
|
||||
{
|
||||
WindowSetResize(*w, { w->minWidth, value }, { w->maxWidth, value });
|
||||
}
|
||||
}
|
||||
}
|
||||
int32_t minWidth_get() const
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
return w->minWidth;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void minWidth_set(int32_t value)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
WindowSetResize(*w, { value, w->minHeight }, { w->maxWidth, w->maxHeight });
|
||||
}
|
||||
}
|
||||
int32_t maxWidth_get() const
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
return w->maxWidth;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void maxWidth_set(int32_t value)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
WindowSetResize(*w, { w->minWidth, w->minHeight }, { value, w->maxHeight });
|
||||
}
|
||||
}
|
||||
int32_t minHeight_get() const
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
return w->minHeight - w->getTitleBarDiffNormal();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void minHeight_set(int32_t value)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
value += w->getTitleBarDiffNormal();
|
||||
WindowSetResize(*w, { w->minWidth, value }, { w->maxWidth, w->maxHeight });
|
||||
}
|
||||
}
|
||||
int32_t maxHeight_get() const
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
return w->maxHeight - w->getTitleBarDiffNormal();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void maxHeight_set(int32_t value)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
value += w->getTitleBarDiffNormal();
|
||||
WindowSetResize(*w, { w->minWidth, w->minHeight }, { w->maxWidth, value });
|
||||
}
|
||||
}
|
||||
bool isSticky_get() const
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
return w->flags.hasAny(WindowFlag::stickToBack, WindowFlag::stickToFront);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<DukValue> widgets_get() const
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
|
||||
std::vector<DukValue> result;
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
for (WidgetIndex widgetIndex = 0; widgetIndex < w->widgets.size(); widgetIndex++)
|
||||
{
|
||||
result.push_back(ScWidget::ToDukValue(ctx, w, widgetIndex));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<int32_t> colours_get() const
|
||||
{
|
||||
std::vector<int32_t> result;
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
result.reserve(std::size(w->colours));
|
||||
for (auto c : w->colours)
|
||||
{
|
||||
auto colour = EnumValue(c.colour);
|
||||
if (c.flags.has(ColourFlag::translucent))
|
||||
colour |= kLegacyColourFlagTranslucent;
|
||||
result.push_back(colour);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
void colours_set(std::vector<int32_t> colours)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
for (size_t i = 0; i < std::size(w->colours); i++)
|
||||
{
|
||||
auto c = ColourWithFlags{ Drawing::Colour::black };
|
||||
if (i < colours.size())
|
||||
{
|
||||
auto colour = (colours[i] & ~kLegacyColourFlagTranslucent) % Drawing::kColourNumTotal;
|
||||
bool isTranslucent = (colours[i] & kLegacyColourFlagTranslucent);
|
||||
c.colour = static_cast<Drawing::Colour>(colour);
|
||||
c.flags.set(ColourFlag::translucent, isTranslucent);
|
||||
}
|
||||
w->colours[i] = c;
|
||||
}
|
||||
w->invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
std::string title_get() const
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr && w->classification == WindowClass::custom)
|
||||
{
|
||||
return GetWindowTitle(w);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
void title_set(std::string value)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr && w->classification == WindowClass::custom)
|
||||
{
|
||||
UpdateWindowTitle(w, value);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t tabIndex_get() const
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr && w->classification == WindowClass::custom)
|
||||
{
|
||||
return w->page;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void tabIndex_set(int32_t tab)
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr && w->classification == WindowClass::custom)
|
||||
{
|
||||
UpdateWindowTab(w, tab);
|
||||
}
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
auto* windowMgr = Ui::GetWindowManager();
|
||||
windowMgr->Close(*w);
|
||||
}
|
||||
}
|
||||
|
||||
DukValue findWidget(std::string name) const
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
auto widgetIndex = FindWidgetIndexByName(w, name);
|
||||
if (widgetIndex)
|
||||
{
|
||||
return ScWidget::ToDukValue(ctx, w, *widgetIndex);
|
||||
}
|
||||
}
|
||||
return GetObjectAsDukValue<ScWidget>(ctx, nullptr);
|
||||
}
|
||||
|
||||
void bringToFront()
|
||||
{
|
||||
auto* w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
auto* windowMgr = Ui::GetWindowManager();
|
||||
w = windowMgr->BringToFront(*w);
|
||||
w->flash();
|
||||
}
|
||||
}
|
||||
|
||||
static void Register(duk_context* ctx)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScWindow::classification_get, nullptr, "classification");
|
||||
dukglue_register_property(ctx, &ScWindow::number_get, nullptr, "number");
|
||||
dukglue_register_property(ctx, &ScWindow::x_get, &ScWindow::x_set, "x");
|
||||
dukglue_register_property(ctx, &ScWindow::y_get, &ScWindow::y_set, "y");
|
||||
dukglue_register_property(ctx, &ScWindow::width_get, &ScWindow::width_set, "width");
|
||||
dukglue_register_property(ctx, &ScWindow::height_get, &ScWindow::height_set, "height");
|
||||
dukglue_register_property(ctx, &ScWindow::minWidth_get, &ScWindow::minWidth_set, "minWidth");
|
||||
dukglue_register_property(ctx, &ScWindow::maxWidth_get, &ScWindow::maxWidth_set, "maxWidth");
|
||||
dukglue_register_property(ctx, &ScWindow::minHeight_get, &ScWindow::minHeight_set, "minHeight");
|
||||
dukglue_register_property(ctx, &ScWindow::maxHeight_get, &ScWindow::maxHeight_set, "maxHeight");
|
||||
dukglue_register_property(ctx, &ScWindow::isSticky_get, nullptr, "isSticky");
|
||||
dukglue_register_property(ctx, &ScWindow::widgets_get, nullptr, "widgets");
|
||||
dukglue_register_property(ctx, &ScWindow::colours_get, &ScWindow::colours_set, "colours");
|
||||
dukglue_register_property(ctx, &ScWindow::title_get, &ScWindow::title_set, "title");
|
||||
dukglue_register_property(ctx, &ScWindow::tabIndex_get, &ScWindow::tabIndex_set, "tabIndex");
|
||||
|
||||
dukglue_register_method(ctx, &ScWindow::close, "close");
|
||||
dukglue_register_method(ctx, &ScWindow::findWidget, "findWidget");
|
||||
dukglue_register_method(ctx, &ScWindow::bringToFront, "bringToFront");
|
||||
}
|
||||
|
||||
private:
|
||||
WindowBase* GetWindow() const
|
||||
{
|
||||
auto* windowMgr = Ui::GetWindowManager();
|
||||
return windowMgr->FindByNumber(_class, _number);
|
||||
}
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
#endif
|
||||
@@ -19,66 +19,75 @@
|
||||
#include "ScTitleSequence.hpp"
|
||||
#include "ScUi.hpp"
|
||||
#include "ScWidget.hpp"
|
||||
#include "ScWindow.hpp"
|
||||
#include "ScWindow.h"
|
||||
|
||||
#include <openrct2/scripting/ScriptEngine.h>
|
||||
|
||||
using namespace OpenRCT2::Scripting;
|
||||
|
||||
ScGraphicsContext OpenRCT2::Scripting::gScGraphicsContext;
|
||||
ScImageManager OpenRCT2::Scripting::gScImageManager;
|
||||
ScTileSelection OpenRCT2::Scripting::gScTileSelection;
|
||||
ScTool OpenRCT2::Scripting::gScTool;
|
||||
ScUi OpenRCT2::Scripting::gScUi;
|
||||
ScViewport OpenRCT2::Scripting::gScViewport;
|
||||
|
||||
ScWidget OpenRCT2::Scripting::gScWidget;
|
||||
|
||||
ScTitleSequence OpenRCT2::Scripting::gScTitleSequence;
|
||||
ScTitleSequenceManager OpenRCT2::Scripting::gScTitleSequenceManager;
|
||||
ScTitleSequencePark OpenRCT2::Scripting::gScTitleSequencePark;
|
||||
ScWindow OpenRCT2::Scripting::gScWindow;
|
||||
|
||||
static void InitialiseContext(JSContext* ctx)
|
||||
{
|
||||
JSValue glb = JS_GetGlobalObject(ctx);
|
||||
JS_SetPropertyStr(ctx, glb, "titleSequenceManager", gScTitleSequenceManager.New(ctx));
|
||||
JS_SetPropertyStr(ctx, glb, "ui", gScUi.New(ctx, &OpenRCT2::GetContext()->GetScriptEngine()));
|
||||
JS_FreeValue(ctx, glb);
|
||||
}
|
||||
|
||||
void UiScriptExtensions::Extend(ScriptEngine& scriptEngine)
|
||||
{
|
||||
auto ctx = scriptEngine.GetContext();
|
||||
JSContext* ctx = scriptEngine.GetContext();
|
||||
|
||||
dukglue_register_global(ctx, std::make_shared<ScTitleSequenceManager>(), "titleSequenceManager");
|
||||
dukglue_register_global(ctx, std::make_shared<ScUi>(scriptEngine), "ui");
|
||||
gScGraphicsContext.Register(ctx);
|
||||
gScImageManager.Register(ctx);
|
||||
gScTileSelection.Register(ctx);
|
||||
gScTool.Register(ctx);
|
||||
gScUi.Register(ctx);
|
||||
gScViewport.Register(ctx);
|
||||
|
||||
ScGraphicsContext::Register(ctx);
|
||||
ScImageManager::Register(ctx);
|
||||
ScTileSelection::Register(ctx);
|
||||
ScTool::Register(ctx);
|
||||
ScUi::Register(ctx);
|
||||
ScViewport::Register(ctx);
|
||||
gScWidget.Register(ctx);
|
||||
|
||||
ScWidget::Register(ctx);
|
||||
ScButtonWidget::Register(ctx);
|
||||
ScColourPickerWidget::Register(ctx);
|
||||
ScCheckBoxWidget::Register(ctx);
|
||||
ScDropdownWidget::Register(ctx);
|
||||
ScGroupBoxWidget::Register(ctx);
|
||||
ScLabelWidget::Register(ctx);
|
||||
ScListViewWidget::Register(ctx);
|
||||
ScSpinnerWidget::Register(ctx);
|
||||
ScTextBoxWidget::Register(ctx);
|
||||
ScViewportWidget::Register(ctx);
|
||||
|
||||
ScTitleSequence::Register(ctx);
|
||||
ScTitleSequenceManager::Register(ctx);
|
||||
ScTitleSequencePark::Register(ctx);
|
||||
ScWindow::Register(ctx);
|
||||
gScTitleSequence.Register(ctx);
|
||||
gScTitleSequenceManager.Register(ctx);
|
||||
gScTitleSequencePark.Register(ctx);
|
||||
gScWindow.Register(ctx);
|
||||
|
||||
InitialiseCustomImages(scriptEngine);
|
||||
InitialiseCustomMenuItems(scriptEngine);
|
||||
scriptEngine.SubscribeToPluginStoppedEvent(
|
||||
[](std::shared_ptr<Plugin> plugin) -> void { CloseWindowsOwnedByPlugin(plugin); });
|
||||
[](std::shared_ptr<Plugin> plugin) -> void { Ui::Windows::CloseWindowsOwnedByPlugin(plugin); });
|
||||
|
||||
scriptEngine.RegisterExtension(InitialiseContext, Unregister);
|
||||
}
|
||||
|
||||
std::shared_ptr<ScWindow> ScWidget::window_get() const
|
||||
void UiScriptExtensions::Unregister()
|
||||
{
|
||||
return std::make_shared<ScWindow>(_class, _number);
|
||||
}
|
||||
gScGraphicsContext.Unregister();
|
||||
gScImageManager.Unregister();
|
||||
gScTileSelection.Unregister();
|
||||
gScTool.Unregister();
|
||||
gScUi.Unregister();
|
||||
gScViewport.Unregister();
|
||||
|
||||
void ScWidget::Register(duk_context* ctx)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScWidget::window_get, nullptr, "window");
|
||||
dukglue_register_property(ctx, &ScWidget::name_get, &ScWidget::name_set, "name");
|
||||
dukglue_register_property(ctx, &ScWidget::type_get, nullptr, "type");
|
||||
dukglue_register_property(ctx, &ScWidget::x_get, &ScWidget::x_set, "x");
|
||||
dukglue_register_property(ctx, &ScWidget::y_get, &ScWidget::y_set, "y");
|
||||
dukglue_register_property(ctx, &ScWidget::width_get, &ScWidget::width_set, "width");
|
||||
dukglue_register_property(ctx, &ScWidget::height_get, &ScWidget::height_set, "height");
|
||||
dukglue_register_property(ctx, &ScWidget::tooltip_get, &ScWidget::tooltip_set, "tooltip");
|
||||
dukglue_register_property(ctx, &ScWidget::isDisabled_get, &ScWidget::isDisabled_set, "isDisabled");
|
||||
dukglue_register_property(ctx, &ScWidget::isVisible_get, &ScWidget::isVisible_set, "isVisible");
|
||||
gScWidget.Unregister();
|
||||
|
||||
gScTitleSequence.Unregister();
|
||||
gScTitleSequenceManager.Unregister();
|
||||
gScTitleSequencePark.Unregister();
|
||||
gScWindow.Unregister();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace OpenRCT2::Scripting
|
||||
{
|
||||
public:
|
||||
static void Extend(ScriptEngine& scriptEngine);
|
||||
static void Unregister();
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
|
||||
@@ -607,6 +607,8 @@ namespace OpenRCT2::Ui::Windows
|
||||
gGamePaused &= ~GAME_PAUSED_MODAL;
|
||||
Audio::Resume();
|
||||
}
|
||||
|
||||
UnregisterJSCallback();
|
||||
}
|
||||
|
||||
void onResize() override
|
||||
@@ -1159,14 +1161,12 @@ namespace OpenRCT2::Ui::Windows
|
||||
};
|
||||
|
||||
WindowBase* LoadsaveOpen(
|
||||
LoadSaveAction action, LoadSaveType type, std::string_view defaultPath, LoadSaveCallback callback,
|
||||
LoadSaveAction action, LoadSaveType type, std::string_view defaultPath, LoadSaveCallback callback, bool isJsCallback,
|
||||
TrackDesign* trackDesign)
|
||||
{
|
||||
_trackDesign = trackDesign;
|
||||
_defaultPath = defaultPath;
|
||||
|
||||
RegisterCallback(callback);
|
||||
|
||||
auto* windowMgr = GetWindowManager();
|
||||
auto* w = static_cast<LoadSaveWindow*>(windowMgr->BringToFrontByClass(WindowClass::loadsave));
|
||||
if (w == nullptr)
|
||||
@@ -1182,6 +1182,8 @@ namespace OpenRCT2::Ui::Windows
|
||||
|
||||
ScreenSize windowSize = { config.fileBrowserWidth, config.fileBrowserHeight };
|
||||
|
||||
RegisterCallback(callback, isJsCallback);
|
||||
|
||||
w = windowMgr->Create<LoadSaveWindow>(
|
||||
WindowClass::loadsave, windowSize,
|
||||
{ WindowFlag::stickToFront, WindowFlag::resizable, WindowFlag::autoPosition, WindowFlag::centreScreen }, action,
|
||||
|
||||
@@ -138,7 +138,7 @@ namespace OpenRCT2::Ui::Windows
|
||||
// LoadSave
|
||||
WindowBase* LoadsaveOpen(
|
||||
LoadSaveAction action, LoadSaveType type, std::string_view defaultPath,
|
||||
std::function<void(ModalResult result, std::string_view)> callback, TrackDesign* trackDesign);
|
||||
std::function<void(ModalResult result, std::string_view)> callback, bool isJsCallback, TrackDesign* trackDesign);
|
||||
void WindowLoadSaveInputKey(WindowBase* w, uint32_t keycode);
|
||||
|
||||
// Main
|
||||
|
||||
+39
-39
@@ -1,6 +1,6 @@
|
||||
file(GLOB_RECURSE OPENRCT2_CORE_SOURCES "${CMAKE_CURRENT_LIST_DIR}/*.cpp")
|
||||
file(GLOB_RECURSE OPENRCT2_CORE_HEADERS "${CMAKE_CURRENT_LIST_DIR}/*.h"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/*.hpp")
|
||||
"${CMAKE_CURRENT_LIST_DIR}/*.hpp")
|
||||
|
||||
if (APPLE)
|
||||
file(GLOB_RECURSE OPENRCT2_CORE_MM_SOURCES "${CMAKE_CURRENT_LIST_DIR}/*.mm")
|
||||
@@ -8,14 +8,14 @@ if (APPLE)
|
||||
endif ()
|
||||
|
||||
if (ENABLE_SCRIPTING)
|
||||
include_directories("${CMAKE_CURRENT_LIST_DIR}/../thirdparty/duktape")
|
||||
include_directories(SYSTEM "${CMAKE_CURRENT_LIST_DIR}/../thirdparty/quickjs-ng")
|
||||
|
||||
# duktape is third party, ignore all warnings
|
||||
set(OPENRCT2_DUKTAPE_SOURCES "${CMAKE_CURRENT_LIST_DIR}/../thirdparty/duktape/duktape.cpp")
|
||||
# quickjs-ng is third party, ignore all warnings
|
||||
set(OPENRCT2_QUICKJS_NG_SOURCES "${CMAKE_CURRENT_LIST_DIR}/../thirdparty/quickjs-ng/quickjs-amalgam.c")
|
||||
if (MSVC)
|
||||
set_source_files_properties(${OPENRCT2_DUKTAPE_SOURCES} PROPERTIES COMPILE_FLAGS "/w")
|
||||
set_source_files_properties(${OPENRCT2_QUICKJS_NG_SOURCES} PROPERTIES COMPILE_FLAGS "/w")
|
||||
else ()
|
||||
set_source_files_properties(${OPENRCT2_DUKTAPE_SOURCES} PROPERTIES COMPILE_FLAGS "-w")
|
||||
set_source_files_properties(${OPENRCT2_QUICKJS_NG_SOURCES} PROPERTIES COMPILE_FLAGS "-w")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
@@ -26,17 +26,17 @@ endif ()
|
||||
# https://github.com/OpenRCT2/OpenRCT2/issues/24936
|
||||
set(LIBOPENRCT2_LINKAGE)
|
||||
if (APPLE)
|
||||
set(LIBOPENRCT2_LINKAGE STATIC)
|
||||
set(LIBOPENRCT2_LINKAGE STATIC)
|
||||
endif()
|
||||
add_library(libopenrct2 ${LIBOPENRCT2_LINKAGE} ${OPENRCT2_CORE_SOURCES} ${OPENRCT2_CORE_MM_SOURCES} ${OPENRCT2_DUKTAPE_SOURCES})
|
||||
add_library(libopenrct2 ${LIBOPENRCT2_LINKAGE} ${OPENRCT2_CORE_SOURCES} ${OPENRCT2_CORE_MM_SOURCES} ${OPENRCT2_QUICKJS_NG_SOURCES})
|
||||
add_library(OpenRCT2::libopenrct2 ALIAS libopenrct2)
|
||||
|
||||
if (CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "13")
|
||||
message(WARNING "Buggy GCC 12 detected! Disabling some warnings")
|
||||
set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/localisation/FormatCodes.cpp" PROPERTIES COMPILE_FLAGS "-Wno-restrict")
|
||||
set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/platform/Platform.Posix.cpp" PROPERTIES COMPILE_FLAGS "-Wno-restrict")
|
||||
set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/ride/Ride.cpp" PROPERTIES COMPILE_FLAGS "-Wno-null-dereference")
|
||||
set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/scenario/Scenario.cpp" PROPERTIES COMPILE_FLAGS "-Wno-restrict")
|
||||
message(WARNING "Buggy GCC 12 detected! Disabling some warnings")
|
||||
set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/localisation/FormatCodes.cpp" PROPERTIES COMPILE_FLAGS "-Wno-restrict")
|
||||
set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/platform/Platform.Posix.cpp" PROPERTIES COMPILE_FLAGS "-Wno-restrict")
|
||||
set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/ride/Ride.cpp" PROPERTIES COMPILE_FLAGS "-Wno-null-dereference")
|
||||
set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/scenario/Scenario.cpp" PROPERTIES COMPILE_FLAGS "-Wno-restrict")
|
||||
endif()
|
||||
if (APPLE)
|
||||
target_link_platform_libraries(libopenrct2)
|
||||
@@ -152,23 +152,23 @@ endif ()
|
||||
|
||||
if (STATIC)
|
||||
target_link_libraries(libopenrct2
|
||||
${PNG_STATIC_LIBRARIES}
|
||||
${ZLIB_STATIC_LIBRARIES}
|
||||
${LIBZIP_STATIC_LIBRARIES}
|
||||
${ZSTD_STATIC_LIBRARIES})
|
||||
${PNG_STATIC_LIBRARIES}
|
||||
${ZLIB_STATIC_LIBRARIES}
|
||||
${LIBZIP_STATIC_LIBRARIES}
|
||||
${ZSTD_STATIC_LIBRARIES})
|
||||
else ()
|
||||
if (NOT MSVC AND NOT EMSCRIPTEN)
|
||||
target_link_libraries(libopenrct2
|
||||
PkgConfig::PNG
|
||||
PkgConfig::ZLIB
|
||||
PkgConfig::LIBZIP
|
||||
PkgConfig::ZSTD)
|
||||
PkgConfig::PNG
|
||||
PkgConfig::ZLIB
|
||||
PkgConfig::LIBZIP
|
||||
PkgConfig::ZSTD)
|
||||
else ()
|
||||
target_link_libraries(libopenrct2
|
||||
${PNG_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
${LIBZIP_LIBRARIES}
|
||||
${ZSTD_LIBRARIES})
|
||||
${PNG_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
${LIBZIP_LIBRARIES}
|
||||
${ZSTD_LIBRARIES})
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
@@ -247,7 +247,7 @@ endif()
|
||||
# Includes
|
||||
target_include_directories(libopenrct2 SYSTEM PRIVATE ${LIBZIP_INCLUDE_DIRS})
|
||||
target_include_directories(libopenrct2 SYSTEM PRIVATE ${PNG_INCLUDE_DIRS}
|
||||
${ZLIB_INCLUDE_DIRS})
|
||||
${ZLIB_INCLUDE_DIRS})
|
||||
target_include_directories(libopenrct2 SYSTEM PRIVATE ${ZSTD_INCLUDE_DIRS})
|
||||
include_directories(libopenrct2 SYSTEM ${CMAKE_CURRENT_LIST_DIR}/../thirdparty)
|
||||
|
||||
@@ -256,20 +256,20 @@ include_directories(libopenrct2 SYSTEM ${CMAKE_CURRENT_LIST_DIR}/../thirdparty)
|
||||
# definitions: Version.cpp and Crash/Platform.cpp
|
||||
if (NOT OPENRCT2_VERSION_TAG STREQUAL "")
|
||||
set_property(SOURCE ${CMAKE_CURRENT_LIST_DIR}/Version.cpp ${CMAKE_CURRENT_LIST_DIR}/Crash/Platform.cpp
|
||||
PROPERTY COMPILE_DEFINITIONS
|
||||
OPENRCT2_VERSION_TAG="${OPENRCT2_VERSION_TAG}")
|
||||
PROPERTY COMPILE_DEFINITIONS
|
||||
OPENRCT2_VERSION_TAG="${OPENRCT2_VERSION_TAG}")
|
||||
endif()
|
||||
|
||||
if (NOT OPENRCT2_BRANCH STREQUAL "master" AND NOT OPENRCT2_BRANCH STREQUAL "")
|
||||
set_property(SOURCE ${CMAKE_CURRENT_LIST_DIR}/Version.cpp ${CMAKE_CURRENT_LIST_DIR}/Crash/Platform.cpp
|
||||
APPEND PROPERTY COMPILE_DEFINITIONS
|
||||
OPENRCT2_BRANCH="${OPENRCT2_BRANCH}")
|
||||
APPEND PROPERTY COMPILE_DEFINITIONS
|
||||
OPENRCT2_BRANCH="${OPENRCT2_BRANCH}")
|
||||
endif()
|
||||
|
||||
if (NOT OPENRCT2_COMMIT_SHA1_SHORT STREQUAL "HEAD" AND NOT OPENRCT2_COMMIT_SHA1_SHORT STREQUAL "")
|
||||
set_property(SOURCE ${CMAKE_CURRENT_LIST_DIR}/Version.cpp ${CMAKE_CURRENT_LIST_DIR}/Crash/Platform.cpp
|
||||
APPEND PROPERTY COMPILE_DEFINITIONS
|
||||
OPENRCT2_COMMIT_SHA1_SHORT="${OPENRCT2_COMMIT_SHA1_SHORT}")
|
||||
APPEND PROPERTY COMPILE_DEFINITIONS
|
||||
OPENRCT2_COMMIT_SHA1_SHORT="${OPENRCT2_COMMIT_SHA1_SHORT}")
|
||||
endif()
|
||||
|
||||
if((X86 OR X86_64) AND NOT MSVC AND NOT EMSCRIPTEN)
|
||||
@@ -285,7 +285,7 @@ if (ENABLE_HEADERS_CHECK AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
add_library(libopenrct2-headers-check OBJECT ${OPENRCT2_CORE_HEADERS})
|
||||
set_target_properties(libopenrct2-headers-check PROPERTIES LINKER_LANGUAGE CXX)
|
||||
set_source_files_properties(${OPENRCT2_CORE_HEADERS} PROPERTIES LANGUAGE CXX)
|
||||
add_definitions("-x c++ -Wno-pragma-once-outside-header -Wno-unused-const-variable")
|
||||
target_compile_options(libopenrct2-headers-check PRIVATE -Wno-pragma-once-outside-header -Wno-unused-const-variable)
|
||||
get_target_property(LIBOPENRCT2_INCLUDE_DIRS libopenrct2 INCLUDE_DIRECTORIES)
|
||||
set_target_properties(libopenrct2-headers-check PROPERTIES INCLUDE_DIRECTORIES "${LIBOPENRCT2_INCLUDE_DIRS}")
|
||||
else ()
|
||||
@@ -299,12 +299,12 @@ endif ()
|
||||
|
||||
# Defines
|
||||
target_compile_definitions(libopenrct2 PUBLIC
|
||||
$<$<BOOL:${USE_MMAP}>:USE_MMAP>
|
||||
$<$<BOOL:${DISABLE_NETWORK}>:DISABLE_NETWORK>
|
||||
$<$<BOOL:${DISABLE_HTTP}>:DISABLE_HTTP>
|
||||
$<$<BOOL:${DISABLE_TTF}>:DISABLE_TTF>
|
||||
$<$<BOOL:${DISABLE_VERSION_CHECKER}>:DISABLE_VERSION_CHECKER>
|
||||
$<$<BOOL:${ENABLE_SCRIPTING}>:ENABLE_SCRIPTING>
|
||||
$<$<BOOL:${USE_MMAP}>:USE_MMAP>
|
||||
$<$<BOOL:${DISABLE_NETWORK}>:DISABLE_NETWORK>
|
||||
$<$<BOOL:${DISABLE_HTTP}>:DISABLE_HTTP>
|
||||
$<$<BOOL:${DISABLE_TTF}>:DISABLE_TTF>
|
||||
$<$<BOOL:${DISABLE_VERSION_CHECKER}>:DISABLE_VERSION_CHECKER>
|
||||
$<$<BOOL:${ENABLE_SCRIPTING}>:ENABLE_SCRIPTING>
|
||||
)
|
||||
|
||||
target_compile_options(libopenrct2 PUBLIC $<$<BOOL:${ENABLE_ASAN}>:-fsanitize=address>)
|
||||
|
||||
@@ -208,7 +208,7 @@ namespace OpenRCT2
|
||||
#endif
|
||||
|
||||
auto* windowMgr = GetWindowManager();
|
||||
windowMgr->CloseAll();
|
||||
windowMgr->Cleanup();
|
||||
|
||||
// Unload objects after closing all windows, this is to overcome windows like
|
||||
// the object selection window which loads objects when closed.
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
#include "../Context.h"
|
||||
#include "../core/Guard.hpp"
|
||||
#include "../scripting/Duktape.hpp"
|
||||
#include "../scripting/HookEngine.h"
|
||||
#include "../scripting/ScriptEngine.h"
|
||||
#include "../world/Map.h"
|
||||
@@ -39,21 +38,21 @@ namespace OpenRCT2::GameActions
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
|
||||
// Create event args object
|
||||
auto obj = Scripting::DukObject(ctx);
|
||||
obj.Set("x", coords.x);
|
||||
obj.Set("y", coords.y);
|
||||
obj.Set("player", _playerId);
|
||||
obj.Set("type", EnumValue(_type));
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, coords.x));
|
||||
JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, coords.y));
|
||||
JS_SetPropertyStr(ctx, obj, "player", JS_NewInt32(ctx, _playerId));
|
||||
JS_SetPropertyStr(ctx, obj, "type", JS_NewInt32(ctx, EnumValue(_type)));
|
||||
|
||||
auto flags = GetActionFlags();
|
||||
obj.Set("isClientOnly", (flags & Flags::ClientOnly) != 0);
|
||||
obj.Set("result", true);
|
||||
JS_SetPropertyStr(ctx, obj, "isClientOnly", JS_NewBool(ctx, (flags & Flags::ClientOnly) != 0));
|
||||
JS_SetPropertyStr(ctx, obj, "result", JS_NewBool(ctx, true));
|
||||
|
||||
// Call the subscriptions
|
||||
auto e = obj.Take();
|
||||
hookEngine.Call(Scripting::HookType::actionLocation, e, true);
|
||||
hookEngine.Call(Scripting::HookType::actionLocation, obj, true, true);
|
||||
|
||||
auto scriptResult = Scripting::AsOrDefault(e["result"], true);
|
||||
auto scriptResult = Scripting::AsOrDefault(ctx, obj, "result", true);
|
||||
JS_FreeValue(ctx, obj);
|
||||
|
||||
return scriptResult;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "../platform/Platform.h"
|
||||
#include "../profiling/Profiling.h"
|
||||
#include "../scenario/Scenario.h"
|
||||
#include "../scripting/Duktape.hpp"
|
||||
#include "../scripting/HookEngine.h"
|
||||
#include "../scripting/ScriptEngine.h"
|
||||
#include "../ui/WindowManager.h"
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "CustomAction.h"
|
||||
|
||||
#include "../../Context.h"
|
||||
@@ -23,17 +22,17 @@ namespace OpenRCT2::GameActions
|
||||
{
|
||||
}
|
||||
|
||||
std::string CustomAction::GetId() const
|
||||
const std::string& CustomAction::GetId() const
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
|
||||
std::string CustomAction::GetJson() const
|
||||
const std::string& CustomAction::GetJson() const
|
||||
{
|
||||
return _json;
|
||||
}
|
||||
|
||||
std::string CustomAction::GetPluginName() const
|
||||
const std::string& CustomAction::GetPluginName() const
|
||||
{
|
||||
return _pluginName;
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ namespace OpenRCT2::GameActions
|
||||
CustomAction() = default;
|
||||
CustomAction(const std::string& id, const std::string& json, const std::string& pluginName);
|
||||
|
||||
std::string GetId() const;
|
||||
std::string GetJson() const;
|
||||
std::string GetPluginName() const;
|
||||
const std::string& GetId() const;
|
||||
const std::string& GetJson() const;
|
||||
const std::string& GetPluginName() const;
|
||||
|
||||
uint16_t GetActionFlags() const override;
|
||||
|
||||
|
||||
@@ -87,6 +87,8 @@ namespace OpenRCT2::File
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reserve capacity for fsize + 1 to support the caller adding a null terminator if they want (needed for quickjs)
|
||||
result.reserve(fsize + 1);
|
||||
result.resize(static_cast<size_t>(fsize));
|
||||
fstream.Read(result.data(), result.size());
|
||||
}
|
||||
|
||||
@@ -7416,15 +7416,7 @@ namespace OpenRCT2
|
||||
auto& hookEngine = GetContext()->GetScriptEngine().GetHookEngine();
|
||||
if (hookEngine.HasSubscriptions(Scripting::HookType::guestGeneration))
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
|
||||
// Create event args object
|
||||
auto obj = Scripting::DukObject(ctx);
|
||||
obj.Set("id", peep->Id.ToUnderlying());
|
||||
|
||||
// Call the subscriptions
|
||||
auto e = obj.Take();
|
||||
hookEngine.Call(Scripting::HookType::guestGeneration, e, true);
|
||||
hookEngine.Call(Scripting::HookType::guestGeneration, { { "id", peep->Id.ToUnderlying() } }, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -164,17 +164,22 @@ static constexpr float kWindowScrollLocations[][2] = {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006E77A1
|
||||
*/
|
||||
void WindowUpdateAll()
|
||||
void WindowCullDead()
|
||||
{
|
||||
// Remove all windows in gWindowList that have the WindowFlag::dead flag
|
||||
gWindowList.erase(
|
||||
std::remove_if(
|
||||
gWindowList.begin(), gWindowList.end(), [](auto&& w) -> bool { return w->flags.has(WindowFlag::dead); }),
|
||||
gWindowList.end());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006E77A1
|
||||
*/
|
||||
void WindowUpdateAll()
|
||||
{
|
||||
WindowCullDead();
|
||||
|
||||
// Periodic update happens every second so 40 ticks.
|
||||
if (gCurrentRealTimeTicks >= gWindowUpdateTicks)
|
||||
|
||||
@@ -295,6 +295,7 @@ namespace OpenRCT2
|
||||
|
||||
void WindowDispatchUpdateAll();
|
||||
void WindowUpdateAllViewports();
|
||||
void WindowCullDead();
|
||||
void WindowUpdateAll();
|
||||
void WindowNotifyLanguageChange();
|
||||
|
||||
|
||||
@@ -658,11 +658,11 @@
|
||||
<ClInclude Include="scripting\bindings\world\ScTile.hpp" />
|
||||
<ClInclude Include="scripting\bindings\world\ScTileElement.hpp" />
|
||||
<ClInclude Include="scripting\bindings\world\ScWeather.hpp" />
|
||||
<ClInclude Include="scripting\Duktape.hpp" />
|
||||
<ClInclude Include="scripting\HookEngine.h" />
|
||||
<ClInclude Include="scripting\IconNames.hpp" />
|
||||
<ClInclude Include="scripting\Plugin.h" />
|
||||
<ClInclude Include="scripting\ScriptEngine.h" />
|
||||
<ClInclude Include="scripting\ScriptUtil.hpp" />
|
||||
<ClInclude Include="scripting\SoundNames.hpp" />
|
||||
<ClInclude Include="SpriteIds.h" />
|
||||
<ClInclude Include="System.hpp" />
|
||||
@@ -1199,6 +1199,7 @@
|
||||
<ClCompile Include="scenes\title\TitleSequence.cpp" />
|
||||
<ClCompile Include="scenes\title\TitleSequenceManager.cpp" />
|
||||
<ClCompile Include="scripting\bindings\entity\ScBalloon.cpp" />
|
||||
<ClCompile Include="scripting\bindings\entity\ScEntity.cpp" />
|
||||
<ClCompile Include="scripting\bindings\entity\ScGuest.cpp" />
|
||||
<ClCompile Include="scripting\bindings\entity\ScLitter.cpp" />
|
||||
<ClCompile Include="scripting\bindings\entity\ScMoneyEffect.cpp" />
|
||||
@@ -1261,11 +1262,12 @@
|
||||
<ClCompile Include="world\tile_element\TrackElement.cpp" />
|
||||
<ClCompile Include="world\tile_element\WallElement.cpp" />
|
||||
<ClCompile Include="world\Wall.cpp" />
|
||||
<ClCompile Include="..\thirdparty\duktape\duktape.cpp">
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<ForcedIncludeFiles>
|
||||
</ForcedIncludeFiles>
|
||||
<ClCompile Include="..\thirdparty\quickjs-ng\quickjs-amalgam.c">
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<LanguageStandard_C>stdc11</LanguageStandard_C>
|
||||
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<AdditionalOptions>/experimental:c11atomics %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -1274,4 +1276,4 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -1900,18 +1900,19 @@ namespace OpenRCT2::Network
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
|
||||
// Create event args object
|
||||
DukObject eObj(ctx);
|
||||
eObj.Set("name", name);
|
||||
eObj.Set("publicKeyHash", publicKeyHash);
|
||||
eObj.Set("ipAddress", connection.Socket->GetIpAddress());
|
||||
eObj.Set("cancel", false);
|
||||
auto e = eObj.Take();
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "name", JSFromStdString(ctx, name));
|
||||
JS_SetPropertyStr(ctx, obj, "publicKeyHash", JSFromStdString(ctx, publicKeyHash));
|
||||
JS_SetPropertyStr(ctx, obj, "ipAddress", JSFromStdString(ctx, connection.Socket->GetIpAddress()));
|
||||
JS_SetPropertyStr(ctx, obj, "cancel", JS_NewBool(ctx, false));
|
||||
|
||||
// Call the subscriptions
|
||||
hookEngine.Call(HookType::networkAuthenticate, e, false);
|
||||
hookEngine.Call(HookType::networkAuthenticate, obj, false, true);
|
||||
|
||||
// Check if any hook has cancelled the join
|
||||
if (AsOrDefault(e["cancel"], false))
|
||||
const bool canceled = AsOrDefault(ctx, obj, "cancel", false);
|
||||
JS_FreeValue(ctx, obj);
|
||||
if (canceled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1931,12 +1932,11 @@ namespace OpenRCT2::Network
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
|
||||
// Create event args object
|
||||
DukObject eObj(ctx);
|
||||
eObj.Set("player", playerId);
|
||||
auto e = eObj.Take();
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "player", JS_NewInt32(ctx, playerId));
|
||||
|
||||
// Call the subscriptions
|
||||
hookEngine.Call(HookType::networkJoin, e, false);
|
||||
hookEngine.Call(HookType::networkJoin, obj, false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1952,12 +1952,11 @@ namespace OpenRCT2::Network
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
|
||||
// Create event args object
|
||||
DukObject eObj(ctx);
|
||||
eObj.Set("player", playerId);
|
||||
auto e = eObj.Take();
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "player", JS_NewInt32(ctx, playerId));
|
||||
|
||||
// Call the subscriptions
|
||||
hookEngine.Call(HookType::networkLeave, e, false);
|
||||
hookEngine.Call(HookType::networkJoin, obj, false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -2902,23 +2901,23 @@ namespace OpenRCT2::Network
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
|
||||
// Create event args object
|
||||
auto objIdx = duk_push_object(ctx);
|
||||
duk_push_number(ctx, playerId);
|
||||
duk_put_prop_string(ctx, objIdx, "player");
|
||||
duk_push_string(ctx, text.c_str());
|
||||
duk_put_prop_string(ctx, objIdx, "message");
|
||||
auto e = DukValue::take_from_stack(ctx);
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "player", JS_NewInt32(ctx, playerId));
|
||||
JS_SetPropertyStr(ctx, obj, "message", Scripting::JSFromStdString(ctx, text));
|
||||
|
||||
// Call the subscriptions
|
||||
hookEngine.Call(Scripting::HookType::networkChat, e, false);
|
||||
hookEngine.Call(Scripting::HookType::networkChat, obj, false, true);
|
||||
|
||||
// Update text from object if subscriptions changed it
|
||||
if (e["message"].type() != DukValue::Type::STRING)
|
||||
auto message = Scripting::JSToOptionalStdString(ctx, obj, "message");
|
||||
JS_FreeValue(ctx, obj);
|
||||
|
||||
if (!message.has_value())
|
||||
{
|
||||
// Subscription set text to non-string, do not relay message
|
||||
return false;
|
||||
}
|
||||
text = e["message"].as_string();
|
||||
text = message.value();
|
||||
if (text.empty())
|
||||
{
|
||||
// Subscription set text to empty string, do not relay message
|
||||
|
||||
@@ -767,7 +767,7 @@ namespace OpenRCT2
|
||||
{
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
scriptEngine.SetParkStorageFromJSON(gameState.pluginStorage);
|
||||
scriptEngine.SetParkStorageFromJSON(gameState.pluginStorage, gameState.scenarioFileName);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1079,19 +1079,19 @@ static void RideRatingsCalculate(RideRating::UpdateState& state, Ride& ride)
|
||||
auto originalRatings = ride.ratings;
|
||||
|
||||
// Create event args object
|
||||
auto obj = DukObject(ctx);
|
||||
obj.Set("rideId", ride.id.ToUnderlying());
|
||||
obj.Set("excitement", originalRatings.excitement);
|
||||
obj.Set("intensity", originalRatings.intensity);
|
||||
obj.Set("nausea", originalRatings.nausea);
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "rideId", JS_NewInt32(ctx, ride.id.ToUnderlying()));
|
||||
JS_SetPropertyStr(ctx, obj, "excitement", JS_NewInt32(ctx, originalRatings.excitement));
|
||||
JS_SetPropertyStr(ctx, obj, "intensity", JS_NewInt32(ctx, originalRatings.intensity));
|
||||
JS_SetPropertyStr(ctx, obj, "nausea", JS_NewInt32(ctx, originalRatings.nausea));
|
||||
|
||||
// Call the subscriptions
|
||||
auto e = obj.Take();
|
||||
hookEngine.Call(HookType::rideRatingsCalculate, e, true);
|
||||
hookEngine.Call(HookType::rideRatingsCalculate, obj, true, true);
|
||||
|
||||
auto scriptExcitement = AsOrDefault(e["excitement"], static_cast<int32_t>(originalRatings.excitement));
|
||||
auto scriptIntensity = AsOrDefault(e["intensity"], static_cast<int32_t>(originalRatings.intensity));
|
||||
auto scriptNausea = AsOrDefault(e["nausea"], static_cast<int32_t>(originalRatings.nausea));
|
||||
auto scriptExcitement = AsOrDefault(ctx, obj, "excitement", static_cast<int32_t>(originalRatings.excitement));
|
||||
auto scriptIntensity = AsOrDefault(ctx, obj, "intensity", static_cast<int32_t>(originalRatings.intensity));
|
||||
auto scriptNausea = AsOrDefault(ctx, obj, "nausea", static_cast<int32_t>(originalRatings.nausea));
|
||||
JS_FreeValue(ctx, obj);
|
||||
|
||||
ride.ratings.excitement = std::clamp<int32_t>(scriptExcitement, 0, INT16_MAX);
|
||||
ride.ratings.intensity = std::clamp<int32_t>(scriptIntensity, 0, INT16_MAX);
|
||||
|
||||
@@ -46,16 +46,15 @@ static void InvokeVehicleCrashHook(const EntityId vehicleId, const std::string_v
|
||||
auto& hookEngine = GetContext()->GetScriptEngine().GetHookEngine();
|
||||
if (hookEngine.HasSubscriptions(Scripting::HookType::vehicleCrash))
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
JSContext* ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
|
||||
// Create event args object
|
||||
auto obj = Scripting::DukObject(ctx);
|
||||
obj.Set("id", vehicleId.ToUnderlying());
|
||||
obj.Set("crashIntoType", crashId);
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "id", JS_NewInt64(ctx, vehicleId.ToUnderlying()));
|
||||
JS_SetPropertyStr(ctx, obj, "crashIntoType", Scripting::JSFromStdString(ctx, crashId));
|
||||
|
||||
// Call the subscriptions
|
||||
auto e = obj.Take();
|
||||
hookEngine.Call(Scripting::HookType::vehicleCrash, e, true);
|
||||
hookEngine.Call(Scripting::HookType::vehicleCrash, obj, true);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,504 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2026 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 "../core/Console.hpp"
|
||||
#include "../core/EnumMap.hpp"
|
||||
#include "../ride/Vehicle.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <dukglue/dukglue.h>
|
||||
#include <duktape.h>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
template<typename T>
|
||||
DukValue GetObjectAsDukValue(duk_context* ctx, const std::shared_ptr<T>& value)
|
||||
{
|
||||
dukglue::types::DukType<std::shared_ptr<T>>::template push<T>(ctx, value);
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T AsOrDefault(const DukValue& value, const T& defaultValue = {}) = delete;
|
||||
|
||||
inline std::string AsOrDefault(const DukValue& value, std::string_view defaultValue)
|
||||
{
|
||||
return value.type() == DukValue::STRING ? value.as_string() : std::string(defaultValue);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline std::string AsOrDefault(const DukValue& value, const std::string& defaultValue)
|
||||
{
|
||||
return value.type() == DukValue::STRING ? value.as_string() : defaultValue;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int32_t AsOrDefault(const DukValue& value, const int32_t& defaultValue)
|
||||
{
|
||||
return value.type() == DukValue::NUMBER ? value.as_int() : defaultValue;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool AsOrDefault(const DukValue& value, const bool& defaultValue)
|
||||
{
|
||||
return value.type() == DukValue::BOOLEAN ? value.as_bool() : defaultValue;
|
||||
}
|
||||
|
||||
enum class DukUndefined
|
||||
{
|
||||
};
|
||||
constexpr DukUndefined undefined{};
|
||||
|
||||
/**
|
||||
* Allows creation of an object on the duktape stack and setting properties on it before
|
||||
* retrieving the DukValue instance of it.
|
||||
*/
|
||||
class DukObject
|
||||
{
|
||||
private:
|
||||
duk_context* _ctx{};
|
||||
duk_idx_t _idx = DUK_INVALID_INDEX;
|
||||
|
||||
public:
|
||||
DukObject(duk_context* ctx)
|
||||
: _ctx(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
DukObject(const DukObject&) = delete;
|
||||
|
||||
DukObject(DukObject&& m) noexcept
|
||||
{
|
||||
_ctx = m._ctx;
|
||||
_idx = m._idx;
|
||||
m._ctx = {};
|
||||
m._idx = {};
|
||||
}
|
||||
|
||||
~DukObject()
|
||||
{
|
||||
PopObjectIfExists();
|
||||
}
|
||||
|
||||
void Set(const char* name, std::nullptr_t)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_null(_ctx);
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
void Set(const char* name, DukUndefined)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_undefined(_ctx);
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
void Set(const char* name, bool value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_boolean(_ctx, value);
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
void Set(const char* name, int32_t value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_int(_ctx, value);
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
void Set(const char* name, uint32_t value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_uint(_ctx, value);
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
void Set(const char* name, int64_t value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_number(_ctx, value);
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
void Set(const char* name, uint64_t value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_number(_ctx, value);
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
void Set(const char* name, double value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_number(_ctx, value);
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
void Set(const char* name, std::string_view value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_lstring(_ctx, value.data(), value.size());
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
void Set(const char* name, const char* value)
|
||||
{
|
||||
Set(name, std::string_view(value));
|
||||
}
|
||||
|
||||
void Set(const char* name, const DukValue& value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
value.push();
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Set(const char* name, const std::optional<T>& value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_null(_ctx);
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
Set(name, *value);
|
||||
}
|
||||
}
|
||||
|
||||
DukValue Take()
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
auto result = DukValue::take_from_stack(_ctx, _idx);
|
||||
_idx = DUK_INVALID_INDEX;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
void PopObjectIfExists()
|
||||
{
|
||||
if (_idx != DUK_INVALID_INDEX)
|
||||
{
|
||||
duk_remove(_ctx, _idx);
|
||||
_idx = DUK_INVALID_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
void EnsureObjectPushed()
|
||||
{
|
||||
if (_idx == DUK_INVALID_INDEX)
|
||||
{
|
||||
_idx = duk_push_object(_ctx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class DukStackFrame
|
||||
{
|
||||
private:
|
||||
duk_context* _ctx{};
|
||||
duk_idx_t _top;
|
||||
|
||||
public:
|
||||
DukStackFrame(duk_context* ctx)
|
||||
: _ctx(ctx)
|
||||
{
|
||||
_top = duk_get_top(ctx);
|
||||
}
|
||||
|
||||
~DukStackFrame()
|
||||
{
|
||||
auto top = duk_get_top(_ctx);
|
||||
if (top != _top)
|
||||
{
|
||||
duk_set_top(_ctx, _top);
|
||||
_ctx = {};
|
||||
Console::Error::WriteLine("duktape stack was not returned to original state!");
|
||||
}
|
||||
_ctx = {};
|
||||
}
|
||||
|
||||
DukStackFrame(const DukStackFrame&) = delete;
|
||||
DukStackFrame(DukStackFrame&&) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* Bi-directional map for converting between strings and enums / numbers.
|
||||
*/
|
||||
template<typename T>
|
||||
using DukEnumMap = EnumMap<T>;
|
||||
|
||||
inline duk_ret_t duk_json_decode_wrapper(duk_context* ctx, void*)
|
||||
{
|
||||
duk_json_decode(ctx, -1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline std::optional<DukValue> DuktapeTryParseJson(duk_context* ctx, std::string_view json)
|
||||
{
|
||||
duk_push_lstring(ctx, json.data(), json.size());
|
||||
if (duk_safe_call(ctx, duk_json_decode_wrapper, nullptr, 1, 1) == DUK_EXEC_SUCCESS)
|
||||
{
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
// Pop error off stack
|
||||
duk_pop(ctx);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string ProcessString(const DukValue& value);
|
||||
|
||||
template<typename T>
|
||||
DukValue ToDuk(duk_context* ctx, const T& value) = delete;
|
||||
template<typename T>
|
||||
T FromDuk(const DukValue& s) = delete;
|
||||
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const std::nullptr_t&)
|
||||
{
|
||||
duk_push_null(ctx);
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const DukUndefined&)
|
||||
{
|
||||
duk_push_undefined(ctx);
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const bool& value)
|
||||
{
|
||||
duk_push_boolean(ctx, value);
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const uint8_t& value)
|
||||
{
|
||||
duk_push_int(ctx, value);
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const uint16_t& value)
|
||||
{
|
||||
duk_push_int(ctx, value);
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const int32_t& value)
|
||||
{
|
||||
duk_push_int(ctx, value);
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const int64_t& value)
|
||||
{
|
||||
duk_push_number(ctx, value);
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const std::string_view& value)
|
||||
{
|
||||
duk_push_lstring(ctx, value.data(), value.size());
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const std::string& value)
|
||||
{
|
||||
return ToDuk(ctx, std::string_view(value));
|
||||
}
|
||||
|
||||
template<size_t TLen>
|
||||
inline DukValue ToDuk(duk_context* ctx, const char (&value)[TLen])
|
||||
{
|
||||
duk_push_string(ctx, value);
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline DukValue ToDuk(duk_context* ctx, const std::optional<T>& value)
|
||||
{
|
||||
return value ? ToDuk(ctx, *value) : ToDuk(ctx, nullptr);
|
||||
}
|
||||
|
||||
template<>
|
||||
CoordsXY inline FromDuk(const DukValue& d)
|
||||
{
|
||||
CoordsXY result;
|
||||
result.x = AsOrDefault(d["x"], 0);
|
||||
result.y = AsOrDefault(d["y"], 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
MapRange inline FromDuk(const DukValue& d)
|
||||
{
|
||||
MapRange range;
|
||||
range.Point1 = FromDuk<CoordsXY>(d["leftTop"]);
|
||||
range.Point2 = FromDuk<CoordsXY>(d["rightBottom"]);
|
||||
return range.Normalise();
|
||||
}
|
||||
|
||||
template<>
|
||||
DukValue inline ToDuk(duk_context* ctx, const CoordsXY& coords)
|
||||
{
|
||||
DukObject dukCoords(ctx);
|
||||
dukCoords.Set("x", coords.x);
|
||||
dukCoords.Set("y", coords.y);
|
||||
return dukCoords.Take();
|
||||
}
|
||||
|
||||
template<>
|
||||
DukValue inline ToDuk(duk_context* ctx, const TileCoordsXY& coords)
|
||||
{
|
||||
DukObject dukCoords(ctx);
|
||||
dukCoords.Set("x", coords.x);
|
||||
dukCoords.Set("y", coords.y);
|
||||
return dukCoords.Take();
|
||||
}
|
||||
|
||||
template<>
|
||||
DukValue inline ToDuk(duk_context* ctx, const ScreenCoordsXY& coords)
|
||||
{
|
||||
DukObject dukCoords(ctx);
|
||||
dukCoords.Set("x", coords.x);
|
||||
dukCoords.Set("y", coords.y);
|
||||
return dukCoords.Take();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const CoordsXYZ& value)
|
||||
{
|
||||
if (value.IsNull())
|
||||
{
|
||||
return ToDuk(ctx, nullptr);
|
||||
}
|
||||
|
||||
DukObject dukCoords(ctx);
|
||||
dukCoords.Set("x", value.x);
|
||||
dukCoords.Set("y", value.y);
|
||||
dukCoords.Set("z", value.z);
|
||||
return dukCoords.Take();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline CoordsXYZ FromDuk(const DukValue& value)
|
||||
{
|
||||
CoordsXYZ result;
|
||||
if (value.type() == DukValue::Type::OBJECT)
|
||||
{
|
||||
result.x = AsOrDefault(value["x"], 0);
|
||||
result.y = AsOrDefault(value["y"], 0);
|
||||
result.z = AsOrDefault(value["z"], 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.SetNull();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const CoordsXYZD& value)
|
||||
{
|
||||
if (value.IsNull())
|
||||
{
|
||||
return ToDuk(ctx, nullptr);
|
||||
}
|
||||
|
||||
DukObject dukCoords(ctx);
|
||||
dukCoords.Set("x", value.x);
|
||||
dukCoords.Set("y", value.y);
|
||||
dukCoords.Set("z", value.z);
|
||||
dukCoords.Set("direction", value.direction);
|
||||
return dukCoords.Take();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const GForces& value)
|
||||
{
|
||||
DukObject dukGForces(ctx);
|
||||
dukGForces.Set("lateralG", value.lateralG);
|
||||
dukGForces.Set("verticalG", value.verticalG);
|
||||
return dukGForces.Take();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const VehicleSpriteGroup& value)
|
||||
{
|
||||
DukObject dukSpriteGroup(ctx);
|
||||
dukSpriteGroup.Set("imageId", value.imageId);
|
||||
dukSpriteGroup.Set("spriteNumImages", OpenRCT2::Entity::Yaw::NumSpritesPrecision(value.spritePrecision));
|
||||
return dukSpriteGroup.Take();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline CoordsXYZD FromDuk(const DukValue& value)
|
||||
{
|
||||
CoordsXYZD result;
|
||||
if (value.type() == DukValue::Type::OBJECT)
|
||||
{
|
||||
result.x = AsOrDefault(value["x"], 0);
|
||||
result.y = AsOrDefault(value["y"], 0);
|
||||
result.z = AsOrDefault(value["z"], 0);
|
||||
result.direction = AsOrDefault(value["direction"], 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.SetNull();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const ScreenSize& value)
|
||||
{
|
||||
DukObject dukCoords(ctx);
|
||||
dukCoords.Set("width", value.width);
|
||||
dukCoords.Set("height", value.height);
|
||||
return dukCoords.Take();
|
||||
}
|
||||
|
||||
template<>
|
||||
ObjectEntryIndex inline FromDuk(const DukValue& d)
|
||||
{
|
||||
if (d.type() == DukValue::Type::NUMBER)
|
||||
{
|
||||
auto value = d.as_int();
|
||||
if (value >= 0 && value <= std::numeric_limits<ObjectEntryIndex>::max())
|
||||
{
|
||||
return static_cast<ObjectEntryIndex>(value);
|
||||
}
|
||||
}
|
||||
return kObjectEntryIndexNull;
|
||||
}
|
||||
|
||||
uint32_t ImageFromDuk(const DukValue& d);
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
#endif
|
||||
@@ -14,8 +14,6 @@
|
||||
#include "../core/EnumMap.hpp"
|
||||
#include "ScriptEngine.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace OpenRCT2::Scripting;
|
||||
|
||||
static const EnumMap<HookType> HooksLookupTable(
|
||||
@@ -54,7 +52,7 @@ HookEngine::HookEngine(ScriptEngine& scriptEngine)
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t HookEngine::Subscribe(HookType type, std::shared_ptr<Plugin> owner, const DukValue& function)
|
||||
uint32_t HookEngine::Subscribe(HookType type, const std::shared_ptr<Plugin>& owner, const JSCallback& function)
|
||||
{
|
||||
auto& hookList = GetHookList(type);
|
||||
auto cookie = _nextCookie++;
|
||||
@@ -76,7 +74,7 @@ void HookEngine::Unsubscribe(HookType type, uint32_t cookie)
|
||||
}
|
||||
}
|
||||
|
||||
void HookEngine::UnsubscribeAll(std::shared_ptr<const Plugin> owner)
|
||||
void HookEngine::UnsubscribeAll(const std::shared_ptr<const Plugin>& owner)
|
||||
{
|
||||
for (auto& hookList : _hookMap)
|
||||
{
|
||||
@@ -115,51 +113,45 @@ void HookEngine::Call(HookType type, bool isGameStateMutable)
|
||||
auto& hookList = GetHookList(type);
|
||||
for (auto& hook : hookList.Hooks)
|
||||
{
|
||||
_scriptEngine.ExecutePluginCall(hook.Owner, hook.Function, {}, isGameStateMutable);
|
||||
_scriptEngine.ExecutePluginCall(hook.Owner, hook.Function.callback, {}, isGameStateMutable);
|
||||
}
|
||||
}
|
||||
|
||||
void HookEngine::Call(HookType type, const DukValue& arg, bool isGameStateMutable)
|
||||
void HookEngine::Call(HookType type, const JSValue arg, bool isGameStateMutable, bool keepArgsAlive)
|
||||
{
|
||||
auto& hookList = GetHookList(type);
|
||||
for (auto& hook : hookList.Hooks)
|
||||
{
|
||||
_scriptEngine.ExecutePluginCall(hook.Owner, hook.Function, { arg }, isGameStateMutable);
|
||||
_scriptEngine.ExecutePluginCall(hook.Owner, hook.Function.callback, { arg }, isGameStateMutable, keepArgsAlive);
|
||||
}
|
||||
}
|
||||
|
||||
void HookEngine::Call(
|
||||
HookType type, const std::initializer_list<std::pair<std::string_view, std::any>>& args, bool isGameStateMutable)
|
||||
HookType type, const std::initializer_list<std::pair<std::string, HookValue>>& args, bool isGameStateMutable)
|
||||
{
|
||||
auto& hookList = GetHookList(type);
|
||||
for (auto& hook : hookList.Hooks)
|
||||
{
|
||||
auto ctx = _scriptEngine.GetContext();
|
||||
JSContext* ctx = hook.Owner.get()->GetContext();
|
||||
|
||||
// Convert key/value pairs into an object
|
||||
auto objIdx = duk_push_object(ctx);
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
for (const auto& arg : args)
|
||||
{
|
||||
if (arg.second.type() == typeid(int32_t))
|
||||
{
|
||||
auto val = std::any_cast<int32_t>(arg.second);
|
||||
duk_push_int(ctx, val);
|
||||
}
|
||||
else if (arg.second.type() == typeid(std::string))
|
||||
{
|
||||
const auto& val = std::any_cast<std::string>(arg.second);
|
||||
duk_push_string(ctx, val.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
duk_put_prop_string(ctx, objIdx, arg.first.data());
|
||||
JSValue member = std::visit(
|
||||
HookValuesToJS{
|
||||
[ctx](int64_t v) { return JS_NewInt64(ctx, v); },
|
||||
[ctx](uint32_t v) { return JS_NewInt64(ctx, v); },
|
||||
[ctx](int32_t v) { return JS_NewInt32(ctx, v); },
|
||||
[ctx](uint16_t v) { return JS_NewInt32(ctx, v); },
|
||||
[ctx](int16_t v) { return JS_NewInt32(ctx, v); },
|
||||
[ctx](const std::string& v) { return JSFromStdString(ctx, v); },
|
||||
},
|
||||
arg.second);
|
||||
JS_SetPropertyStr(ctx, obj, arg.first.c_str(), member);
|
||||
}
|
||||
|
||||
std::vector<DukValue> dukArgs;
|
||||
dukArgs.push_back(DukValue::take_from_stack(ctx));
|
||||
_scriptEngine.ExecutePluginCall(hook.Owner, hook.Function, dukArgs, isGameStateMutable);
|
||||
_scriptEngine.ExecutePluginCall(hook.Owner, hook.Function.callback, { obj }, isGameStateMutable);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "Duktape.hpp"
|
||||
#include "ScriptUtil.hpp"
|
||||
|
||||
#include <any>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
@@ -49,14 +49,22 @@ namespace OpenRCT2::Scripting
|
||||
constexpr size_t NUM_HookTypeS = static_cast<size_t>(HookType::count);
|
||||
HookType GetHookType(const std::string& name);
|
||||
|
||||
using HookValue = std::variant<int32_t, int16_t, uint16_t, std::string>;
|
||||
|
||||
template<class... Ts>
|
||||
struct HookValuesToJS : Ts...
|
||||
{
|
||||
using Ts::operator()...;
|
||||
};
|
||||
|
||||
struct Hook
|
||||
{
|
||||
uint32_t Cookie;
|
||||
std::shared_ptr<Plugin> Owner;
|
||||
DukValue Function;
|
||||
JSCallback Function;
|
||||
|
||||
Hook() = default;
|
||||
Hook(uint32_t cookie, std::shared_ptr<Plugin> owner, const DukValue& function)
|
||||
Hook(uint32_t cookie, const std::shared_ptr<Plugin>& owner, const JSCallback& function)
|
||||
: Cookie(cookie)
|
||||
, Owner(owner)
|
||||
, Function(function)
|
||||
@@ -84,16 +92,15 @@ namespace OpenRCT2::Scripting
|
||||
public:
|
||||
HookEngine(ScriptEngine& scriptEngine);
|
||||
HookEngine(const HookEngine&) = delete;
|
||||
uint32_t Subscribe(HookType type, std::shared_ptr<Plugin> owner, const DukValue& function);
|
||||
uint32_t Subscribe(HookType type, const std::shared_ptr<Plugin>& owner, const JSCallback& function);
|
||||
void Unsubscribe(HookType type, uint32_t cookie);
|
||||
void UnsubscribeAll(std::shared_ptr<const Plugin> owner);
|
||||
void UnsubscribeAll(const std::shared_ptr<const Plugin>& owner);
|
||||
void UnsubscribeAll();
|
||||
bool HasSubscriptions(HookType type) const;
|
||||
bool IsValidHookForPlugin(HookType type, Plugin& plugin) const;
|
||||
void Call(HookType type, bool isGameStateMutable);
|
||||
void Call(HookType type, const DukValue& arg, bool isGameStateMutable);
|
||||
void Call(
|
||||
HookType type, const std::initializer_list<std::pair<std::string_view, std::any>>& args, bool isGameStateMutable);
|
||||
void Call(HookType type, JSValue arg, bool isGameStateMutable, bool keepArgsAlive = false);
|
||||
void Call(HookType type, const std::initializer_list<std::pair<std::string, HookValue>>& args, bool isGameStateMutable);
|
||||
|
||||
private:
|
||||
HookList& GetHookList(HookType type);
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
|
||||
#include "Plugin.h"
|
||||
|
||||
#include "../Context.h"
|
||||
#include "../Diagnostic.h"
|
||||
#include "../OpenRCT2.h"
|
||||
#include "../core/File.h"
|
||||
#include "Duktape.hpp"
|
||||
#include "ScriptEngine.h"
|
||||
|
||||
#include <fstream>
|
||||
@@ -22,9 +22,8 @@
|
||||
|
||||
using namespace OpenRCT2::Scripting;
|
||||
|
||||
Plugin::Plugin(duk_context* context, std::string_view path)
|
||||
: _context(context)
|
||||
, _path(path)
|
||||
Plugin::Plugin(std::string_view path)
|
||||
: _path(path)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -33,45 +32,46 @@ void Plugin::SetCode(std::string_view code)
|
||||
_code = code;
|
||||
}
|
||||
|
||||
static JSValue registerPlugin(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
Plugin* plugin = static_cast<Plugin*>(JS_GetContextOpaque(ctx));
|
||||
plugin->SetMetadata(argv[0]);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void Plugin::Load()
|
||||
{
|
||||
if (_context)
|
||||
{
|
||||
JS_FreeContext(_context);
|
||||
}
|
||||
|
||||
if (!_path.empty())
|
||||
{
|
||||
LoadCodeFromFile();
|
||||
}
|
||||
|
||||
std::string projectedVariables = "console,context,date,map,network,park,profiler";
|
||||
if (!gOpenRCT2Headless)
|
||||
auto& scriptEngine = OpenRCT2::GetContext()->GetScriptEngine();
|
||||
_context = scriptEngine.CreateContext();
|
||||
JS_SetContextOpaque(_context, this);
|
||||
|
||||
JSValue registerFunc = JS_NewCFunction(_context, registerPlugin, "registerPlugin", 1);
|
||||
JSValue glb = JS_GetGlobalObject(_context);
|
||||
JS_SetPropertyStr(_context, glb, "registerPlugin", registerFunc);
|
||||
JS_FreeValue(_context, glb);
|
||||
|
||||
JSValue res = JS_Eval(_context, _code.c_str(), _code.length(), _path.c_str(), JS_EVAL_TYPE_GLOBAL);
|
||||
if (JS_IsException(res))
|
||||
{
|
||||
projectedVariables += ",ui";
|
||||
JSValue exceptionVal = JS_GetException(_context);
|
||||
std::string details = Stringify(_context, exceptionVal);
|
||||
JS_FreeValue(_context, exceptionVal);
|
||||
JS_FreeValue(_context, res);
|
||||
JS_FreeContext(_context);
|
||||
throw std::runtime_error("Failed to load plug-in script: " + _path + "\n" + details);
|
||||
}
|
||||
JS_FreeValue(_context, res);
|
||||
|
||||
// Wrap the script in a function and pass the global objects as variables
|
||||
// so that if the script modifies them, they are not modified for other scripts.
|
||||
|
||||
// clang-format off
|
||||
auto code = _code;
|
||||
code =
|
||||
" (function(" + projectedVariables + ") {"
|
||||
" var __metadata__ = null;"
|
||||
" var registerPlugin = function(m) { __metadata__ = m };"
|
||||
" (function(__metadata__) {"
|
||||
+ code +
|
||||
" })();"
|
||||
" return __metadata__;"
|
||||
" })(" + projectedVariables + ");";
|
||||
// clang-format on
|
||||
|
||||
auto flags = DUK_COMPILE_EVAL | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE | DUK_COMPILE_NOFILENAME;
|
||||
auto result = duk_eval_raw(_context, code.c_str(), code.size(), flags);
|
||||
if (result != DUK_ERR_NONE)
|
||||
{
|
||||
auto val = std::string(duk_safe_to_string(_context, -1));
|
||||
duk_pop(_context);
|
||||
throw std::runtime_error("Failed to load plug-in script: " + val + " at " + _path);
|
||||
}
|
||||
|
||||
_metadata = GetMetadata(DukValue::take_from_stack(_context));
|
||||
_hasLoaded = true;
|
||||
}
|
||||
|
||||
@@ -82,23 +82,24 @@ void Plugin::Start()
|
||||
throw std::runtime_error("Plugin has not been loaded.");
|
||||
}
|
||||
|
||||
const auto& mainFunc = _metadata.Main;
|
||||
if (mainFunc.context() == nullptr)
|
||||
const JSValue mainFunc = _metadata.Main.callback;
|
||||
if (!JS_IsFunction(_context, mainFunc))
|
||||
{
|
||||
throw std::runtime_error("No main function specified.");
|
||||
}
|
||||
|
||||
_hasStarted = true;
|
||||
|
||||
mainFunc.push();
|
||||
auto result = duk_pcall(_context, 0);
|
||||
if (result != DUK_ERR_NONE)
|
||||
const JSValue res = JS_Call(_context, mainFunc, JS_UNDEFINED, 0, nullptr);
|
||||
if (JS_IsException(res))
|
||||
{
|
||||
auto val = std::string(duk_safe_to_string(_context, -1));
|
||||
duk_pop(_context);
|
||||
throw std::runtime_error("[" + _metadata.Name + "] " + val);
|
||||
JSValue exceptionVal = JS_GetException(_context);
|
||||
std::string details = Stringify(_context, exceptionVal);
|
||||
JS_FreeValue(_context, exceptionVal);
|
||||
JS_FreeValue(_context, res);
|
||||
throw std::runtime_error("[" + _metadata.Name + "]\n" + details);
|
||||
}
|
||||
duk_pop(_context);
|
||||
JS_FreeValue(_context, res);
|
||||
}
|
||||
|
||||
void Plugin::StopBegin()
|
||||
@@ -112,16 +113,16 @@ void Plugin::StopEnd()
|
||||
_hasStarted = false;
|
||||
}
|
||||
|
||||
void Plugin::ThrowIfStopping() const
|
||||
{
|
||||
if (IsStopping())
|
||||
{
|
||||
duk_error(_context, DUK_ERR_ERROR, "Plugin is stopping.");
|
||||
}
|
||||
}
|
||||
|
||||
void Plugin::Unload()
|
||||
{
|
||||
if (!_context)
|
||||
{
|
||||
throw std::runtime_error("Plugin is not loaded");
|
||||
}
|
||||
|
||||
JS_FreeContext(_context);
|
||||
_context = nullptr;
|
||||
|
||||
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105937, fixed in GCC13
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
@@ -139,34 +140,51 @@ void Plugin::LoadCodeFromFile()
|
||||
_code = File::ReadAllText(_path);
|
||||
}
|
||||
|
||||
static std::string TryGetString(const DukValue& value, const std::string& message)
|
||||
std::string Plugin::TryGetString(const JSValue value, const char* property, const std::string& message) const
|
||||
{
|
||||
if (value.type() != DukValue::Type::STRING)
|
||||
const JSValue res = JS_GetPropertyStr(_context, value, property);
|
||||
if (!JS_IsString(res))
|
||||
{
|
||||
JS_FreeValue(_context, res);
|
||||
throw std::runtime_error(message);
|
||||
return value.as_string();
|
||||
}
|
||||
std::string str = JSToStdString(_context, res);
|
||||
JS_FreeValue(_context, res);
|
||||
return str;
|
||||
}
|
||||
|
||||
PluginMetadata Plugin::GetMetadata(const DukValue& dukMetadata)
|
||||
void Plugin::SetMetadata(const JSValue obj)
|
||||
{
|
||||
PluginMetadata metadata;
|
||||
if (dukMetadata.type() == DukValue::Type::OBJECT)
|
||||
PluginMetadata metadata{};
|
||||
if (JS_IsObject(obj))
|
||||
{
|
||||
metadata.Name = TryGetString(dukMetadata["name"], "Plugin name not specified.");
|
||||
metadata.Version = TryGetString(dukMetadata["version"], "Plugin version not specified.");
|
||||
metadata.Type = ParsePluginType(TryGetString(dukMetadata["type"], "Plugin type not specified."));
|
||||
metadata.Name = TryGetString(obj, "name", "Plugin name not specified.");
|
||||
metadata.Version = TryGetString(obj, "version", "Plugin version not specified.");
|
||||
metadata.Type = ParsePluginType(TryGetString(obj, "type", "Plugin type not specified."));
|
||||
|
||||
CheckForLicence(dukMetadata["licence"], metadata.Name);
|
||||
|
||||
auto dukMinApiVersion = dukMetadata["minApiVersion"];
|
||||
if (dukMinApiVersion.type() == DukValue::Type::NUMBER)
|
||||
const JSValue licence = JS_GetPropertyStr(_context, obj, "licence");
|
||||
int64_t licenseLen = -1;
|
||||
if (!JS_IsString(licence) || JS_GetLength(_context, licence, &licenseLen) == -1 || licenseLen == 0)
|
||||
{
|
||||
metadata.MinApiVersion = dukMinApiVersion.as_uint();
|
||||
LOG_ERROR("Plugin %s does not specify a licence", _metadata.Name.c_str());
|
||||
}
|
||||
JS_FreeValue(_context, licence);
|
||||
|
||||
auto dukTargetApiVersion = dukMetadata["targetApiVersion"];
|
||||
if (dukTargetApiVersion.type() == DukValue::Type::NUMBER)
|
||||
const JSValue minApiVersion = JS_GetPropertyStr(_context, obj, "minApiVersion");
|
||||
if (JS_IsNumber(minApiVersion))
|
||||
{
|
||||
metadata.TargetApiVersion = dukTargetApiVersion.as_uint();
|
||||
int64_t val = -1;
|
||||
JS_ToInt64(_context, &val, minApiVersion);
|
||||
metadata.MinApiVersion = val;
|
||||
}
|
||||
JS_FreeValue(_context, minApiVersion);
|
||||
|
||||
const JSValue targetApiVersion = JS_GetPropertyStr(_context, obj, "targetApiVersion");
|
||||
if (JS_IsNumber(targetApiVersion))
|
||||
{
|
||||
int64_t val = -1;
|
||||
JS_ToInt64(_context, &val, targetApiVersion);
|
||||
metadata.TargetApiVersion = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -174,23 +192,25 @@ PluginMetadata Plugin::GetMetadata(const DukValue& dukMetadata)
|
||||
u8"Plug-in “%s” does not specify a target API version or specifies it incorrectly. Emulating deprecated APIs.",
|
||||
metadata.Name.c_str());
|
||||
}
|
||||
JS_FreeValue(_context, targetApiVersion);
|
||||
|
||||
auto dukAuthors = dukMetadata["authors"];
|
||||
dukAuthors.push();
|
||||
if (dukAuthors.is_array())
|
||||
const JSValue authors = JS_GetPropertyStr(_context, obj, "authors");
|
||||
if (JS_IsArray(authors))
|
||||
{
|
||||
auto elements = dukAuthors.as_array();
|
||||
std::transform(elements.begin(), elements.end(), std::back_inserter(metadata.Authors), [](const DukValue& v) {
|
||||
return v.as_string();
|
||||
JSIterateArray(_context, authors, [&metadata](JSContext* ctx2, JSValue val) {
|
||||
if (JS_IsString(val))
|
||||
metadata.Authors.emplace_back(JSToStdString(ctx2, val));
|
||||
});
|
||||
}
|
||||
else if (dukAuthors.type() == DukValue::Type::STRING)
|
||||
else if (JS_IsString(authors))
|
||||
{
|
||||
metadata.Authors = { dukAuthors.as_string() };
|
||||
metadata.Authors = { JSToStdString(_context, authors) };
|
||||
}
|
||||
metadata.Main = dukMetadata["main"];
|
||||
JS_FreeValue(_context, authors);
|
||||
|
||||
metadata.Main = JSToCallback(_context, obj, "main");
|
||||
}
|
||||
return metadata;
|
||||
_metadata = metadata;
|
||||
}
|
||||
|
||||
PluginType Plugin::ParsePluginType(std::string_view type)
|
||||
@@ -204,12 +224,6 @@ PluginType Plugin::ParsePluginType(std::string_view type)
|
||||
throw std::invalid_argument("Unknown plugin type.");
|
||||
}
|
||||
|
||||
void Plugin::CheckForLicence(const DukValue& dukLicence, std::string_view pluginName)
|
||||
{
|
||||
if (dukLicence.type() != DukValue::Type::STRING || dukLicence.as_string().empty())
|
||||
LOG_ERROR("Plugin %s does not specify a licence", std::string(pluginName).c_str());
|
||||
}
|
||||
|
||||
int32_t Plugin::GetTargetAPIVersion() const
|
||||
{
|
||||
if (_metadata.TargetApiVersion)
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "Duktape.hpp"
|
||||
#include "ScriptUtil.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <quickjs.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
@@ -49,13 +50,13 @@ namespace OpenRCT2::Scripting
|
||||
PluginType Type{};
|
||||
int32_t MinApiVersion{};
|
||||
std::optional<int32_t> TargetApiVersion{};
|
||||
DukValue Main;
|
||||
JSCallback Main;
|
||||
};
|
||||
|
||||
class Plugin
|
||||
{
|
||||
private:
|
||||
duk_context* _context{};
|
||||
JSContext* _context = nullptr;
|
||||
std::string _path;
|
||||
PluginMetadata _metadata{};
|
||||
std::string _code;
|
||||
@@ -63,12 +64,19 @@ namespace OpenRCT2::Scripting
|
||||
bool _hasStarted{};
|
||||
bool _isStopping{};
|
||||
|
||||
std::string TryGetString(JSValue value, const char* property, const std::string& message) const;
|
||||
|
||||
public:
|
||||
std::string_view GetPath() const
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
JSContext* GetContext() const
|
||||
{
|
||||
return _context;
|
||||
}
|
||||
|
||||
bool HasPath() const
|
||||
{
|
||||
return !_path.empty();
|
||||
@@ -79,6 +87,8 @@ namespace OpenRCT2::Scripting
|
||||
return _metadata;
|
||||
}
|
||||
|
||||
void SetMetadata(JSValue obj);
|
||||
|
||||
const std::string& GetCode() const
|
||||
{
|
||||
return _code;
|
||||
@@ -102,7 +112,7 @@ namespace OpenRCT2::Scripting
|
||||
int32_t GetTargetAPIVersion() const;
|
||||
|
||||
Plugin() = default;
|
||||
Plugin(duk_context* context, std::string_view path);
|
||||
explicit Plugin(std::string_view path);
|
||||
Plugin(const Plugin&) = delete;
|
||||
Plugin(Plugin&&) = delete;
|
||||
|
||||
@@ -112,7 +122,6 @@ namespace OpenRCT2::Scripting
|
||||
void StopBegin();
|
||||
void StopEnd();
|
||||
|
||||
void ThrowIfStopping() const;
|
||||
void Unload();
|
||||
|
||||
bool IsTransient() const;
|
||||
@@ -120,9 +129,8 @@ namespace OpenRCT2::Scripting
|
||||
private:
|
||||
void LoadCodeFromFile();
|
||||
|
||||
static PluginMetadata GetMetadata(const DukValue& dukMetadata);
|
||||
static PluginType ParsePluginType(std::string_view type);
|
||||
static void CheckForLicence(const DukValue& dukLicence, std::string_view pluginName);
|
||||
static void CheckForLicence(JSValue dukLicence, std::string_view pluginName);
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,8 +13,8 @@
|
||||
|
||||
#include "../actions/general/CustomAction.h"
|
||||
#include "../core/FileWatcher.h"
|
||||
#include "../core/Guard.hpp"
|
||||
#include "../management/Finance.h"
|
||||
#include "../world/Location.hpp"
|
||||
#include "HookEngine.h"
|
||||
#include "Plugin.h"
|
||||
|
||||
@@ -23,14 +23,12 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <quickjs.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
struct duk_hthread;
|
||||
typedef struct duk_hthread duk_context;
|
||||
|
||||
namespace OpenRCT2::GameActions
|
||||
{
|
||||
class GameAction;
|
||||
@@ -56,7 +54,7 @@ namespace OpenRCT2::Scripting
|
||||
static constexpr int32_t kApiVersionNetworkIDs = 77;
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
class ScSocketBase;
|
||||
struct SocketDataBase;
|
||||
#endif
|
||||
|
||||
class ScriptExecutionInfo
|
||||
@@ -105,34 +103,13 @@ namespace OpenRCT2::Scripting
|
||||
}
|
||||
};
|
||||
|
||||
class DukContext
|
||||
{
|
||||
private:
|
||||
duk_context* _context{};
|
||||
|
||||
public:
|
||||
DukContext();
|
||||
DukContext(DukContext&) = delete;
|
||||
DukContext(DukContext&& src) noexcept
|
||||
: _context(std::move(src._context))
|
||||
{
|
||||
src._context = {};
|
||||
}
|
||||
~DukContext();
|
||||
|
||||
operator duk_context*()
|
||||
{
|
||||
return _context;
|
||||
}
|
||||
};
|
||||
|
||||
using IntervalHandle = uint32_t;
|
||||
struct ScriptInterval
|
||||
{
|
||||
std::shared_ptr<Plugin> Owner;
|
||||
uint32_t Delay{};
|
||||
int64_t LastTimestamp{};
|
||||
DukValue Callback;
|
||||
JSCallback Callback;
|
||||
bool Repeat{};
|
||||
bool Deleted{};
|
||||
};
|
||||
@@ -142,7 +119,8 @@ namespace OpenRCT2::Scripting
|
||||
private:
|
||||
InteractiveConsole& _console;
|
||||
IPlatformEnvironment& _env;
|
||||
DukContext _context;
|
||||
static JSRuntime* _runtime;
|
||||
JSContext* _replContext = nullptr;
|
||||
bool _initialised{};
|
||||
bool _hotReloadingInitialised{};
|
||||
bool _transientPluginsEnabled{};
|
||||
@@ -153,8 +131,8 @@ namespace OpenRCT2::Scripting
|
||||
uint32_t _lastHotReloadCheckTick{};
|
||||
HookEngine _hookEngine;
|
||||
ScriptExecutionInfo _execInfo;
|
||||
DukValue _sharedStorage;
|
||||
DukValue _parkStorage;
|
||||
JSValue _sharedStorage = JS_UNDEFINED;
|
||||
JSValue _parkStorage = JS_UNDEFINED;
|
||||
|
||||
uint32_t _lastIntervalTimestamp{};
|
||||
std::map<IntervalHandle, ScriptInterval> _intervals;
|
||||
@@ -165,26 +143,37 @@ namespace OpenRCT2::Scripting
|
||||
std::mutex _changedPluginFilesMutex;
|
||||
std::vector<std::function<void(std::shared_ptr<Plugin>)>> _pluginStoppedSubscriptions;
|
||||
|
||||
struct ExtensionCallbacks
|
||||
{
|
||||
std::function<void(JSContext*)> newContext;
|
||||
std::function<void()> unregister;
|
||||
};
|
||||
|
||||
std::vector<ExtensionCallbacks> _extensions;
|
||||
|
||||
struct CustomActionInfo
|
||||
{
|
||||
std::shared_ptr<Plugin> Owner;
|
||||
std::string Name;
|
||||
DukValue Query;
|
||||
DukValue Execute;
|
||||
JSCallback Query;
|
||||
JSCallback Execute;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, CustomActionInfo> _customActions;
|
||||
#ifndef DISABLE_NETWORK
|
||||
std::list<std::shared_ptr<ScSocketBase>> _sockets;
|
||||
std::vector<SocketDataBase*> _sockets;
|
||||
#endif
|
||||
|
||||
void InitialiseContext(JSContext* ctx) const;
|
||||
|
||||
public:
|
||||
ScriptEngine(InteractiveConsole& console, IPlatformEnvironment& env);
|
||||
ScriptEngine(ScriptEngine&) = delete;
|
||||
~ScriptEngine();
|
||||
|
||||
duk_context* GetContext()
|
||||
JSContext* GetContext()
|
||||
{
|
||||
return _context;
|
||||
return _replContext;
|
||||
}
|
||||
HookEngine& GetHookEngine()
|
||||
{
|
||||
@@ -194,11 +183,11 @@ namespace OpenRCT2::Scripting
|
||||
{
|
||||
return _execInfo;
|
||||
}
|
||||
DukValue GetSharedStorage()
|
||||
JSValue GetSharedStorage()
|
||||
{
|
||||
return _sharedStorage;
|
||||
}
|
||||
DukValue GetParkStorage()
|
||||
JSValue GetParkStorage()
|
||||
{
|
||||
return _parkStorage;
|
||||
}
|
||||
@@ -223,20 +212,21 @@ namespace OpenRCT2::Scripting
|
||||
|
||||
void ClearParkStorage();
|
||||
std::string GetParkStorageAsJSON();
|
||||
void SetParkStorageFromJSON(std::string_view value);
|
||||
void SetParkStorageFromJSON(const std::string& value, const std::string& filename);
|
||||
|
||||
void Initialise();
|
||||
JSContext* CreateContext() const;
|
||||
void LoadTransientPlugins();
|
||||
void UnloadTransientPlugins();
|
||||
void StopUnloadRegisterAllPlugins();
|
||||
void Tick();
|
||||
std::future<void> Eval(const std::string& s);
|
||||
DukValue ExecutePluginCall(
|
||||
const std::shared_ptr<Plugin>& plugin, const DukValue& func, const std::vector<DukValue>& args,
|
||||
bool isGameStateMutable);
|
||||
DukValue ExecutePluginCall(
|
||||
std::shared_ptr<Plugin> plugin, const DukValue& func, const DukValue& thisValue, const std::vector<DukValue>& args,
|
||||
bool isGameStateMutable);
|
||||
void ExecutePluginCall(
|
||||
const std::shared_ptr<Plugin>& plugin, JSValue func, const std::vector<JSValue>& args, bool isGameStateMutable,
|
||||
bool keepArgsAlive = false);
|
||||
JSValue ExecutePluginCall(
|
||||
const std::shared_ptr<Plugin>& plugin, JSValue func, JSValue thisValue, const std::vector<JSValue>& args,
|
||||
bool isGameStateMutable, bool keepArgsAlive = false, bool keepRetValueAlive = false);
|
||||
|
||||
void LogPluginInfo(std::string_view message);
|
||||
void LogPluginInfo(const std::shared_ptr<Plugin>& plugin, std::string_view message);
|
||||
@@ -246,32 +236,43 @@ namespace OpenRCT2::Scripting
|
||||
_pluginStoppedSubscriptions.push_back(callback);
|
||||
}
|
||||
|
||||
void RegisterExtension(std::function<void(JSContext*)> newContext, std::function<void()> unregister)
|
||||
{
|
||||
_extensions.emplace_back(newContext, unregister);
|
||||
newContext(_replContext);
|
||||
}
|
||||
|
||||
void AddNetworkPlugin(std::string_view code);
|
||||
void RemoveNetworkPlugins();
|
||||
|
||||
[[nodiscard]] GameActions::Result QueryOrExecuteCustomGameAction(
|
||||
const GameActions::CustomAction& action, bool isExecute);
|
||||
bool RegisterCustomAction(
|
||||
const std::shared_ptr<Plugin>& plugin, std::string_view action, const DukValue& query, const DukValue& execute);
|
||||
const std::shared_ptr<Plugin>& plugin, std::string_view action, const JSCallback& query, const JSCallback& execute);
|
||||
void RunGameActionHooks(const GameActions::GameAction& action, GameActions::Result& result, bool isExecute);
|
||||
[[nodiscard]] std::unique_ptr<GameActions::GameAction> CreateGameAction(
|
||||
const std::string& actionid, const DukValue& args, const std::string& pluginName);
|
||||
[[nodiscard]] DukValue GameActionResultToDuk(const GameActions::GameAction& action, const GameActions::Result& result);
|
||||
[[nodiscard]] std::pair<std::unique_ptr<GameActions::GameAction>, bool> CreateGameAction(
|
||||
JSContext* ctx, const std::string& actionid, JSValue args, const std::string& pluginName);
|
||||
[[nodiscard]] JSValue GameActionResultToJS(
|
||||
JSContext* ctx, const GameActions::GameAction& action, const GameActions::Result& result);
|
||||
|
||||
void SaveSharedStorage();
|
||||
|
||||
IntervalHandle AddInterval(const std::shared_ptr<Plugin>& plugin, int32_t delay, bool repeat, DukValue&& callback);
|
||||
IntervalHandle AddInterval(
|
||||
const std::shared_ptr<Plugin>& plugin, int32_t delay, bool repeat, const JSCallback& callback);
|
||||
void RemoveInterval(const std::shared_ptr<Plugin>& plugin, IntervalHandle handle);
|
||||
|
||||
static std::string_view ExpenditureTypeToString(ExpenditureType expenditureType);
|
||||
static ExpenditureType StringToExpenditureType(std::string_view expenditureType);
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
void AddSocket(const std::shared_ptr<ScSocketBase>& socket);
|
||||
void AddSocket(SocketDataBase* data);
|
||||
void RemoveSocket(SocketDataBase* data);
|
||||
#endif
|
||||
|
||||
private:
|
||||
void RegisterConstants();
|
||||
static void RegisterClasses(JSContext* ctx);
|
||||
static void UnregisterClasses();
|
||||
static void RegisterConstants(JSContext* ctx);
|
||||
void RefreshPlugins();
|
||||
std::vector<std::string> GetPluginFiles() const;
|
||||
void UnregisterPlugin(std::string_view path);
|
||||
@@ -283,7 +284,7 @@ namespace OpenRCT2::Scripting
|
||||
void LoadPlugin(std::shared_ptr<Plugin>& plugin);
|
||||
void UnloadPlugin(std::shared_ptr<Plugin>& plugin);
|
||||
void StartPlugin(std::shared_ptr<Plugin> plugin);
|
||||
void StopPlugin(std::shared_ptr<Plugin> plugin);
|
||||
void StopPlugin(std::shared_ptr<Plugin> plugin, bool unregistering = false);
|
||||
void ReloadPlugin(std::shared_ptr<Plugin> plugin);
|
||||
static bool ShouldLoadScript(std::string_view path);
|
||||
bool ShouldStartPlugin(const std::shared_ptr<Plugin>& plugin);
|
||||
@@ -292,7 +293,7 @@ namespace OpenRCT2::Scripting
|
||||
void AutoReloadPlugins();
|
||||
void ProcessREPL();
|
||||
void RemoveCustomGameActions(const std::shared_ptr<Plugin>& plugin);
|
||||
[[nodiscard]] GameActions::Result DukToGameActionResult(const DukValue& d);
|
||||
[[nodiscard]] static GameActions::Result JSToGameActionResult(JSContext* ctx, JSValue d);
|
||||
|
||||
void InitSharedStorage();
|
||||
void LoadSharedStorage();
|
||||
@@ -306,10 +307,66 @@ namespace OpenRCT2::Scripting
|
||||
};
|
||||
|
||||
bool IsGameStateMutable();
|
||||
void ThrowIfGameStateNotMutable();
|
||||
int32_t GetTargetAPIVersion();
|
||||
|
||||
std::string Stringify(const DukValue& value);
|
||||
std::string Stringify(JSContext* context, JSValue value);
|
||||
|
||||
class ScBase
|
||||
{
|
||||
private:
|
||||
JSClassID classId = JS_INVALID_CLASS_ID;
|
||||
|
||||
public:
|
||||
void Unregister()
|
||||
{
|
||||
classId = JS_INVALID_CLASS_ID;
|
||||
}
|
||||
|
||||
protected:
|
||||
[[nodiscard]] JSValue MakeWithOpaque(JSContext* ctx, std::span<const JSCFunctionListEntry> classFuncs, void* opaque)
|
||||
{
|
||||
JSValue obj = JS_NewObjectClass(ctx, classId);
|
||||
if (JS_IsException(obj))
|
||||
throw std::runtime_error("Failed to create new object for class.");
|
||||
JS_SetOpaque(obj, opaque);
|
||||
|
||||
// Note: Usually one would set a class prototype rather than setting the functions as properties on every creation.
|
||||
// However, that causes the attached functions to not be "own properties" which make them a little less
|
||||
// visible to the user, and also does not match the previous behaviour with the DukTape engine.
|
||||
JS_SetPropertyFunctionList(ctx, obj, classFuncs.data(), static_cast<int>(classFuncs.size()));
|
||||
return obj;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] T GetOpaque(JSValue obj) const
|
||||
{
|
||||
return static_cast<T>(JS_GetOpaque(obj, classId));
|
||||
}
|
||||
|
||||
void RegisterBase(JSContext* ctx, const JSClassDef& classDef)
|
||||
{
|
||||
Guard::Assert(classId == JS_INVALID_CLASS_ID);
|
||||
// Note: Technically JS_NewClassID is meant to be called once during the lifetime of the program
|
||||
// whereas JS_NewClass is meant to be called for each runtime.
|
||||
// If we ever have any more runtimes, this flow would be wrong.
|
||||
// More runtimes would also require refactoring the way values are passed to the JS code when
|
||||
// calling callbacks.
|
||||
JSRuntime* rt = JS_GetRuntime(ctx);
|
||||
JS_NewClassID(rt, &classId);
|
||||
JS_NewClass(rt, classId, &classDef);
|
||||
JS_SetClassProto(ctx, classId, JS_NewObject(ctx));
|
||||
}
|
||||
|
||||
void RegisterBaseStr(JSContext* ctx, const char* className)
|
||||
{
|
||||
RegisterBase(ctx, { className, nullptr, nullptr, nullptr, nullptr });
|
||||
}
|
||||
|
||||
void RegisterBaseStr(JSContext* ctx, const char* className, JSClassFinalizer* finalizer)
|
||||
{
|
||||
RegisterBase(ctx, { className, finalizer, nullptr, nullptr, nullptr });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
|
||||
@@ -0,0 +1,614 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2025 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "../world/Location.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <quickjs.h>
|
||||
#include <string>
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
struct JSCallback
|
||||
{
|
||||
JSContext* context = nullptr;
|
||||
JSValue callback = JS_UNDEFINED;
|
||||
|
||||
JSCallback() {};
|
||||
|
||||
JSCallback(JSContext* ctx, JSValue func)
|
||||
: context(ctx)
|
||||
, callback(ctx && JS_IsFunction(ctx, func) ? JS_DupValue(ctx, func) : JS_UNDEFINED)
|
||||
{
|
||||
}
|
||||
|
||||
JSCallback(const JSCallback& other)
|
||||
: context(other.context)
|
||||
, callback(
|
||||
other.context && JS_IsFunction(other.context, other.callback) ? JS_DupValue(other.context, other.callback)
|
||||
: JS_UNDEFINED)
|
||||
{
|
||||
}
|
||||
|
||||
JSCallback(JSCallback&& other) noexcept
|
||||
: context(other.context)
|
||||
, callback(other.callback)
|
||||
{
|
||||
other.context = nullptr;
|
||||
other.callback = JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSCallback& operator=(const JSCallback& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
if (context)
|
||||
{
|
||||
JS_FreeValue(context, callback);
|
||||
}
|
||||
context = other.context;
|
||||
callback = JS_DupValue(other.context, other.callback);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
JSCallback& operator=(JSCallback&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
if (context)
|
||||
{
|
||||
JS_FreeValue(context, callback);
|
||||
}
|
||||
context = other.context;
|
||||
callback = other.callback;
|
||||
other.context = nullptr;
|
||||
other.callback = JS_UNDEFINED;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
return context && JS_IsFunction(context, callback);
|
||||
}
|
||||
|
||||
~JSCallback() noexcept
|
||||
{
|
||||
if (context)
|
||||
{
|
||||
JS_FreeValue(context, callback);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline JSCallback JSToCallback(JSContext* ctx, JSValue obj, const char* name)
|
||||
{
|
||||
JSValue property = JS_GetPropertyStr(ctx, obj, name);
|
||||
JSCallback out(ctx, property);
|
||||
JS_FreeValue(ctx, property);
|
||||
return out;
|
||||
}
|
||||
|
||||
inline std::string JSToStdString(JSContext* ctx, JSValue obj)
|
||||
{
|
||||
size_t len;
|
||||
if (const char* buf = JS_ToCStringLen(ctx, &len, obj))
|
||||
{
|
||||
std::string str(buf, len);
|
||||
JS_FreeCString(ctx, buf);
|
||||
return str;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
inline std::string JSToStdString(JSContext* ctx, JSValue obj, const char* property)
|
||||
{
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, property);
|
||||
std::string output = JSToStdString(ctx, val);
|
||||
JS_FreeValue(ctx, val);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline JSValue JSFromStdString(JSContext* ctx, const std::string_view str)
|
||||
{
|
||||
return JS_NewStringLen(ctx, str.data(), str.length());
|
||||
}
|
||||
|
||||
inline std::optional<std::string> JSToOptionalStdString(JSContext* ctx, JSValue obj, const char* property)
|
||||
{
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, property);
|
||||
std::optional<std::string> output = std::nullopt;
|
||||
if (JS_IsString(val))
|
||||
{
|
||||
output = JSToStdString(ctx, val);
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline std::optional<int32_t> JSToOptionalInt(JSContext* ctx, JSValue obj, const char* property)
|
||||
{
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, property);
|
||||
std::optional<int32_t> output = std::nullopt;
|
||||
if (JS_IsNumber(val))
|
||||
{
|
||||
int32_t intVal = -1;
|
||||
if (JS_ToInt32(ctx, &intVal, val) >= 0)
|
||||
{
|
||||
output = std::make_optional(intVal);
|
||||
}
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline std::optional<int64_t> JSToOptionalInt64(JSContext* ctx, JSValue obj, const char* property)
|
||||
{
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, property);
|
||||
std::optional<int64_t> output = std::nullopt;
|
||||
if (JS_IsNumber(val))
|
||||
{
|
||||
int64_t intVal = -1;
|
||||
if (JS_ToInt64(ctx, &intVal, val) >= 0)
|
||||
{
|
||||
output = std::make_optional(intVal);
|
||||
}
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline std::optional<uint32_t> JSToOptionalUint(JSContext* ctx, JSValue obj, const char* property)
|
||||
{
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, property);
|
||||
std::optional<uint32_t> output = std::nullopt;
|
||||
if (JS_IsNumber(val))
|
||||
{
|
||||
uint32_t uintVal = 0;
|
||||
if (JS_ToUint32(ctx, &uintVal, val) >= 0)
|
||||
{
|
||||
output = std::make_optional(uintVal);
|
||||
}
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline std::optional<bool> JSToOptionalBool(JSContext* ctx, JSValue obj, const char* property)
|
||||
{
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, property);
|
||||
std::optional<bool> output = std::nullopt;
|
||||
if (JS_IsBool(val))
|
||||
{
|
||||
const int result = JS_ToBool(ctx, val);
|
||||
if (result != -1)
|
||||
{
|
||||
output = std::make_optional(static_cast<bool>(result));
|
||||
}
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline bool AsOrDefault(JSContext* ctx, JSValue obj, const char* property, bool def)
|
||||
{
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, property);
|
||||
bool output;
|
||||
if (JS_IsBool(val))
|
||||
{
|
||||
int result = JS_ToBool(ctx, val);
|
||||
output = result >= 0 ? result : def;
|
||||
}
|
||||
else
|
||||
{
|
||||
output = def;
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline int32_t AsOrDefault(JSContext* ctx, JSValue obj, const char* property, int32_t def)
|
||||
{
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, property);
|
||||
int32_t output;
|
||||
if (!JS_IsNumber(val))
|
||||
{
|
||||
output = def;
|
||||
}
|
||||
else if (JS_ToInt32(ctx, &output, val) < 0)
|
||||
{
|
||||
output = def;
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline uint32_t AsOrDefault(JSContext* ctx, JSValue obj, const char* property, uint32_t def)
|
||||
{
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, property);
|
||||
uint32_t output;
|
||||
if (!JS_IsNumber(val))
|
||||
{
|
||||
output = def;
|
||||
}
|
||||
else if (JS_ToUint32(ctx, &output, val) < 0)
|
||||
{
|
||||
output = def;
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline int64_t AsOrDefault(JSContext* ctx, JSValue obj, const char* property, int64_t def)
|
||||
{
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, property);
|
||||
int64_t output;
|
||||
if (!JS_IsNumber(val))
|
||||
{
|
||||
output = def;
|
||||
}
|
||||
else if (JS_ToInt64(ctx, &output, val) < 0)
|
||||
{
|
||||
output = def;
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
return output;
|
||||
}
|
||||
|
||||
// Note the default parameter needs to be const char*
|
||||
// If you use a type like string_view, calls can instead resolve to the boolean version of the function.
|
||||
inline std::string AsOrDefault(JSContext* ctx, JSValue obj, const char* property, const char* def)
|
||||
{
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, property);
|
||||
std::string output;
|
||||
if (!JS_IsString(val))
|
||||
{
|
||||
output = std::string(def);
|
||||
}
|
||||
else
|
||||
{
|
||||
output = JSToStdString(ctx, val);
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline void JSIterateArray(JSContext* ctx, JSValue val, const std::function<void(JSContext*, JSValue)>& callback)
|
||||
{
|
||||
if (JS_IsArray(val))
|
||||
{
|
||||
int64_t arrayLen = -1;
|
||||
JS_GetLength(ctx, val, &arrayLen);
|
||||
for (int64_t i = 0; i < arrayLen; i++)
|
||||
{
|
||||
JSValue elem = JS_GetPropertyInt64(ctx, val, i);
|
||||
callback(ctx, elem);
|
||||
JS_FreeValue(ctx, elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void JSIterateArray(
|
||||
JSContext* ctx, JSValue obj, const char* property, const std::function<void(JSContext*, JSValue)>& callback)
|
||||
{
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, property);
|
||||
JSIterateArray(ctx, val, callback);
|
||||
JS_FreeValue(ctx, val);
|
||||
}
|
||||
|
||||
inline int32_t JSToInt(JSContext* ctx, JSValue val)
|
||||
{
|
||||
int32_t output = -1;
|
||||
if (JS_IsNumber(val))
|
||||
{
|
||||
JS_ToInt32(ctx, &output, val);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
inline int32_t JSToInt(JSContext* ctx, JSValue obj, const char* property)
|
||||
{
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, property);
|
||||
int32_t output = JSToInt(ctx, val);
|
||||
JS_FreeValue(ctx, val);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline int64_t JSToInt64(JSContext* ctx, JSValue val)
|
||||
{
|
||||
int64_t output = -1;
|
||||
if (JS_IsNumber(val))
|
||||
{
|
||||
JS_ToInt64(ctx, &output, val);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
inline int64_t JSToInt64(JSContext* ctx, JSValue obj, const char* property)
|
||||
{
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, property);
|
||||
int64_t output = JSToInt64(ctx, val);
|
||||
JS_FreeValue(ctx, val);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline uint32_t JSToUint(JSContext* ctx, JSValue val)
|
||||
{
|
||||
uint32_t output = 0;
|
||||
if (JS_IsNumber(val))
|
||||
{
|
||||
JS_ToUint32(ctx, &output, val);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
inline uint32_t JSToUint(JSContext* ctx, JSValue obj, const char* property)
|
||||
{
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, property);
|
||||
uint32_t output = JSToUint(ctx, val);
|
||||
JS_FreeValue(ctx, val);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline CoordsXY JSToCoordsXY(JSContext* ctx, JSValue obj)
|
||||
{
|
||||
return { AsOrDefault(ctx, obj, "x", 0), AsOrDefault(ctx, obj, "y", 0) };
|
||||
}
|
||||
|
||||
inline CoordsXY JSToCoordXY(JSContext* ctx, JSValue obj, const char* property)
|
||||
{
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, property);
|
||||
CoordsXY output = JSToCoordsXY(ctx, val);
|
||||
JS_FreeValue(ctx, val);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline CoordsXYZ JSToCoordsXYZ(JSContext* ctx, JSValue obj)
|
||||
{
|
||||
CoordsXYZ result;
|
||||
if (JS_IsObject(obj))
|
||||
{
|
||||
result.x = AsOrDefault(ctx, obj, "x", 0);
|
||||
result.y = AsOrDefault(ctx, obj, "y", 0);
|
||||
result.z = AsOrDefault(ctx, obj, "z", 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.SetNull();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline CoordsXYZD JSToCoordsXYZD(JSContext* ctx, JSValue obj)
|
||||
{
|
||||
CoordsXYZD result;
|
||||
if (JS_IsObject(obj))
|
||||
{
|
||||
result = CoordsXYZD(JSToCoordsXYZ(ctx, obj), AsOrDefault(ctx, obj, "direction", 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.SetNull();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline JSValue ToJSValue(JSContext* ctx, uint8_t val)
|
||||
{
|
||||
return JS_NewInt32(ctx, val);
|
||||
}
|
||||
|
||||
inline JSValue ToJSValue(JSContext* ctx, const CoordsXY& coords)
|
||||
{
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, coords.x));
|
||||
JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, coords.y));
|
||||
return obj;
|
||||
}
|
||||
|
||||
inline JSValue ToJSValue(JSContext* ctx, const TileCoordsXY& coords)
|
||||
{
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, coords.x));
|
||||
JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, coords.y));
|
||||
return obj;
|
||||
}
|
||||
|
||||
inline JSValue ToJSValue(JSContext* ctx, const ScreenCoordsXY& coords)
|
||||
{
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, coords.x));
|
||||
JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, coords.y));
|
||||
return obj;
|
||||
}
|
||||
|
||||
inline JSValue ToJSValue(JSContext* ctx, const ScreenSize& size)
|
||||
{
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "width", JS_NewInt32(ctx, size.width));
|
||||
JS_SetPropertyStr(ctx, obj, "height", JS_NewInt32(ctx, size.height));
|
||||
return obj;
|
||||
}
|
||||
|
||||
inline JSValue ToJSValue(JSContext* ctx, const CoordsXYZ& value)
|
||||
{
|
||||
if (value.IsNull())
|
||||
{
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, value.x));
|
||||
JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, value.y));
|
||||
JS_SetPropertyStr(ctx, obj, "z", JS_NewInt32(ctx, value.z));
|
||||
return obj;
|
||||
}
|
||||
|
||||
inline JSValue ToJSValue(JSContext* ctx, const CoordsXYZD& value)
|
||||
{
|
||||
if (value.IsNull())
|
||||
{
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
JSValue obj = ToJSValue(ctx, static_cast<const CoordsXYZ&>(value));
|
||||
JS_SetPropertyStr(ctx, obj, "direction", JS_NewUint32(ctx, value.direction));
|
||||
return obj;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
JSValue ToJSValue(JSContext* ctx, const std::optional<T>& value)
|
||||
{
|
||||
return value ? ToJSValue(ctx, *value) : JS_NULL;
|
||||
}
|
||||
|
||||
#define JS_THROW_IF_GAME_STATE_NOT_MUTABLE() \
|
||||
if (!Scripting::IsGameStateMutable()) \
|
||||
{ \
|
||||
JS_ThrowPlainError(ctx, "Game state is not mutable in this context."); \
|
||||
return JS_EXCEPTION; \
|
||||
}
|
||||
|
||||
#define JS_UNPACK_INT32(var, ctx, val) \
|
||||
int32_t var; \
|
||||
if (!JS_IsNumber(val)) \
|
||||
{ \
|
||||
JS_ThrowTypeError(ctx, "Expected number"); \
|
||||
return JS_EXCEPTION; \
|
||||
} \
|
||||
if (JS_ToInt32(ctx, &var, val) < 0) \
|
||||
{ \
|
||||
return JS_EXCEPTION; \
|
||||
}
|
||||
|
||||
#define JS_UNPACK_INT64(var, ctx, val) \
|
||||
int64_t var; \
|
||||
if (!JS_IsNumber(val)) \
|
||||
{ \
|
||||
JS_ThrowTypeError(ctx, "Expected number"); \
|
||||
return JS_EXCEPTION; \
|
||||
} \
|
||||
if (JS_ToInt64(ctx, &var, val) < 0) \
|
||||
{ \
|
||||
return JS_EXCEPTION; \
|
||||
}
|
||||
|
||||
#define JS_UNPACK_UINT32(var, ctx, val) \
|
||||
uint32_t var; \
|
||||
if (!JS_IsNumber(val)) \
|
||||
{ \
|
||||
JS_ThrowTypeError(ctx, "Expected number"); \
|
||||
return JS_EXCEPTION; \
|
||||
} \
|
||||
if (JS_ToUint32(ctx, &var, val) < 0) \
|
||||
{ \
|
||||
return JS_EXCEPTION; \
|
||||
}
|
||||
|
||||
#define JS_UNPACK_MONEY64(var, ctx, val) \
|
||||
money64 var; \
|
||||
if (!JS_IsNumber(val)) \
|
||||
{ \
|
||||
JS_ThrowTypeError(ctx, "Expected number"); \
|
||||
return JS_EXCEPTION; \
|
||||
} \
|
||||
if (JS_ToInt64(ctx, &var, val) < 0) \
|
||||
{ \
|
||||
return JS_EXCEPTION; \
|
||||
}
|
||||
|
||||
#define JS_UNPACK_STR(var, ctx, val) \
|
||||
std::string var; \
|
||||
if (!JS_IsString(val)) \
|
||||
{ \
|
||||
JS_ThrowTypeError(ctx, "Expected string"); \
|
||||
return JS_EXCEPTION; \
|
||||
} \
|
||||
{ \
|
||||
size_t len; \
|
||||
if (const char* buf = JS_ToCStringLen(ctx, &len, val)) \
|
||||
{ \
|
||||
var = std::string(buf, len); \
|
||||
JS_FreeCString(ctx, buf); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
return JS_EXCEPTION; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define JS_UNPACK_BOOL(var, ctx, val) \
|
||||
bool var; \
|
||||
if (!JS_IsBool(val)) \
|
||||
{ \
|
||||
JS_ThrowTypeError(ctx, "Expected boolean"); \
|
||||
return JS_EXCEPTION; \
|
||||
} \
|
||||
{ \
|
||||
const int result = JS_ToBool(ctx, val); \
|
||||
if (result == -1) \
|
||||
{ \
|
||||
return JS_EXCEPTION; \
|
||||
} \
|
||||
var = result; \
|
||||
}
|
||||
|
||||
#define JS_UNPACK_ARRAY(var, ctx, val) \
|
||||
JSValue var = val; \
|
||||
if (!JS_IsArray(val)) \
|
||||
{ \
|
||||
JS_ThrowTypeError(ctx, "Expected array"); \
|
||||
return JS_EXCEPTION; \
|
||||
}
|
||||
|
||||
#define JS_UNPACK_CALLBACK(var, ctx, val) \
|
||||
JSCallback var(ctx, val); \
|
||||
if (!var.IsValid()) \
|
||||
{ \
|
||||
JS_ThrowTypeError(ctx, "Expected function"); \
|
||||
return JS_EXCEPTION; \
|
||||
}
|
||||
|
||||
#define JS_UNPACK_OBJECT(var, ctx, val) \
|
||||
JSValue var = val; \
|
||||
if (!JS_IsObject(var)) \
|
||||
{ \
|
||||
JS_ThrowTypeError(ctx, "Expected object"); \
|
||||
return JS_EXCEPTION; \
|
||||
}
|
||||
|
||||
#define JS_UNPACK_COORDSXY(var, ctx, val) \
|
||||
CoordsXY var; \
|
||||
if (!JS_IsObject(var)) \
|
||||
{ \
|
||||
JS_ThrowTypeError(ctx, "Expected CoordsXY"); \
|
||||
return JS_EXCEPTION; \
|
||||
} \
|
||||
{ \
|
||||
auto x = JSToOptionalInt(ctx, position, "x"); \
|
||||
auto y = JSToOptionalInt(ctx, position, "y"); \
|
||||
if (x.has_value() && y.has_value()) \
|
||||
{ \
|
||||
var = CoordsXY(x.value(), y.value()); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
JS_ThrowTypeError(ctx, "Expected CoordsXY"); \
|
||||
return JS_EXCEPTION; \
|
||||
} \
|
||||
}
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
#endif
|
||||
@@ -15,40 +15,43 @@
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
ScBalloon::ScBalloon(EntityId Id)
|
||||
: ScEntity(Id)
|
||||
JSValue ScBalloon::New(JSContext* ctx, EntityId entityId)
|
||||
{
|
||||
JSValue obj = gScEntity.New(ctx, entityId);
|
||||
AddFuncs(ctx, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void ScBalloon::Register(duk_context* ctx)
|
||||
void ScBalloon::AddFuncs(JSContext* ctx, JSValue obj)
|
||||
{
|
||||
dukglue_set_base_class<ScEntity, ScBalloon>(ctx);
|
||||
dukglue_register_property(ctx, &ScBalloon::colour_get, &ScBalloon::colour_set, "colour");
|
||||
static constexpr JSCFunctionListEntry funcs[] = { JS_CGETSET_DEF(
|
||||
"colour", &ScBalloon::colour_get, &ScBalloon::colour_set) };
|
||||
JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs));
|
||||
}
|
||||
|
||||
Balloon* ScBalloon::GetBalloon() const
|
||||
Balloon* ScBalloon::GetBalloon(JSValue thisVal)
|
||||
{
|
||||
return getGameState().entities.GetEntity<Balloon>(_id);
|
||||
auto id = GetEntityId(thisVal);
|
||||
return getGameState().entities.GetEntity<Balloon>(id);
|
||||
}
|
||||
|
||||
uint8_t ScBalloon::colour_get() const
|
||||
JSValue ScBalloon::colour_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto balloon = GetBalloon();
|
||||
if (balloon != nullptr)
|
||||
{
|
||||
return EnumValue(balloon->colour);
|
||||
}
|
||||
return 0;
|
||||
auto balloon = GetBalloon(thisVal);
|
||||
return JS_NewUint32(ctx, balloon == nullptr ? 0 : EnumValue(balloon->colour));
|
||||
}
|
||||
|
||||
void ScBalloon::colour_set(uint8_t value)
|
||||
JSValue ScBalloon::colour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
auto balloon = GetBalloon();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto balloon = GetBalloon(thisVal);
|
||||
if (balloon != nullptr)
|
||||
{
|
||||
balloon->colour = static_cast<Drawing::Colour>(value);
|
||||
balloon->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -20,19 +20,17 @@ namespace OpenRCT2
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
|
||||
class ScBalloon : public ScEntity
|
||||
class ScBalloon final : public ScEntity
|
||||
{
|
||||
public:
|
||||
ScBalloon(EntityId Id);
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
static JSValue New(JSContext* ctx, EntityId entityId);
|
||||
|
||||
private:
|
||||
Balloon* GetBalloon() const;
|
||||
static void AddFuncs(JSContext* ctx, JSValue obj);
|
||||
static Balloon* GetBalloon(JSValue thisVal);
|
||||
|
||||
uint8_t colour_get() const;
|
||||
void colour_set(uint8_t);
|
||||
static JSValue colour_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue colour_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2025 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 "ScEntity.hpp"
|
||||
|
||||
#include "../../../entity/Staff.h"
|
||||
#include "ScBalloon.hpp"
|
||||
#include "ScGuest.hpp"
|
||||
#include "ScLitter.hpp"
|
||||
#include "ScMoneyEffect.hpp"
|
||||
#include "ScParticle.hpp"
|
||||
#include "ScPeep.hpp"
|
||||
#include "ScStaff.hpp"
|
||||
#include "ScVehicle.hpp"
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
static inline std::string EntityTypeToString(const EntityBase* entity)
|
||||
{
|
||||
const auto targetApiVersion = GetTargetAPIVersion();
|
||||
|
||||
if (entity != nullptr)
|
||||
{
|
||||
switch (entity->Type)
|
||||
{
|
||||
case EntityType::vehicle:
|
||||
return "car";
|
||||
case EntityType::guest:
|
||||
if (targetApiVersion <= kApiVersionPeepDeprecation)
|
||||
return "peep";
|
||||
return "guest";
|
||||
case EntityType::staff:
|
||||
if (targetApiVersion <= kApiVersionPeepDeprecation)
|
||||
return "peep";
|
||||
return "staff";
|
||||
case EntityType::steamParticle:
|
||||
return "steam_particle";
|
||||
case EntityType::moneyEffect:
|
||||
return "money_effect";
|
||||
case EntityType::crashedVehicleParticle:
|
||||
return "crashed_vehicle_particle";
|
||||
case EntityType::explosionCloud:
|
||||
return "explosion_cloud";
|
||||
case EntityType::crashSplash:
|
||||
return "crash_splash";
|
||||
case EntityType::explosionFlare:
|
||||
return "explosion_flare";
|
||||
case EntityType::balloon:
|
||||
return "balloon";
|
||||
case EntityType::duck:
|
||||
return "duck";
|
||||
case EntityType::jumpingFountain:
|
||||
return "jumping_fountain";
|
||||
case EntityType::litter:
|
||||
return "litter";
|
||||
case EntityType::null:
|
||||
return "unknown";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
using OpaqueEntityData = struct
|
||||
{
|
||||
EntityId id;
|
||||
};
|
||||
|
||||
JSValue ScEntity::id_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto entity = GetEntity(thisVal);
|
||||
if (entity == nullptr)
|
||||
return JS_UNDEFINED;
|
||||
|
||||
return JS_NewInt32(ctx, entity->Id.ToUnderlying());
|
||||
}
|
||||
|
||||
JSValue ScEntity::type_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto entity = GetEntity(thisVal);
|
||||
auto type = EntityTypeToString(entity);
|
||||
return JSFromStdString(ctx, type);
|
||||
}
|
||||
|
||||
// x getter and setter
|
||||
JSValue ScEntity::x_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto entity = GetEntity(thisVal);
|
||||
return JS_NewInt32(ctx, entity != nullptr ? entity->x : 0);
|
||||
}
|
||||
JSValue ScEntity::x_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
JS_UNPACK_INT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto entity = GetEntity(thisVal);
|
||||
if (entity != nullptr)
|
||||
{
|
||||
entity->MoveTo({ value, entity->y, entity->z });
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
// y getter and setter
|
||||
JSValue ScEntity::y_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto entity = GetEntity(thisVal);
|
||||
return JS_NewInt32(ctx, entity != nullptr ? entity->y : 0);
|
||||
}
|
||||
JSValue ScEntity::y_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
JS_UNPACK_INT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto entity = GetEntity(thisVal);
|
||||
if (entity != nullptr)
|
||||
{
|
||||
entity->MoveTo({ entity->x, value, entity->z });
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
// z getter and setter
|
||||
JSValue ScEntity::z_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto entity = GetEntity(thisVal);
|
||||
return JS_NewInt32(ctx, entity != nullptr ? entity->z : 0);
|
||||
}
|
||||
JSValue ScEntity::z_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
JS_UNPACK_INT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto entity = GetEntity(thisVal);
|
||||
if (entity != nullptr)
|
||||
{
|
||||
entity->MoveTo({ entity->x, entity->y, value });
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue ScEntity::remove(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto entity = GetEntity(thisVal);
|
||||
if (entity != nullptr)
|
||||
{
|
||||
entity->Invalidate();
|
||||
switch (entity->Type)
|
||||
{
|
||||
case EntityType::vehicle:
|
||||
JS_ThrowPlainError(ctx, "Removing a vehicle is currently unsupported.");
|
||||
return JS_EXCEPTION;
|
||||
case EntityType::guest:
|
||||
case EntityType::staff:
|
||||
{
|
||||
auto peep = entity->As<Peep>();
|
||||
// We can't remove a single peep from a ride at the moment as this can cause complications with the
|
||||
// vehicle car having an unsupported peep capacity.
|
||||
if (peep == nullptr || peep->State == PeepState::onRide || peep->State == PeepState::enteringRide)
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "Removing a peep that is on a ride is currently unsupported.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
peep->Remove();
|
||||
break;
|
||||
}
|
||||
case EntityType::steamParticle:
|
||||
case EntityType::moneyEffect:
|
||||
case EntityType::crashedVehicleParticle:
|
||||
case EntityType::explosionCloud:
|
||||
case EntityType::crashSplash:
|
||||
case EntityType::explosionFlare:
|
||||
case EntityType::jumpingFountain:
|
||||
case EntityType::balloon:
|
||||
case EntityType::duck:
|
||||
case EntityType::litter:
|
||||
getGameState().entities.EntityRemove(entity);
|
||||
break;
|
||||
case EntityType::null:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
EntityId ScEntity::GetEntityId(JSValue thisVal)
|
||||
{
|
||||
return gScEntity.GetOpaque<OpaqueEntityData*>(thisVal)->id;
|
||||
}
|
||||
|
||||
EntityBase* ScEntity::GetEntity(JSValue thisVal)
|
||||
{
|
||||
auto id = GetEntityId(thisVal);
|
||||
return OpenRCT2::getGameState().entities.GetEntity(id);
|
||||
}
|
||||
|
||||
JSValue ScEntity::NewInstance(JSContext* ctx, EntityId entityId)
|
||||
{
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("id", &ScEntity::id_get, nullptr), JS_CGETSET_DEF("type", &ScEntity::type_get, nullptr),
|
||||
JS_CGETSET_DEF("x", &ScEntity::x_get, &ScEntity::x_set), JS_CGETSET_DEF("y", &ScEntity::y_get, &ScEntity::y_set),
|
||||
JS_CGETSET_DEF("z", &ScEntity::z_get, &ScEntity::z_set), JS_CFUNC_DEF("remove", 0, &ScEntity::remove)
|
||||
};
|
||||
return MakeWithOpaque(ctx, funcs, new OpaqueEntityData{ entityId });
|
||||
}
|
||||
|
||||
// this one exists as a hack to make a template work in ScMap
|
||||
JSValue ScEntity::New(JSContext* ctx, EntityId entityId)
|
||||
{
|
||||
return gScEntity.NewInstance(ctx, entityId);
|
||||
}
|
||||
|
||||
void ScEntity::Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "Entity", Finalize);
|
||||
}
|
||||
|
||||
void ScEntity::Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
OpaqueEntityData* data = gScEntity.GetOpaque<OpaqueEntityData*>(thisVal);
|
||||
if (data)
|
||||
delete data;
|
||||
}
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
#endif
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "../../../GameState.h"
|
||||
#include "../../../entity/EntityRegistry.h"
|
||||
#include "../../../entity/Peep.h"
|
||||
#include "../../Duktape.hpp"
|
||||
#include "../../ScriptEngine.h"
|
||||
|
||||
#include <string_view>
|
||||
@@ -23,188 +22,41 @@
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScEntity
|
||||
class ScEntity;
|
||||
extern ScEntity gScEntity;
|
||||
|
||||
class ScEntity : public ScBase
|
||||
{
|
||||
protected:
|
||||
EntityId _id{ EntityId::GetNull() };
|
||||
|
||||
public:
|
||||
ScEntity(EntityId id)
|
||||
: _id(id)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
DukValue id_get() const
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
|
||||
auto entity = GetEntity();
|
||||
if (entity == nullptr)
|
||||
return ToDuk(ctx, nullptr);
|
||||
|
||||
return ToDuk(ctx, entity->Id.ToUnderlying());
|
||||
}
|
||||
|
||||
std::string type_get() const
|
||||
{
|
||||
const auto targetApiVersion = GetTargetAPIVersion();
|
||||
|
||||
auto entity = GetEntity();
|
||||
if (entity != nullptr)
|
||||
{
|
||||
switch (entity->Type)
|
||||
{
|
||||
case EntityType::vehicle:
|
||||
return "car";
|
||||
case EntityType::guest:
|
||||
if (targetApiVersion <= kApiVersionPeepDeprecation)
|
||||
return "peep";
|
||||
return "guest";
|
||||
case EntityType::staff:
|
||||
if (targetApiVersion <= kApiVersionPeepDeprecation)
|
||||
return "peep";
|
||||
return "staff";
|
||||
case EntityType::steamParticle:
|
||||
return "steam_particle";
|
||||
case EntityType::moneyEffect:
|
||||
return "money_effect";
|
||||
case EntityType::crashedVehicleParticle:
|
||||
return "crashed_vehicle_particle";
|
||||
case EntityType::explosionCloud:
|
||||
return "explosion_cloud";
|
||||
case EntityType::crashSplash:
|
||||
return "crash_splash";
|
||||
case EntityType::explosionFlare:
|
||||
return "explosion_flare";
|
||||
case EntityType::balloon:
|
||||
return "balloon";
|
||||
case EntityType::duck:
|
||||
return "duck";
|
||||
case EntityType::jumpingFountain:
|
||||
return "jumping_fountain";
|
||||
case EntityType::litter:
|
||||
return "litter";
|
||||
case EntityType::null:
|
||||
return "unknown";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
static JSValue id_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue type_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
// x getter and setter
|
||||
int32_t x_get() const
|
||||
{
|
||||
auto entity = GetEntity();
|
||||
return entity != nullptr ? entity->x : 0;
|
||||
}
|
||||
void x_set(int32_t value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto entity = GetEntity();
|
||||
if (entity != nullptr)
|
||||
{
|
||||
entity->MoveTo({ value, entity->y, entity->z });
|
||||
}
|
||||
}
|
||||
static JSValue x_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue x_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
// y getter and setter
|
||||
int32_t y_get() const
|
||||
{
|
||||
auto entity = GetEntity();
|
||||
return entity != nullptr ? entity->y : 0;
|
||||
}
|
||||
void y_set(int32_t value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto entity = GetEntity();
|
||||
if (entity != nullptr)
|
||||
{
|
||||
entity->MoveTo({ entity->x, value, entity->z });
|
||||
}
|
||||
}
|
||||
static JSValue y_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue y_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
// z getter and setter
|
||||
int16_t z_get() const
|
||||
{
|
||||
auto entity = GetEntity();
|
||||
return entity != nullptr ? entity->z : 0;
|
||||
}
|
||||
void z_set(int16_t value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto entity = GetEntity();
|
||||
if (entity != nullptr)
|
||||
{
|
||||
entity->MoveTo({ entity->x, entity->y, value });
|
||||
}
|
||||
}
|
||||
static JSValue z_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue z_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
void remove()
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto entity = GetEntity();
|
||||
if (entity != nullptr)
|
||||
{
|
||||
entity->Invalidate();
|
||||
switch (entity->Type)
|
||||
{
|
||||
case EntityType::vehicle:
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Removing a vehicle is currently unsupported.");
|
||||
break;
|
||||
case EntityType::guest:
|
||||
case EntityType::staff:
|
||||
{
|
||||
auto peep = entity->As<Peep>();
|
||||
// We can't remove a single peep from a ride at the moment as this can cause complications with the
|
||||
// vehicle car having an unsupported peep capacity.
|
||||
if (peep == nullptr || peep->State == PeepState::onRide || peep->State == PeepState::enteringRide)
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Removing a peep that is on a ride is currently unsupported.");
|
||||
}
|
||||
else
|
||||
{
|
||||
peep->Remove();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EntityType::steamParticle:
|
||||
case EntityType::moneyEffect:
|
||||
case EntityType::crashedVehicleParticle:
|
||||
case EntityType::explosionCloud:
|
||||
case EntityType::crashSplash:
|
||||
case EntityType::explosionFlare:
|
||||
case EntityType::jumpingFountain:
|
||||
case EntityType::balloon:
|
||||
case EntityType::duck:
|
||||
case EntityType::litter:
|
||||
getGameState().entities.EntityRemove(entity);
|
||||
break;
|
||||
case EntityType::null:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
static JSValue remove(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
|
||||
EntityBase* GetEntity() const
|
||||
{
|
||||
return getGameState().entities.GetEntity(_id);
|
||||
}
|
||||
protected:
|
||||
static EntityId GetEntityId(JSValue thisVal);
|
||||
static EntityBase* GetEntity(JSValue thisVal);
|
||||
|
||||
public:
|
||||
static void Register(duk_context* ctx)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScEntity::id_get, nullptr, "id");
|
||||
dukglue_register_property(ctx, &ScEntity::type_get, nullptr, "type");
|
||||
dukglue_register_property(ctx, &ScEntity::x_get, &ScEntity::x_set, "x");
|
||||
dukglue_register_property(ctx, &ScEntity::y_get, &ScEntity::y_set, "y");
|
||||
dukglue_register_property(ctx, &ScEntity::z_get, &ScEntity::z_set, "z");
|
||||
dukglue_register_method(ctx, &ScEntity::remove, "remove");
|
||||
}
|
||||
JSValue NewInstance(JSContext* ctx, EntityId entityId);
|
||||
static JSValue New(JSContext* ctx, EntityId entityId);
|
||||
|
||||
void Register(JSContext* ctx);
|
||||
|
||||
private:
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal);
|
||||
};
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,7 @@ namespace OpenRCT2
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
static const DukEnumMap<ShopItem> ShopItemMap(
|
||||
static const EnumMap<ShopItem> ShopItemMap(
|
||||
{
|
||||
{ "beef_noodles", ShopItem::beefNoodles },
|
||||
{ "burger", ShopItem::burger },
|
||||
@@ -79,7 +79,7 @@ namespace OpenRCT2::Scripting
|
||||
// guest cannot carry), 6 is subtracted from the value.
|
||||
static_assert((EnumValue(ShopItem::count) - 6) == 50, "ShopItem::count changed, update scripting binding!");
|
||||
|
||||
static const DukEnumMap<uint32_t> VoucherTypeMap(
|
||||
static const EnumMap<uint32_t> VoucherTypeMap(
|
||||
{
|
||||
{ "entry_free", VOUCHER_TYPE_PARK_ENTRY_FREE },
|
||||
{ "entry_half_price", VOUCHER_TYPE_PARK_ENTRY_HALF_PRICE },
|
||||
@@ -87,110 +87,112 @@ namespace OpenRCT2::Scripting
|
||||
{ "food_drink_free", VOUCHER_TYPE_FOOD_OR_DRINK_FREE },
|
||||
});
|
||||
|
||||
class ScThought
|
||||
class ScThought;
|
||||
extern ScThought gScThought;
|
||||
|
||||
class ScThought final : public ScBase
|
||||
{
|
||||
private:
|
||||
PeepThought _backing;
|
||||
|
||||
public:
|
||||
ScThought(PeepThought backing);
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
JSValue New(JSContext* ctx, PeepThought thought);
|
||||
void Register(JSContext* ctx);
|
||||
|
||||
private:
|
||||
std::string type_get() const;
|
||||
uint16_t item_get() const;
|
||||
uint8_t freshness_get() const;
|
||||
uint8_t freshTimeout_get() const;
|
||||
std::string toString() const;
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal);
|
||||
static PeepThought GetThought(JSValue thisVal);
|
||||
|
||||
static JSValue type_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue item_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue freshness_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue freshTimeout_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue toString(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
};
|
||||
|
||||
class ScGuest : public ScPeep
|
||||
class ScGuest final : public ScPeep
|
||||
{
|
||||
public:
|
||||
ScGuest(EntityId id);
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
static JSValue New(JSContext* ctx, EntityId entityId);
|
||||
|
||||
private:
|
||||
Guest* GetGuest() const;
|
||||
static void AddFuncs(JSContext* ctx, JSValue obj);
|
||||
static Guest* GetGuest(JSValue thisVal);
|
||||
|
||||
uint8_t tshirtColour_get() const;
|
||||
void tshirtColour_set(uint8_t value);
|
||||
static JSValue tshirtColour_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue tshirtColour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t trousersColour_get() const;
|
||||
void trousersColour_set(uint8_t value);
|
||||
static JSValue trousersColour_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue trousersColour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t balloonColour_get() const;
|
||||
void balloonColour_set(uint8_t value);
|
||||
static JSValue balloonColour_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue balloonColour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t hatColour_get() const;
|
||||
void hatColour_set(uint8_t value);
|
||||
static JSValue hatColour_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue hatColour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t umbrellaColour_get() const;
|
||||
void umbrellaColour_set(uint8_t value);
|
||||
static JSValue umbrellaColour_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue umbrellaColour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t happiness_get() const;
|
||||
void happiness_set(uint8_t value);
|
||||
static JSValue happiness_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue happiness_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t happinessTarget_get() const;
|
||||
void happinessTarget_set(uint8_t value);
|
||||
static JSValue happinessTarget_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue happinessTarget_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t nausea_get() const;
|
||||
void nausea_set(uint8_t value);
|
||||
static JSValue nausea_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue nausea_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t nauseaTarget_get() const;
|
||||
void nauseaTarget_set(uint8_t value);
|
||||
static JSValue nauseaTarget_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue nauseaTarget_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t hunger_get() const;
|
||||
void hunger_set(uint8_t value);
|
||||
static JSValue hunger_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue hunger_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t thirst_get() const;
|
||||
void thirst_set(uint8_t value);
|
||||
static JSValue thirst_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue thirst_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t toilet_get() const;
|
||||
void toilet_set(uint8_t value);
|
||||
static JSValue toilet_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue toilet_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t mass_get() const;
|
||||
void mass_set(uint8_t value);
|
||||
static JSValue mass_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue mass_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t minIntensity_get() const;
|
||||
void minIntensity_set(uint8_t value);
|
||||
static JSValue minIntensity_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue minIntensity_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t maxIntensity_get() const;
|
||||
void maxIntensity_set(uint8_t value);
|
||||
static JSValue maxIntensity_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue maxIntensity_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t nauseaTolerance_get() const;
|
||||
void nauseaTolerance_set(uint8_t value);
|
||||
static JSValue nauseaTolerance_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue nauseaTolerance_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
int32_t cash_get() const;
|
||||
void cash_set(int32_t value);
|
||||
static JSValue cash_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue cash_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
bool isInPark_get() const;
|
||||
static JSValue isInPark_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
bool isLost_get() const;
|
||||
static JSValue isLost_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
uint8_t lostCountdown_get() const;
|
||||
void lostCountdown_set(uint8_t value);
|
||||
static JSValue lostCountdown_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue lostCountdown_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
DukValue favouriteRide_get() const;
|
||||
void favouriteRide_set(const DukValue& value);
|
||||
static JSValue favouriteRide_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue favouriteRide_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
DukValue thoughts_get() const;
|
||||
static JSValue thoughts_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
DukValue items_get() const;
|
||||
bool has_item(const DukValue& item) const;
|
||||
void give_item(const DukValue& item) const;
|
||||
void remove_item(const DukValue& item) const;
|
||||
void remove_all_items() const;
|
||||
static JSValue items_get(JSContext* ctx, JSValue thisVal);
|
||||
static bool has_item(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
static JSValue has_item(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
static JSValue give_item(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
static JSValue remove_item(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
static JSValue remove_all_items(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
|
||||
std::vector<std::string> availableAnimations_get() const;
|
||||
std::vector<uint32_t> getAnimationSpriteIds(std::string groupKey, uint8_t rotation) const;
|
||||
std::string animation_get() const;
|
||||
void animation_set(std::string groupKey);
|
||||
uint8_t animationOffset_get() const;
|
||||
void animationOffset_set(uint8_t offset);
|
||||
uint8_t animationLength_get() const;
|
||||
static JSValue availableAnimations_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue getAnimationSpriteIds(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
static JSValue animation_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue animation_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
static JSValue animationOffset_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue animationOffset_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
static JSValue animationLength_get(JSContext* ctx, JSValue thisVal);
|
||||
};
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -11,11 +11,12 @@
|
||||
|
||||
#include "ScLitter.hpp"
|
||||
|
||||
#include "../../../core/EnumMap.hpp"
|
||||
#include "../../../entity/Litter.h"
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
static const DukEnumMap<Litter::Type> LitterTypeMap(
|
||||
static const EnumMap<Litter::Type> LitterTypeMap(
|
||||
{
|
||||
{ "vomit", Litter::Type::vomit },
|
||||
{ "vomit_alt", Litter::Type::vomitAlt },
|
||||
@@ -31,57 +32,62 @@ namespace OpenRCT2::Scripting
|
||||
{ "empty_bowl_blue", Litter::Type::emptyBowlBlue },
|
||||
});
|
||||
|
||||
ScLitter::ScLitter(EntityId Id)
|
||||
: ScEntity(Id)
|
||||
JSValue ScLitter::New(JSContext* ctx, EntityId entityId)
|
||||
{
|
||||
JSValue obj = gScEntity.New(ctx, entityId);
|
||||
AddFuncs(ctx, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void ScLitter::Register(duk_context* ctx)
|
||||
void ScLitter::AddFuncs(JSContext* ctx, JSValue obj)
|
||||
{
|
||||
dukglue_set_base_class<ScEntity, ScLitter>(ctx);
|
||||
dukglue_register_property(ctx, &ScLitter::litterType_get, &ScLitter::litterType_set, "litterType");
|
||||
dukglue_register_property(ctx, &ScLitter::creationTick_get, nullptr, "creationTick");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("litterType", &ScLitter::litterType_get, &ScLitter::litterType_set),
|
||||
JS_CGETSET_DEF("creationTick", &ScLitter::creationTick_get, nullptr)
|
||||
};
|
||||
JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs));
|
||||
}
|
||||
|
||||
Litter* ScLitter::GetLitter() const
|
||||
Litter* ScLitter::GetLitter(JSValue thisVal)
|
||||
{
|
||||
return getGameState().entities.GetEntity<Litter>(_id);
|
||||
auto id = GetEntityId(thisVal);
|
||||
return getGameState().entities.GetEntity<Litter>(id);
|
||||
}
|
||||
|
||||
std::string ScLitter::litterType_get() const
|
||||
JSValue ScLitter::litterType_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto* litter = GetLitter();
|
||||
auto* litter = GetLitter(thisVal);
|
||||
if (litter != nullptr)
|
||||
{
|
||||
auto it = LitterTypeMap.find(litter->SubType);
|
||||
if (it != LitterTypeMap.end())
|
||||
{
|
||||
return std::string{ it->first };
|
||||
return JSFromStdString(ctx, it->first);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void ScLitter::litterType_set(const std::string& litterType)
|
||||
JSValue ScLitter::litterType_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
JS_UNPACK_STR(litterType, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
|
||||
auto it = LitterTypeMap.find(litterType);
|
||||
if (it == LitterTypeMap.end())
|
||||
return;
|
||||
auto* litter = GetLitter();
|
||||
litter->SubType = it->second;
|
||||
litter->Invalidate();
|
||||
if (it != LitterTypeMap.end())
|
||||
{
|
||||
auto* litter = GetLitter(thisVal);
|
||||
litter->SubType = it->second;
|
||||
litter->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint32_t ScLitter::creationTick_get() const
|
||||
JSValue ScLitter::creationTick_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto* litter = GetLitter();
|
||||
if (litter == nullptr)
|
||||
return 0;
|
||||
return litter->creationTick;
|
||||
auto* litter = GetLitter(thisVal);
|
||||
return JS_NewUint32(ctx, litter == nullptr ? 0 : litter->creationTick);
|
||||
}
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,22 +20,20 @@ namespace OpenRCT2
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScLitter : public ScEntity
|
||||
class ScLitter final : public ScEntity
|
||||
{
|
||||
public:
|
||||
ScLitter(EntityId Id);
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
static JSValue New(JSContext* ctx, EntityId entityId);
|
||||
|
||||
private:
|
||||
Litter* GetLitter() const;
|
||||
static void AddFuncs(JSContext* ctx, JSValue obj);
|
||||
static Litter* GetLitter(JSValue thisVal);
|
||||
|
||||
std::string litterType_get() const;
|
||||
void litterType_set(const std::string& litterType);
|
||||
static JSValue litterType_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue litterType_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
uint32_t creationTick_get() const;
|
||||
static JSValue creationTick_get(JSContext* ctx, JSValue thisVal);
|
||||
};
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
#endif
|
||||
|
||||
@@ -16,41 +16,43 @@
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
ScMoneyEffect::ScMoneyEffect(EntityId Id)
|
||||
: ScEntity(Id)
|
||||
JSValue ScMoneyEffect::New(JSContext* ctx, EntityId entityId)
|
||||
{
|
||||
JSValue obj = gScEntity.New(ctx, entityId);
|
||||
AddFuncs(ctx, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void ScMoneyEffect::Register(duk_context* ctx)
|
||||
void ScMoneyEffect::AddFuncs(JSContext* ctx, JSValue obj)
|
||||
{
|
||||
dukglue_set_base_class<ScEntity, ScMoneyEffect>(ctx);
|
||||
dukglue_register_property(ctx, &ScMoneyEffect::value_get, &ScMoneyEffect::value_set, "value");
|
||||
static constexpr JSCFunctionListEntry funcs[] = { JS_CGETSET_DEF(
|
||||
"value", &ScMoneyEffect::value_get, &ScMoneyEffect::value_set) };
|
||||
JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs));
|
||||
}
|
||||
|
||||
MoneyEffect* ScMoneyEffect::GetMoneyEffect() const
|
||||
MoneyEffect* ScMoneyEffect::GetMoneyEffect(JSValue thisVal)
|
||||
{
|
||||
return getGameState().entities.GetEntity<MoneyEffect>(_id);
|
||||
auto id = GetEntityId(thisVal);
|
||||
return getGameState().entities.GetEntity<MoneyEffect>(id);
|
||||
}
|
||||
|
||||
money64 ScMoneyEffect::value_get() const
|
||||
JSValue ScMoneyEffect::value_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto moneyEffect = GetMoneyEffect();
|
||||
if (moneyEffect != nullptr)
|
||||
{
|
||||
return moneyEffect->Value;
|
||||
}
|
||||
return 0;
|
||||
auto moneyEffect = GetMoneyEffect(thisVal);
|
||||
return JS_NewUint32(ctx, moneyEffect == nullptr ? 0 : moneyEffect->Value);
|
||||
}
|
||||
|
||||
void ScMoneyEffect::value_set(money64 value)
|
||||
JSValue ScMoneyEffect::value_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
auto moneyEffect = GetMoneyEffect();
|
||||
JS_UNPACK_MONEY64(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto moneyEffect = GetMoneyEffect(thisVal);
|
||||
if (moneyEffect != nullptr)
|
||||
{
|
||||
moneyEffect->SetValue(value);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,20 +20,17 @@ namespace OpenRCT2
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
|
||||
class ScMoneyEffect : public ScEntity
|
||||
class ScMoneyEffect final : public ScEntity
|
||||
{
|
||||
public:
|
||||
ScMoneyEffect(EntityId Id);
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
static JSValue New(JSContext* ctx, EntityId entityId);
|
||||
|
||||
private:
|
||||
MoneyEffect* GetMoneyEffect() const;
|
||||
static void AddFuncs(JSContext* ctx, JSValue obj);
|
||||
static MoneyEffect* GetMoneyEffect(JSValue thisVal);
|
||||
|
||||
money64 value_get() const;
|
||||
void value_set(money64);
|
||||
static JSValue value_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue value_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
};
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
#endif
|
||||
|
||||
@@ -9,13 +9,14 @@
|
||||
|
||||
#include "ScParticle.hpp"
|
||||
|
||||
#include "../../../core/EnumMap.hpp"
|
||||
#include "../ride/ScRide.hpp"
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
static const DukEnumMap<uint8_t> CrashParticleTypeMap(
|
||||
static const EnumMap<uint8_t> CrashParticleTypeMap(
|
||||
{
|
||||
{ "corner", 0 },
|
||||
{ "rod", 1 },
|
||||
@@ -24,206 +25,232 @@ namespace OpenRCT2::Scripting
|
||||
{ "seat", 4 },
|
||||
});
|
||||
|
||||
ScCrashedVehicleParticle::ScCrashedVehicleParticle(EntityId id)
|
||||
: ScEntity(id)
|
||||
JSValue ScCrashedVehicleParticle::New(JSContext* ctx, EntityId entityId)
|
||||
{
|
||||
JSValue obj = gScEntity.New(ctx, entityId);
|
||||
AddFuncs(ctx, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void ScCrashedVehicleParticle::Register(duk_context* ctx)
|
||||
void ScCrashedVehicleParticle::AddFuncs(JSContext* ctx, JSValue obj)
|
||||
{
|
||||
dukglue_set_base_class<ScEntity, ScCrashedVehicleParticle>(ctx);
|
||||
dukglue_register_property(
|
||||
ctx, &ScCrashedVehicleParticle::acceleration_get, &ScCrashedVehicleParticle::acceleration_set, "acceleration");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCrashedVehicleParticle::velocity_get, &ScCrashedVehicleParticle::velocity_set, "velocity");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCrashedVehicleParticle::colours_get, &ScCrashedVehicleParticle::colours_set, "colours");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCrashedVehicleParticle::timeToLive_get, &ScCrashedVehicleParticle::timeToLive_set, "timeToLive");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCrashedVehicleParticle::crashedSpriteBase_get, &ScCrashedVehicleParticle::crashedSpriteBase_set,
|
||||
"crashParticleType");
|
||||
dukglue_register_property(ctx, &ScCrashedVehicleParticle::frame_get, &ScCrashedVehicleParticle::frame_set, "frame");
|
||||
dukglue_register_method(ctx, &ScCrashedVehicleParticle::Launch, "launch");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF(
|
||||
"acceleration", &ScCrashedVehicleParticle::acceleration_get, &ScCrashedVehicleParticle::acceleration_set),
|
||||
JS_CGETSET_DEF("velocity", &ScCrashedVehicleParticle::velocity_get, &ScCrashedVehicleParticle::velocity_set),
|
||||
JS_CGETSET_DEF("colours", &ScCrashedVehicleParticle::colours_get, &ScCrashedVehicleParticle::colours_set),
|
||||
JS_CGETSET_DEF("timeToLive", &ScCrashedVehicleParticle::timeToLive_get, &ScCrashedVehicleParticle::timeToLive_set),
|
||||
JS_CGETSET_DEF(
|
||||
"crashParticleType", &ScCrashedVehicleParticle::crashedSpriteBase_get,
|
||||
&ScCrashedVehicleParticle::crashedSpriteBase_set),
|
||||
JS_CGETSET_DEF("frame", &ScCrashedVehicleParticle::frame_get, &ScCrashedVehicleParticle::frame_set),
|
||||
JS_CFUNC_DEF("launch", 1, &ScCrashedVehicleParticle::Launch),
|
||||
};
|
||||
JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs));
|
||||
}
|
||||
|
||||
VehicleCrashParticle* ScCrashedVehicleParticle::GetCrashedVehicleParticle() const
|
||||
VehicleCrashParticle* ScCrashedVehicleParticle::GetCrashedVehicleParticle(JSValue thisVal)
|
||||
{
|
||||
return getGameState().entities.GetEntity<VehicleCrashParticle>(_id);
|
||||
auto id = GetEntityId(thisVal);
|
||||
return getGameState().entities.GetEntity<VehicleCrashParticle>(id);
|
||||
}
|
||||
|
||||
void ScCrashedVehicleParticle::frame_set(uint8_t value)
|
||||
JSValue ScCrashedVehicleParticle::frame_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
auto entity = GetCrashedVehicleParticle();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto entity = GetCrashedVehicleParticle(thisVal);
|
||||
if (entity != nullptr)
|
||||
{
|
||||
entity->frame = std::clamp<uint16_t>(value, 0, kCrashedVehicleParticleNumberSprites - 1)
|
||||
* kCrashedVehicleParticleFrameToSprite;
|
||||
entity->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
uint8_t ScCrashedVehicleParticle::frame_get() const
|
||||
JSValue ScCrashedVehicleParticle::frame_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto entity = GetCrashedVehicleParticle();
|
||||
if (entity != nullptr)
|
||||
{
|
||||
return entity->frame / kCrashedVehicleParticleFrameToSprite;
|
||||
}
|
||||
return 0;
|
||||
auto entity = GetCrashedVehicleParticle(thisVal);
|
||||
auto frame = (entity != nullptr) ? entity->frame / kCrashedVehicleParticleFrameToSprite : 0;
|
||||
return JS_NewUint32(ctx, frame);
|
||||
}
|
||||
|
||||
void ScCrashedVehicleParticle::crashedSpriteBase_set(const std::string& value)
|
||||
JSValue ScCrashedVehicleParticle::crashedSpriteBase_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
auto entity = GetCrashedVehicleParticle();
|
||||
JS_UNPACK_STR(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto entity = GetCrashedVehicleParticle(thisVal);
|
||||
if (entity != nullptr)
|
||||
{
|
||||
entity->crashed_sprite_base = CrashParticleTypeMap[value];
|
||||
entity->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
std::string ScCrashedVehicleParticle::crashedSpriteBase_get() const
|
||||
JSValue ScCrashedVehicleParticle::crashedSpriteBase_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto entity = GetCrashedVehicleParticle();
|
||||
auto entity = GetCrashedVehicleParticle(thisVal);
|
||||
if (entity != nullptr)
|
||||
{
|
||||
return std::string(CrashParticleTypeMap[entity->crashed_sprite_base]);
|
||||
return JSFromStdString(ctx, CrashParticleTypeMap[entity->crashed_sprite_base]);
|
||||
}
|
||||
return {};
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void ScCrashedVehicleParticle::timeToLive_set(uint16_t value)
|
||||
JSValue ScCrashedVehicleParticle::timeToLive_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
auto entity = GetCrashedVehicleParticle();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto entity = GetCrashedVehicleParticle(thisVal);
|
||||
if (entity != nullptr)
|
||||
{
|
||||
entity->time_to_live = value;
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
uint16_t ScCrashedVehicleParticle::timeToLive_get() const
|
||||
JSValue ScCrashedVehicleParticle::timeToLive_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto entity = GetCrashedVehicleParticle();
|
||||
if (entity != nullptr)
|
||||
{
|
||||
return entity->time_to_live;
|
||||
}
|
||||
return 0;
|
||||
auto entity = GetCrashedVehicleParticle(thisVal);
|
||||
return JS_NewUint32(ctx, entity == nullptr ? 0 : entity->time_to_live);
|
||||
}
|
||||
|
||||
void ScCrashedVehicleParticle::velocity_set(const DukValue& value)
|
||||
JSValue ScCrashedVehicleParticle::velocity_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
auto entity = GetCrashedVehicleParticle();
|
||||
JS_UNPACK_OBJECT(obj, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto entity = GetCrashedVehicleParticle(thisVal);
|
||||
if (entity != nullptr)
|
||||
{
|
||||
auto velocity = FromDuk<CoordsXYZ>(value);
|
||||
auto velocity = JSToCoordsXYZ(ctx, obj);
|
||||
entity->velocity_x = velocity.x;
|
||||
entity->velocity_y = velocity.y;
|
||||
entity->velocity_z = velocity.z;
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
DukValue ScCrashedVehicleParticle::velocity_get() const
|
||||
JSValue ScCrashedVehicleParticle::velocity_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto entity = GetCrashedVehicleParticle();
|
||||
auto entity = GetCrashedVehicleParticle(thisVal);
|
||||
if (entity != nullptr)
|
||||
{
|
||||
return ToDuk(ctx, CoordsXYZ(entity->velocity_x, entity->velocity_y, entity->velocity_z));
|
||||
return ToJSValue(ctx, CoordsXYZ(entity->velocity_x, entity->velocity_y, entity->velocity_z));
|
||||
}
|
||||
return {};
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void ScCrashedVehicleParticle::acceleration_set(const DukValue& value)
|
||||
JSValue ScCrashedVehicleParticle::acceleration_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
auto entity = GetCrashedVehicleParticle();
|
||||
JS_UNPACK_OBJECT(obj, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto entity = GetCrashedVehicleParticle(thisVal);
|
||||
if (entity != nullptr)
|
||||
{
|
||||
auto acceleration = FromDuk<CoordsXYZ>(value);
|
||||
auto acceleration = JSToCoordsXYZ(ctx, obj);
|
||||
entity->acceleration_x = acceleration.x;
|
||||
entity->acceleration_y = acceleration.y;
|
||||
entity->acceleration_z = acceleration.z;
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
DukValue ScCrashedVehicleParticle::acceleration_get() const
|
||||
JSValue ScCrashedVehicleParticle::acceleration_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto entity = GetCrashedVehicleParticle();
|
||||
auto entity = GetCrashedVehicleParticle(thisVal);
|
||||
if (entity != nullptr)
|
||||
{
|
||||
return ToDuk(ctx, CoordsXYZ(entity->acceleration_x, entity->acceleration_y, entity->acceleration_z));
|
||||
return ToJSValue(ctx, CoordsXYZ(entity->acceleration_x, entity->acceleration_y, entity->acceleration_z));
|
||||
}
|
||||
return {};
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void ScCrashedVehicleParticle::Launch(const DukValue& value)
|
||||
JSValue ScCrashedVehicleParticle::Launch(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto entity = GetCrashedVehicleParticle();
|
||||
JS_UNPACK_OBJECT(obj, ctx, argv[0]);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto entity = GetCrashedVehicleParticle(thisVal);
|
||||
if (entity != nullptr)
|
||||
{
|
||||
entity->SetSpriteData();
|
||||
entity->Launch();
|
||||
|
||||
if (value.type() == DukValue::Type::UNDEFINED)
|
||||
return;
|
||||
if (JS_IsUndefined(obj))
|
||||
return JS_UNDEFINED;
|
||||
|
||||
if (value["colours"].type() == DukValue::Type::OBJECT)
|
||||
auto colours = JS_GetPropertyStr(ctx, obj, "colours");
|
||||
auto acceleration = JS_GetPropertyStr(ctx, obj, "acceleration");
|
||||
auto velocity = JS_GetPropertyStr(ctx, obj, "velocity");
|
||||
auto timeToLive = JS_GetPropertyStr(ctx, obj, "timeToLive");
|
||||
auto frame = JS_GetPropertyStr(ctx, obj, "frame");
|
||||
auto crashParticleType = JS_GetPropertyStr(ctx, obj, "crashParticleType");
|
||||
|
||||
if (JS_IsObject(colours))
|
||||
{
|
||||
auto coloursInt = FromDuk<VehicleColour>(value["colours"]);
|
||||
entity->colour[0] = coloursInt.Body;
|
||||
entity->colour[1] = coloursInt.Trim;
|
||||
entity->colour[0] = static_cast<Drawing::Colour>(JSToUint(ctx, colours, "body"));
|
||||
entity->colour[1] = static_cast<Drawing::Colour>(JSToUint(ctx, colours, "trim"));
|
||||
}
|
||||
if (value["acceleration"].type() == DukValue::Type::OBJECT)
|
||||
if (JS_IsObject(acceleration))
|
||||
{
|
||||
auto accelerationXYZ = FromDuk<CoordsXYZ>(value["acceleration"]);
|
||||
auto accelerationXYZ = JSToCoordsXYZ(ctx, acceleration);
|
||||
entity->acceleration_x = accelerationXYZ.x;
|
||||
entity->acceleration_y = accelerationXYZ.y;
|
||||
entity->acceleration_z = accelerationXYZ.z;
|
||||
}
|
||||
if (value["velocity"].type() == DukValue::Type::OBJECT)
|
||||
if (JS_IsObject(velocity))
|
||||
{
|
||||
auto velocityXYZ = FromDuk<CoordsXYZ>(value["velocity"]);
|
||||
auto velocityXYZ = JSToCoordsXYZ(ctx, velocity);
|
||||
entity->velocity_x = velocityXYZ.x;
|
||||
entity->velocity_y = velocityXYZ.y;
|
||||
entity->velocity_z = velocityXYZ.z;
|
||||
}
|
||||
if (value["timeToLive"].type() == DukValue::Type::NUMBER)
|
||||
if (JS_IsNumber(timeToLive))
|
||||
{
|
||||
entity->time_to_live = value["timeToLive"].as_uint();
|
||||
entity->time_to_live = JSToUint(ctx, timeToLive);
|
||||
}
|
||||
if (value["frame"].type() == DukValue::Type::NUMBER)
|
||||
if (JS_IsNumber(frame))
|
||||
{
|
||||
entity->frame = std::clamp<uint16_t>(value["frame"].as_uint(), 0, kCrashedVehicleParticleNumberSprites - 1)
|
||||
entity->frame = std::clamp<uint16_t>(JSToUint(ctx, frame), 0, kCrashedVehicleParticleNumberSprites - 1)
|
||||
* kCrashedVehicleParticleFrameToSprite;
|
||||
}
|
||||
if (value["crashParticleType"].type() == DukValue::Type::STRING)
|
||||
if (JS_IsString(crashParticleType))
|
||||
{
|
||||
entity->crashed_sprite_base = CrashParticleTypeMap[value["crashParticleType"].as_string()];
|
||||
auto key = JSToStdString(ctx, crashParticleType);
|
||||
entity->crashed_sprite_base = CrashParticleTypeMap[key];
|
||||
}
|
||||
entity->Invalidate();
|
||||
|
||||
JS_FreeValue(ctx, colours);
|
||||
JS_FreeValue(ctx, acceleration);
|
||||
JS_FreeValue(ctx, velocity);
|
||||
JS_FreeValue(ctx, timeToLive);
|
||||
JS_FreeValue(ctx, frame);
|
||||
JS_FreeValue(ctx, crashParticleType);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
DukValue ScCrashedVehicleParticle::colours_get() const
|
||||
JSValue ScCrashedVehicleParticle::colours_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto entity = GetCrashedVehicleParticle();
|
||||
auto entity = GetCrashedVehicleParticle(thisVal);
|
||||
if (entity != nullptr)
|
||||
{
|
||||
DukObject dukColour(ctx);
|
||||
dukColour.Set("body", EnumValue(entity->colour[0]));
|
||||
dukColour.Set("trim", EnumValue(entity->colour[1]));
|
||||
return dukColour.Take();
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "body", JS_NewInt32(ctx, EnumValue(entity->colour[0])));
|
||||
JS_SetPropertyStr(ctx, obj, "trim", JS_NewInt32(ctx, EnumValue(entity->colour[1])));
|
||||
return obj;
|
||||
}
|
||||
return ToDuk(ctx, nullptr);
|
||||
return JS_NULL;
|
||||
}
|
||||
void ScCrashedVehicleParticle::colours_set(const DukValue& value)
|
||||
JSValue ScCrashedVehicleParticle::colours_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
auto entity = GetCrashedVehicleParticle();
|
||||
JS_UNPACK_OBJECT(obj, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto entity = GetCrashedVehicleParticle(thisVal);
|
||||
if (entity != nullptr)
|
||||
{
|
||||
auto colours = FromDuk<VehicleColour>(value);
|
||||
entity->colour[0] = colours.Body;
|
||||
entity->colour[1] = colours.Trim;
|
||||
entity->colour[0] = static_cast<Drawing::Colour>(JSToUint(ctx, obj, "body"));
|
||||
entity->colour[1] = static_cast<Drawing::Colour>(JSToUint(ctx, obj, "trim"));
|
||||
entity->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
|
||||
@@ -19,35 +19,34 @@
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScCrashedVehicleParticle : public ScEntity
|
||||
class ScCrashedVehicleParticle final : public ScEntity
|
||||
{
|
||||
public:
|
||||
ScCrashedVehicleParticle(EntityId id);
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
static JSValue New(JSContext* ctx, EntityId entityId);
|
||||
|
||||
private:
|
||||
VehicleCrashParticle* GetCrashedVehicleParticle() const;
|
||||
static void AddFuncs(JSContext* ctx, JSValue obj);
|
||||
static VehicleCrashParticle* GetCrashedVehicleParticle(JSValue thisVal);
|
||||
|
||||
DukValue colours_get() const;
|
||||
void colours_set(const DukValue& value);
|
||||
static JSValue colours_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue colours_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
DukValue acceleration_get() const;
|
||||
void acceleration_set(const DukValue& value);
|
||||
static JSValue acceleration_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue acceleration_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
DukValue velocity_get() const;
|
||||
void velocity_set(const DukValue& value);
|
||||
static JSValue velocity_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue velocity_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
uint8_t frame_get() const;
|
||||
void frame_set(uint8_t value);
|
||||
static JSValue frame_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue frame_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
void crashedSpriteBase_set(const std::string& value);
|
||||
std::string crashedSpriteBase_get() const;
|
||||
static JSValue crashedSpriteBase_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue crashedSpriteBase_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
void timeToLive_set(uint16_t value);
|
||||
uint16_t timeToLive_get() const;
|
||||
static JSValue timeToLive_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue timeToLive_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
void Launch(const DukValue& value);
|
||||
static JSValue Launch(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
|
||||
@@ -11,11 +11,12 @@
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "../../../core/EnumMap.hpp"
|
||||
#include "ScEntity.hpp"
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
static const DukEnumMap<uint32_t> PeepFlagMap(
|
||||
static const EnumMap<uint32_t> PeepFlagMap(
|
||||
{
|
||||
{ "leavingPark", PEEP_FLAGS_LEAVING_PARK },
|
||||
{ "slowWalk", PEEP_FLAGS_SLOW_WALK },
|
||||
@@ -49,65 +50,74 @@ namespace OpenRCT2::Scripting
|
||||
class ScPeep : public ScEntity
|
||||
{
|
||||
public:
|
||||
ScPeep(EntityId id)
|
||||
: ScEntity(id)
|
||||
static JSValue New(JSContext* ctx, EntityId entityId)
|
||||
{
|
||||
}
|
||||
|
||||
static void Register(duk_context* ctx)
|
||||
{
|
||||
dukglue_set_base_class<ScEntity, ScPeep>(ctx);
|
||||
dukglue_register_property(ctx, &ScPeep::peepType_get, nullptr, "peepType");
|
||||
dukglue_register_property(ctx, &ScPeep::name_get, &ScPeep::name_set, "name");
|
||||
dukglue_register_property(ctx, &ScPeep::destination_get, &ScPeep::destination_set, "destination");
|
||||
dukglue_register_property(ctx, &ScPeep::direction_get, &ScPeep::direction_set, "direction");
|
||||
dukglue_register_property(ctx, &ScPeep::energy_get, &ScPeep::energy_set, "energy");
|
||||
dukglue_register_property(ctx, &ScPeep::energyTarget_get, &ScPeep::energyTarget_set, "energyTarget");
|
||||
dukglue_register_method(ctx, &ScPeep::getFlag, "getFlag");
|
||||
dukglue_register_method(ctx, &ScPeep::setFlag, "setFlag");
|
||||
JSValue obj = gScEntity.New(ctx, entityId);
|
||||
AddFuncs(ctx, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string peepType_get() const
|
||||
static void AddFuncs(JSContext* ctx, JSValue obj)
|
||||
{
|
||||
auto peep = GetPeep();
|
||||
if (peep != nullptr)
|
||||
{
|
||||
return peep->Is<Staff>() ? "staff" : "guest";
|
||||
}
|
||||
return {};
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("peepType", &ScPeep::peepType_get, nullptr),
|
||||
JS_CGETSET_DEF("name", &ScPeep::name_get, &ScPeep::name_set),
|
||||
JS_CGETSET_DEF("destination", &ScPeep::destination_get, &ScPeep::destination_set),
|
||||
JS_CGETSET_DEF("direction", &ScPeep::direction_get, &ScPeep::direction_set),
|
||||
JS_CGETSET_DEF("energy", &ScPeep::energy_get, &ScPeep::energy_set),
|
||||
JS_CGETSET_DEF("energyTarget", &ScPeep::energyTarget_get, &ScPeep::energyTarget_set),
|
||||
JS_CFUNC_DEF("getFlag", 1, &ScPeep::getFlag),
|
||||
JS_CFUNC_DEF("setFlag", 2, &ScPeep::setFlag),
|
||||
};
|
||||
JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs));
|
||||
}
|
||||
|
||||
std::string name_get() const
|
||||
static JSValue peepType_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto peep = GetPeep();
|
||||
return peep != nullptr ? peep->GetName() : std::string();
|
||||
auto peep = GetPeep(thisVal);
|
||||
if (peep != nullptr)
|
||||
{
|
||||
return JSFromStdString(ctx, peep->Is<Staff>() ? "staff" : "guest");
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
void name_set(const std::string& value)
|
||||
|
||||
static JSValue name_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto peep = GetPeep();
|
||||
auto peep = GetPeep(thisVal);
|
||||
return JSFromStdString(ctx, peep != nullptr ? peep->GetName() : std::string());
|
||||
}
|
||||
static JSValue name_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
JS_UNPACK_STR(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto peep = GetPeep(thisVal);
|
||||
if (peep != nullptr)
|
||||
{
|
||||
peep->SetName(value);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool getFlag(const std::string& key) const
|
||||
static JSValue getFlag(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto peep = GetPeep();
|
||||
JS_UNPACK_STR(key, ctx, argv[0]);
|
||||
auto peep = GetPeep(thisVal);
|
||||
if (peep != nullptr)
|
||||
{
|
||||
auto mask = PeepFlagMap[key];
|
||||
return (peep->PeepFlags & mask) != 0;
|
||||
return JS_NewBool(ctx, (peep->PeepFlags & mask) != 0);
|
||||
}
|
||||
return false;
|
||||
return JS_NewBool(ctx, false);
|
||||
}
|
||||
|
||||
void setFlag(const std::string& key, bool value)
|
||||
static JSValue setFlag(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto peep = GetPeep();
|
||||
JS_UNPACK_STR(key, ctx, argv[0]);
|
||||
JS_UNPACK_BOOL(value, ctx, argv[1]);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto peep = GetPeep(thisVal);
|
||||
if (peep != nullptr)
|
||||
{
|
||||
auto mask = PeepFlagMap[key];
|
||||
@@ -117,86 +127,95 @@ namespace OpenRCT2::Scripting
|
||||
peep->PeepFlags &= ~mask;
|
||||
peep->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
DukValue destination_get() const
|
||||
static JSValue destination_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto peep = GetPeep();
|
||||
auto peep = GetPeep(thisVal);
|
||||
if (peep != nullptr)
|
||||
{
|
||||
return ToDuk(ctx, peep->GetDestination());
|
||||
return ToJSValue(ctx, peep->GetDestination());
|
||||
}
|
||||
return ToDuk(ctx, nullptr);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
void destination_set(const DukValue& value)
|
||||
static JSValue destination_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto peep = GetPeep();
|
||||
JS_UNPACK_OBJECT(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto peep = GetPeep(thisVal);
|
||||
if (peep != nullptr)
|
||||
{
|
||||
auto pos = FromDuk<CoordsXY>(value);
|
||||
auto pos = JSToCoordsXY(ctx, value);
|
||||
peep->SetDestination(pos);
|
||||
peep->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint8_t direction_get() const
|
||||
static JSValue direction_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto peep = GetPeep();
|
||||
return peep != nullptr ? peep->PeepDirection : 0;
|
||||
auto peep = GetPeep(thisVal);
|
||||
return JS_NewUint32(ctx, peep != nullptr ? peep->PeepDirection : 0);
|
||||
}
|
||||
|
||||
void direction_set(const uint8_t value)
|
||||
static JSValue direction_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto peep = GetPeep();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto peep = GetPeep(thisVal);
|
||||
if (peep != nullptr && value < kNumOrthogonalDirections)
|
||||
{
|
||||
peep->PeepDirection = value;
|
||||
peep->Orientation = value << 3;
|
||||
peep->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint8_t energy_get() const
|
||||
static JSValue energy_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto peep = GetPeep();
|
||||
return peep != nullptr ? peep->Energy : 0;
|
||||
auto peep = GetPeep(thisVal);
|
||||
return JS_NewUint32(ctx, peep != nullptr ? peep->Energy : 0);
|
||||
}
|
||||
void energy_set(uint8_t value)
|
||||
static JSValue energy_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto peep = GetPeep();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto peep = GetPeep(thisVal);
|
||||
if (peep != nullptr)
|
||||
{
|
||||
value = std::clamp(value, kPeepMinEnergy, kPeepMaxEnergy);
|
||||
value = std::clamp(static_cast<uint8_t>(value), kPeepMinEnergy, kPeepMaxEnergy);
|
||||
peep->Energy = value;
|
||||
peep->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint8_t energyTarget_get() const
|
||||
static JSValue energyTarget_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto peep = GetPeep();
|
||||
return peep != nullptr ? peep->EnergyTarget : 0;
|
||||
auto peep = GetPeep(thisVal);
|
||||
return JS_NewUint32(ctx, peep != nullptr ? peep->EnergyTarget : 0);
|
||||
}
|
||||
void energyTarget_set(uint8_t value)
|
||||
static JSValue energyTarget_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto peep = GetPeep();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto peep = GetPeep(thisVal);
|
||||
if (peep != nullptr)
|
||||
{
|
||||
value = std::clamp(value, kPeepMinEnergy, kPeepMaxEnergyTarget);
|
||||
peep->EnergyTarget = value;
|
||||
auto target = std::clamp(static_cast<uint8_t>(value), kPeepMinEnergy, kPeepMaxEnergyTarget);
|
||||
peep->EnergyTarget = target;
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
protected:
|
||||
Peep* GetPeep() const
|
||||
static Peep* GetPeep(JSValue thisVal)
|
||||
{
|
||||
return getGameState().entities.GetEntity<Peep>(_id);
|
||||
auto id = GetEntityId(thisVal);
|
||||
return getGameState().entities.GetEntity<Peep>(id);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -21,59 +21,65 @@
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
ScStaff::ScStaff(EntityId Id)
|
||||
: ScPeep(Id)
|
||||
JSValue ScStaff::New(JSContext* ctx, EntityId entityId)
|
||||
{
|
||||
JSValue obj = ScPeep::New(ctx, entityId);
|
||||
AddFuncs(ctx, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void ScStaff::Register(duk_context* ctx)
|
||||
void ScStaff::AddFuncs(JSContext* ctx, JSValue obj)
|
||||
{
|
||||
dukglue_set_base_class<ScPeep, ScStaff>(ctx);
|
||||
dukglue_register_property(ctx, &ScStaff::staffType_get, &ScStaff::staffType_set, "staffType");
|
||||
dukglue_register_property(ctx, &ScStaff::colour_get, &ScStaff::colour_set, "colour");
|
||||
dukglue_register_property(ctx, &ScStaff::availableCostumes_get, nullptr, "availableCostumes");
|
||||
dukglue_register_property(ctx, &ScStaff::costume_get, &ScStaff::costume_set, "costume");
|
||||
dukglue_register_property(ctx, &ScStaff::patrolArea_get, nullptr, "patrolArea");
|
||||
dukglue_register_property(ctx, &ScStaff::orders_get, &ScStaff::orders_set, "orders");
|
||||
dukglue_register_property(ctx, &ScStaff::availableAnimations_get, nullptr, "availableAnimations");
|
||||
dukglue_register_property(ctx, &ScStaff::animation_get, &ScStaff::animation_set, "animation");
|
||||
dukglue_register_property(ctx, &ScStaff::animationOffset_get, &ScStaff::animationOffset_set, "animationOffset");
|
||||
dukglue_register_property(ctx, &ScStaff::animationLength_get, nullptr, "animationLength");
|
||||
dukglue_register_method(ctx, &ScStaff::getAnimationSpriteIds, "getAnimationSpriteIds");
|
||||
dukglue_register_method(ctx, &ScStaff::getCostumeStrings, "getCostumeStrings");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("staffType", &ScStaff::staffType_get, &ScStaff::staffType_set),
|
||||
JS_CGETSET_DEF("colour", &ScStaff::colour_get, &ScStaff::colour_set),
|
||||
JS_CGETSET_DEF("availableCostumes", &ScStaff::availableCostumes_get, nullptr),
|
||||
JS_CGETSET_DEF("costume", &ScStaff::costume_get, &ScStaff::costume_set),
|
||||
JS_CGETSET_DEF("patrolArea", &ScStaff::patrolArea_get, nullptr),
|
||||
JS_CGETSET_DEF("orders", &ScStaff::orders_get, &ScStaff::orders_set),
|
||||
JS_CGETSET_DEF("availableAnimations", &ScStaff::availableAnimations_get, nullptr),
|
||||
JS_CGETSET_DEF("animation", &ScStaff::animation_get, &ScStaff::animation_set),
|
||||
JS_CGETSET_DEF("animationOffset", &ScStaff::animationOffset_get, &ScStaff::animationOffset_set),
|
||||
JS_CGETSET_DEF("animationLength", &ScStaff::animationLength_get, nullptr),
|
||||
JS_CFUNC_DEF("getAnimationSpriteIds", 2, &ScStaff::getAnimationSpriteIds),
|
||||
JS_CFUNC_DEF("getCostumeStrings", 0, &ScStaff::getCostumeStrings)
|
||||
};
|
||||
JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs));
|
||||
}
|
||||
|
||||
Staff* ScStaff::GetStaff() const
|
||||
Staff* ScStaff::GetStaff(JSValue thisVal)
|
||||
{
|
||||
return getGameState().entities.GetEntity<Staff>(_id);
|
||||
auto id = GetEntityId(thisVal);
|
||||
return getGameState().entities.GetEntity<Staff>(id);
|
||||
}
|
||||
|
||||
std::string ScStaff::staffType_get() const
|
||||
JSValue ScStaff::staffType_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto peep = GetStaff();
|
||||
auto peep = GetStaff(thisVal);
|
||||
if (peep != nullptr)
|
||||
{
|
||||
switch (peep->AssignedStaffType)
|
||||
{
|
||||
case StaffType::handyman:
|
||||
return "handyman";
|
||||
return JSFromStdString(ctx, "handyman");
|
||||
case StaffType::mechanic:
|
||||
return "mechanic";
|
||||
return JSFromStdString(ctx, "mechanic");
|
||||
case StaffType::security:
|
||||
return "security";
|
||||
return JSFromStdString(ctx, "security");
|
||||
case StaffType::entertainer:
|
||||
return "entertainer";
|
||||
return JSFromStdString(ctx, "entertainer");
|
||||
case StaffType::count:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void ScStaff::staffType_set(const std::string& value)
|
||||
JSValue ScStaff::staffType_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto peep = GetStaff();
|
||||
JS_UNPACK_STR(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto peep = GetStaff(thisVal);
|
||||
if (peep != nullptr)
|
||||
{
|
||||
if (value == "handyman" && peep->AssignedStaffType != StaffType::handyman)
|
||||
@@ -106,23 +112,26 @@ namespace OpenRCT2::Scripting
|
||||
peep->AnimationType = peep->NextAnimationType = PeepAnimationType::walking;
|
||||
peep->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint8_t ScStaff::colour_get() const
|
||||
JSValue ScStaff::colour_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto peep = GetStaff();
|
||||
return peep != nullptr ? EnumValue(peep->TshirtColour) : 0;
|
||||
auto peep = GetStaff(thisVal);
|
||||
return JS_NewUint32(ctx, peep != nullptr ? EnumValue(peep->TshirtColour) : 0);
|
||||
}
|
||||
|
||||
void ScStaff::colour_set(uint8_t value)
|
||||
JSValue ScStaff::colour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto peep = GetStaff();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto peep = GetStaff(thisVal);
|
||||
if (peep != nullptr)
|
||||
{
|
||||
peep->TshirtColour = static_cast<Drawing::Colour>(value);
|
||||
peep->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static const std::vector<AnimationGroupResult> costumesByStaffType(StaffType staffType)
|
||||
@@ -132,39 +141,42 @@ namespace OpenRCT2::Scripting
|
||||
return getAnimationGroupsByPeepType(animPeepType);
|
||||
}
|
||||
|
||||
std::vector<std::string> ScStaff::availableCostumes_get() const
|
||||
JSValue ScStaff::availableCostumes_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
std::vector<std::string> availableCostumes{};
|
||||
auto peep = GetStaff();
|
||||
JSValue availableCostumes = JS_NewArray(ctx);
|
||||
auto peep = GetStaff(thisVal);
|
||||
if (peep != nullptr)
|
||||
{
|
||||
auto idx = 0;
|
||||
for (auto& costume : costumesByStaffType(peep->AssignedStaffType))
|
||||
{
|
||||
availableCostumes.push_back(std::string(costume.scriptName));
|
||||
JS_SetPropertyInt64(ctx, availableCostumes, idx++, JSFromStdString(ctx, costume.scriptName));
|
||||
}
|
||||
}
|
||||
return availableCostumes;
|
||||
}
|
||||
|
||||
std::vector<std::string> ScStaff::getCostumeStrings() const
|
||||
JSValue ScStaff::getCostumeStrings(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto peep = GetStaff();
|
||||
auto peep = GetStaff(thisVal);
|
||||
auto animPeepType = getAnimationPeepType(peep->AssignedStaffType);
|
||||
|
||||
std::vector<std::string> availableCostumes{};
|
||||
JSValue availableCostumes = JS_NewArray(ctx);
|
||||
auto idx = 0;
|
||||
|
||||
for (auto& costume : getAvailableCostumeStrings(animPeepType))
|
||||
{
|
||||
availableCostumes.push_back(costume.friendlyName);
|
||||
JS_SetPropertyInt64(ctx, availableCostumes, idx++, JSFromStdString(ctx, costume.friendlyName));
|
||||
}
|
||||
return availableCostumes;
|
||||
}
|
||||
|
||||
std::string ScStaff::costume_get() const
|
||||
JSValue ScStaff::costume_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto peep = GetStaff();
|
||||
auto peep = GetStaff(thisVal);
|
||||
if (peep == nullptr)
|
||||
{
|
||||
return {};
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
auto& costumes = costumesByStaffType(peep->AssignedStaffType);
|
||||
@@ -175,69 +187,78 @@ namespace OpenRCT2::Scripting
|
||||
|
||||
if (costume != costumes.end())
|
||||
{
|
||||
return std::string(costume->scriptName);
|
||||
return JSFromStdString(ctx, costume->scriptName);
|
||||
}
|
||||
else
|
||||
return "";
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void ScStaff::costume_set(const DukValue& value)
|
||||
JSValue ScStaff::costume_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
|
||||
auto peep = GetStaff();
|
||||
auto peep = GetStaff(thisVal);
|
||||
if (peep == nullptr)
|
||||
{
|
||||
return;
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
auto& costumes = costumesByStaffType(peep->AssignedStaffType);
|
||||
auto costume = costumes.end();
|
||||
|
||||
// Split by type passed so as to not break old plugins
|
||||
if (value.type() == DukValue::Type::STRING)
|
||||
if (JS_IsString(value))
|
||||
{
|
||||
costume = std::find_if(costumes.begin(), costumes.end(), [value](auto& candidate) {
|
||||
return candidate.scriptName == value.as_string();
|
||||
JS_UNPACK_STR(valueString, ctx, value);
|
||||
costume = std::find_if(costumes.begin(), costumes.end(), [valueString](auto& candidate) {
|
||||
return candidate.scriptName == valueString;
|
||||
});
|
||||
}
|
||||
else if (value.type() == DukValue::Type::NUMBER)
|
||||
else if (JS_IsNumber(value))
|
||||
{
|
||||
auto target = RCT12PeepAnimationGroup(value.as_uint() + EnumValue(RCT12PeepAnimationGroup::entertainerPanda));
|
||||
JS_UNPACK_UINT32(number, ctx, value);
|
||||
auto target = RCT12PeepAnimationGroup(number + EnumValue(RCT12PeepAnimationGroup::entertainerPanda));
|
||||
costume = std::find_if(
|
||||
costumes.begin(), costumes.end(), [target](auto& candidate) { return candidate.legacyPosition == target; });
|
||||
}
|
||||
|
||||
if (costume == costumes.end())
|
||||
throw DukException() << "Invalid costume for this staff member";
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "Invalid costume for this staff member");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
peep->AnimationObjectIndex = costume->objectId;
|
||||
peep->AnimationGroup = costume->group;
|
||||
peep->Invalidate();
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
std::shared_ptr<ScPatrolArea> ScStaff::patrolArea_get() const
|
||||
JSValue ScStaff::patrolArea_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return std::make_shared<ScPatrolArea>(_id);
|
||||
auto staffId = GetEntityId(thisVal);
|
||||
return gScPatrolArea.New(ctx, staffId);
|
||||
}
|
||||
|
||||
uint8_t ScStaff::orders_get() const
|
||||
JSValue ScStaff::orders_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto peep = GetStaff();
|
||||
return peep != nullptr ? peep->StaffOrders : 0;
|
||||
auto peep = GetStaff(thisVal);
|
||||
return JS_NewUint32(ctx, peep != nullptr ? peep->StaffOrders : 0);
|
||||
}
|
||||
|
||||
void ScStaff::orders_set(uint8_t value)
|
||||
JSValue ScStaff::orders_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto peep = GetStaff();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto peep = GetStaff(thisVal);
|
||||
if (peep != nullptr)
|
||||
{
|
||||
peep->StaffOrders = value;
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
const DukEnumMap<PeepAnimationType>& ScStaff::animationsByStaffType(StaffType staffType) const
|
||||
EnumMap<PeepAnimationType> ScStaff::animationsByStaffType(StaffType staffType)
|
||||
{
|
||||
AnimationPeepType animPeepType{};
|
||||
switch (staffType)
|
||||
@@ -258,33 +279,36 @@ namespace OpenRCT2::Scripting
|
||||
return getAnimationsByPeepType(animPeepType);
|
||||
}
|
||||
|
||||
std::vector<std::string> ScStaff::availableAnimations_get() const
|
||||
JSValue ScStaff::availableAnimations_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
std::vector<std::string> availableAnimations{};
|
||||
JSValue availableAnimations = JS_NewArray(ctx);
|
||||
|
||||
auto* peep = GetStaff();
|
||||
auto* peep = GetStaff(thisVal);
|
||||
if (peep != nullptr)
|
||||
{
|
||||
auto idx = 0;
|
||||
for (auto& animation : animationsByStaffType(peep->AssignedStaffType))
|
||||
{
|
||||
availableAnimations.push_back(std::string(animation.first));
|
||||
JS_SetPropertyInt64(ctx, availableAnimations, idx++, JSFromStdString(ctx, animation.first));
|
||||
}
|
||||
}
|
||||
|
||||
return availableAnimations;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> ScStaff::getAnimationSpriteIds(std::string groupKey, uint8_t rotation) const
|
||||
JSValue ScStaff::getAnimationSpriteIds(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
std::vector<uint32_t> spriteIds{};
|
||||
JS_UNPACK_STR(groupKey, ctx, argv[0]);
|
||||
JS_UNPACK_UINT32(rotation, ctx, argv[1]);
|
||||
JSValue spriteIds = JS_NewArray(ctx);
|
||||
|
||||
auto* peep = GetStaff();
|
||||
auto* peep = GetStaff(thisVal);
|
||||
if (peep == nullptr)
|
||||
{
|
||||
return spriteIds;
|
||||
}
|
||||
|
||||
auto& animationGroups = animationsByStaffType(peep->AssignedStaffType);
|
||||
auto animationGroups = animationsByStaffType(peep->AssignedStaffType);
|
||||
auto animationType = animationGroups.TryGet(groupKey);
|
||||
if (animationType == std::nullopt)
|
||||
{
|
||||
@@ -295,6 +319,7 @@ namespace OpenRCT2::Scripting
|
||||
auto* animObj = objManager.GetLoadedObject<PeepAnimationsObject>(peep->AnimationObjectIndex);
|
||||
|
||||
const auto& animationGroup = animObj->GetPeepAnimation(peep->AnimationGroup, *animationType);
|
||||
auto idx = 0;
|
||||
for (auto frameOffset : animationGroup.frameOffsets)
|
||||
{
|
||||
auto imageId = animationGroup.baseImage;
|
||||
@@ -303,35 +328,36 @@ namespace OpenRCT2::Scripting
|
||||
else
|
||||
imageId += frameOffset;
|
||||
|
||||
spriteIds.push_back(imageId);
|
||||
JS_SetPropertyInt64(ctx, spriteIds, idx++, JS_NewUint32(ctx, imageId));
|
||||
}
|
||||
|
||||
return spriteIds;
|
||||
}
|
||||
|
||||
std::string ScStaff::animation_get() const
|
||||
JSValue ScStaff::animation_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto* peep = GetStaff();
|
||||
auto* peep = GetStaff(thisVal);
|
||||
if (peep == nullptr)
|
||||
{
|
||||
return "";
|
||||
return JSFromStdString(ctx, "");
|
||||
}
|
||||
|
||||
auto& animationGroups = animationsByStaffType(peep->AssignedStaffType);
|
||||
std::string_view action = animationGroups[peep->AnimationType];
|
||||
return std::string(action);
|
||||
auto animationGroups = animationsByStaffType(peep->AssignedStaffType);
|
||||
return JSFromStdString(ctx, animationGroups[peep->AnimationType]);
|
||||
}
|
||||
|
||||
void ScStaff::animation_set(std::string groupKey)
|
||||
JSValue ScStaff::animation_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
JS_UNPACK_STR(groupKey, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
|
||||
auto* peep = GetStaff();
|
||||
auto& animationGroups = animationsByStaffType(peep->AssignedStaffType);
|
||||
auto* peep = GetStaff(thisVal);
|
||||
auto animationGroups = animationsByStaffType(peep->AssignedStaffType);
|
||||
auto newType = animationGroups.TryGet(groupKey);
|
||||
if (newType == std::nullopt)
|
||||
{
|
||||
throw DukException() << "Invalid animation for this staff member (" << groupKey << ")";
|
||||
JS_ThrowPlainError(ctx, "Invalid animation for this staff member (%s)", groupKey.data());
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
peep->AnimationType = peep->NextAnimationType = *newType;
|
||||
@@ -350,27 +376,27 @@ namespace OpenRCT2::Scripting
|
||||
peep->Invalidate();
|
||||
peep->UpdateSpriteBoundingBox();
|
||||
peep->Invalidate();
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint8_t ScStaff::animationOffset_get() const
|
||||
JSValue ScStaff::animationOffset_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto* peep = GetStaff();
|
||||
auto* peep = GetStaff(thisVal);
|
||||
if (peep == nullptr)
|
||||
{
|
||||
return 0;
|
||||
return JS_NewUint32(ctx, 0);
|
||||
}
|
||||
|
||||
if (peep->IsActionWalking())
|
||||
return peep->WalkingAnimationFrameNum;
|
||||
else
|
||||
return peep->AnimationFrameNum;
|
||||
auto frame = peep->IsActionWalking() ? peep->WalkingAnimationFrameNum : peep->AnimationFrameNum;
|
||||
return JS_NewUint32(ctx, frame);
|
||||
}
|
||||
|
||||
void ScStaff::animationOffset_set(uint8_t offset)
|
||||
JSValue ScStaff::animationOffset_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
JS_UNPACK_UINT32(offset, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
|
||||
auto* peep = GetStaff();
|
||||
auto* peep = GetStaff(thisVal);
|
||||
|
||||
auto& objManager = GetContext()->GetObjectManager();
|
||||
auto* animObj = objManager.GetLoadedObject<PeepAnimationsObject>(peep->AnimationObjectIndex);
|
||||
@@ -388,230 +414,223 @@ namespace OpenRCT2::Scripting
|
||||
peep->Invalidate();
|
||||
peep->UpdateSpriteBoundingBox();
|
||||
peep->Invalidate();
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint8_t ScStaff::animationLength_get() const
|
||||
JSValue ScStaff::animationLength_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto* peep = GetStaff();
|
||||
auto* peep = GetStaff(thisVal);
|
||||
if (peep == nullptr)
|
||||
{
|
||||
return 0;
|
||||
return JS_NewUint32(ctx, 0);
|
||||
}
|
||||
|
||||
auto& objManager = GetContext()->GetObjectManager();
|
||||
auto* animObj = objManager.GetLoadedObject<PeepAnimationsObject>(peep->AnimationObjectIndex);
|
||||
|
||||
const auto& animationGroup = animObj->GetPeepAnimation(peep->AnimationGroup, peep->AnimationType);
|
||||
return static_cast<uint8_t>(animationGroup.frameOffsets.size());
|
||||
auto length = static_cast<uint8_t>(animationGroup.frameOffsets.size());
|
||||
return JS_NewUint32(ctx, length);
|
||||
}
|
||||
|
||||
ScHandyman::ScHandyman(EntityId Id)
|
||||
: ScStaff(Id)
|
||||
JSValue ScHandyman::New(JSContext* ctx, EntityId entityId)
|
||||
{
|
||||
JSValue obj = ScStaff::New(ctx, entityId);
|
||||
AddFuncs(ctx, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void ScHandyman::Register(duk_context* ctx)
|
||||
void ScHandyman::AddFuncs(JSContext* ctx, JSValue obj)
|
||||
{
|
||||
dukglue_set_base_class<ScStaff, ScHandyman>(ctx);
|
||||
dukglue_register_property(ctx, &ScHandyman::lawnsMown_get, nullptr, "lawnsMown");
|
||||
dukglue_register_property(ctx, &ScHandyman::gardensWatered_get, nullptr, "gardensWatered");
|
||||
dukglue_register_property(ctx, &ScHandyman::litterSwept_get, nullptr, "litterSwept");
|
||||
dukglue_register_property(ctx, &ScHandyman::binsEmptied_get, nullptr, "binsEmptied");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("lawnsMown", &ScHandyman::lawnsMown_get, nullptr),
|
||||
JS_CGETSET_DEF("gardensWatered", &ScHandyman::gardensWatered_get, nullptr),
|
||||
JS_CGETSET_DEF("litterSwept", &ScHandyman::litterSwept_get, nullptr),
|
||||
JS_CGETSET_DEF("binsEmptied", &ScHandyman::binsEmptied_get, nullptr),
|
||||
};
|
||||
JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs));
|
||||
}
|
||||
|
||||
Staff* ScHandyman::GetHandyman() const
|
||||
JSValue ScHandyman::lawnsMown_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().entities.GetEntity<Staff>(_id);
|
||||
}
|
||||
|
||||
DukValue ScHandyman::lawnsMown_get() const
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto* ctx = scriptEngine.GetContext();
|
||||
auto peep = GetHandyman();
|
||||
auto peep = GetStaff(thisVal);
|
||||
if (peep != nullptr && peep->AssignedStaffType == StaffType::handyman)
|
||||
{
|
||||
duk_push_uint(ctx, peep->StaffLawnsMown);
|
||||
return JS_NewUint32(ctx, peep->StaffLawnsMown);
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_push_null(ctx);
|
||||
return JS_NULL;
|
||||
}
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
DukValue ScHandyman::gardensWatered_get() const
|
||||
JSValue ScHandyman::gardensWatered_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto* ctx = scriptEngine.GetContext();
|
||||
auto peep = GetHandyman();
|
||||
auto peep = GetStaff(thisVal);
|
||||
if (peep != nullptr && peep->AssignedStaffType == StaffType::handyman)
|
||||
{
|
||||
duk_push_uint(ctx, peep->StaffGardensWatered);
|
||||
return JS_NewUint32(ctx, peep->StaffGardensWatered);
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_push_null(ctx);
|
||||
return JS_NULL;
|
||||
}
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
DukValue ScHandyman::litterSwept_get() const
|
||||
JSValue ScHandyman::litterSwept_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto* ctx = scriptEngine.GetContext();
|
||||
auto peep = GetHandyman();
|
||||
auto peep = GetStaff(thisVal);
|
||||
if (peep != nullptr && peep->AssignedStaffType == StaffType::handyman)
|
||||
{
|
||||
duk_push_uint(ctx, peep->StaffLitterSwept);
|
||||
return JS_NewUint32(ctx, peep->StaffLitterSwept);
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_push_null(ctx);
|
||||
return JS_NULL;
|
||||
}
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
DukValue ScHandyman::binsEmptied_get() const
|
||||
JSValue ScHandyman::binsEmptied_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto* ctx = scriptEngine.GetContext();
|
||||
auto peep = GetHandyman();
|
||||
auto peep = GetStaff(thisVal);
|
||||
if (peep != nullptr && peep->AssignedStaffType == StaffType::handyman)
|
||||
{
|
||||
duk_push_uint(ctx, peep->StaffBinsEmptied);
|
||||
return JS_NewUint32(ctx, peep->StaffBinsEmptied);
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_push_null(ctx);
|
||||
return JS_NULL;
|
||||
}
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
ScMechanic::ScMechanic(EntityId Id)
|
||||
: ScStaff(Id)
|
||||
JSValue ScMechanic::New(JSContext* ctx, EntityId entityId)
|
||||
{
|
||||
JSValue obj = ScStaff::New(ctx, entityId);
|
||||
AddFuncs(ctx, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void ScMechanic::Register(duk_context* ctx)
|
||||
void ScMechanic::AddFuncs(JSContext* ctx, JSValue obj)
|
||||
{
|
||||
dukglue_set_base_class<ScStaff, ScMechanic>(ctx);
|
||||
dukglue_register_property(ctx, &ScMechanic::ridesFixed_get, nullptr, "ridesFixed");
|
||||
dukglue_register_property(ctx, &ScMechanic::ridesInspected_get, nullptr, "ridesInspected");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("ridesFixed", &ScMechanic::ridesFixed_get, nullptr),
|
||||
JS_CGETSET_DEF("ridesInspected", &ScMechanic::ridesInspected_get, nullptr),
|
||||
};
|
||||
JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs));
|
||||
}
|
||||
|
||||
Staff* ScMechanic::GetMechanic() const
|
||||
JSValue ScMechanic::ridesFixed_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().entities.GetEntity<Staff>(_id);
|
||||
}
|
||||
|
||||
DukValue ScMechanic::ridesFixed_get() const
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto* ctx = scriptEngine.GetContext();
|
||||
auto peep = GetMechanic();
|
||||
auto peep = GetStaff(thisVal);
|
||||
if (peep != nullptr && peep->IsMechanic())
|
||||
{
|
||||
duk_push_uint(ctx, peep->StaffRidesFixed);
|
||||
return JS_NewUint32(ctx, peep->StaffRidesFixed);
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_push_null(ctx);
|
||||
return JS_NULL;
|
||||
}
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
DukValue ScMechanic::ridesInspected_get() const
|
||||
JSValue ScMechanic::ridesInspected_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto* ctx = scriptEngine.GetContext();
|
||||
auto peep = GetMechanic();
|
||||
auto peep = GetStaff(thisVal);
|
||||
if (peep != nullptr && peep->IsMechanic())
|
||||
{
|
||||
duk_push_uint(ctx, peep->StaffRidesInspected);
|
||||
return JS_NewUint32(ctx, peep->StaffRidesInspected);
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_push_null(ctx);
|
||||
return JS_NULL;
|
||||
}
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
ScSecurity::ScSecurity(EntityId Id)
|
||||
: ScStaff(Id)
|
||||
JSValue ScSecurity::New(JSContext* ctx, EntityId entityId)
|
||||
{
|
||||
JSValue obj = ScStaff::New(ctx, entityId);
|
||||
AddFuncs(ctx, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void ScSecurity::Register(duk_context* ctx)
|
||||
void ScSecurity::AddFuncs(JSContext* ctx, JSValue obj)
|
||||
{
|
||||
dukglue_set_base_class<ScStaff, ScSecurity>(ctx);
|
||||
dukglue_register_property(ctx, &ScSecurity::vandalsStopped_get, nullptr, "vandalsStopped");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("vandalsStopped", &ScSecurity::vandalsStopped_get, nullptr),
|
||||
};
|
||||
JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs));
|
||||
}
|
||||
|
||||
Staff* ScSecurity::GetSecurity() const
|
||||
JSValue ScSecurity::vandalsStopped_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().entities.GetEntity<Staff>(_id);
|
||||
}
|
||||
|
||||
DukValue ScSecurity::vandalsStopped_get() const
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto* ctx = scriptEngine.GetContext();
|
||||
auto peep = GetSecurity();
|
||||
auto peep = GetStaff(thisVal);
|
||||
if (peep != nullptr && peep->AssignedStaffType == StaffType::security)
|
||||
{
|
||||
duk_push_uint(ctx, peep->StaffVandalsStopped);
|
||||
return JS_NewUint32(ctx, peep->StaffVandalsStopped);
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_push_null(ctx);
|
||||
return JS_NULL;
|
||||
}
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
ScPatrolArea::ScPatrolArea(EntityId id)
|
||||
: _staffId(id)
|
||||
using OpaquePatrolAreaData = struct
|
||||
{
|
||||
EntityId staffId;
|
||||
};
|
||||
|
||||
JSValue ScPatrolArea::New(JSContext* ctx, EntityId staffId)
|
||||
{
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("tiles", &ScPatrolArea::tiles_get, &ScPatrolArea::tiles_set),
|
||||
JS_CFUNC_DEF("clear", 0, &ScPatrolArea::clear),
|
||||
JS_CFUNC_DEF("add", 1, &ScPatrolArea::add),
|
||||
JS_CFUNC_DEF("remove", 1, &ScPatrolArea::remove),
|
||||
JS_CFUNC_DEF("contains", 1, &ScPatrolArea::contains),
|
||||
};
|
||||
return MakeWithOpaque(ctx, funcs, new OpaquePatrolAreaData{ staffId });
|
||||
}
|
||||
|
||||
void ScPatrolArea::Register(duk_context* ctx)
|
||||
void ScPatrolArea::Register(JSContext* ctx)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScPatrolArea::tiles_get, &ScPatrolArea::tiles_set, "tiles");
|
||||
dukglue_register_method(ctx, &ScPatrolArea::clear, "clear");
|
||||
dukglue_register_method(ctx, &ScPatrolArea::add, "add");
|
||||
dukglue_register_method(ctx, &ScPatrolArea::remove, "remove");
|
||||
dukglue_register_method(ctx, &ScPatrolArea::contains, "contains");
|
||||
RegisterBaseStr(ctx, "PatrolArea", Finalize);
|
||||
}
|
||||
|
||||
Staff* ScPatrolArea::GetStaff() const
|
||||
void ScPatrolArea::Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
return getGameState().entities.GetEntity<Staff>(_staffId);
|
||||
OpaquePatrolAreaData* data = gScPatrolArea.GetOpaque<OpaquePatrolAreaData*>(thisVal);
|
||||
if (data)
|
||||
delete data;
|
||||
}
|
||||
|
||||
void ScPatrolArea::ModifyArea(const DukValue& coordsOrRange, bool value) const
|
||||
Staff* ScPatrolArea::GetStaff(JSValue thisVal)
|
||||
{
|
||||
auto staff = GetStaff();
|
||||
OpaquePatrolAreaData* data = gScPatrolArea.GetOpaque<OpaquePatrolAreaData*>(thisVal);
|
||||
return getGameState().entities.GetEntity<Staff>(data->staffId);
|
||||
}
|
||||
|
||||
void ScPatrolArea::ModifyArea(JSContext* ctx, JSValue thisVal, JSValue coordsOrRange, bool reset)
|
||||
{
|
||||
auto staff = GetStaff(thisVal);
|
||||
if (staff != nullptr)
|
||||
{
|
||||
if (coordsOrRange.is_array())
|
||||
if (JS_IsArray(coordsOrRange))
|
||||
{
|
||||
auto dukCoords = coordsOrRange.as_array();
|
||||
for (const auto& dukCoord : dukCoords)
|
||||
{
|
||||
auto coord = FromDuk<CoordsXY>(dukCoord);
|
||||
staff->SetPatrolArea(coord, value);
|
||||
JSIterateArray(ctx, coordsOrRange, [staff, reset](JSContext* ctx2, JSValue v) {
|
||||
auto coord = JSToCoordsXY(ctx2, v);
|
||||
staff->SetPatrolArea(coord, reset);
|
||||
MapInvalidateTileFull(coord);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto mapRange = FromDuk<MapRange>(coordsOrRange);
|
||||
MapRange mapRange = { JSToCoordXY(ctx, coordsOrRange, "leftTop"),
|
||||
JSToCoordXY(ctx, coordsOrRange, "rightBottom") };
|
||||
for (int32_t y = mapRange.GetY1(); y <= mapRange.GetY2(); y += kCoordsXYStep)
|
||||
{
|
||||
for (int32_t x = mapRange.GetX1(); x <= mapRange.GetX2(); x += kCoordsXYStep)
|
||||
{
|
||||
CoordsXY coord(x, y);
|
||||
staff->SetPatrolArea(coord, value);
|
||||
staff->SetPatrolArea(coord, reset);
|
||||
MapInvalidateTileFull(coord);
|
||||
}
|
||||
}
|
||||
@@ -620,78 +639,82 @@ namespace OpenRCT2::Scripting
|
||||
}
|
||||
}
|
||||
|
||||
DukValue ScPatrolArea::tiles_get() const
|
||||
JSValue ScPatrolArea::tiles_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto array = JS_NewArray(ctx);
|
||||
|
||||
duk_push_array(ctx);
|
||||
|
||||
auto staff = GetStaff();
|
||||
auto staff = GetStaff(thisVal);
|
||||
if (staff != nullptr && staff->PatrolInfo != nullptr)
|
||||
{
|
||||
auto tiles = staff->PatrolInfo->ToVector();
|
||||
|
||||
duk_uarridx_t index = 0;
|
||||
auto index = 0;
|
||||
for (const auto& tile : tiles)
|
||||
{
|
||||
auto dukCoord = ToDuk(ctx, tile.ToCoordsXY());
|
||||
dukCoord.push();
|
||||
duk_put_prop_index(ctx, -2, index);
|
||||
auto coords = ToJSValue(ctx, tile);
|
||||
JS_SetPropertyInt64(ctx, array, index, coords);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
return DukValue::take_from_stack(ctx, -1);
|
||||
return array;
|
||||
}
|
||||
|
||||
void ScPatrolArea::tiles_set(const DukValue& value)
|
||||
JSValue ScPatrolArea::tiles_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
|
||||
auto staff = GetStaff();
|
||||
auto staff = GetStaff(thisVal);
|
||||
if (staff != nullptr)
|
||||
{
|
||||
staff->ClearPatrolArea();
|
||||
if (value.is_array())
|
||||
if (JS_IsArray(value))
|
||||
{
|
||||
ModifyArea(value, true);
|
||||
ModifyArea(ctx, thisVal, value, true);
|
||||
}
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void ScPatrolArea::clear()
|
||||
JSValue ScPatrolArea::clear(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
|
||||
auto staff = GetStaff();
|
||||
auto staff = GetStaff(thisVal);
|
||||
if (staff != nullptr)
|
||||
{
|
||||
staff->ClearPatrolArea();
|
||||
UpdateConsolidatedPatrolAreas();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void ScPatrolArea::add(const DukValue& coordsOrRange)
|
||||
JSValue ScPatrolArea::add(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
ModifyArea(coordsOrRange, true);
|
||||
JS_UNPACK_OBJECT(coordsOrRange, ctx, argv[0])
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
ModifyArea(ctx, thisVal, coordsOrRange, true);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void ScPatrolArea::remove(const DukValue& coordsOrRange)
|
||||
JSValue ScPatrolArea::remove(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
ModifyArea(coordsOrRange, false);
|
||||
JS_UNPACK_OBJECT(coordsOrRange, ctx, argv[0])
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
ModifyArea(ctx, thisVal, coordsOrRange, false);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool ScPatrolArea::contains(const DukValue& coord) const
|
||||
JSValue ScPatrolArea::contains(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto staff = GetStaff();
|
||||
JS_UNPACK_OBJECT(coord, ctx, argv[0])
|
||||
auto staff = GetStaff(thisVal);
|
||||
if (staff != nullptr)
|
||||
{
|
||||
auto pos = FromDuk<CoordsXY>(coord);
|
||||
return staff->IsLocationInPatrol(pos);
|
||||
auto pos = JSToCoordsXY(ctx, coord);
|
||||
return JS_NewBool(ctx, staff->IsLocationInPatrol(pos));
|
||||
}
|
||||
return false;
|
||||
return JS_NewBool(ctx, false);
|
||||
}
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -23,110 +23,105 @@ namespace OpenRCT2
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScPatrolArea
|
||||
class ScPatrolArea;
|
||||
extern ScPatrolArea gScPatrolArea;
|
||||
|
||||
class ScPatrolArea final : public ScBase
|
||||
{
|
||||
private:
|
||||
EntityId _staffId;
|
||||
|
||||
public:
|
||||
ScPatrolArea(EntityId id);
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
JSValue New(JSContext* ctx, EntityId staffId);
|
||||
void Register(JSContext* ctx);
|
||||
|
||||
private:
|
||||
Staff* GetStaff() const;
|
||||
void ModifyArea(const DukValue& coordsOrRange, bool value) const;
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal);
|
||||
static Staff* GetStaff(JSValue thisVal);
|
||||
static void ModifyArea(JSContext* ctx, JSValue thisVal, JSValue coordsOrRange, bool reset);
|
||||
|
||||
DukValue tiles_get() const;
|
||||
void tiles_set(const DukValue& value);
|
||||
static JSValue tiles_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue tiles_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
void clear();
|
||||
void add(const DukValue& coordsOrRange);
|
||||
void remove(const DukValue& coordsOrRange);
|
||||
bool contains(const DukValue& coord) const;
|
||||
static JSValue clear(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
static JSValue add(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
static JSValue remove(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
static JSValue contains(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
};
|
||||
|
||||
class ScStaff : public ScPeep
|
||||
{
|
||||
public:
|
||||
ScStaff(EntityId Id);
|
||||
static JSValue New(JSContext* ctx, EntityId entityId);
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
protected:
|
||||
static Staff* GetStaff(JSValue thisVal);
|
||||
|
||||
private:
|
||||
Staff* GetStaff() const;
|
||||
static void AddFuncs(JSContext* ctx, JSValue obj);
|
||||
|
||||
std::string staffType_get() const;
|
||||
void staffType_set(const std::string& value);
|
||||
static JSValue staffType_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue staffType_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t colour_get() const;
|
||||
void colour_set(uint8_t value);
|
||||
static JSValue colour_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue colour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
std::vector<std::string> availableCostumes_get() const;
|
||||
std::vector<std::string> getCostumeStrings() const;
|
||||
std::string costume_get() const;
|
||||
void costume_set(const DukValue& value);
|
||||
static JSValue availableCostumes_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue getCostumeStrings(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
static JSValue costume_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue costume_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
std::shared_ptr<ScPatrolArea> patrolArea_get() const;
|
||||
static JSValue patrolArea_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
uint8_t orders_get() const;
|
||||
void orders_set(uint8_t value);
|
||||
static JSValue orders_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue orders_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
const DukEnumMap<PeepAnimationType>& animationsByStaffType(StaffType staffType) const;
|
||||
std::vector<uint32_t> getAnimationSpriteIds(std::string groupKey, uint8_t rotation) const;
|
||||
std::vector<std::string> availableAnimations_get() const;
|
||||
std::string animation_get() const;
|
||||
void animation_set(std::string groupKey);
|
||||
uint8_t animationOffset_get() const;
|
||||
void animationOffset_set(uint8_t offset);
|
||||
uint8_t animationLength_get() const;
|
||||
static EnumMap<PeepAnimationType> animationsByStaffType(StaffType staffType);
|
||||
static JSValue getAnimationSpriteIds(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
static JSValue availableAnimations_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue animation_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue animation_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
static JSValue animationOffset_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue animationOffset_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
static JSValue animationLength_get(JSContext* ctx, JSValue thisVal);
|
||||
};
|
||||
|
||||
class ScHandyman : public ScStaff
|
||||
class ScHandyman final : public ScStaff
|
||||
{
|
||||
public:
|
||||
ScHandyman(EntityId Id);
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
static JSValue New(JSContext* ctx, EntityId entityId);
|
||||
|
||||
private:
|
||||
Staff* GetHandyman() const;
|
||||
static void AddFuncs(JSContext* ctx, JSValue obj);
|
||||
|
||||
DukValue lawnsMown_get() const;
|
||||
static JSValue lawnsMown_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
DukValue gardensWatered_get() const;
|
||||
static JSValue gardensWatered_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
DukValue litterSwept_get() const;
|
||||
static JSValue litterSwept_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
DukValue binsEmptied_get() const;
|
||||
static JSValue binsEmptied_get(JSContext* ctx, JSValue thisVal);
|
||||
};
|
||||
|
||||
class ScMechanic : public ScStaff
|
||||
class ScMechanic final : public ScStaff
|
||||
{
|
||||
public:
|
||||
ScMechanic(EntityId Id);
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
static JSValue New(JSContext* ctx, EntityId entityId);
|
||||
|
||||
private:
|
||||
Staff* GetMechanic() const;
|
||||
static void AddFuncs(JSContext* ctx, JSValue obj);
|
||||
|
||||
DukValue ridesFixed_get() const;
|
||||
static JSValue ridesFixed_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
DukValue ridesInspected_get() const;
|
||||
static JSValue ridesInspected_get(JSContext* ctx, JSValue thisVal);
|
||||
};
|
||||
|
||||
class ScSecurity : public ScStaff
|
||||
class ScSecurity final : public ScStaff
|
||||
{
|
||||
public:
|
||||
ScSecurity(EntityId Id);
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
static JSValue New(JSContext* ctx, EntityId entityId);
|
||||
|
||||
private:
|
||||
Staff* GetSecurity() const;
|
||||
static void AddFuncs(JSContext* ctx, JSValue obj);
|
||||
|
||||
DukValue vandalsStopped_get() const;
|
||||
static JSValue vandalsStopped_get(JSContext* ctx, JSValue thisVal);
|
||||
};
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "ScVehicle.hpp"
|
||||
|
||||
#include "../../../core/EnumMap.hpp"
|
||||
#include "../../../ride/Track.h"
|
||||
#include "../../../ride/TrackData.h"
|
||||
#include "../../../ride/ted/TrackElementDescriptor.h"
|
||||
@@ -23,7 +24,7 @@ using namespace OpenRCT2::TrackMetadata;
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
static const DukEnumMap<Vehicle::Status> VehicleStatusMap(
|
||||
static const EnumMap<Vehicle::Status> VehicleStatusMap(
|
||||
{
|
||||
{ "moving_to_end_of_station", Vehicle::Status::movingToEndOfStation },
|
||||
{ "waiting_for_passengers", Vehicle::Status::waitingForPassengers },
|
||||
@@ -58,312 +59,337 @@ namespace OpenRCT2::Scripting
|
||||
{ "stopped_by_block_brake", Vehicle::Status::stoppedByBlockBrakes },
|
||||
});
|
||||
|
||||
ScVehicle::ScVehicle(EntityId id)
|
||||
: ScEntity(id)
|
||||
JSValue ScVehicle::New(JSContext* ctx, EntityId entityId)
|
||||
{
|
||||
JSValue obj = gScEntity.New(ctx, entityId);
|
||||
AddFuncs(ctx, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void ScVehicle::Register(duk_context* ctx)
|
||||
void ScVehicle::AddFuncs(JSContext* ctx, JSValue obj)
|
||||
{
|
||||
dukglue_set_base_class<ScEntity, ScVehicle>(ctx);
|
||||
dukglue_register_property(ctx, &ScVehicle::ride_get, &ScVehicle::ride_set, "ride");
|
||||
dukglue_register_property(ctx, &ScVehicle::rideObject_get, &ScVehicle::rideObject_set, "rideObject");
|
||||
dukglue_register_property(ctx, &ScVehicle::vehicleObject_get, &ScVehicle::vehicleObject_set, "vehicleObject");
|
||||
dukglue_register_property(ctx, &ScVehicle::spriteType_get, &ScVehicle::spriteType_set, "spriteType");
|
||||
dukglue_register_property(ctx, &ScVehicle::numSeats_get, &ScVehicle::numSeats_set, "numSeats");
|
||||
dukglue_register_property(ctx, &ScVehicle::nextCarOnTrain_get, &ScVehicle::nextCarOnTrain_set, "nextCarOnTrain");
|
||||
dukglue_register_property(
|
||||
ctx, &ScVehicle::previousCarOnRide_get, &ScVehicle::previousCarOnRide_set, "previousCarOnRide");
|
||||
dukglue_register_property(ctx, &ScVehicle::nextCarOnRide_get, &ScVehicle::nextCarOnRide_set, "nextCarOnRide");
|
||||
dukglue_register_property(ctx, &ScVehicle::currentStation_get, &ScVehicle::currentStation_set, "currentStation");
|
||||
dukglue_register_property(ctx, &ScVehicle::mass_get, &ScVehicle::mass_set, "mass");
|
||||
dukglue_register_property(ctx, &ScVehicle::acceleration_get, &ScVehicle::acceleration_set, "acceleration");
|
||||
dukglue_register_property(ctx, &ScVehicle::velocity_get, &ScVehicle::velocity_set, "velocity");
|
||||
dukglue_register_property(ctx, &ScVehicle::bankRotation_get, &ScVehicle::bankRotation_set, "bankRotation");
|
||||
dukglue_register_property(
|
||||
ctx, &ScVehicle::flag_get<VehicleFlag::carIsReversed>, &ScVehicle::flag_set<VehicleFlag::carIsReversed>,
|
||||
"isReversed");
|
||||
dukglue_register_property(
|
||||
ctx, &ScVehicle::flag_get<VehicleFlag::crashed>, &ScVehicle::flag_set<VehicleFlag::crashed>, "isCrashed");
|
||||
dukglue_register_property(ctx, &ScVehicle::colours_get, &ScVehicle::colours_set, "colours");
|
||||
dukglue_register_property(ctx, &ScVehicle::trackLocation_get, nullptr, "trackLocation");
|
||||
dukglue_register_property(ctx, &ScVehicle::trackProgress_get, nullptr, "trackProgress");
|
||||
dukglue_register_property(ctx, &ScVehicle::remainingDistance_get, nullptr, "remainingDistance");
|
||||
dukglue_register_property(ctx, &ScVehicle::subposition_get, nullptr, "subposition");
|
||||
dukglue_register_property(
|
||||
ctx, &ScVehicle::poweredAcceleration_get, &ScVehicle::poweredAcceleration_set, "poweredAcceleration");
|
||||
dukglue_register_property(ctx, &ScVehicle::poweredMaxSpeed_get, &ScVehicle::poweredMaxSpeed_set, "poweredMaxSpeed");
|
||||
dukglue_register_property(ctx, &ScVehicle::status_get, &ScVehicle::status_set, "status");
|
||||
dukglue_register_property(ctx, &ScVehicle::spin_get, &ScVehicle::spin_set, "spin");
|
||||
dukglue_register_property(ctx, &ScVehicle::guests_get, nullptr, "peeps");
|
||||
dukglue_register_property(ctx, &ScVehicle::guests_get, nullptr, "guests");
|
||||
dukglue_register_property(ctx, &ScVehicle::gForces_get, nullptr, "gForces");
|
||||
dukglue_register_method(ctx, &ScVehicle::travelBy, "travelBy");
|
||||
dukglue_register_method(ctx, &ScVehicle::moveToTrack, "moveToTrack");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("ride", &ScVehicle::ride_get, &ScVehicle::ride_set),
|
||||
JS_CGETSET_DEF("rideObject", &ScVehicle::rideObject_get, &ScVehicle::rideObject_set),
|
||||
JS_CGETSET_DEF("vehicleObject", &ScVehicle::vehicleObject_get, &ScVehicle::vehicleObject_set),
|
||||
JS_CGETSET_DEF("spriteType", &ScVehicle::spriteType_get, &ScVehicle::spriteType_set),
|
||||
JS_CGETSET_DEF("numSeats", &ScVehicle::numSeats_get, &ScVehicle::numSeats_set),
|
||||
JS_CGETSET_DEF("nextCarOnTrain", &ScVehicle::nextCarOnTrain_get, &ScVehicle::nextCarOnTrain_set),
|
||||
JS_CGETSET_DEF("previousCarOnRide", &ScVehicle::previousCarOnRide_get, &ScVehicle::previousCarOnRide_set),
|
||||
JS_CGETSET_DEF("nextCarOnRide", &ScVehicle::nextCarOnRide_get, &ScVehicle::nextCarOnRide_set),
|
||||
JS_CGETSET_DEF("currentStation", &ScVehicle::currentStation_get, &ScVehicle::currentStation_set),
|
||||
JS_CGETSET_DEF("mass", &ScVehicle::mass_get, &ScVehicle::mass_set),
|
||||
JS_CGETSET_DEF("acceleration", &ScVehicle::acceleration_get, &ScVehicle::acceleration_set),
|
||||
JS_CGETSET_DEF("velocity", &ScVehicle::velocity_get, &ScVehicle::velocity_set),
|
||||
JS_CGETSET_DEF("bankRotation", &ScVehicle::bankRotation_get, &ScVehicle::bankRotation_set),
|
||||
JS_CGETSET_DEF(
|
||||
"isReversed", &ScVehicle::flag_get<VehicleFlag::carIsReversed>,
|
||||
&ScVehicle::flag_set<VehicleFlag::carIsReversed>),
|
||||
JS_CGETSET_DEF("isCrashed", &ScVehicle::flag_get<VehicleFlag::crashed>, &ScVehicle::flag_set<VehicleFlag::crashed>),
|
||||
JS_CGETSET_DEF("colours", &ScVehicle::colours_get, &ScVehicle::colours_set),
|
||||
JS_CGETSET_DEF("trackLocation", &ScVehicle::trackLocation_get, nullptr),
|
||||
JS_CGETSET_DEF("trackProgress", &ScVehicle::trackProgress_get, nullptr),
|
||||
JS_CGETSET_DEF("remainingDistance", &ScVehicle::remainingDistance_get, nullptr),
|
||||
JS_CGETSET_DEF("subposition", &ScVehicle::subposition_get, nullptr),
|
||||
JS_CGETSET_DEF("poweredAcceleration", &ScVehicle::poweredAcceleration_get, &ScVehicle::poweredAcceleration_set),
|
||||
JS_CGETSET_DEF("poweredMaxSpeed", &ScVehicle::poweredMaxSpeed_get, &ScVehicle::poweredMaxSpeed_set),
|
||||
JS_CGETSET_DEF("status", &ScVehicle::status_get, &ScVehicle::status_set),
|
||||
JS_CGETSET_DEF("spin", &ScVehicle::spin_get, &ScVehicle::spin_set),
|
||||
JS_CGETSET_DEF("peeps", &ScVehicle::guests_get, nullptr),
|
||||
JS_CGETSET_DEF("guests", &ScVehicle::guests_get, nullptr),
|
||||
JS_CGETSET_DEF("gForces", &ScVehicle::gForces_get, nullptr),
|
||||
JS_CFUNC_DEF("travelBy", 1, &ScVehicle::travelBy),
|
||||
JS_CFUNC_DEF("moveToTrack", 3, &ScVehicle::moveToTrack),
|
||||
};
|
||||
JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs));
|
||||
}
|
||||
|
||||
Vehicle* ScVehicle::GetVehicle() const
|
||||
Vehicle* ScVehicle::GetVehicle(JSValue thisVal)
|
||||
{
|
||||
return getGameState().entities.GetEntity<Vehicle>(_id);
|
||||
auto id = GetEntityId(thisVal);
|
||||
return getGameState().entities.GetEntity<Vehicle>(id);
|
||||
}
|
||||
|
||||
ObjectEntryIndex ScVehicle::rideObject_get() const
|
||||
JSValue ScVehicle::rideObject_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
return vehicle != nullptr ? vehicle->ride_subtype : 0;
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->ride_subtype : 0);
|
||||
}
|
||||
void ScVehicle::rideObject_set(ObjectEntryIndex value)
|
||||
JSValue ScVehicle::rideObject_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
vehicle->ride_subtype = value;
|
||||
vehicle->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint8_t ScVehicle::vehicleObject_get() const
|
||||
JSValue ScVehicle::vehicleObject_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
return vehicle != nullptr ? vehicle->vehicle_type : 0;
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->vehicle_type : 0);
|
||||
}
|
||||
void ScVehicle::vehicleObject_set(uint8_t value)
|
||||
JSValue ScVehicle::vehicleObject_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
vehicle->vehicle_type = value;
|
||||
vehicle->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint8_t ScVehicle::spriteType_get() const
|
||||
JSValue ScVehicle::spriteType_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
return vehicle != nullptr ? EnumValue(vehicle->pitch) : 0;
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
return JS_NewUint32(ctx, vehicle != nullptr ? EnumValue(vehicle->pitch) : 0);
|
||||
}
|
||||
void ScVehicle::spriteType_set(uint8_t value)
|
||||
JSValue ScVehicle::spriteType_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
vehicle->pitch = static_cast<VehiclePitch>(value);
|
||||
vehicle->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
int32_t ScVehicle::ride_get() const
|
||||
JSValue ScVehicle::ride_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
return (vehicle != nullptr ? vehicle->ride : RideId::GetNull()).ToUnderlying();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
auto rideId = vehicle != nullptr ? vehicle->ride : RideId::GetNull();
|
||||
return JS_NewUint32(ctx, rideId.ToUnderlying());
|
||||
}
|
||||
void ScVehicle::ride_set(int32_t value)
|
||||
JSValue ScVehicle::ride_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_INT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
vehicle->ride = RideId::FromUnderlying(value);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint8_t ScVehicle::numSeats_get() const
|
||||
JSValue ScVehicle::numSeats_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
return vehicle != nullptr ? vehicle->num_seats & kVehicleSeatNumMask : 0;
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->num_seats & kVehicleSeatNumMask : 0);
|
||||
}
|
||||
void ScVehicle::numSeats_set(uint8_t value)
|
||||
JSValue ScVehicle::numSeats_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
vehicle->num_seats &= ~kVehicleSeatNumMask;
|
||||
vehicle->num_seats |= value & kVehicleSeatNumMask;
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
DukValue ScVehicle::nextCarOnTrain_get() const
|
||||
JSValue ScVehicle::nextCarOnTrain_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto vehicle = GetVehicle();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
if (!vehicle->next_vehicle_on_train.IsNull())
|
||||
{
|
||||
return ToDuk<int32_t>(ctx, vehicle->next_vehicle_on_train.ToUnderlying());
|
||||
return JS_NewUint32(ctx, vehicle->next_vehicle_on_train.ToUnderlying());
|
||||
}
|
||||
}
|
||||
return ToDuk(ctx, nullptr);
|
||||
return JS_NULL;
|
||||
}
|
||||
void ScVehicle::nextCarOnTrain_set(DukValue value)
|
||||
JSValue ScVehicle::nextCarOnTrain_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
if (value.type() == DukValue::Type::NUMBER)
|
||||
{
|
||||
vehicle->next_vehicle_on_train = EntityId::FromUnderlying(value.as_uint());
|
||||
}
|
||||
else
|
||||
if (JS_IsNull(jsValue))
|
||||
{
|
||||
vehicle->next_vehicle_on_train = EntityId::GetNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
JS_UNPACK_UINT32(entityId, ctx, jsValue);
|
||||
vehicle->next_vehicle_on_train = EntityId::FromUnderlying(entityId);
|
||||
}
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
DukValue ScVehicle::previousCarOnRide_get() const
|
||||
JSValue ScVehicle::previousCarOnRide_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
|
||||
const auto* vehicle = GetVehicle();
|
||||
const auto* vehicle = GetVehicle(thisVal);
|
||||
if (vehicle == nullptr)
|
||||
return ToDuk(ctx, nullptr);
|
||||
return JS_NULL;
|
||||
|
||||
if (vehicle->prev_vehicle_on_ride.IsNull())
|
||||
return ToDuk(ctx, nullptr);
|
||||
return JS_NULL;
|
||||
|
||||
return ToDuk(ctx, vehicle->prev_vehicle_on_ride.ToUnderlying());
|
||||
return JS_NewUint32(ctx, vehicle->prev_vehicle_on_ride.ToUnderlying());
|
||||
}
|
||||
void ScVehicle::previousCarOnRide_set(DukValue value)
|
||||
JSValue ScVehicle::previousCarOnRide_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto* vehicle = GetVehicle();
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto* vehicle = GetVehicle(thisVal);
|
||||
if (vehicle == nullptr)
|
||||
return;
|
||||
return JS_UNDEFINED;
|
||||
|
||||
if (value.type() == DukValue::Type::NUMBER)
|
||||
{
|
||||
vehicle->prev_vehicle_on_ride = EntityId::FromUnderlying(value.as_uint());
|
||||
}
|
||||
else
|
||||
if (JS_IsNull(jsValue))
|
||||
{
|
||||
vehicle->prev_vehicle_on_ride = EntityId::GetNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
JS_UNPACK_UINT32(entityId, ctx, jsValue);
|
||||
vehicle->prev_vehicle_on_ride = EntityId::FromUnderlying(entityId);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
DukValue ScVehicle::nextCarOnRide_get() const
|
||||
JSValue ScVehicle::nextCarOnRide_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
|
||||
const auto* vehicle = GetVehicle();
|
||||
const auto* vehicle = GetVehicle(thisVal);
|
||||
if (vehicle == nullptr)
|
||||
return ToDuk(ctx, nullptr);
|
||||
return JS_NULL;
|
||||
|
||||
if (vehicle->next_vehicle_on_ride.IsNull())
|
||||
return ToDuk(ctx, nullptr);
|
||||
return JS_NULL;
|
||||
|
||||
return ToDuk(ctx, vehicle->next_vehicle_on_ride.ToUnderlying());
|
||||
return JS_NewUint32(ctx, vehicle->next_vehicle_on_ride.ToUnderlying());
|
||||
}
|
||||
void ScVehicle::nextCarOnRide_set(DukValue value)
|
||||
JSValue ScVehicle::nextCarOnRide_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto* vehicle = GetVehicle();
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto* vehicle = GetVehicle(thisVal);
|
||||
if (vehicle == nullptr)
|
||||
return;
|
||||
return JS_UNDEFINED;
|
||||
|
||||
if (value.type() == DukValue::Type::NUMBER)
|
||||
{
|
||||
vehicle->next_vehicle_on_ride = EntityId::FromUnderlying(value.as_uint());
|
||||
}
|
||||
else
|
||||
if (JS_IsNull(jsValue))
|
||||
{
|
||||
vehicle->next_vehicle_on_ride = EntityId::GetNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
JS_UNPACK_UINT32(entityId, ctx, jsValue);
|
||||
vehicle->next_vehicle_on_ride = EntityId::FromUnderlying(entityId);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
StationIndex::UnderlyingType ScVehicle::currentStation_get() const
|
||||
JSValue ScVehicle::currentStation_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
return vehicle != nullptr ? vehicle->current_station.ToUnderlying() : 0;
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->current_station.ToUnderlying() : 0);
|
||||
}
|
||||
void ScVehicle::currentStation_set(StationIndex::UnderlyingType value)
|
||||
JSValue ScVehicle::currentStation_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
vehicle->current_station = StationIndex::FromUnderlying(value);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint16_t ScVehicle::mass_get() const
|
||||
JSValue ScVehicle::mass_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
return vehicle != nullptr ? vehicle->mass : 0;
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->mass : 0);
|
||||
}
|
||||
void ScVehicle::mass_set(uint16_t value)
|
||||
JSValue ScVehicle::mass_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
vehicle->mass = value;
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
int32_t ScVehicle::acceleration_get() const
|
||||
JSValue ScVehicle::acceleration_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
return vehicle != nullptr ? vehicle->acceleration : 0;
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
return JS_NewInt32(ctx, vehicle != nullptr ? vehicle->acceleration : 0);
|
||||
}
|
||||
void ScVehicle::acceleration_set(int32_t value)
|
||||
JSValue ScVehicle::acceleration_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_INT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
vehicle->acceleration = value;
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
int32_t ScVehicle::velocity_get() const
|
||||
JSValue ScVehicle::velocity_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
return vehicle != nullptr ? vehicle->velocity : 0;
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
return JS_NewInt32(ctx, vehicle != nullptr ? vehicle->velocity : 0);
|
||||
}
|
||||
void ScVehicle::velocity_set(int32_t value)
|
||||
JSValue ScVehicle::velocity_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_INT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
vehicle->velocity = value;
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint8_t ScVehicle::bankRotation_get() const
|
||||
JSValue ScVehicle::bankRotation_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
return vehicle != nullptr ? EnumValue(vehicle->roll) : 0;
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
return JS_NewUint32(ctx, vehicle != nullptr ? EnumValue(vehicle->roll) : 0);
|
||||
}
|
||||
void ScVehicle::bankRotation_set(uint8_t value)
|
||||
JSValue ScVehicle::bankRotation_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
vehicle->roll = static_cast<VehicleRoll>(value);
|
||||
vehicle->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
template<VehicleFlag flag>
|
||||
bool ScVehicle::flag_get() const
|
||||
JSValue ScVehicle::flag_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
return vehicle != nullptr ? vehicle->flags.has(static_cast<VehicleFlag>(flag)) : false;
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
return JS_NewBool(ctx, vehicle != nullptr ? vehicle->flags.has(static_cast<VehicleFlag>(flag)) : false);
|
||||
}
|
||||
|
||||
template<VehicleFlag flag>
|
||||
void ScVehicle::flag_set(bool value)
|
||||
JSValue ScVehicle::flag_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_BOOL(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
if (value)
|
||||
@@ -376,196 +402,209 @@ namespace OpenRCT2::Scripting
|
||||
}
|
||||
vehicle->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
DukValue ScVehicle::colours_get() const
|
||||
JSValue ScVehicle::colours_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto vehicle = GetVehicle();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
return ToDuk<VehicleColour>(ctx, vehicle->colours);
|
||||
return ToJSValue(ctx, vehicle->colours);
|
||||
}
|
||||
return ToDuk(ctx, nullptr);
|
||||
return JS_NULL;
|
||||
}
|
||||
void ScVehicle::colours_set(const DukValue& value)
|
||||
JSValue ScVehicle::colours_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_OBJECT(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
vehicle->colours = FromDuk<VehicleColour>(value);
|
||||
vehicle->colours = JSToVehicleColours(ctx, value);
|
||||
vehicle->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
DukValue ScVehicle::trackLocation_get() const
|
||||
JSValue ScVehicle::trackLocation_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto vehicle = GetVehicle();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
DukObject dukCoords(ctx);
|
||||
dukCoords.Set("x", vehicle->TrackLocation.x);
|
||||
dukCoords.Set("y", vehicle->TrackLocation.y);
|
||||
dukCoords.Set("z", vehicle->TrackLocation.z);
|
||||
dukCoords.Set("direction", vehicle->GetTrackDirection());
|
||||
dukCoords.Set("trackType", EnumValue(vehicle->GetTrackType()));
|
||||
return dukCoords.Take();
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, vehicle->TrackLocation.x));
|
||||
JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, vehicle->TrackLocation.y));
|
||||
JS_SetPropertyStr(ctx, obj, "z", JS_NewInt32(ctx, vehicle->TrackLocation.z));
|
||||
JS_SetPropertyStr(ctx, obj, "direction", JS_NewUint32(ctx, vehicle->GetTrackDirection()));
|
||||
JS_SetPropertyStr(ctx, obj, "trackType", JS_NewUint32(ctx, EnumValue(vehicle->GetTrackType())));
|
||||
return obj;
|
||||
}
|
||||
return ToDuk(ctx, nullptr);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
uint16_t ScVehicle::trackProgress_get() const
|
||||
JSValue ScVehicle::trackProgress_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
return vehicle != nullptr ? vehicle->track_progress : 0;
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->track_progress : 0);
|
||||
}
|
||||
|
||||
int32_t ScVehicle::remainingDistance_get() const
|
||||
JSValue ScVehicle::remainingDistance_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
return vehicle != nullptr ? vehicle->remaining_distance : 0;
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
return JS_NewInt32(ctx, vehicle != nullptr ? vehicle->remaining_distance : 0);
|
||||
}
|
||||
|
||||
uint8_t ScVehicle::subposition_get() const
|
||||
JSValue ScVehicle::subposition_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
return vehicle != nullptr ? static_cast<uint8_t>(vehicle->TrackSubposition) : 0;
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
return JS_NewUint32(ctx, vehicle != nullptr ? static_cast<uint8_t>(vehicle->TrackSubposition) : 0);
|
||||
}
|
||||
|
||||
uint8_t ScVehicle::poweredAcceleration_get() const
|
||||
JSValue ScVehicle::poweredAcceleration_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
return vehicle != nullptr ? vehicle->powered_acceleration : 0;
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->powered_acceleration : 0);
|
||||
}
|
||||
void ScVehicle::poweredAcceleration_set(uint8_t value)
|
||||
JSValue ScVehicle::poweredAcceleration_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
vehicle->powered_acceleration = value;
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint8_t ScVehicle::poweredMaxSpeed_get() const
|
||||
JSValue ScVehicle::poweredMaxSpeed_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
return vehicle != nullptr ? vehicle->speed : 0;
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->speed : 0);
|
||||
}
|
||||
void ScVehicle::poweredMaxSpeed_set(uint8_t value)
|
||||
JSValue ScVehicle::poweredMaxSpeed_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
vehicle->speed = value;
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
std::string ScVehicle::status_get() const
|
||||
JSValue ScVehicle::status_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
return std::string(VehicleStatusMap[vehicle->status]);
|
||||
return JSFromStdString(ctx, VehicleStatusMap[vehicle->status]);
|
||||
}
|
||||
return {};
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
void ScVehicle::status_set(const std::string& value)
|
||||
JSValue ScVehicle::status_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_STR(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
vehicle->status = VehicleStatusMap[value];
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint8_t ScVehicle::spin_get() const
|
||||
JSValue ScVehicle::spin_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
return vehicle->spin_sprite;
|
||||
}
|
||||
return 0;
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->spin_sprite : 0);
|
||||
}
|
||||
void ScVehicle::spin_set(const uint8_t value)
|
||||
JSValue ScVehicle::spin_set(JSContext* ctx, JSValue thisVal, JSValue jsValue)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_UINT32(value, ctx, jsValue);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
vehicle->spin_sprite = value;
|
||||
vehicle->Invalidate();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
std::vector<DukValue> ScVehicle::guests_get() const
|
||||
JSValue ScVehicle::guests_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
std::vector<DukValue> result;
|
||||
auto vehicle = GetVehicle();
|
||||
auto result = JS_NewArray(ctx);
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
size_t len = 0;
|
||||
for (size_t i = 0; i < std::size(vehicle->peep); i++)
|
||||
{
|
||||
auto peep = vehicle->peep[i];
|
||||
if (peep.IsNull())
|
||||
if (!peep.IsNull())
|
||||
{
|
||||
result.push_back(ToDuk(ctx, nullptr));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(ToDuk<int32_t>(ctx, peep.ToUnderlying()));
|
||||
// Set all peep slots between last valid peep and current to NULL (if there were any null peeps).
|
||||
for (size_t j = len; j < i; j++)
|
||||
{
|
||||
JS_SetPropertyInt64(ctx, result, i, JS_NULL);
|
||||
}
|
||||
|
||||
JS_SetPropertyInt64(ctx, result, i, JS_NewUint32(ctx, peep.ToUnderlying()));
|
||||
len = i + 1;
|
||||
}
|
||||
}
|
||||
result.resize(len);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DukValue ScVehicle::gForces_get() const
|
||||
JSValue ScVehicle::gForces_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto vehicle = GetVehicle();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
GForces gForces = vehicle->GetGForces();
|
||||
return ToDuk<GForces>(ctx, gForces);
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "lateralG", JS_NewInt32(ctx, gForces.lateralG));
|
||||
JS_SetPropertyStr(ctx, obj, "verticalG", JS_NewInt32(ctx, gForces.verticalG));
|
||||
return obj;
|
||||
}
|
||||
return ToDuk(ctx, nullptr);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
void ScVehicle::travelBy(int32_t value)
|
||||
JSValue ScVehicle::travelBy(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_INT32(value, ctx, argv[0]);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
vehicle->MoveRelativeDistance(value);
|
||||
EntityTweener::Get().RemoveEntity(vehicle);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void ScVehicle::moveToTrack(int32_t x, int32_t y, int32_t elementIndex)
|
||||
JSValue ScVehicle::moveToTrack(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto vehicle = GetVehicle();
|
||||
JS_UNPACK_INT32(x, ctx, argv[0]);
|
||||
JS_UNPACK_INT32(y, ctx, argv[1]);
|
||||
JS_UNPACK_INT32(elementIndex, ctx, argv[2]);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
auto vehicle = GetVehicle(thisVal);
|
||||
if (vehicle == nullptr)
|
||||
return;
|
||||
return JS_UNDEFINED;
|
||||
|
||||
CoordsXY coords = TileCoordsXY(x, y).ToCoordsXY();
|
||||
auto el = MapGetNthElementAt(coords, elementIndex);
|
||||
if (el == nullptr)
|
||||
return;
|
||||
return JS_UNDEFINED;
|
||||
|
||||
auto origin = GetTrackSegmentOrigin(CoordsXYE(coords, el));
|
||||
if (!origin)
|
||||
return;
|
||||
return JS_UNDEFINED;
|
||||
|
||||
const auto& trackType = el->AsTrack()->GetTrackType();
|
||||
const auto& ted = GetTrackElementDescriptor(trackType);
|
||||
@@ -585,6 +624,7 @@ namespace OpenRCT2::Scripting
|
||||
|
||||
vehicle->UpdateTrackChange();
|
||||
EntityTweener::Get().RemoveEntity(vehicle);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
|
||||
@@ -13,97 +13,96 @@
|
||||
|
||||
#include "../../../entity/EntityTweener.h"
|
||||
#include "../../../ride/Ride.h"
|
||||
#include "../../../ride/Vehicle.h"
|
||||
#include "ScEntity.hpp"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScVehicle : public ScEntity
|
||||
class ScVehicle final : public ScEntity
|
||||
{
|
||||
public:
|
||||
ScVehicle(EntityId id);
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
static JSValue New(JSContext* ctx, EntityId entityId);
|
||||
|
||||
private:
|
||||
Vehicle* GetVehicle() const;
|
||||
static void AddFuncs(JSContext* ctx, JSValue obj);
|
||||
static Vehicle* GetVehicle(JSValue thisVal);
|
||||
|
||||
ObjectEntryIndex rideObject_get() const;
|
||||
void rideObject_set(ObjectEntryIndex value);
|
||||
static JSValue rideObject_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue rideObject_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t vehicleObject_get() const;
|
||||
void vehicleObject_set(uint8_t value);
|
||||
static JSValue vehicleObject_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue vehicleObject_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t spriteType_get() const;
|
||||
void spriteType_set(uint8_t value);
|
||||
static JSValue spriteType_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue spriteType_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
int32_t ride_get() const;
|
||||
void ride_set(int32_t value);
|
||||
static JSValue ride_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue ride_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t numSeats_get() const;
|
||||
void numSeats_set(uint8_t value);
|
||||
static JSValue numSeats_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue numSeats_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
DukValue nextCarOnTrain_get() const;
|
||||
void nextCarOnTrain_set(DukValue value);
|
||||
static JSValue nextCarOnTrain_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue nextCarOnTrain_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
DukValue previousCarOnRide_get() const;
|
||||
void previousCarOnRide_set(DukValue value);
|
||||
static JSValue previousCarOnRide_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue previousCarOnRide_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
DukValue nextCarOnRide_get() const;
|
||||
void nextCarOnRide_set(DukValue value);
|
||||
static JSValue nextCarOnRide_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue nextCarOnRide_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
StationIndex::UnderlyingType currentStation_get() const;
|
||||
void currentStation_set(StationIndex::UnderlyingType value);
|
||||
static JSValue currentStation_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue currentStation_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint16_t mass_get() const;
|
||||
void mass_set(uint16_t value);
|
||||
static JSValue mass_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue mass_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
int32_t acceleration_get() const;
|
||||
void acceleration_set(int32_t value);
|
||||
static JSValue acceleration_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue acceleration_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
int32_t velocity_get() const;
|
||||
void velocity_set(int32_t value);
|
||||
static JSValue velocity_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue velocity_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t bankRotation_get() const;
|
||||
void bankRotation_set(uint8_t value);
|
||||
static JSValue bankRotation_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue bankRotation_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
template<VehicleFlag flag>
|
||||
bool flag_get() const;
|
||||
static JSValue flag_get(JSContext* ctx, JSValue thisVal);
|
||||
template<VehicleFlag flag>
|
||||
void flag_set(bool value);
|
||||
static JSValue flag_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
DukValue colours_get() const;
|
||||
void colours_set(const DukValue& value);
|
||||
static JSValue colours_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue colours_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
DukValue trackLocation_get() const;
|
||||
void trackLocation_set(const DukValue& value);
|
||||
static JSValue trackLocation_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
uint16_t trackProgress_get() const;
|
||||
static JSValue trackProgress_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
int32_t remainingDistance_get() const;
|
||||
static JSValue remainingDistance_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
uint8_t subposition_get() const;
|
||||
static JSValue subposition_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
uint8_t poweredAcceleration_get() const;
|
||||
void poweredAcceleration_set(uint8_t value);
|
||||
static JSValue poweredAcceleration_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue poweredAcceleration_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t poweredMaxSpeed_get() const;
|
||||
void poweredMaxSpeed_set(uint8_t value);
|
||||
static JSValue poweredMaxSpeed_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue poweredMaxSpeed_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
std::string status_get() const;
|
||||
void status_set(const std::string& value);
|
||||
static JSValue status_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue status_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
uint8_t spin_get() const;
|
||||
void spin_set(uint8_t value);
|
||||
static JSValue spin_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue spin_set(JSContext* ctx, JSValue thisVal, JSValue jsValue);
|
||||
|
||||
std::vector<DukValue> guests_get() const;
|
||||
static JSValue guests_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
DukValue gForces_get() const;
|
||||
static JSValue gForces_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
void travelBy(int32_t value);
|
||||
static JSValue travelBy(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
|
||||
void moveToTrack(int32_t x, int32_t y, int32_t elementIndex);
|
||||
static JSValue moveToTrack(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
};
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -14,372 +14,456 @@
|
||||
#include "../../../Cheats.h"
|
||||
#include "../../../GameState.h"
|
||||
#include "../../../world/Park.h"
|
||||
#include "../../Duktape.hpp"
|
||||
#include "../../ScriptEngine.h"
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScCheats
|
||||
class ScCheats;
|
||||
extern ScCheats gScCheats;
|
||||
|
||||
class ScCheats final : public ScBase
|
||||
{
|
||||
public:
|
||||
static void Register(duk_context* ctx)
|
||||
JSValue New(JSContext* ctx)
|
||||
{
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::allowArbitraryRideTypeChanges_get, &ScCheats::allowArbitraryRideTypeChanges_set,
|
||||
"allowArbitraryRideTypeChanges");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::allowTrackPlaceInvalidHeights_get, &ScCheats::allowTrackPlaceInvalidHeights_set,
|
||||
"allowTrackPlaceInvalidHeights");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::buildInPauseMode_get, &ScCheats::buildInPauseMode_set, "buildInPauseMode");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::disableAllBreakdowns_get, &ScCheats::disableAllBreakdowns_set, "disableAllBreakdowns");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::disableBrakesFailure_get, &ScCheats::disableBrakesFailure_set, "disableBrakesFailure");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::disableClearanceChecks_get, &ScCheats::disableClearanceChecks_set, "disableClearanceChecks");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::disableLittering_get, &ScCheats::disableLittering_set, "disableLittering");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::disablePlantAging_get, &ScCheats::disablePlantAging_set, "disablePlantAging");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::allowRegularPathAsQueue_get, &ScCheats::allowRegularPathAsQueue_set, "allowRegularPathAsQueue");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::allowSpecialColourSchemes_get, &ScCheats::allowSpecialColourSchemes_set,
|
||||
"allowSpecialColourSchemes");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::disableRideValueAging_get, &ScCheats::disableRideValueAging_set, "disableRideValueAging");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::disableSupportLimits_get, &ScCheats::disableSupportLimits_set, "disableSupportLimits");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::disableTrainLengthLimit_get, &ScCheats::disableTrainLengthLimit_set, "disableTrainLengthLimit");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::disableVandalism_get, &ScCheats::disableVandalism_set, "disableVandalism");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::enableAllDrawableTrackPieces_get, &ScCheats::enableAllDrawableTrackPieces_set,
|
||||
"enableAllDrawableTrackPieces");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::enableChainLiftOnAllTrack_get, &ScCheats::enableChainLiftOnAllTrack_set,
|
||||
"enableChainLiftOnAllTrack");
|
||||
dukglue_register_property(ctx, &ScCheats::fastLiftHill_get, &ScCheats::fastLiftHill_set, "fastLiftHill");
|
||||
dukglue_register_property(ctx, &ScCheats::freezeWeather_get, &ScCheats::freezeWeather_set, "freezeWeather");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::ignoreResearchStatus_get, &ScCheats::ignoreResearchStatus_set, "ignoreResearchStatus");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::ignoreRideIntensity_get, &ScCheats::ignoreRideIntensity_set, "ignoreRideIntensity");
|
||||
dukglue_register_property(ctx, &ScCheats::ignoreRidePrice_get, &ScCheats::ignoreRidePrice_set, "ignoreRidePrice");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::neverendingMarketing_get, &ScCheats::neverendingMarketing_set, "neverendingMarketing");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::forcedParkRating_get, &ScCheats::forcedParkRating_set, "forcedParkRating");
|
||||
dukglue_register_property(ctx, &ScCheats::sandboxMode_get, &ScCheats::sandboxMode_set, "sandboxMode");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::showAllOperatingModes_get, &ScCheats::showAllOperatingModes_set, "showAllOperatingModes");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::showVehiclesFromOtherTrackTypes_get, &ScCheats::showVehiclesFromOtherTrackTypes_set,
|
||||
"showVehiclesFromOtherTrackTypes");
|
||||
dukglue_register_property(
|
||||
ctx, &ScCheats::makeAllDestructible_get, &ScCheats::makeAllDestructible_set, "makeAllDestructible");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF(
|
||||
"allowArbitraryRideTypeChanges", &ScCheats::allowArbitraryRideTypeChanges_get,
|
||||
&ScCheats::allowArbitraryRideTypeChanges_set),
|
||||
JS_CGETSET_DEF(
|
||||
"allowTrackPlaceInvalidHeights", &ScCheats::allowTrackPlaceInvalidHeights_get,
|
||||
&ScCheats::allowTrackPlaceInvalidHeights_set),
|
||||
JS_CGETSET_DEF("buildInPauseMode", &ScCheats::buildInPauseMode_get, &ScCheats::buildInPauseMode_set),
|
||||
JS_CGETSET_DEF(
|
||||
"disableAllBreakdowns", &ScCheats::disableAllBreakdowns_get, &ScCheats::disableAllBreakdowns_set),
|
||||
JS_CGETSET_DEF(
|
||||
"disableBrakesFailure", &ScCheats::disableBrakesFailure_get, &ScCheats::disableBrakesFailure_set),
|
||||
JS_CGETSET_DEF(
|
||||
"disableClearanceChecks", &ScCheats::disableClearanceChecks_get, &ScCheats::disableClearanceChecks_set),
|
||||
JS_CGETSET_DEF("disableLittering", &ScCheats::disableLittering_get, &ScCheats::disableLittering_set),
|
||||
JS_CGETSET_DEF("disablePlantAging", &ScCheats::disablePlantAging_get, &ScCheats::disablePlantAging_set),
|
||||
JS_CGETSET_DEF(
|
||||
"allowRegularPathAsQueue", &ScCheats::allowRegularPathAsQueue_get, &ScCheats::allowRegularPathAsQueue_set),
|
||||
JS_CGETSET_DEF(
|
||||
"allowSpecialColourSchemes", &ScCheats::allowSpecialColourSchemes_get,
|
||||
&ScCheats::allowSpecialColourSchemes_set),
|
||||
JS_CGETSET_DEF(
|
||||
"disableRideValueAging", &ScCheats::disableRideValueAging_get, &ScCheats::disableRideValueAging_set),
|
||||
JS_CGETSET_DEF(
|
||||
"disableSupportLimits", &ScCheats::disableSupportLimits_get, &ScCheats::disableSupportLimits_set),
|
||||
JS_CGETSET_DEF(
|
||||
"disableTrainLengthLimit", &ScCheats::disableTrainLengthLimit_get, &ScCheats::disableTrainLengthLimit_set),
|
||||
JS_CGETSET_DEF("disableVandalism", &ScCheats::disableVandalism_get, &ScCheats::disableVandalism_set),
|
||||
JS_CGETSET_DEF(
|
||||
"enableAllDrawableTrackPieces", &ScCheats::enableAllDrawableTrackPieces_get,
|
||||
&ScCheats::enableAllDrawableTrackPieces_set),
|
||||
JS_CGETSET_DEF(
|
||||
"enableChainLiftOnAllTrack", &ScCheats::enableChainLiftOnAllTrack_get,
|
||||
&ScCheats::enableChainLiftOnAllTrack_set),
|
||||
JS_CGETSET_DEF("fastLiftHill", &ScCheats::fastLiftHill_get, &ScCheats::fastLiftHill_set),
|
||||
JS_CGETSET_DEF("freezeWeather", &ScCheats::freezeWeather_get, &ScCheats::freezeWeather_set),
|
||||
JS_CGETSET_DEF(
|
||||
"ignoreResearchStatus", &ScCheats::ignoreResearchStatus_get, &ScCheats::ignoreResearchStatus_set),
|
||||
JS_CGETSET_DEF("ignoreRideIntensity", &ScCheats::ignoreRideIntensity_get, &ScCheats::ignoreRideIntensity_set),
|
||||
JS_CGETSET_DEF("ignoreRidePrice", &ScCheats::ignoreRidePrice_get, &ScCheats::ignoreRidePrice_set),
|
||||
JS_CGETSET_DEF(
|
||||
"neverendingMarketing", &ScCheats::neverendingMarketing_get, &ScCheats::neverendingMarketing_set),
|
||||
JS_CGETSET_DEF("forcedParkRating", &ScCheats::forcedParkRating_get, &ScCheats::forcedParkRating_set),
|
||||
JS_CGETSET_DEF("sandboxMode", &ScCheats::sandboxMode_get, &ScCheats::sandboxMode_set),
|
||||
JS_CGETSET_DEF(
|
||||
"showAllOperatingModes", &ScCheats::showAllOperatingModes_get, &ScCheats::showAllOperatingModes_set),
|
||||
JS_CGETSET_DEF(
|
||||
"showVehiclesFromOtherTrackTypes", &ScCheats::showVehiclesFromOtherTrackTypes_get,
|
||||
&ScCheats::showVehiclesFromOtherTrackTypes_set),
|
||||
JS_CGETSET_DEF("makeAllDestructible", &ScCheats::makeAllDestructible_get, &ScCheats::makeAllDestructible_set)
|
||||
};
|
||||
return MakeWithOpaque(ctx, funcs, nullptr);
|
||||
}
|
||||
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "Cheats");
|
||||
}
|
||||
|
||||
private:
|
||||
bool allowArbitraryRideTypeChanges_get()
|
||||
static JSValue allowArbitraryRideTypeChanges_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.allowArbitraryRideTypeChanges;
|
||||
return JS_NewBool(ctx, getGameState().cheats.allowArbitraryRideTypeChanges);
|
||||
}
|
||||
|
||||
void allowArbitraryRideTypeChanges_set(bool value)
|
||||
static JSValue allowArbitraryRideTypeChanges_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.allowArbitraryRideTypeChanges = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value)
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.allowArbitraryRideTypeChanges = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool allowTrackPlaceInvalidHeights_get()
|
||||
static JSValue allowTrackPlaceInvalidHeights_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.allowTrackPlaceInvalidHeights;
|
||||
return JS_NewBool(ctx, getGameState().cheats.allowTrackPlaceInvalidHeights);
|
||||
}
|
||||
|
||||
void allowTrackPlaceInvalidHeights_set(bool value)
|
||||
static JSValue allowTrackPlaceInvalidHeights_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.allowTrackPlaceInvalidHeights = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value)
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.allowTrackPlaceInvalidHeights = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool buildInPauseMode_get()
|
||||
static JSValue buildInPauseMode_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.buildInPauseMode;
|
||||
return JS_NewBool(ctx, getGameState().cheats.buildInPauseMode);
|
||||
}
|
||||
|
||||
void buildInPauseMode_set(bool value)
|
||||
static JSValue buildInPauseMode_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.buildInPauseMode = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value)
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.buildInPauseMode = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool disableAllBreakdowns_get()
|
||||
static JSValue disableAllBreakdowns_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.disableAllBreakdowns;
|
||||
return JS_NewBool(ctx, getGameState().cheats.disableAllBreakdowns);
|
||||
}
|
||||
|
||||
void disableAllBreakdowns_set(bool value)
|
||||
static JSValue disableAllBreakdowns_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.disableAllBreakdowns = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value)
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.disableAllBreakdowns = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool disableBrakesFailure_get()
|
||||
static JSValue disableBrakesFailure_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.disableBrakesFailure;
|
||||
return JS_NewBool(ctx, getGameState().cheats.disableBrakesFailure);
|
||||
}
|
||||
|
||||
void disableBrakesFailure_set(bool value)
|
||||
static JSValue disableBrakesFailure_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.disableBrakesFailure = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value)
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.disableBrakesFailure = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool disableClearanceChecks_get()
|
||||
static JSValue disableClearanceChecks_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.disableClearanceChecks;
|
||||
return JS_NewBool(ctx, getGameState().cheats.disableClearanceChecks);
|
||||
}
|
||||
|
||||
void disableClearanceChecks_set(bool value)
|
||||
static JSValue disableClearanceChecks_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.disableClearanceChecks = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value)
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.disableClearanceChecks = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool disableLittering_get()
|
||||
static JSValue disableLittering_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.disableLittering;
|
||||
return JS_NewBool(ctx, getGameState().cheats.disableLittering);
|
||||
}
|
||||
|
||||
void disableLittering_set(bool value)
|
||||
static JSValue disableLittering_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.disableLittering = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.disableLittering = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool disablePlantAging_get()
|
||||
static JSValue disablePlantAging_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.disablePlantAging;
|
||||
return JS_NewBool(ctx, getGameState().cheats.disablePlantAging);
|
||||
}
|
||||
|
||||
void disablePlantAging_set(bool value)
|
||||
static JSValue disablePlantAging_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.disablePlantAging = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.disablePlantAging = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool allowRegularPathAsQueue_get()
|
||||
static JSValue allowRegularPathAsQueue_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.allowRegularPathAsQueue;
|
||||
return JS_NewBool(ctx, getGameState().cheats.allowRegularPathAsQueue);
|
||||
}
|
||||
|
||||
void allowRegularPathAsQueue_set(bool value)
|
||||
static JSValue allowRegularPathAsQueue_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.allowRegularPathAsQueue = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.allowRegularPathAsQueue = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool allowSpecialColourSchemes_get()
|
||||
static JSValue allowSpecialColourSchemes_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.allowSpecialColourSchemes;
|
||||
return JS_NewBool(ctx, getGameState().cheats.allowSpecialColourSchemes);
|
||||
}
|
||||
|
||||
void allowSpecialColourSchemes_set(bool value)
|
||||
static JSValue allowSpecialColourSchemes_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.allowSpecialColourSchemes = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.allowSpecialColourSchemes = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool disableRideValueAging_get()
|
||||
static JSValue disableRideValueAging_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.disableRideValueAging;
|
||||
return JS_NewBool(ctx, getGameState().cheats.disableRideValueAging);
|
||||
}
|
||||
|
||||
void disableRideValueAging_set(bool value)
|
||||
static JSValue disableRideValueAging_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.disableRideValueAging = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.disableRideValueAging = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool disableSupportLimits_get()
|
||||
static JSValue disableSupportLimits_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.disableSupportLimits;
|
||||
return JS_NewBool(ctx, getGameState().cheats.disableSupportLimits);
|
||||
}
|
||||
|
||||
void disableSupportLimits_set(bool value)
|
||||
static JSValue disableSupportLimits_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.disableSupportLimits = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.disableSupportLimits = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool disableTrainLengthLimit_get()
|
||||
static JSValue disableTrainLengthLimit_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.disableTrainLengthLimit;
|
||||
return JS_NewBool(ctx, getGameState().cheats.disableTrainLengthLimit);
|
||||
}
|
||||
|
||||
void disableTrainLengthLimit_set(bool value)
|
||||
static JSValue disableTrainLengthLimit_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.disableTrainLengthLimit = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.disableTrainLengthLimit = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool disableVandalism_get()
|
||||
static JSValue disableVandalism_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.disableVandalism;
|
||||
return JS_NewBool(ctx, getGameState().cheats.disableVandalism);
|
||||
}
|
||||
|
||||
void disableVandalism_set(bool value)
|
||||
static JSValue disableVandalism_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.disableVandalism = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.disableVandalism = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool enableAllDrawableTrackPieces_get()
|
||||
static JSValue enableAllDrawableTrackPieces_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.enableAllDrawableTrackPieces;
|
||||
return JS_NewBool(ctx, getGameState().cheats.enableAllDrawableTrackPieces);
|
||||
}
|
||||
|
||||
void enableAllDrawableTrackPieces_set(bool value)
|
||||
static JSValue enableAllDrawableTrackPieces_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.enableAllDrawableTrackPieces = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.enableAllDrawableTrackPieces = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool enableChainLiftOnAllTrack_get()
|
||||
static JSValue enableChainLiftOnAllTrack_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.enableChainLiftOnAllTrack;
|
||||
return JS_NewBool(ctx, getGameState().cheats.enableChainLiftOnAllTrack);
|
||||
}
|
||||
|
||||
void enableChainLiftOnAllTrack_set(bool value)
|
||||
static JSValue enableChainLiftOnAllTrack_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.enableChainLiftOnAllTrack = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.enableChainLiftOnAllTrack = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool fastLiftHill_get()
|
||||
static JSValue fastLiftHill_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.unlockOperatingLimits;
|
||||
return JS_NewBool(ctx, getGameState().cheats.unlockOperatingLimits);
|
||||
}
|
||||
|
||||
void fastLiftHill_set(bool value)
|
||||
static JSValue fastLiftHill_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.unlockOperatingLimits = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.unlockOperatingLimits = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool freezeWeather_get()
|
||||
static JSValue freezeWeather_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.freezeWeather;
|
||||
return JS_NewBool(ctx, getGameState().cheats.freezeWeather);
|
||||
}
|
||||
|
||||
void freezeWeather_set(bool value)
|
||||
static JSValue freezeWeather_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.freezeWeather = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.freezeWeather = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool ignoreResearchStatus_get()
|
||||
static JSValue ignoreResearchStatus_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.ignoreResearchStatus;
|
||||
return JS_NewBool(ctx, getGameState().cheats.ignoreResearchStatus);
|
||||
}
|
||||
|
||||
void ignoreResearchStatus_set(bool value)
|
||||
static JSValue ignoreResearchStatus_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.ignoreResearchStatus = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value)
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.ignoreResearchStatus = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool ignoreRideIntensity_get()
|
||||
static JSValue ignoreRideIntensity_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.ignoreRideIntensity;
|
||||
return JS_NewBool(ctx, getGameState().cheats.ignoreRideIntensity);
|
||||
}
|
||||
|
||||
void ignoreRideIntensity_set(bool value)
|
||||
static JSValue ignoreRideIntensity_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.ignoreRideIntensity = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value)
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.ignoreRideIntensity = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool ignoreRidePrice_get()
|
||||
static JSValue ignoreRidePrice_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.ignorePrice;
|
||||
return JS_NewBool(ctx, getGameState().cheats.ignorePrice);
|
||||
}
|
||||
|
||||
void ignoreRidePrice_set(bool value)
|
||||
static JSValue ignoreRidePrice_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.ignorePrice = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.ignorePrice = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool neverendingMarketing_get()
|
||||
static JSValue neverendingMarketing_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.neverendingMarketing;
|
||||
return JS_NewBool(ctx, getGameState().cheats.neverendingMarketing);
|
||||
}
|
||||
|
||||
void neverendingMarketing_set(bool value)
|
||||
static JSValue neverendingMarketing_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.neverendingMarketing = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.neverendingMarketing = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
int32_t forcedParkRating_get()
|
||||
static JSValue forcedParkRating_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.forcedParkRating;
|
||||
return JS_NewInt32(ctx, getGameState().cheats.forcedParkRating);
|
||||
}
|
||||
|
||||
void forcedParkRating_set(int32_t value)
|
||||
static JSValue forcedParkRating_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
int32_t adjusted = std::max(-1, std::min(value, 999));
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
int32_t adjusted = std::max(-1, std::min(valueInt, 999));
|
||||
getGameState().cheats.forcedParkRating = adjusted;
|
||||
Park::SetForcedRating(adjusted);
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool sandboxMode_get()
|
||||
static JSValue sandboxMode_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.sandboxMode;
|
||||
return JS_NewBool(ctx, getGameState().cheats.sandboxMode);
|
||||
}
|
||||
|
||||
void sandboxMode_set(bool value)
|
||||
static JSValue sandboxMode_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.sandboxMode = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.sandboxMode = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool showAllOperatingModes_get()
|
||||
static JSValue showAllOperatingModes_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.showAllOperatingModes;
|
||||
return JS_NewBool(ctx, getGameState().cheats.showAllOperatingModes);
|
||||
}
|
||||
|
||||
void showAllOperatingModes_set(bool value)
|
||||
static JSValue showAllOperatingModes_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.showAllOperatingModes = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.showAllOperatingModes = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool showVehiclesFromOtherTrackTypes_get()
|
||||
static JSValue showVehiclesFromOtherTrackTypes_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.showVehiclesFromOtherTrackTypes;
|
||||
return JS_NewBool(ctx, getGameState().cheats.showVehiclesFromOtherTrackTypes);
|
||||
}
|
||||
|
||||
void showVehiclesFromOtherTrackTypes_set(bool value)
|
||||
static JSValue showVehiclesFromOtherTrackTypes_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.showVehiclesFromOtherTrackTypes = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.showVehiclesFromOtherTrackTypes = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool makeAllDestructible_get()
|
||||
static JSValue makeAllDestructible_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return getGameState().cheats.makeAllDestructible;
|
||||
return JS_NewBool(ctx, getGameState().cheats.makeAllDestructible);
|
||||
}
|
||||
|
||||
void makeAllDestructible_set(bool value)
|
||||
static JSValue makeAllDestructible_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
getGameState().cheats.makeAllDestructible = value;
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
getGameState().cheats.makeAllDestructible = valueBool;
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -14,11 +14,13 @@
|
||||
#include "../../../Context.h"
|
||||
#include "../../../config/Config.h"
|
||||
#include "../../../localisation/LocalisationService.h"
|
||||
#include "../../Duktape.hpp"
|
||||
#include "../../ScriptEngine.h"
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScConfiguration;
|
||||
extern ScConfiguration gScConfiguration;
|
||||
|
||||
enum class ScConfigurationKind
|
||||
{
|
||||
User,
|
||||
@@ -26,36 +28,53 @@ namespace OpenRCT2::Scripting
|
||||
Park
|
||||
};
|
||||
|
||||
class ScConfiguration
|
||||
class ScConfiguration final : public ScBase
|
||||
{
|
||||
private:
|
||||
ScConfigurationKind _kind;
|
||||
DukValue _backingObject;
|
||||
|
||||
public:
|
||||
// context.configuration
|
||||
ScConfiguration()
|
||||
: _kind(ScConfigurationKind::User)
|
||||
struct ConfigurationData
|
||||
{
|
||||
ScConfigurationKind _kind;
|
||||
std::string _pluginName;
|
||||
};
|
||||
|
||||
static JSValue GetParkStorageForPlugin(JSContext* ctx, const std::string& pluginName)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
JSValue parkStore = scriptEngine.GetParkStorage();
|
||||
JSValue pluginStore = JS_GetPropertyStr(ctx, parkStore, pluginName.c_str());
|
||||
|
||||
// Create if it doesn't exist
|
||||
if (!JS_IsObject(pluginStore))
|
||||
{
|
||||
JS_FreeValue(ctx, pluginStore);
|
||||
pluginStore = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, parkStore, pluginName.c_str(), JS_DupValue(ctx, pluginStore));
|
||||
}
|
||||
|
||||
return pluginStore;
|
||||
}
|
||||
|
||||
// context.sharedStorage / context.getParkStorage
|
||||
ScConfiguration(ScConfigurationKind kind, const DukValue& backingObject)
|
||||
: _kind(kind)
|
||||
, _backingObject(backingObject)
|
||||
static JSValue GetStoreForConfigType(JSContext* ctx, const ConfigurationData* data)
|
||||
{
|
||||
switch (data->_kind)
|
||||
{
|
||||
case ScConfigurationKind::Park:
|
||||
{
|
||||
return GetParkStorageForPlugin(ctx, data->_pluginName);
|
||||
}
|
||||
case ScConfigurationKind::Shared:
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
return JS_DupValue(ctx, scriptEngine.GetSharedStorage());
|
||||
}
|
||||
case ScConfigurationKind::User:
|
||||
default:
|
||||
Guard::Fail("Invalid ScConfigurationKind");
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
static void Register(duk_context* ctx)
|
||||
{
|
||||
dukglue_register_method(ctx, &ScConfiguration::getAll, "getAll");
|
||||
dukglue_register_method(ctx, &ScConfiguration::get, "get");
|
||||
dukglue_register_method(ctx, &ScConfiguration::set, "set");
|
||||
dukglue_register_method(ctx, &ScConfiguration::has, "has");
|
||||
}
|
||||
|
||||
private:
|
||||
std::pair<std::string_view, std::string_view> GetNextNamespace(std::string_view input) const
|
||||
static std::pair<std::string_view, std::string_view> GetNextNamespace(std::string_view input)
|
||||
{
|
||||
auto pos = input.find('.');
|
||||
if (pos == std::string_view::npos)
|
||||
@@ -66,16 +85,16 @@ namespace OpenRCT2::Scripting
|
||||
return std::make_pair(input.substr(0, pos), input.substr(pos + 1));
|
||||
}
|
||||
|
||||
std::pair<std::string_view, std::string_view> GetNamespaceAndKey(std::string_view input) const
|
||||
static std::pair<std::string_view, std::string_view> GetNamespaceAndKey(std::string_view input)
|
||||
{
|
||||
auto pos = input.find_last_of('.');
|
||||
return pos == std::string_view::npos ? std::make_pair(std::string_view(), input)
|
||||
: std::make_pair(input.substr(0, pos), input.substr(pos + 1));
|
||||
}
|
||||
|
||||
std::optional<DukValue> GetNamespaceObject(std::string_view ns) const
|
||||
static JSValue GetNamespaceObject(JSContext* ctx, const ConfigurationData* data, std::string_view ns)
|
||||
{
|
||||
auto store = _backingObject;
|
||||
auto store = GetStoreForConfigType(ctx, data);
|
||||
if (!ns.empty())
|
||||
{
|
||||
auto k = ns;
|
||||
@@ -83,17 +102,19 @@ namespace OpenRCT2::Scripting
|
||||
do
|
||||
{
|
||||
auto [next, remainder] = GetNextNamespace(k);
|
||||
store = store[next];
|
||||
auto oldStore = store;
|
||||
store = JS_GetPropertyStr(ctx, store, std::string(next).c_str());
|
||||
JS_FreeValue(ctx, oldStore);
|
||||
k = remainder;
|
||||
end = store.type() == DukValue::Type::UNDEFINED || remainder.empty();
|
||||
end = JS_IsUndefined(store) || remainder.empty();
|
||||
} while (!end);
|
||||
}
|
||||
return store.type() == DukValue::OBJECT ? std::make_optional(store) : std::nullopt;
|
||||
return store;
|
||||
}
|
||||
|
||||
DukValue GetOrCreateNamespaceObject(duk_context* ctx, std::string_view ns) const
|
||||
static JSValue GetOrCreateNamespaceObject(JSContext* ctx, const ConfigurationData* data, std::string_view ns)
|
||||
{
|
||||
auto store = _backingObject;
|
||||
auto store = GetStoreForConfigType(ctx, data);
|
||||
if (!ns.empty())
|
||||
{
|
||||
std::string_view k = ns;
|
||||
@@ -101,19 +122,21 @@ namespace OpenRCT2::Scripting
|
||||
do
|
||||
{
|
||||
auto [next, remainder] = GetNextNamespace(k);
|
||||
auto subStore = store[next];
|
||||
std::string nextStr(next);
|
||||
k = remainder;
|
||||
if (subStore.type() == DukValue::Type::UNDEFINED)
|
||||
|
||||
auto subStore = JS_GetPropertyStr(ctx, store, nextStr.c_str());
|
||||
auto oldStore = store;
|
||||
if (JS_IsUndefined(subStore))
|
||||
{
|
||||
store.push();
|
||||
duk_push_object(ctx);
|
||||
store = DukValue::copy_from_stack(ctx);
|
||||
duk_put_prop_lstring(ctx, -2, next.data(), next.size());
|
||||
duk_pop(ctx);
|
||||
store = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, oldStore, nextStr.c_str(), JS_DupValue(ctx, store));
|
||||
JS_FreeValue(ctx, oldStore);
|
||||
}
|
||||
else
|
||||
{
|
||||
store = std::move(subStore);
|
||||
store = subStore;
|
||||
JS_FreeValue(ctx, oldStore);
|
||||
}
|
||||
end = remainder.empty();
|
||||
} while (!end);
|
||||
@@ -121,14 +144,14 @@ namespace OpenRCT2::Scripting
|
||||
return store;
|
||||
}
|
||||
|
||||
bool IsValidNamespace(std::string_view ns) const
|
||||
static bool IsValidNamespace(std::string_view ns, ScConfigurationKind kind)
|
||||
{
|
||||
if (!ns.empty() && (ns[0] == '.' || ns[ns.size() - 1] == '.'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_kind != ScConfigurationKind::Park)
|
||||
if (kind != ScConfigurationKind::Park)
|
||||
{
|
||||
if (ns.empty())
|
||||
{
|
||||
@@ -146,154 +169,211 @@ namespace OpenRCT2::Scripting
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsValidKey(std::string_view key) const
|
||||
static bool IsValidKey(std::string_view key)
|
||||
{
|
||||
return !key.empty() && key.find('.') == std::string_view::npos;
|
||||
}
|
||||
|
||||
DukValue getAll(const DukValue& dukNamespace) const
|
||||
static JSValue getAll(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
DukValue result;
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
JSValue jsNamespace = argv[0];
|
||||
ConfigurationData* data = gScConfiguration.GetOpaque<ConfigurationData*>(thisVal);
|
||||
|
||||
std::string ns = "";
|
||||
if (dukNamespace.type() == DukValue::Type::STRING)
|
||||
if (JS_IsString(jsNamespace))
|
||||
{
|
||||
ns = dukNamespace.as_string();
|
||||
ns = JSToStdString(ctx, jsNamespace);
|
||||
}
|
||||
else if (dukNamespace.type() != DukValue::Type::UNDEFINED)
|
||||
else if (!JS_IsUndefined(jsNamespace))
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Namespace was invalid.");
|
||||
JS_ThrowPlainError(ctx, "Namespace was invalid.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
if (IsValidNamespace(ns))
|
||||
if (IsValidNamespace(ns, data->_kind))
|
||||
{
|
||||
if (_kind == ScConfigurationKind::User)
|
||||
if (data->_kind == ScConfigurationKind::User)
|
||||
{
|
||||
DukObject obj(ctx);
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
if (ns == "general")
|
||||
{
|
||||
obj.Set("general.language", Config::Get().general.language);
|
||||
obj.Set("general.showFps", Config::Get().general.showFPS);
|
||||
auto& localisationService = GetContext()->GetLocalisationService();
|
||||
auto locale = localisationService.GetCurrentLanguageLocale();
|
||||
JS_SetPropertyStr(ctx, obj, "general.language", JSFromStdString(ctx, locale));
|
||||
JS_SetPropertyStr(ctx, obj, "general.showFps", JS_NewBool(ctx, Config::Get().general.showFPS));
|
||||
}
|
||||
result = obj.Take();
|
||||
return obj;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto obj = GetNamespaceObject(ns);
|
||||
result = obj ? *obj : DukObject(ctx).Take();
|
||||
auto obj = GetNamespaceObject(ctx, data, ns);
|
||||
if (!JS_IsObject(obj))
|
||||
{
|
||||
JS_FreeValue(ctx, obj);
|
||||
return JS_NewObject(ctx);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Namespace was invalid.");
|
||||
JS_ThrowPlainError(ctx, "Namespace was invalid.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DukValue get(const std::string& key, const DukValue& defaultValue) const
|
||||
static JSValue get(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
if (_kind == ScConfigurationKind::User)
|
||||
JS_UNPACK_STR(key, ctx, argv[0]);
|
||||
JSValue defaultValue = argv[1];
|
||||
ConfigurationData* data = gScConfiguration.GetOpaque<ConfigurationData*>(thisVal);
|
||||
|
||||
if (data->_kind == ScConfigurationKind::User)
|
||||
{
|
||||
if (key == "general.language")
|
||||
{
|
||||
auto& localisationService = GetContext()->GetLocalisationService();
|
||||
auto locale = localisationService.GetCurrentLanguageLocale();
|
||||
duk_push_lstring(ctx, locale.data(), locale.size());
|
||||
return DukValue::take_from_stack(ctx);
|
||||
return JSFromStdString(ctx, locale);
|
||||
}
|
||||
if (key == "general.showFps")
|
||||
{
|
||||
duk_push_boolean(ctx, Config::Get().general.showFPS);
|
||||
return DukValue::take_from_stack(ctx);
|
||||
return JS_NewBool(ctx, Config::Get().general.showFPS);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto [ns, n] = GetNamespaceAndKey(key);
|
||||
if (!IsValidNamespace(ns))
|
||||
if (!IsValidNamespace(ns, data->_kind))
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Namespace was invalid.");
|
||||
JS_ThrowPlainError(ctx, "Namespace was invalid.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
else if (!IsValidKey(n))
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Key was invalid.");
|
||||
JS_ThrowPlainError(ctx, "Key was invalid.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto obj = GetNamespaceObject(ns);
|
||||
if (obj)
|
||||
auto obj = GetNamespaceObject(ctx, data, ns);
|
||||
if (JS_IsObject(obj))
|
||||
{
|
||||
auto val = (*obj)[n];
|
||||
if (val.type() != DukValue::Type::UNDEFINED)
|
||||
auto val = JS_GetPropertyStr(ctx, obj, std::string(n).c_str());
|
||||
JS_FreeValue(ctx, obj);
|
||||
if (!JS_IsUndefined(val))
|
||||
{
|
||||
return val;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
JS_FreeValue(ctx, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
return JS_DupValue(ctx, defaultValue);
|
||||
}
|
||||
|
||||
void set(const std::string& key, const DukValue& value) const
|
||||
static JSValue set(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto ctx = scriptEngine.GetContext();
|
||||
if (_kind == ScConfigurationKind::User)
|
||||
JS_UNPACK_STR(key, ctx, argv[0]);
|
||||
JSValue value = argv[1];
|
||||
ConfigurationData* data = gScConfiguration.GetOpaque<ConfigurationData*>(thisVal);
|
||||
|
||||
if (data->_kind == ScConfigurationKind::User)
|
||||
{
|
||||
try
|
||||
if (key == "general.showFps")
|
||||
{
|
||||
if (key == "general.showFps")
|
||||
if (JS_IsBool(value))
|
||||
{
|
||||
Config::Get().general.showFPS = value.as_bool();
|
||||
Config::Get().general.showFPS = JS_ToBool(ctx, value) > 0;
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Property does not exist.");
|
||||
JS_ThrowPlainError(ctx, "Invalid value for this property.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
}
|
||||
catch (const DukException&)
|
||||
else
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Invalid value for this property.");
|
||||
JS_ThrowPlainError(ctx, "Property does not exist.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto [ns, n] = GetNamespaceAndKey(key);
|
||||
if (!IsValidNamespace(ns))
|
||||
if (!IsValidNamespace(ns, data->_kind))
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Namespace was invalid.");
|
||||
JS_ThrowPlainError(ctx, "Namespace was invalid.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
else if (!IsValidKey(n))
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Key was invalid.");
|
||||
JS_ThrowPlainError(ctx, "Key was invalid.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto obj = GetOrCreateNamespaceObject(ctx, ns);
|
||||
obj.push();
|
||||
if (value.type() == DukValue::Type::UNDEFINED)
|
||||
{
|
||||
duk_del_prop_lstring(ctx, -1, n.data(), n.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
value.push();
|
||||
duk_put_prop_lstring(ctx, -2, n.data(), n.size());
|
||||
}
|
||||
duk_pop(ctx);
|
||||
auto obj = GetOrCreateNamespaceObject(ctx, data, ns);
|
||||
JS_SetPropertyStr(ctx, obj, std::string(n).c_str(), JS_DupValue(ctx, value));
|
||||
JS_FreeValue(ctx, obj);
|
||||
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
scriptEngine.SaveSharedStorage();
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool has(const std::string& key) const
|
||||
static JSValue has(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto val = get(key, DukValue());
|
||||
return val.type() != DukValue::Type::UNDEFINED;
|
||||
std::array newArgs = { argv[0], JS_UNDEFINED };
|
||||
auto val = get(ctx, thisVal, 2, newArgs.data());
|
||||
if (JS_IsException(val))
|
||||
{
|
||||
return val;
|
||||
}
|
||||
bool retval = !JS_IsUndefined(val);
|
||||
JS_FreeValue(ctx, val);
|
||||
return JS_NewBool(ctx, retval);
|
||||
}
|
||||
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CFUNC_DEF("getAll", 1, ScConfiguration::getAll),
|
||||
JS_CFUNC_DEF("get", 2, ScConfiguration::get),
|
||||
JS_CFUNC_DEF("set", 2, ScConfiguration::set),
|
||||
JS_CFUNC_DEF("has", 1, ScConfiguration::has),
|
||||
};
|
||||
|
||||
public:
|
||||
// context.configuration
|
||||
JSValue New(JSContext* ctx)
|
||||
{
|
||||
return MakeWithOpaque(ctx, funcs, new ConfigurationData{ ScConfigurationKind::User, {} });
|
||||
}
|
||||
|
||||
// context.sharedStorage / context.getParkStorage
|
||||
JSValue New(JSContext* ctx, ScConfigurationKind kind, std::string_view pluginName = {})
|
||||
{
|
||||
return MakeWithOpaque(ctx, funcs, new ConfigurationData{ kind, std::string(pluginName) });
|
||||
}
|
||||
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "Configuration", Finalize);
|
||||
}
|
||||
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
ConfigurationData* data = gScConfiguration.GetOpaque<ConfigurationData*>(thisVal);
|
||||
if (data)
|
||||
delete data;
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
|
||||
@@ -12,55 +12,64 @@
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "../../../interface/InteractiveConsole.h"
|
||||
#include "../../Duktape.hpp"
|
||||
#include "../../ScriptEngine.h"
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScConsole
|
||||
class ScConsole;
|
||||
extern ScConsole gScConsole;
|
||||
|
||||
class ScConsole final : public ScBase
|
||||
{
|
||||
private:
|
||||
InteractiveConsole& _console;
|
||||
static JSValue clear(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
if (const auto console = gScConsole.GetOpaque<InteractiveConsole*>(thisVal))
|
||||
console->Clear();
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue log(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
if (const auto console = gScConsole.GetOpaque<InteractiveConsole*>(thisVal))
|
||||
{
|
||||
std::string line;
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
line.push_back(' ');
|
||||
line += Stringify(ctx, argv[i]);
|
||||
}
|
||||
console->WriteLine(line);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue executeLegacy(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
if (const auto console = gScConsole.GetOpaque<InteractiveConsole*>(thisVal))
|
||||
{
|
||||
JS_UNPACK_STR(str, ctx, argv[0]);
|
||||
console->Execute(str);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CFUNC_DEF("clear", 0, clear),
|
||||
JS_CFUNC_DEF("log", 0, log),
|
||||
JS_CFUNC_DEF("executeLegacy", 1, executeLegacy),
|
||||
};
|
||||
|
||||
public:
|
||||
ScConsole(InteractiveConsole& console)
|
||||
: _console(console)
|
||||
JSValue New(JSContext* ctx, InteractiveConsole& console)
|
||||
{
|
||||
return MakeWithOpaque(ctx, funcs, &console);
|
||||
}
|
||||
|
||||
void clear()
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
_console.Clear();
|
||||
}
|
||||
|
||||
duk_ret_t log(duk_context* ctx)
|
||||
{
|
||||
std::string line;
|
||||
auto nargs = duk_get_top(ctx);
|
||||
for (duk_idx_t i = 0; i < nargs; i++)
|
||||
{
|
||||
auto arg = DukValue::copy_from_stack(ctx, i);
|
||||
auto argsz = Stringify(arg);
|
||||
if (i != 0)
|
||||
{
|
||||
line.push_back(' ');
|
||||
}
|
||||
line += argsz;
|
||||
}
|
||||
_console.WriteLine(line);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void executeLegacy(const std::string& command)
|
||||
{
|
||||
_console.Execute(command);
|
||||
}
|
||||
|
||||
static void Register(duk_context* ctx)
|
||||
{
|
||||
dukglue_register_method(ctx, &ScConsole::clear, "clear");
|
||||
dukglue_register_method_varargs(ctx, &ScConsole::log, "log");
|
||||
dukglue_register_method(ctx, &ScConsole::executeLegacy, "executeLegacy");
|
||||
RegisterBaseStr(ctx, "Console");
|
||||
}
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "../../../localisation/Formatting.h"
|
||||
#include "../../../object/ObjectManager.h"
|
||||
#include "../../../scenario/Scenario.h"
|
||||
#include "../../Duktape.hpp"
|
||||
#include "../../HookEngine.h"
|
||||
#include "../../IconNames.hpp"
|
||||
#include "../../ScriptEngine.h"
|
||||
@@ -32,459 +31,428 @@
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScContext
|
||||
class ScContext;
|
||||
extern ScContext gScContext;
|
||||
|
||||
class ScContext final : public ScBase
|
||||
{
|
||||
private:
|
||||
ScriptExecutionInfo& _execInfo;
|
||||
HookEngine& _hookEngine;
|
||||
|
||||
public:
|
||||
ScContext(ScriptExecutionInfo& execInfo, HookEngine& hookEngine)
|
||||
: _execInfo(execInfo)
|
||||
, _hookEngine(hookEngine)
|
||||
static JSValue apiVersion_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return JS_NewInt32(ctx, kPluginApiVersion);
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t apiVersion_get()
|
||||
static JSValue configuration_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return kPluginApiVersion;
|
||||
return gScConfiguration.New(ctx);
|
||||
}
|
||||
|
||||
std::shared_ptr<ScConfiguration> configuration_get()
|
||||
static JSValue sharedStorage_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return std::make_shared<ScConfiguration>();
|
||||
return gScConfiguration.New(ctx, ScConfigurationKind::Shared);
|
||||
}
|
||||
|
||||
std::shared_ptr<ScConfiguration> sharedStorage_get()
|
||||
static JSValue getParkStorage(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JSValue jsPluginName = argv[0];
|
||||
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
return std::make_shared<ScConfiguration>(ScConfigurationKind::Shared, scriptEngine.GetSharedStorage());
|
||||
}
|
||||
|
||||
std::shared_ptr<ScConfiguration> GetParkStorageForPlugin(std::string_view pluginName)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto parkStore = scriptEngine.GetParkStorage();
|
||||
auto pluginStore = parkStore[pluginName];
|
||||
|
||||
// Create if it doesn't exist
|
||||
if (pluginStore.type() != DukValue::Type::OBJECT)
|
||||
JSValue result = JS_UNDEFINED;
|
||||
if (JS_IsString(jsPluginName))
|
||||
{
|
||||
auto* ctx = scriptEngine.GetContext();
|
||||
parkStore.push();
|
||||
duk_push_object(ctx);
|
||||
duk_put_prop_lstring(ctx, -2, pluginName.data(), pluginName.size());
|
||||
duk_pop(ctx);
|
||||
|
||||
pluginStore = parkStore[pluginName];
|
||||
}
|
||||
|
||||
return std::make_shared<ScConfiguration>(ScConfigurationKind::Park, pluginStore);
|
||||
}
|
||||
|
||||
std::shared_ptr<ScConfiguration> getParkStorage(const DukValue& dukPluginName)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
|
||||
std::shared_ptr<ScConfiguration> result;
|
||||
if (dukPluginName.type() == DukValue::Type::STRING)
|
||||
{
|
||||
auto& pluginName = dukPluginName.as_string();
|
||||
std::string pluginName = JSToStdString(ctx, jsPluginName);
|
||||
if (pluginName.empty())
|
||||
{
|
||||
duk_error(scriptEngine.GetContext(), DUK_ERR_ERROR, "Plugin name is empty");
|
||||
return JS_ThrowPlainError(ctx, "Plugin name is empty");
|
||||
}
|
||||
result = GetParkStorageForPlugin(pluginName);
|
||||
result = gScConfiguration.New(ctx, ScConfigurationKind::Park, pluginName);
|
||||
}
|
||||
else if (dukPluginName.type() == DukValue::Type::UNDEFINED)
|
||||
else if (JS_IsUndefined(jsPluginName))
|
||||
{
|
||||
auto plugin = _execInfo.GetCurrentPlugin();
|
||||
const auto& plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
if (plugin == nullptr)
|
||||
{
|
||||
duk_error(
|
||||
scriptEngine.GetContext(), DUK_ERR_ERROR, "Plugin name must be specified when used from console.");
|
||||
return JS_ThrowPlainError(ctx, "Plugin name must be specified when used from console.");
|
||||
}
|
||||
result = GetParkStorageForPlugin(plugin->GetMetadata().Name);
|
||||
result = gScConfiguration.New(ctx, ScConfigurationKind::Park, plugin->GetMetadata().Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_error(scriptEngine.GetContext(), DUK_ERR_ERROR, "Invalid plugin name.");
|
||||
return JS_ThrowPlainError(ctx, "Invalid plugin name.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string mode_get()
|
||||
static JSValue mode_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
if (gLegacyScene == LegacyScene::titleSequence)
|
||||
return "title";
|
||||
return JSFromStdString(ctx, "title");
|
||||
else if (gLegacyScene == LegacyScene::scenarioEditor)
|
||||
return "scenario_editor";
|
||||
return JSFromStdString(ctx, "scenario_editor");
|
||||
else if (gLegacyScene == LegacyScene::trackDesigner)
|
||||
return "track_designer";
|
||||
return JSFromStdString(ctx, "track_designer");
|
||||
else if (gLegacyScene == LegacyScene::trackDesignsManager)
|
||||
return "track_manager";
|
||||
return "normal";
|
||||
return JSFromStdString(ctx, "track_manager");
|
||||
return JSFromStdString(ctx, "normal");
|
||||
}
|
||||
|
||||
bool paused_get()
|
||||
static JSValue paused_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return GameIsPaused();
|
||||
return JS_NewBool(ctx, GameIsPaused());
|
||||
}
|
||||
|
||||
void paused_set(const bool& value)
|
||||
static JSValue paused_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
if (value != GameIsPaused())
|
||||
JS_UNPACK_BOOL(valueBool, ctx, value)
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
|
||||
if (valueBool != GameIsPaused())
|
||||
PauseToggle();
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void captureImage(const DukValue& options)
|
||||
static JSValue captureImage(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
JSValue options = argv[0];
|
||||
try
|
||||
{
|
||||
CaptureOptions captureOptions;
|
||||
captureOptions.Filename = fs::u8path(AsOrDefault(options["filename"], ""));
|
||||
captureOptions.Rotation = options["rotation"].as_uint() & 3;
|
||||
captureOptions.Zoom = ZoomLevel(options["zoom"].as_uint());
|
||||
captureOptions.Transparent = AsOrDefault(options["transparent"], false);
|
||||
|
||||
auto dukPosition = options["position"];
|
||||
if (dukPosition.type() == DukValue::Type::OBJECT)
|
||||
auto rotation = JSToOptionalInt(ctx, options, "rotation");
|
||||
auto zoom = JSToOptionalInt(ctx, options, "zoom");
|
||||
if (!rotation.has_value() || !zoom.has_value())
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "Invalid options.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
CaptureOptions captureOptions;
|
||||
captureOptions.Filename = fs::u8path(AsOrDefault(ctx, options, "filename", ""));
|
||||
captureOptions.Rotation = rotation.value() & 3;
|
||||
captureOptions.Zoom = ZoomLevel(zoom.value());
|
||||
captureOptions.Transparent = AsOrDefault(ctx, options, "transparent", false);
|
||||
|
||||
JSValue jsPosition = JS_GetPropertyStr(ctx, options, "position");
|
||||
if (JS_IsObject(jsPosition))
|
||||
{
|
||||
auto width = JSToOptionalInt(ctx, options, "width");
|
||||
auto height = JSToOptionalInt(ctx, options, "height");
|
||||
auto x = JSToOptionalInt(ctx, jsPosition, "x");
|
||||
auto y = JSToOptionalInt(ctx, jsPosition, "y");
|
||||
|
||||
if (!width.has_value() || !height.has_value() || !x.has_value() || !y.has_value())
|
||||
{
|
||||
JS_FreeValue(ctx, jsPosition);
|
||||
JS_ThrowPlainError(ctx, "Invalid options.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
CaptureView view;
|
||||
view.Width = options["width"].as_int();
|
||||
view.Height = options["height"].as_int();
|
||||
view.Position.x = dukPosition["x"].as_int();
|
||||
view.Position.y = dukPosition["y"].as_int();
|
||||
view.Width = width.value();
|
||||
view.Height = height.value();
|
||||
view.Position.x = x.value();
|
||||
view.Position.y = y.value();
|
||||
captureOptions.View = view;
|
||||
}
|
||||
JS_FreeValue(ctx, jsPosition);
|
||||
|
||||
CaptureImage(captureOptions);
|
||||
}
|
||||
catch (const DukException&)
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Invalid options.");
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, ex.what());
|
||||
JS_ThrowPlainError(ctx, "%s", ex.what());
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
DukValue getObject(const std::string& typez, int32_t index) const
|
||||
static JSValue getObject(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
// deprecated function, moved to ObjectManager.getObject.
|
||||
ScObjectManager objectManager;
|
||||
return objectManager.getObject(typez, index);
|
||||
return gScObjectManager.getObject(ctx, thisVal, argc, argv);
|
||||
}
|
||||
|
||||
std::vector<DukValue> getAllObjects(const std::string& typez) const
|
||||
static JSValue getAllObjects(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
// deprecated function, moved to ObjectManager.getAllObjects.
|
||||
ScObjectManager objectManager;
|
||||
return objectManager.getAllObjects(typez);
|
||||
return gScObjectManager.getAllObjects(ctx, thisVal, argc, argv);
|
||||
}
|
||||
|
||||
DukValue getTrackSegment(uint16_t type)
|
||||
static JSValue getTrackSegment(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
JS_UNPACK_INT32(type, ctx, argv[0])
|
||||
|
||||
if (type >= EnumValue(TrackElemType::count))
|
||||
{
|
||||
return ToDuk(ctx, nullptr);
|
||||
return JS_NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetObjectAsDukValue(ctx, std::make_shared<ScTrackSegment>(static_cast<TrackElemType>(type)));
|
||||
return gScTrackSegment.New(ctx, static_cast<TrackElemType>(type));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DukValue> getAllTrackSegments()
|
||||
static JSValue getAllTrackSegments(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
|
||||
std::vector<DukValue> result;
|
||||
auto result = JS_NewArray(ctx);
|
||||
int64_t index = 0;
|
||||
for (uint16_t type = 0; type < EnumValue(TrackElemType::count); type++)
|
||||
{
|
||||
auto obj = std::make_shared<ScTrackSegment>(static_cast<TrackElemType>(type));
|
||||
if (obj != nullptr)
|
||||
{
|
||||
result.push_back(GetObjectAsDukValue(ctx, obj));
|
||||
}
|
||||
auto obj = gScTrackSegment.New(ctx, static_cast<TrackElemType>(type));
|
||||
JS_SetPropertyInt64(ctx, result, index++, obj);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t getRandom(int32_t min, int32_t max)
|
||||
static JSValue getRandom(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
JS_UNPACK_INT32(min, ctx, argv[0]);
|
||||
JS_UNPACK_INT32(max, ctx, argv[1]);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
|
||||
if (min >= max)
|
||||
return min;
|
||||
return JS_NewInt32(ctx, min);
|
||||
int32_t range = max - min;
|
||||
return min + ScenarioRandMax(range);
|
||||
return JS_NewInt64(ctx, min + ScenarioRandMax(range));
|
||||
}
|
||||
|
||||
duk_ret_t formatString(duk_context* ctx)
|
||||
static JSValue formatString(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto nargs = duk_get_top(ctx);
|
||||
if (nargs >= 1)
|
||||
if (argc >= 1)
|
||||
{
|
||||
auto dukFmt = DukValue::copy_from_stack(ctx, 0);
|
||||
if (dukFmt.type() == DukValue::Type::STRING)
|
||||
const JSValue jsFmt = argv[0];
|
||||
if (JS_IsString(jsFmt))
|
||||
{
|
||||
FmtString fmt(dukFmt.as_string());
|
||||
FmtString fmt(JSToStdString(ctx, jsFmt));
|
||||
|
||||
std::vector<FormatArg_t> args;
|
||||
for (duk_idx_t i = 1; i < nargs; i++)
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
auto dukArg = DukValue::copy_from_stack(ctx, i);
|
||||
switch (dukArg.type())
|
||||
const JSValue jsArg = argv[i];
|
||||
if (JS_IsNumber(jsArg))
|
||||
{
|
||||
case DukValue::Type::NUMBER:
|
||||
args.push_back(dukArg.as_int());
|
||||
break;
|
||||
case DukValue::Type::STRING:
|
||||
args.push_back(dukArg.as_string());
|
||||
break;
|
||||
default:
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Invalid format argument.");
|
||||
break;
|
||||
args.emplace_back(JSToInt(ctx, jsArg));
|
||||
}
|
||||
else if (JS_IsString(jsArg))
|
||||
{
|
||||
args.emplace_back(JSToStdString(ctx, jsArg));
|
||||
}
|
||||
else
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "Invalid format argument.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
}
|
||||
|
||||
auto result = FormatStringAny(fmt, args);
|
||||
duk_push_lstring(ctx, result.c_str(), result.size());
|
||||
return JSFromStdString(ctx, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Invalid format string.");
|
||||
JS_ThrowPlainError(ctx, "Invalid format string.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Invalid format string.");
|
||||
}
|
||||
return 1;
|
||||
JS_ThrowPlainError(ctx, "Invalid format string.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// HACK workaround to resolve issue #14853
|
||||
// The exception thrown in duk_error was causing a crash when RAII kicked in for this lambda.
|
||||
// Only ensuring it was not in the same generated method fixed it.
|
||||
__declspec(noinline)
|
||||
#endif
|
||||
std::shared_ptr<ScDisposable>
|
||||
CreateSubscription(HookType hookType, const DukValue& callback)
|
||||
static JSValue subscribe(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto owner = _execInfo.GetCurrentPlugin();
|
||||
auto cookie = _hookEngine.Subscribe(hookType, owner, callback);
|
||||
return std::make_shared<ScDisposable>([this, hookType, cookie]() { _hookEngine.Unsubscribe(hookType, cookie); });
|
||||
}
|
||||
JS_UNPACK_STR(hook, ctx, argv[0]);
|
||||
JS_UNPACK_CALLBACK(callback, ctx, argv[1]);
|
||||
|
||||
std::shared_ptr<ScDisposable> subscribe(const std::string& hook, const DukValue& callback)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto ctx = scriptEngine.GetContext();
|
||||
|
||||
auto hookType = GetHookType(hook);
|
||||
if (hookType == HookType::notDefined)
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Unknown hook type");
|
||||
JS_ThrowPlainError(ctx, "Unknown hook type");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
if (!callback.is_function())
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Expected function for callback");
|
||||
}
|
||||
|
||||
auto owner = _execInfo.GetCurrentPlugin();
|
||||
auto owner = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
if (owner == nullptr)
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Not in a plugin context");
|
||||
JS_ThrowPlainError(ctx, "Not in a plugin context");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
if (!_hookEngine.IsValidHookForPlugin(hookType, *owner))
|
||||
auto& hookEngine = scriptEngine.GetHookEngine();
|
||||
if (!hookEngine.IsValidHookForPlugin(hookType, *owner))
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Hook type not available for this plugin type.");
|
||||
JS_ThrowPlainError(ctx, "Hook type not available for this plugin type.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
return CreateSubscription(hookType, callback);
|
||||
auto cookie = hookEngine.Subscribe(hookType, owner, callback);
|
||||
return gScDisposable.New(ctx, [&hookEngine, hookType, cookie]() { hookEngine.Unsubscribe(hookType, cookie); });
|
||||
}
|
||||
|
||||
void queryAction(const std::string& action, const DukValue& args, const DukValue& callback)
|
||||
static JSValue queryAction(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
QueryOrExecuteAction(action, args, callback, false);
|
||||
JS_UNPACK_STR(action, ctx, argv[0]);
|
||||
JS_UNPACK_OBJECT(args, ctx, argv[1]);
|
||||
|
||||
return QueryOrExecuteAction(ctx, action, args, JSCallback(ctx, argv[2]), false);
|
||||
}
|
||||
|
||||
void executeAction(const std::string& action, const DukValue& args, const DukValue& callback)
|
||||
static JSValue executeAction(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
QueryOrExecuteAction(action, args, callback, true);
|
||||
JS_UNPACK_STR(action, ctx, argv[0]);
|
||||
JS_UNPACK_OBJECT(args, ctx, argv[1]);
|
||||
|
||||
return QueryOrExecuteAction(ctx, action, args, JSCallback(ctx, argv[2]), true);
|
||||
}
|
||||
|
||||
void QueryOrExecuteAction(const std::string& actionid, const DukValue& args, const DukValue& callback, bool isExecute)
|
||||
static JSValue QueryOrExecuteAction(
|
||||
JSContext* ctx, const std::string& actionid, JSValue args, const JSCallback& callback, bool isExecute)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto ctx = scriptEngine.GetContext();
|
||||
try
|
||||
auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
auto pair = scriptEngine.CreateGameAction(ctx, actionid, args, plugin->GetMetadata().Name);
|
||||
|
||||
std::unique_ptr<GameActions::GameAction> action = std::move(pair.first);
|
||||
|
||||
if (pair.second)
|
||||
return JS_ThrowPlainError(ctx, "Invalid action parameters.");
|
||||
|
||||
if (action != nullptr)
|
||||
{
|
||||
auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
auto action = scriptEngine.CreateGameAction(actionid, args, plugin->GetMetadata().Name);
|
||||
if (action != nullptr)
|
||||
if (isExecute)
|
||||
{
|
||||
if (isExecute)
|
||||
{
|
||||
action->SetCallback(
|
||||
[this, plugin,
|
||||
callback](const GameActions::GameAction* act, const GameActions::Result* res) -> void {
|
||||
HandleGameActionResult(plugin, *act, *res, callback);
|
||||
});
|
||||
GameActions::Execute(action.get(), getGameState());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto res = GameActions::Query(action.get(), getGameState());
|
||||
HandleGameActionResult(plugin, *action, res, callback);
|
||||
}
|
||||
action->SetCallback(
|
||||
[plugin, callback](const GameActions::GameAction* act, const GameActions::Result* res) -> void {
|
||||
HandleGameActionResult(plugin, *act, *res, callback);
|
||||
});
|
||||
GameActions::Execute(action.get(), getGameState());
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Unknown action.");
|
||||
auto res = GameActions::Query(action.get(), getGameState());
|
||||
HandleGameActionResult(plugin, *action, res, callback);
|
||||
}
|
||||
}
|
||||
catch (DukException&)
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Invalid action parameters.");
|
||||
}
|
||||
}
|
||||
|
||||
void HandleGameActionResult(
|
||||
const std::shared_ptr<Plugin>& plugin, const GameActions::GameAction& action, const GameActions::Result& res,
|
||||
const DukValue& callback)
|
||||
{
|
||||
if (callback.is_function())
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto dukResult = scriptEngine.GameActionResultToDuk(action, res);
|
||||
// Call the plugin callback and pass the result object
|
||||
scriptEngine.ExecutePluginCall(plugin, callback, { dukResult }, false);
|
||||
}
|
||||
}
|
||||
|
||||
void registerAction(const std::string& action, const DukValue& query, const DukValue& execute)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
auto ctx = scriptEngine.GetContext();
|
||||
if (!query.is_function())
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "query was not a function.");
|
||||
}
|
||||
else if (!execute.is_function())
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "execute was not a function.");
|
||||
}
|
||||
else if (!scriptEngine.RegisterCustomAction(plugin, action, query, execute))
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "action has already been registered.");
|
||||
}
|
||||
}
|
||||
|
||||
int32_t SetIntervalOrTimeout(DukValue callback, int32_t delay, bool repeat)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto ctx = scriptEngine.GetContext();
|
||||
auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
|
||||
int32_t handle = 0;
|
||||
if (callback.is_function())
|
||||
{
|
||||
handle = scriptEngine.AddInterval(plugin, delay, repeat, std::move(callback));
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "callback was not a function.");
|
||||
return JS_ThrowPlainError(ctx, "Unknown action.");
|
||||
}
|
||||
return handle;
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void ClearIntervalOrTimeout(int32_t handle)
|
||||
static void HandleGameActionResult(
|
||||
const std::shared_ptr<Plugin>& plugin, const GameActions::GameAction& action, const GameActions::Result& res,
|
||||
const JSCallback& callback)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
JSContext* ctx = plugin ? plugin->GetContext() : scriptEngine.GetContext();
|
||||
JSValue jsResult = scriptEngine.GameActionResultToJS(ctx, action, res);
|
||||
// Call the plugin callback and pass the result object
|
||||
scriptEngine.ExecutePluginCall(plugin, callback.callback, { jsResult }, false);
|
||||
}
|
||||
|
||||
static JSValue registerAction(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_STR(action, ctx, argv[0]);
|
||||
JS_UNPACK_CALLBACK(query, ctx, argv[1]);
|
||||
JS_UNPACK_CALLBACK(execute, ctx, argv[2]);
|
||||
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
if (!scriptEngine.RegisterCustomAction(plugin, action, query, execute))
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "action has already been registered.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static int32_t SetIntervalOrTimeout(const JSCallback& callback, int32_t delay, bool repeat)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
|
||||
return scriptEngine.AddInterval(plugin, delay, repeat, callback);
|
||||
}
|
||||
|
||||
static void ClearIntervalOrTimeout(int32_t handle)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
scriptEngine.RemoveInterval(plugin, handle);
|
||||
}
|
||||
|
||||
int32_t setInterval(DukValue callback, int32_t delay)
|
||||
static JSValue setInterval(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
return SetIntervalOrTimeout(callback, delay, true);
|
||||
JS_UNPACK_CALLBACK(callback, ctx, argv[0]);
|
||||
JS_UNPACK_INT32(delay, ctx, argv[1]);
|
||||
return JS_NewInt32(ctx, SetIntervalOrTimeout(callback, delay, true));
|
||||
}
|
||||
|
||||
int32_t setTimeout(DukValue callback, int32_t delay)
|
||||
static JSValue setTimeout(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
return SetIntervalOrTimeout(callback, delay, false);
|
||||
JS_UNPACK_CALLBACK(callback, ctx, argv[0]);
|
||||
JS_UNPACK_INT32(delay, ctx, argv[1]);
|
||||
return JS_NewInt32(ctx, SetIntervalOrTimeout(callback, delay, false));
|
||||
}
|
||||
|
||||
void clearInterval(int32_t handle)
|
||||
static JSValue clearInterval(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_INT32(handle, ctx, argv[0]);
|
||||
ClearIntervalOrTimeout(handle);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void clearTimeout(int32_t handle)
|
||||
static JSValue clearTimeout(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_INT32(handle, ctx, argv[0]);
|
||||
ClearIntervalOrTimeout(handle);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
int32_t getIcon(const std::string& iconName)
|
||||
static JSValue getIcon(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
return GetIconByName(iconName);
|
||||
JS_UNPACK_STR(iconName, ctx, argv[0]);
|
||||
return JS_NewInt64(ctx, GetIconByName(iconName));
|
||||
}
|
||||
|
||||
public:
|
||||
static void Register(duk_context* ctx)
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScContext::apiVersion_get, nullptr, "apiVersion");
|
||||
dukglue_register_property(ctx, &ScContext::configuration_get, nullptr, "configuration");
|
||||
dukglue_register_property(ctx, &ScContext::sharedStorage_get, nullptr, "sharedStorage");
|
||||
dukglue_register_method(ctx, &ScContext::getParkStorage, "getParkStorage");
|
||||
dukglue_register_property(ctx, &ScContext::mode_get, nullptr, "mode");
|
||||
dukglue_register_property(ctx, &ScContext::paused_get, &ScContext::paused_set, "paused");
|
||||
dukglue_register_method(ctx, &ScContext::captureImage, "captureImage");
|
||||
dukglue_register_method(ctx, &ScContext::getObject, "getObject");
|
||||
dukglue_register_method(ctx, &ScContext::getAllObjects, "getAllObjects");
|
||||
dukglue_register_method(ctx, &ScContext::getTrackSegment, "getTrackSegment");
|
||||
dukglue_register_method(ctx, &ScContext::getAllTrackSegments, "getAllTrackSegments");
|
||||
dukglue_register_method(ctx, &ScContext::getRandom, "getRandom");
|
||||
dukglue_register_method_varargs(ctx, &ScContext::formatString, "formatString");
|
||||
dukglue_register_method(ctx, &ScContext::subscribe, "subscribe");
|
||||
dukglue_register_method(ctx, &ScContext::queryAction, "queryAction");
|
||||
dukglue_register_method(ctx, &ScContext::executeAction, "executeAction");
|
||||
dukglue_register_method(ctx, &ScContext::registerAction, "registerAction");
|
||||
dukglue_register_method(ctx, &ScContext::setInterval, "setInterval");
|
||||
dukglue_register_method(ctx, &ScContext::setTimeout, "setTimeout");
|
||||
dukglue_register_method(ctx, &ScContext::clearInterval, "clearInterval");
|
||||
dukglue_register_method(ctx, &ScContext::clearTimeout, "clearTimeout");
|
||||
dukglue_register_method(ctx, &ScContext::getIcon, "getIcon");
|
||||
RegisterBaseStr(ctx, "Context");
|
||||
}
|
||||
|
||||
JSValue New(JSContext* ctx)
|
||||
{
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("apiVersion", ScContext::apiVersion_get, nullptr),
|
||||
JS_CGETSET_DEF("configuration", ScContext::configuration_get, nullptr),
|
||||
JS_CGETSET_DEF("sharedStorage", ScContext::sharedStorage_get, nullptr),
|
||||
JS_CFUNC_DEF("getParkStorage", 1, ScContext::getParkStorage),
|
||||
JS_CGETSET_DEF("mode", ScContext::mode_get, nullptr),
|
||||
JS_CGETSET_DEF("paused", ScContext::paused_get, &ScContext::paused_set),
|
||||
JS_CFUNC_DEF("captureImage", 1, ScContext::captureImage),
|
||||
JS_CFUNC_DEF("getObject", 2, ScContext::getObject),
|
||||
JS_CFUNC_DEF("getAllObjects", 2, ScContext::getAllObjects),
|
||||
JS_CFUNC_DEF("getTrackSegment", 1, ScContext::getTrackSegment),
|
||||
JS_CFUNC_DEF("getAllTrackSegments", 0, ScContext::getAllTrackSegments),
|
||||
JS_CFUNC_DEF("getRandom", 2, ScContext::getRandom),
|
||||
JS_CFUNC_DEF("formatString", 0, ScContext::formatString),
|
||||
JS_CFUNC_DEF("subscribe", 2, ScContext::subscribe),
|
||||
JS_CFUNC_DEF("queryAction", 3, ScContext::queryAction),
|
||||
JS_CFUNC_DEF("executeAction", 3, ScContext::executeAction),
|
||||
JS_CFUNC_DEF("registerAction", 3, ScContext::registerAction),
|
||||
JS_CFUNC_DEF("setInterval", 2, ScContext::setInterval),
|
||||
JS_CFUNC_DEF("setTimeout", 2, ScContext::setTimeout),
|
||||
JS_CFUNC_DEF("clearInterval", 1, ScContext::clearInterval),
|
||||
JS_CFUNC_DEF("clearTimeout", 1, ScContext::clearTimeout),
|
||||
JS_CFUNC_DEF("getIcon", 1, ScContext::getIcon),
|
||||
};
|
||||
return MakeWithOpaque(ctx, funcs, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t ImageFromDuk(const DukValue& d)
|
||||
{
|
||||
uint32_t img{};
|
||||
if (d.type() == DukValue::Type::NUMBER)
|
||||
{
|
||||
img = d.as_uint();
|
||||
if (GetTargetAPIVersion() <= kApiVersionG2Reorder)
|
||||
{
|
||||
img = NewIconIndex(d.as_uint());
|
||||
}
|
||||
}
|
||||
else if (d.type() == DukValue::Type::STRING)
|
||||
{
|
||||
img = GetIconByName(d.as_c_string());
|
||||
}
|
||||
return img;
|
||||
}
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,34 +11,47 @@
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "../../Duktape.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScDisposable
|
||||
class ScDisposable;
|
||||
extern ScDisposable gScDisposable;
|
||||
|
||||
class ScDisposable final : public ScBase
|
||||
{
|
||||
private:
|
||||
std::function<void()> _onDispose;
|
||||
using OpaqueType = std::function<void()>;
|
||||
|
||||
public:
|
||||
ScDisposable(const std::function<void()>& onDispose)
|
||||
: _onDispose(onDispose)
|
||||
static JSValue dispose(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
}
|
||||
|
||||
void dispose() const
|
||||
{
|
||||
if (_onDispose)
|
||||
OpaqueType* data = gScDisposable.GetOpaque<OpaqueType*>(thisVal);
|
||||
if (data && *data)
|
||||
{
|
||||
_onDispose();
|
||||
(*data)();
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static void Register(duk_context* ctx)
|
||||
JSValue New(JSContext* ctx, const std::function<void()>& f)
|
||||
{
|
||||
dukglue_register_method(ctx, &ScDisposable::dispose, "dispose");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CFUNC_DEF("dispose", 0, ScDisposable::dispose),
|
||||
};
|
||||
|
||||
return MakeWithOpaque(ctx, funcs, f ? new OpaqueType(f) : nullptr);
|
||||
}
|
||||
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "Disposable", Finalize);
|
||||
}
|
||||
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
OpaqueType* data = gScDisposable.GetOpaque<OpaqueType*>(thisVal);
|
||||
if (data)
|
||||
delete data;
|
||||
}
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -11,74 +11,74 @@
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "../../Duktape.hpp"
|
||||
#include "../../ScriptEngine.h"
|
||||
#include "../game/ScContext.hpp"
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScPlugin
|
||||
class ScPlugin;
|
||||
extern ScPlugin gScPlugin;
|
||||
class ScPlugin final : public ScBase
|
||||
{
|
||||
public:
|
||||
static void Register(duk_context* ctx)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScPlugin::plugins_get, nullptr, "plugins");
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<DukValue> plugins_get()
|
||||
{
|
||||
auto ctx = getContext();
|
||||
auto& allPlugins = getallPlugins();
|
||||
return formatMetadata(ctx, allPlugins);
|
||||
}
|
||||
|
||||
duk_context* getContext()
|
||||
{
|
||||
// Get the context from the script engine
|
||||
ScriptEngine& scriptEngine = GetContext()->GetScriptEngine();
|
||||
return scriptEngine.GetContext();
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<Plugin>> getallPlugins()
|
||||
static const std::vector<std::shared_ptr<Plugin>> getallPlugins()
|
||||
{
|
||||
// Get all of the plugins from the script engine
|
||||
ScriptEngine& scriptEngine = GetContext()->GetScriptEngine();
|
||||
return scriptEngine.GetPlugins();
|
||||
}
|
||||
|
||||
const std::vector<DukValue> formatMetadata(duk_context* ctx, const std::vector<std::shared_ptr<Plugin>>& allPlugins)
|
||||
static JSValue plugins_get(JSContext* ctx, JSValue)
|
||||
{
|
||||
std::vector<DukValue> formattedMetadata;
|
||||
duk_idx_t dukIdx = DUK_INVALID_INDEX;
|
||||
// Iterate through all plugins and and cast their data to Duk objects
|
||||
auto& allPlugins = getallPlugins();
|
||||
return formatMetadata(ctx, allPlugins);
|
||||
}
|
||||
|
||||
static JSValue formatMetadata(
|
||||
JSContext* ctx, const std::vector<std::shared_ptr<OpenRCT2::Scripting::Plugin>>& allPlugins)
|
||||
{
|
||||
JSValue formattedMetadata = JS_NewArray(ctx);
|
||||
// Iterate through all plugins and and cast their data to JSValue objects
|
||||
int64_t index = 0;
|
||||
for (const auto& pluginPtr : allPlugins)
|
||||
{
|
||||
// Pull out metadata
|
||||
Plugin& plugin = *pluginPtr;
|
||||
PluginMetadata metadata = plugin.GetMetadata();
|
||||
// Create object using Duk stack
|
||||
dukIdx = duk_push_object(ctx);
|
||||
// Create object using context
|
||||
JSValue val = JS_NewObject(ctx);
|
||||
// Name and Version
|
||||
duk_push_string(ctx, metadata.Name.c_str());
|
||||
duk_put_prop_string(ctx, dukIdx, "name");
|
||||
duk_push_string(ctx, metadata.Version.c_str());
|
||||
duk_put_prop_string(ctx, dukIdx, "version");
|
||||
JS_SetPropertyStr(ctx, val, "name", JSFromStdString(ctx, metadata.Name));
|
||||
JS_SetPropertyStr(ctx, val, "version", JSFromStdString(ctx, metadata.Version));
|
||||
// Authors
|
||||
duk_idx_t arrIdx = duk_push_array(ctx);
|
||||
for (auto [s, idx] = std::tuple{ metadata.Authors.begin(), 0 }; s != metadata.Authors.end(); s++, idx++)
|
||||
JSValue authorsArray = JS_NewArray(ctx);
|
||||
|
||||
int64_t idx = 0;
|
||||
for (auto& str : metadata.Authors)
|
||||
{
|
||||
auto& str = *s;
|
||||
duk_push_string(ctx, str.c_str());
|
||||
duk_put_prop_index(ctx, arrIdx, idx);
|
||||
JSValue authorStr = JSFromStdString(ctx, str);
|
||||
JS_SetPropertyInt64(ctx, authorsArray, idx++, authorStr);
|
||||
}
|
||||
duk_put_prop_string(ctx, dukIdx, "authors");
|
||||
// Take from Duk stack
|
||||
formattedMetadata.push_back(DukValue::take_from_stack(ctx, dukIdx));
|
||||
dukIdx = DUK_INVALID_INDEX;
|
||||
JS_SetPropertyStr(ctx, val, "authors", authorsArray);
|
||||
JS_SetPropertyInt64(ctx, formattedMetadata, index++, val);
|
||||
}
|
||||
return formattedMetadata;
|
||||
}
|
||||
|
||||
public:
|
||||
JSValue New(JSContext* ctx)
|
||||
{
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("plugins", ScPlugin::plugins_get, nullptr),
|
||||
};
|
||||
|
||||
return MakeWithOpaque(ctx, funcs, nullptr);
|
||||
}
|
||||
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "PluginManager");
|
||||
}
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
|
||||
@@ -12,92 +12,92 @@
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "../../../profiling/Profiling.h"
|
||||
#include "../../Duktape.hpp"
|
||||
|
||||
#include "../../ScriptEngine.h"
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScProfiler
|
||||
class ScProfiler;
|
||||
extern ScProfiler gScProfiler;
|
||||
class ScProfiler final : public ScBase
|
||||
{
|
||||
private:
|
||||
duk_context* _ctx{};
|
||||
|
||||
public:
|
||||
ScProfiler(duk_context* ctx)
|
||||
: _ctx(ctx)
|
||||
static JSValue GetFunctionIndexArray(
|
||||
JSContext* ctx, const std::vector<Profiling::Function*>& all, const std::vector<Profiling::Function*>& items)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
DukValue getData()
|
||||
{
|
||||
const auto& data = Profiling::getData();
|
||||
duk_push_array(_ctx);
|
||||
duk_uarridx_t index = 0;
|
||||
for (const auto& f : data)
|
||||
{
|
||||
DukObject obj(_ctx);
|
||||
obj.Set("name", f->getName());
|
||||
obj.Set("callCount", f->getCallCount());
|
||||
obj.Set("minTime", f->getMinTime());
|
||||
obj.Set("maxTime", f->getMaxTime());
|
||||
obj.Set("totalTime", f->getTotalTime());
|
||||
obj.Set("averageTime", f->getAverageTime());
|
||||
obj.Set("parents", GetFunctionIndexArray(data, f->getParents()));
|
||||
obj.Set("children", GetFunctionIndexArray(data, f->getChildren()));
|
||||
obj.Take().push();
|
||||
duk_put_prop_index(_ctx, /* duk stack index */ -2, index);
|
||||
index++;
|
||||
}
|
||||
return DukValue::take_from_stack(_ctx);
|
||||
}
|
||||
|
||||
DukValue GetFunctionIndexArray(
|
||||
const std::vector<Profiling::Function*>& all, const std::vector<Profiling::Function*>& items)
|
||||
{
|
||||
duk_push_array(_ctx);
|
||||
duk_uarridx_t index = 0;
|
||||
JSValue functionArray = JS_NewArray(ctx);
|
||||
int64_t index = 0;
|
||||
for (const auto& item : items)
|
||||
{
|
||||
auto it = std::find(all.begin(), all.end(), item);
|
||||
if (it != all.end())
|
||||
{
|
||||
auto value = static_cast<duk_int_t>(std::distance(all.begin(), it));
|
||||
duk_push_int(_ctx, value);
|
||||
duk_put_prop_index(_ctx, /* duk stack index */ -2, index);
|
||||
index++;
|
||||
auto value = static_cast<uint64_t>(std::distance(all.begin(), it));
|
||||
JS_SetPropertyInt64(ctx, functionArray, index++, JS_NewInt64(ctx, value));
|
||||
}
|
||||
}
|
||||
return DukValue::take_from_stack(_ctx);
|
||||
return functionArray;
|
||||
}
|
||||
|
||||
void start()
|
||||
static JSValue getData(JSContext* ctx, JSValue, int, JSValue*)
|
||||
{
|
||||
const auto& data = Profiling::getData();
|
||||
JSValue profileData = JS_NewArray(ctx);
|
||||
int64_t index = 0;
|
||||
for (const auto& f : data)
|
||||
{
|
||||
JSValue val = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, val, "name", JSFromStdString(ctx, f->getName()));
|
||||
JS_SetPropertyStr(ctx, val, "callCount", JS_NewInt64(ctx, f->getCallCount()));
|
||||
JS_SetPropertyStr(ctx, val, "minTime", JS_NewFloat64(ctx, f->getMinTime()));
|
||||
JS_SetPropertyStr(ctx, val, "maxTime", JS_NewFloat64(ctx, f->getMaxTime()));
|
||||
JS_SetPropertyStr(ctx, val, "totalTime", JS_NewFloat64(ctx, f->getTotalTime()));
|
||||
JS_SetPropertyStr(ctx, val, "averageTime", JS_NewFloat64(ctx, f->getAverageTime()));
|
||||
JS_SetPropertyStr(ctx, val, "parents", GetFunctionIndexArray(ctx, data, f->getParents()));
|
||||
JS_SetPropertyStr(ctx, val, "children", GetFunctionIndexArray(ctx, data, f->getChildren()));
|
||||
JS_SetPropertyInt64(ctx, profileData, index++, val);
|
||||
}
|
||||
return profileData;
|
||||
}
|
||||
|
||||
static JSValue start(JSContext*, JSValue, int, JSValue*)
|
||||
{
|
||||
Profiling::enable();
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void stop()
|
||||
static JSValue stop(JSContext*, JSValue, int, JSValue*)
|
||||
{
|
||||
Profiling::disable();
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void reset()
|
||||
static JSValue reset(JSContext*, JSValue, int, JSValue*)
|
||||
{
|
||||
Profiling::resetData();
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
bool enabled_get() const
|
||||
static JSValue enabled_get(JSContext* ctx, JSValue)
|
||||
{
|
||||
return Profiling::isEnabled();
|
||||
return JS_NewBool(ctx, Profiling::isEnabled());
|
||||
}
|
||||
|
||||
public:
|
||||
static void Register(duk_context* ctx)
|
||||
JSValue New(JSContext* ctx)
|
||||
{
|
||||
dukglue_register_method(ctx, &ScProfiler::getData, "getData");
|
||||
dukglue_register_method(ctx, &ScProfiler::start, "start");
|
||||
dukglue_register_method(ctx, &ScProfiler::stop, "stop");
|
||||
dukglue_register_method(ctx, &ScProfiler::reset, "reset");
|
||||
dukglue_register_property(ctx, &ScProfiler::enabled_get, nullptr, "enabled");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CFUNC_DEF("getData", 0, ScProfiler::getData),
|
||||
JS_CFUNC_DEF("start", 0, ScProfiler::start),
|
||||
JS_CFUNC_DEF("stop", 0, ScProfiler::stop),
|
||||
JS_CFUNC_DEF("reset", 0, ScProfiler::reset),
|
||||
JS_CGETSET_DEF("enabled", ScProfiler::enabled_get, nullptr),
|
||||
};
|
||||
|
||||
return MakeWithOpaque(ctx, funcs, nullptr);
|
||||
}
|
||||
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "Profiler");
|
||||
}
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -21,103 +21,102 @@
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
ScNetwork::ScNetwork(duk_context* ctx)
|
||||
: _context(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
std::string ScNetwork::mode_get() const
|
||||
JSValue ScNetwork::mode_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
switch (Network::GetMode())
|
||||
{
|
||||
case Network::Mode::server:
|
||||
return "server";
|
||||
return JSFromStdString(ctx, "server");
|
||||
case Network::Mode::client:
|
||||
return "client";
|
||||
return JSFromStdString(ctx, "client");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return "none";
|
||||
return JSFromStdString(ctx, "none");
|
||||
}
|
||||
|
||||
int32_t ScNetwork::numPlayers_get() const
|
||||
JSValue ScNetwork::numPlayers_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
return Network::GetNumPlayers();
|
||||
return JS_NewInt32(ctx, Network::GetNumPlayers());
|
||||
#else
|
||||
return 0;
|
||||
return JS_NewInt32(ctx, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
int32_t ScNetwork::numGroups_get() const
|
||||
JSValue ScNetwork::numGroups_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
return Network::GetNumGroups();
|
||||
return JS_NewInt32(ctx, Network::GetNumGroups());
|
||||
#else
|
||||
return 0;
|
||||
return JS_NewInt32(ctx, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
int32_t ScNetwork::defaultGroup_get() const
|
||||
JSValue ScNetwork::defaultGroup_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
return Network::GetDefaultGroup();
|
||||
return JS_NewInt32(ctx, Network::GetDefaultGroup());
|
||||
#else
|
||||
return 0;
|
||||
return JS_NewInt32(ctx, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ScNetwork::defaultGroup_set(int32_t value)
|
||||
JSValue ScNetwork::defaultGroup_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
auto action = GameActions::NetworkModifyGroupAction(GameActions::ModifyGroupType::SetDefault, value);
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
|
||||
auto action = GameActions::NetworkModifyGroupAction(GameActions::ModifyGroupType::SetDefault, valueInt);
|
||||
GameActions::Execute(&action, getGameState());
|
||||
#endif
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<ScPlayerGroup>> ScNetwork::groups_get() const
|
||||
JSValue ScNetwork::groups_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
std::vector<std::shared_ptr<ScPlayerGroup>> groups;
|
||||
JSValue groups = JS_NewArray(ctx);
|
||||
#ifndef DISABLE_NETWORK
|
||||
auto numGroups = Network::GetNumGroups();
|
||||
for (int32_t i = 0; i < numGroups; i++)
|
||||
{
|
||||
auto groupId = Network::GetGroupID(i);
|
||||
groups.push_back(std::make_shared<ScPlayerGroup>(groupId));
|
||||
JS_SetPropertyInt64(ctx, groups, i, gScPlayerGroup.New(ctx, groupId));
|
||||
}
|
||||
#endif
|
||||
return groups;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<ScPlayer>> ScNetwork::players_get() const
|
||||
JSValue ScNetwork::players_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
std::vector<std::shared_ptr<ScPlayer>> players;
|
||||
JSValue players = JS_NewArray(ctx);
|
||||
#ifndef DISABLE_NETWORK
|
||||
auto numPlayers = Network::GetNumPlayers();
|
||||
for (int32_t i = 0; i < numPlayers; i++)
|
||||
{
|
||||
auto playerId = Network::GetPlayerID(i);
|
||||
players.push_back(std::make_shared<ScPlayer>(playerId));
|
||||
JS_SetPropertyInt64(ctx, players, i, gScPlayer.New(ctx, playerId));
|
||||
}
|
||||
#endif
|
||||
return players;
|
||||
}
|
||||
|
||||
std::shared_ptr<ScPlayer> ScNetwork::currentPlayer_get() const
|
||||
JSValue ScNetwork::currentPlayer_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
std::shared_ptr<ScPlayer> player;
|
||||
#ifndef DISABLE_NETWORK
|
||||
auto playerId = Network::GetCurrentPlayerId();
|
||||
player = std::make_shared<ScPlayer>(playerId);
|
||||
return gScPlayer.New(ctx, playerId);
|
||||
#else
|
||||
return JS_NULL;
|
||||
#endif
|
||||
return player;
|
||||
}
|
||||
|
||||
std::shared_ptr<ScPlayer> ScNetwork::getPlayer(int32_t id) const
|
||||
JSValue ScNetwork::getPlayer(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
JS_UNPACK_INT32(id, ctx, argv[0]);
|
||||
if (GetTargetAPIVersion() < kApiVersionNetworkIDs)
|
||||
{
|
||||
auto index = id;
|
||||
@@ -125,7 +124,7 @@ namespace OpenRCT2::Scripting
|
||||
if (index < numPlayers)
|
||||
{
|
||||
auto playerId = Network::GetPlayerID(index);
|
||||
return std::make_shared<ScPlayer>(playerId);
|
||||
return gScPlayer.New(ctx, playerId);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -133,50 +132,49 @@ namespace OpenRCT2::Scripting
|
||||
auto index = Network::GetPlayerIndex(id);
|
||||
if (index != -1)
|
||||
{
|
||||
return std::make_shared<ScPlayer>(id);
|
||||
return gScPlayer.New(ctx, id);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
return nullptr;
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
DukValue ScNetwork::stats_get() const
|
||||
JSValue ScNetwork::stats_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
auto obj = DukObject(_context);
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
auto networkStats = Network::GetStats();
|
||||
{
|
||||
duk_push_array(_context);
|
||||
duk_uarridx_t index = 0;
|
||||
for (auto v : networkStats.bytesReceived)
|
||||
JSValue recvStatsArr = JS_NewArray(ctx);
|
||||
uint32_t index = 0;
|
||||
for (const auto v : networkStats.bytesReceived)
|
||||
{
|
||||
duk_push_number(_context, v);
|
||||
duk_put_prop_index(_context, -2, index);
|
||||
JS_SetPropertyUint32(ctx, recvStatsArr, index, JS_NewInt64(ctx, static_cast<int64_t>(v)));
|
||||
index++;
|
||||
}
|
||||
obj.Set("bytesReceived", DukValue::take_from_stack(_context));
|
||||
JS_SetPropertyStr(ctx, obj, "bytesReceived", recvStatsArr);
|
||||
}
|
||||
{
|
||||
duk_push_array(_context);
|
||||
duk_uarridx_t index = 0;
|
||||
JSValue sentStatsArr = JS_NewArray(ctx);
|
||||
uint32_t index = 0;
|
||||
for (auto v : networkStats.bytesSent)
|
||||
{
|
||||
duk_push_number(_context, v);
|
||||
duk_put_prop_index(_context, -2, index);
|
||||
JS_SetPropertyUint32(ctx, sentStatsArr, index, JS_NewInt64(ctx, static_cast<int64_t>(v)));
|
||||
index++;
|
||||
}
|
||||
obj.Set("bytesSent", DukValue::take_from_stack(_context));
|
||||
JS_SetPropertyStr(ctx, obj, "bytesSent", sentStatsArr);
|
||||
}
|
||||
return obj.Take();
|
||||
return obj;
|
||||
#else
|
||||
return ToDuk(_context, nullptr);
|
||||
return JS_NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::shared_ptr<ScPlayerGroup> ScNetwork::getGroup(int32_t id) const
|
||||
JSValue ScNetwork::getGroup(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
JS_UNPACK_INT32(id, ctx, argv[0])
|
||||
|
||||
if (GetTargetAPIVersion() < kApiVersionNetworkIDs)
|
||||
{
|
||||
auto index = id;
|
||||
@@ -184,7 +182,7 @@ namespace OpenRCT2::Scripting
|
||||
if (index < numGroups)
|
||||
{
|
||||
auto groupId = Network::GetGroupID(index);
|
||||
return std::make_shared<ScPlayerGroup>(groupId);
|
||||
return gScPlayerGroup.New(ctx, groupId);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -192,24 +190,27 @@ namespace OpenRCT2::Scripting
|
||||
auto index = Network::GetGroupIndex(id);
|
||||
if (index != -1)
|
||||
{
|
||||
return std::make_shared<ScPlayerGroup>(id);
|
||||
return gScPlayerGroup.New(ctx, id);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
void ScNetwork::addGroup()
|
||||
JSValue ScNetwork::addGroup(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
auto networkModifyGroup = GameActions::NetworkModifyGroupAction(GameActions::ModifyGroupType::AddGroup);
|
||||
GameActions::Execute(&networkModifyGroup, getGameState());
|
||||
#endif
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void ScNetwork::removeGroup(int32_t id)
|
||||
JSValue ScNetwork::removeGroup(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
JS_UNPACK_INT32(id, ctx, argv[0]);
|
||||
|
||||
if (GetTargetAPIVersion() < kApiVersionNetworkIDs)
|
||||
{
|
||||
auto index = id;
|
||||
@@ -231,11 +232,14 @@ namespace OpenRCT2::Scripting
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void ScNetwork::kickPlayer(int32_t id)
|
||||
JSValue ScNetwork::kickPlayer(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
JS_UNPACK_INT32(id, ctx, argv[0]);
|
||||
|
||||
if (GetTargetAPIVersion() < kApiVersionNetworkIDs)
|
||||
{
|
||||
auto index = id;
|
||||
@@ -257,32 +261,32 @@ namespace OpenRCT2::Scripting
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void ScNetwork::sendMessage(std::string message, DukValue players)
|
||||
JSValue ScNetwork::sendMessage(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
if (players.is_array())
|
||||
JS_UNPACK_STR(message, ctx, argv[0]);
|
||||
JSValue players = argv[1];
|
||||
|
||||
if (JS_IsArray(players))
|
||||
{
|
||||
if (Network::GetMode() == Network::Mode::server)
|
||||
{
|
||||
std::vector<uint8_t> playerIds;
|
||||
auto playerArray = players.as_array();
|
||||
for (const auto& item : playerArray)
|
||||
{
|
||||
if (item.type() == DukValue::Type::NUMBER)
|
||||
{
|
||||
playerIds.push_back(static_cast<uint8_t>(item.as_uint()));
|
||||
}
|
||||
}
|
||||
if (!playerArray.empty())
|
||||
JSIterateArray(ctx, players, [&playerIds](JSContext* ctx2, JSValue val) {
|
||||
playerIds.push_back(static_cast<uint8_t>(JSToInt(ctx2, val)));
|
||||
});
|
||||
if (!playerIds.empty())
|
||||
{
|
||||
Network::SendChat(message.c_str(), playerIds);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_error(players.context(), DUK_ERR_ERROR, "Only servers can send private messages.");
|
||||
JS_ThrowPlainError(ctx, "Only servers can send private messages.");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -290,59 +294,57 @@ namespace OpenRCT2::Scripting
|
||||
Network::SendChat(message.c_str());
|
||||
}
|
||||
#endif
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
std::shared_ptr<ScListener> ScNetwork::createListener()
|
||||
JSValue ScNetwork::createListener(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
auto socket = std::make_shared<ScListener>(plugin);
|
||||
scriptEngine.AddSocket(socket);
|
||||
return socket;
|
||||
return gScListener.New(ctx, GetContext()->GetScriptEngine().GetExecInfo().GetCurrentPlugin());
|
||||
}
|
||||
#else
|
||||
void ScNetwork::createListener()
|
||||
JSValue ScNetwork::createListener(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
duk_error(_context, DUK_ERR_ERROR, "Networking has been disabled.");
|
||||
return JS_ThrowPlainError(ctx, "Networking has been disabled.");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
std::shared_ptr<ScSocket> ScNetwork::createSocket()
|
||||
JSValue ScNetwork::createSocket(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
auto socket = std::make_shared<ScSocket>(plugin);
|
||||
scriptEngine.AddSocket(socket);
|
||||
return socket;
|
||||
return gScSocket.New(ctx, GetContext()->GetScriptEngine().GetExecInfo().GetCurrentPlugin());
|
||||
}
|
||||
#else
|
||||
void ScNetwork::createSocket()
|
||||
JSValue ScNetwork::createSocket(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
duk_error(_context, DUK_ERR_ERROR, "Networking has been disabled.");
|
||||
return JS_ThrowPlainError(ctx, "Networking has been disabled.");
|
||||
}
|
||||
#endif
|
||||
|
||||
void ScNetwork::Register(duk_context* ctx)
|
||||
JSValue ScNetwork::New(JSContext* ctx)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScNetwork::mode_get, nullptr, "mode");
|
||||
dukglue_register_property(ctx, &ScNetwork::numGroups_get, nullptr, "numGroups");
|
||||
dukglue_register_property(ctx, &ScNetwork::numPlayers_get, nullptr, "numPlayers");
|
||||
dukglue_register_property(ctx, &ScNetwork::groups_get, nullptr, "groups");
|
||||
dukglue_register_property(ctx, &ScNetwork::players_get, nullptr, "players");
|
||||
dukglue_register_property(ctx, &ScNetwork::currentPlayer_get, nullptr, "currentPlayer");
|
||||
dukglue_register_property(ctx, &ScNetwork::defaultGroup_get, &ScNetwork::defaultGroup_set, "defaultGroup");
|
||||
dukglue_register_property(ctx, &ScNetwork::stats_get, nullptr, "stats");
|
||||
dukglue_register_method(ctx, &ScNetwork::addGroup, "addGroup");
|
||||
dukglue_register_method(ctx, &ScNetwork::getGroup, "getGroup");
|
||||
dukglue_register_method(ctx, &ScNetwork::removeGroup, "removeGroup");
|
||||
dukglue_register_method(ctx, &ScNetwork::getPlayer, "getPlayer");
|
||||
dukglue_register_method(ctx, &ScNetwork::kickPlayer, "kickPlayer");
|
||||
dukglue_register_method(ctx, &ScNetwork::sendMessage, "sendMessage");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("mode", ScNetwork::mode_get, nullptr),
|
||||
JS_CGETSET_DEF("numGroups", ScNetwork::numGroups_get, nullptr),
|
||||
JS_CGETSET_DEF("numPlayers", ScNetwork::numPlayers_get, nullptr),
|
||||
JS_CGETSET_DEF("groups", ScNetwork::groups_get, nullptr),
|
||||
JS_CGETSET_DEF("players", ScNetwork::players_get, nullptr),
|
||||
JS_CGETSET_DEF("currentPlayer", ScNetwork::currentPlayer_get, nullptr),
|
||||
JS_CGETSET_DEF("defaultGroup", ScNetwork::defaultGroup_get, ScNetwork::defaultGroup_set),
|
||||
JS_CGETSET_DEF("stats", ScNetwork::stats_get, nullptr),
|
||||
|
||||
dukglue_register_method(ctx, &ScNetwork::createListener, "createListener");
|
||||
dukglue_register_method(ctx, &ScNetwork::createSocket, "createSocket");
|
||||
JS_CFUNC_DEF("addGroup", 0, ScNetwork::addGroup),
|
||||
JS_CFUNC_DEF("getGroup", 1, ScNetwork::getGroup),
|
||||
JS_CFUNC_DEF("removeGroup", 1, ScNetwork::removeGroup),
|
||||
JS_CFUNC_DEF("getPlayer", 1, ScNetwork::getPlayer),
|
||||
JS_CFUNC_DEF("kickPlayer", 1, ScNetwork::kickPlayer),
|
||||
JS_CFUNC_DEF("sendMessage", 2, ScNetwork::sendMessage),
|
||||
|
||||
JS_CFUNC_DEF("createListener", 0, ScNetwork::createListener),
|
||||
JS_CFUNC_DEF("createSocket", 0, ScNetwork::createSocket)
|
||||
};
|
||||
|
||||
return MakeWithOpaque(ctx, funcs, nullptr);
|
||||
}
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "../../Duktape.hpp"
|
||||
#include "../../ScriptEngine.h"
|
||||
#include "ScPlayer.hpp"
|
||||
#include "ScPlayerGroup.hpp"
|
||||
#include "ScSocket.hpp"
|
||||
@@ -20,57 +20,48 @@
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScNetwork
|
||||
class ScNetwork final : public ScBase
|
||||
{
|
||||
private:
|
||||
#ifdef __clang__
|
||||
[[maybe_unused]]
|
||||
#endif
|
||||
duk_context* _context;
|
||||
static JSValue mode_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue numPlayers_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue numGroups_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue defaultGroup_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue defaultGroup_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
static JSValue groups_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
static JSValue players_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
static JSValue currentPlayer_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
static JSValue getPlayer(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
|
||||
static JSValue stats_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
static JSValue getGroup(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
|
||||
static JSValue addGroup(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
|
||||
static JSValue removeGroup(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
|
||||
static JSValue kickPlayer(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
|
||||
static JSValue sendMessage(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
|
||||
static JSValue createListener(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
|
||||
static JSValue createSocket(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
|
||||
public:
|
||||
ScNetwork(duk_context* ctx);
|
||||
JSValue New(JSContext* ctx);
|
||||
|
||||
std::string mode_get() const;
|
||||
int32_t numPlayers_get() const;
|
||||
int32_t numGroups_get() const;
|
||||
int32_t defaultGroup_get() const;
|
||||
void defaultGroup_set(int32_t value);
|
||||
|
||||
std::vector<std::shared_ptr<ScPlayerGroup>> groups_get() const;
|
||||
|
||||
std::vector<std::shared_ptr<ScPlayer>> players_get() const;
|
||||
|
||||
std::shared_ptr<ScPlayer> currentPlayer_get() const;
|
||||
|
||||
std::shared_ptr<ScPlayer> getPlayer(int32_t id) const;
|
||||
|
||||
DukValue stats_get() const;
|
||||
|
||||
std::shared_ptr<ScPlayerGroup> getGroup(int32_t id) const;
|
||||
|
||||
void addGroup();
|
||||
|
||||
void removeGroup(int32_t id);
|
||||
|
||||
void kickPlayer(int32_t id);
|
||||
|
||||
void sendMessage(std::string message, DukValue players);
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
std::shared_ptr<ScListener> createListener();
|
||||
#else
|
||||
void createListener();
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
std::shared_ptr<ScSocket> createSocket();
|
||||
#else
|
||||
void createSocket();
|
||||
#endif
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "Network");
|
||||
}
|
||||
};
|
||||
|
||||
extern ScNetwork gScNetwork;
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,103 +20,128 @@
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
ScPlayer::ScPlayer(int32_t id)
|
||||
: _id(id)
|
||||
using OpaquePlayerData = struct
|
||||
{
|
||||
int32_t id;
|
||||
};
|
||||
|
||||
int32_t ScPlayer::GetPlayerId(JSValue thisVal)
|
||||
{
|
||||
OpaquePlayerData* data = gScPlayer.GetOpaque<OpaquePlayerData*>(thisVal);
|
||||
if (data)
|
||||
return data->id;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t ScPlayer::id_get() const
|
||||
JSValue ScPlayer::id_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return _id;
|
||||
return JS_NewInt32(ctx, GetPlayerId(thisVal));
|
||||
}
|
||||
|
||||
std::string ScPlayer::name_get() const
|
||||
JSValue ScPlayer::name_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
auto index = Network::GetPlayerIndex(_id);
|
||||
auto index = Network::GetPlayerIndex(GetPlayerId(thisVal));
|
||||
if (index == -1)
|
||||
return {};
|
||||
return Network::GetPlayerName(index);
|
||||
return JSFromStdString(ctx, {});
|
||||
return JSFromStdString(ctx, Network::GetPlayerName(index));
|
||||
#else
|
||||
return {};
|
||||
return JSFromStdString(ctx, {});
|
||||
#endif
|
||||
}
|
||||
|
||||
int32_t ScPlayer::group_get() const
|
||||
JSValue ScPlayer::group_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
auto index = Network::GetPlayerIndex(_id);
|
||||
auto index = Network::GetPlayerIndex(GetPlayerId(thisVal));
|
||||
if (index == -1)
|
||||
return {};
|
||||
return Network::GetPlayerGroup(index);
|
||||
return JS_NewInt32(ctx, {});
|
||||
return JS_NewInt32(ctx, Network::GetPlayerGroup(index));
|
||||
#else
|
||||
return 0;
|
||||
return JS_NewInt32(ctx, 0);
|
||||
#endif
|
||||
}
|
||||
void ScPlayer::group_set(int32_t value)
|
||||
JSValue ScPlayer::group_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
auto playerSetGroupAction = GameActions::PlayerSetGroupAction(_id, value);
|
||||
JS_UNPACK_INT32(valueInt, ctx, value)
|
||||
auto playerSetGroupAction = GameActions::PlayerSetGroupAction(GetPlayerId(thisVal), valueInt);
|
||||
GameActions::Execute(&playerSetGroupAction, getGameState());
|
||||
#endif
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
int32_t ScPlayer::ping_get() const
|
||||
JSValue ScPlayer::ping_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
auto index = Network::GetPlayerIndex(_id);
|
||||
auto index = Network::GetPlayerIndex(GetPlayerId(thisVal));
|
||||
if (index == -1)
|
||||
return {};
|
||||
return Network::GetPlayerPing(index);
|
||||
return JS_NewInt32(ctx, {});
|
||||
return JS_NewInt32(ctx, Network::GetPlayerPing(index));
|
||||
#else
|
||||
return 0;
|
||||
return JS_NewInt32(ctx, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
int32_t ScPlayer::commandsRan_get() const
|
||||
JSValue ScPlayer::commandsRan_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
auto index = Network::GetPlayerIndex(_id);
|
||||
auto index = Network::GetPlayerIndex(GetPlayerId(thisVal));
|
||||
if (index == -1)
|
||||
return {};
|
||||
return Network::GetPlayerCommandsRan(index);
|
||||
return JS_NewInt32(ctx, {});
|
||||
return JS_NewInt32(ctx, Network::GetPlayerCommandsRan(index));
|
||||
#else
|
||||
return 0;
|
||||
return JS_NewInt32(ctx, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
int32_t ScPlayer::moneySpent_get() const
|
||||
JSValue ScPlayer::moneySpent_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
auto index = Network::GetPlayerIndex(_id);
|
||||
auto index = Network::GetPlayerIndex(GetPlayerId(thisVal));
|
||||
if (index == -1)
|
||||
return {};
|
||||
return Network::GetPlayerMoneySpent(index);
|
||||
return JS_NewInt64(ctx, {});
|
||||
return JS_NewInt64(ctx, Network::GetPlayerMoneySpent(index));
|
||||
#else
|
||||
return 0;
|
||||
return JS_NewInt64(ctx, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string ScPlayer::ipAddress_get() const
|
||||
JSValue ScPlayer::ipAddress_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return Network::GetPlayerIPAddress(_id);
|
||||
return JSFromStdString(ctx, Network::GetPlayerIPAddress(GetPlayerId(thisVal)));
|
||||
}
|
||||
|
||||
std::string ScPlayer::publicKeyHash_get() const
|
||||
JSValue ScPlayer::publicKeyHash_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return Network::GetPlayerPublicKeyHash(_id);
|
||||
return JSFromStdString(ctx, Network::GetPlayerPublicKeyHash(GetPlayerId(thisVal)));
|
||||
}
|
||||
|
||||
void ScPlayer::Register(duk_context* ctx)
|
||||
JSValue ScPlayer::New(JSContext* ctx, int32_t id)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScPlayer::id_get, nullptr, "id");
|
||||
dukglue_register_property(ctx, &ScPlayer::name_get, nullptr, "name");
|
||||
dukglue_register_property(ctx, &ScPlayer::group_get, &ScPlayer::group_set, "group");
|
||||
dukglue_register_property(ctx, &ScPlayer::ping_get, nullptr, "ping");
|
||||
dukglue_register_property(ctx, &ScPlayer::commandsRan_get, nullptr, "commandsRan");
|
||||
dukglue_register_property(ctx, &ScPlayer::moneySpent_get, nullptr, "moneySpent");
|
||||
dukglue_register_property(ctx, &ScPlayer::ipAddress_get, nullptr, "ipAddress");
|
||||
dukglue_register_property(ctx, &ScPlayer::publicKeyHash_get, nullptr, "publicKeyHash");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("id", ScPlayer::id_get, nullptr),
|
||||
JS_CGETSET_DEF("name", ScPlayer::name_get, nullptr),
|
||||
JS_CGETSET_DEF("group", ScPlayer::group_get, ScPlayer::group_set),
|
||||
JS_CGETSET_DEF("ping", ScPlayer::ping_get, nullptr),
|
||||
JS_CGETSET_DEF("commandsRan", ScPlayer::commandsRan_get, nullptr),
|
||||
JS_CGETSET_DEF("moneySpent", ScPlayer::moneySpent_get, nullptr),
|
||||
JS_CGETSET_DEF("ipAddress", ScPlayer::ipAddress_get, nullptr),
|
||||
JS_CGETSET_DEF("publicKeyHash", ScPlayer::publicKeyHash_get, nullptr),
|
||||
};
|
||||
return MakeWithOpaque(ctx, funcs, new OpaquePlayerData{ id });
|
||||
}
|
||||
|
||||
void ScPlayer::Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "Player", Finalize);
|
||||
}
|
||||
|
||||
void ScPlayer::Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
OpaquePlayerData* data = gScPlayer.GetOpaque<OpaquePlayerData*>(thisVal);
|
||||
if (data)
|
||||
delete data;
|
||||
}
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -11,38 +11,43 @@
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "../../Duktape.hpp"
|
||||
|
||||
#include <string.h>
|
||||
#include "../../ScriptEngine.h"
|
||||
#include "quickjs.h"
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScPlayer
|
||||
class ScPlayer;
|
||||
extern ScPlayer gScPlayer;
|
||||
|
||||
class ScPlayer : public ScBase
|
||||
{
|
||||
private:
|
||||
int32_t _id;
|
||||
static int32_t GetPlayerId(JSValue thisVal);
|
||||
|
||||
static JSValue id_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
static JSValue name_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
static JSValue group_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue group_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
static JSValue ping_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
static JSValue commandsRan_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
static JSValue moneySpent_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
static JSValue ipAddress_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
static JSValue publicKeyHash_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
public:
|
||||
ScPlayer(int32_t id);
|
||||
JSValue New(JSContext* ctx, int32_t id);
|
||||
|
||||
int32_t id_get() const;
|
||||
void Register(JSContext* ctx);
|
||||
|
||||
std::string name_get() const;
|
||||
|
||||
int32_t group_get() const;
|
||||
void group_set(int32_t value);
|
||||
|
||||
int32_t ping_get() const;
|
||||
|
||||
int32_t commandsRan_get() const;
|
||||
|
||||
int32_t moneySpent_get() const;
|
||||
|
||||
std::string ipAddress_get() const;
|
||||
|
||||
std::string publicKeyHash_get() const;
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
private:
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal);
|
||||
};
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -19,98 +19,112 @@
|
||||
#include "../../../core/String.hpp"
|
||||
#include "../../../network/Network.h"
|
||||
#include "../../../network/NetworkAction.h"
|
||||
#include "../../Duktape.hpp"
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
ScPlayerGroup::ScPlayerGroup(int32_t id)
|
||||
: _id(id)
|
||||
using OpaquePlayerGroupData = struct
|
||||
{
|
||||
int32_t id;
|
||||
};
|
||||
|
||||
int32_t ScPlayerGroup::GetGroupId(JSValue thisVal)
|
||||
{
|
||||
OpaquePlayerGroupData* data = gScPlayerGroup.GetOpaque<OpaquePlayerGroupData*>(thisVal);
|
||||
if (data)
|
||||
return data->id;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t ScPlayerGroup::id_get()
|
||||
JSValue ScPlayerGroup::id_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
return _id;
|
||||
return JS_NewInt32(ctx, GetGroupId(thisVal));
|
||||
}
|
||||
|
||||
std::string ScPlayerGroup::name_get() const
|
||||
JSValue ScPlayerGroup::name_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
auto index = Network::GetGroupIndex(_id);
|
||||
auto index = Network::GetGroupIndex(GetGroupId(thisVal));
|
||||
if (index == -1)
|
||||
return {};
|
||||
return Network::GetGroupName(index);
|
||||
return JSFromStdString(ctx, {});
|
||||
return JSFromStdString(ctx, Network::GetGroupName(index));
|
||||
#else
|
||||
return {};
|
||||
return JSFromStdString(ctx, {});
|
||||
#endif
|
||||
}
|
||||
|
||||
void ScPlayerGroup::name_set(std::string value)
|
||||
JSValue ScPlayerGroup::name_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
auto action = GameActions::NetworkModifyGroupAction(GameActions::ModifyGroupType::SetName, _id, value);
|
||||
JS_UNPACK_STR(valueStr, ctx, value);
|
||||
auto action = GameActions::NetworkModifyGroupAction(
|
||||
GameActions::ModifyGroupType::SetName, GetGroupId(thisVal), valueStr);
|
||||
GameActions::Execute(&action, getGameState());
|
||||
#endif
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
static std::string TransformPermissionKeyToJS(const std::string& s)
|
||||
static JSValue TransformPermissionKeyToJS(JSContext* ctx, const std::string& s)
|
||||
{
|
||||
auto result = s.substr(sizeof("PERMISSION_") - 1);
|
||||
for (auto& c : result)
|
||||
{
|
||||
c = std::tolower(static_cast<unsigned char>(c));
|
||||
}
|
||||
return result;
|
||||
return JSFromStdString(ctx, result);
|
||||
}
|
||||
|
||||
static std::string TransformPermissionKeyToInternal(const std::string& s)
|
||||
static std::string TransformPermissionKeyToInternal(JSContext* ctx, JSValue s)
|
||||
{
|
||||
return "PERMISSION_" + String::toUpper(s);
|
||||
if (JS_IsString(s))
|
||||
return "PERMISSION_" + String::toUpper(JSToStdString(ctx, s));
|
||||
return std::string();
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<std::string> ScPlayerGroup::permissions_get() const
|
||||
JSValue ScPlayerGroup::permissions_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
auto index = Network::GetGroupIndex(_id);
|
||||
if (index == -1)
|
||||
return {};
|
||||
|
||||
// Create array of permissions
|
||||
std::vector<std::string> result;
|
||||
JSValue result = JS_NewArray(ctx);
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
auto index = Network::GetGroupIndex(GetGroupId(thisVal));
|
||||
if (index == -1)
|
||||
return result;
|
||||
|
||||
auto permissionIndex = 0;
|
||||
int64_t resultIdx = 0;
|
||||
for (const auto& action : Network::NetworkActions::Actions)
|
||||
{
|
||||
if (Network::CanPerformAction(index, static_cast<Network::Permission>(permissionIndex)))
|
||||
{
|
||||
result.push_back(TransformPermissionKeyToJS(action.PermissionName));
|
||||
JS_SetPropertyInt64(ctx, result, resultIdx++, TransformPermissionKeyToJS(ctx, action.PermissionName));
|
||||
}
|
||||
permissionIndex++;
|
||||
}
|
||||
return result;
|
||||
#else
|
||||
return {};
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
void ScPlayerGroup::permissions_set(std::vector<std::string> value)
|
||||
JSValue ScPlayerGroup::permissions_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
auto groupIndex = Network::GetGroupIndex(_id);
|
||||
JS_UNPACK_ARRAY(array, ctx, value);
|
||||
|
||||
int32_t id = GetGroupId(thisVal);
|
||||
auto groupIndex = Network::GetGroupIndex(id);
|
||||
if (groupIndex == -1)
|
||||
return;
|
||||
return JS_UNDEFINED;
|
||||
|
||||
// First clear all permissions
|
||||
auto networkAction = GameActions::NetworkModifyGroupAction(
|
||||
GameActions::ModifyGroupType::SetPermissions, _id, "", 0, GameActions::PermissionState::ClearAll);
|
||||
GameActions::ModifyGroupType::SetPermissions, id, "", 0, GameActions::PermissionState::ClearAll);
|
||||
GameActions::Execute(&networkAction, getGameState());
|
||||
|
||||
std::vector<bool> enabledPermissions;
|
||||
enabledPermissions.resize(Network::NetworkActions::Actions.size());
|
||||
for (const auto& p : value)
|
||||
{
|
||||
auto permissionName = TransformPermissionKeyToInternal(p);
|
||||
// Don't use vector<bool> since the weird bitpacking specialisation does not work with the lambda (on some compilers)
|
||||
std::vector<uint8_t> enabledPermissions(Network::NetworkActions::Actions.size());
|
||||
JSIterateArray(ctx, array, [&enabledPermissions](JSContext* ctx2, JSValue x) {
|
||||
auto permissionName = TransformPermissionKeyToInternal(ctx2, x);
|
||||
|
||||
auto permissionIndex = 0;
|
||||
for (const auto& action : Network::NetworkActions::Actions)
|
||||
@@ -121,7 +135,7 @@ namespace OpenRCT2::Scripting
|
||||
}
|
||||
permissionIndex++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < enabledPermissions.size(); i++)
|
||||
{
|
||||
@@ -130,19 +144,35 @@ namespace OpenRCT2::Scripting
|
||||
if (toggle)
|
||||
{
|
||||
auto networkAction2 = GameActions::NetworkModifyGroupAction(
|
||||
GameActions::ModifyGroupType::SetPermissions, _id, "", static_cast<uint32_t>(i),
|
||||
GameActions::ModifyGroupType::SetPermissions, id, "", static_cast<uint32_t>(i),
|
||||
GameActions::PermissionState::Toggle);
|
||||
GameActions::Execute(&networkAction2, getGameState());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void ScPlayerGroup::Register(duk_context* ctx)
|
||||
JSValue ScPlayerGroup::New(JSContext* ctx, int32_t id)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScPlayerGroup::id_get, nullptr, "id");
|
||||
dukglue_register_property(ctx, &ScPlayerGroup::name_get, &ScPlayerGroup::name_set, "name");
|
||||
dukglue_register_property(ctx, &ScPlayerGroup::permissions_get, &ScPlayerGroup::permissions_set, "permissions");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("id", ScPlayerGroup::id_get, nullptr),
|
||||
JS_CGETSET_DEF("name", ScPlayerGroup::name_get, ScPlayerGroup::name_set),
|
||||
JS_CGETSET_DEF("permissions", ScPlayerGroup::permissions_get, ScPlayerGroup::permissions_set),
|
||||
};
|
||||
return MakeWithOpaque(ctx, funcs, new OpaquePlayerGroupData{ id });
|
||||
}
|
||||
|
||||
void ScPlayerGroup::Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "PlayerGroup", Finalize);
|
||||
}
|
||||
|
||||
void ScPlayerGroup::Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
OpaquePlayerGroupData* data = gScPlayerGroup.GetOpaque<OpaquePlayerGroupData*>(thisVal);
|
||||
if (data)
|
||||
delete data;
|
||||
}
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -11,29 +11,33 @@
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "../../Duktape.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../../ScriptEngine.h"
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScPlayerGroup
|
||||
class ScPlayerGroup;
|
||||
extern ScPlayerGroup gScPlayerGroup;
|
||||
|
||||
class ScPlayerGroup : public ScBase
|
||||
{
|
||||
int32_t _id;
|
||||
private:
|
||||
static int32_t GetGroupId(JSValue thisVal);
|
||||
|
||||
static JSValue id_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
static JSValue name_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue name_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
static JSValue permissions_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue permissions_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
|
||||
public:
|
||||
ScPlayerGroup(int32_t id);
|
||||
JSValue New(JSContext* ctx, int32_t id);
|
||||
|
||||
int32_t id_get();
|
||||
void Register(JSContext* ctx);
|
||||
|
||||
std::string name_get() const;
|
||||
void name_set(std::string value);
|
||||
|
||||
std::vector<std::string> permissions_get() const;
|
||||
void permissions_set(std::vector<std::string> value);
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
private:
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal);
|
||||
};
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "../../../Context.h"
|
||||
#include "../../../config/Config.h"
|
||||
#include "../../../network/Socket.h"
|
||||
#include "../../Duktape.hpp"
|
||||
#include "../../ScriptEngine.h"
|
||||
|
||||
#include <memory>
|
||||
@@ -26,9 +25,9 @@ namespace OpenRCT2::Scripting
|
||||
class EventList
|
||||
{
|
||||
private:
|
||||
std::vector<std::vector<DukValue>> _listeners;
|
||||
std::vector<std::vector<JSCallback>> _listeners;
|
||||
|
||||
std::vector<DukValue>& GetListenerList(uint32_t id)
|
||||
std::vector<JSCallback>& GetListenerList(uint32_t id)
|
||||
{
|
||||
if (_listeners.size() <= id)
|
||||
{
|
||||
@@ -39,7 +38,7 @@ namespace OpenRCT2::Scripting
|
||||
|
||||
public:
|
||||
void Raise(
|
||||
uint32_t id, const std::shared_ptr<Plugin>& plugin, const std::vector<DukValue>& args, bool isGameStateMutable)
|
||||
uint32_t id, const std::shared_ptr<Plugin>& plugin, const std::vector<JSValue>& args, bool isGameStateMutable)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
|
||||
@@ -47,23 +46,26 @@ namespace OpenRCT2::Scripting
|
||||
auto listeners = GetListenerList(id);
|
||||
for (size_t i = 0; i < listeners.size(); i++)
|
||||
{
|
||||
scriptEngine.ExecutePluginCall(plugin, listeners[i], args, isGameStateMutable);
|
||||
scriptEngine.ExecutePluginCall(plugin, listeners[i].callback, args, isGameStateMutable);
|
||||
|
||||
// Safety, listeners might get reallocated
|
||||
listeners = GetListenerList(id);
|
||||
}
|
||||
}
|
||||
|
||||
void AddListener(uint32_t id, const DukValue& listener)
|
||||
void AddListener(uint32_t id, const JSCallback& listener)
|
||||
{
|
||||
auto& listeners = GetListenerList(id);
|
||||
listeners.push_back(listener);
|
||||
}
|
||||
|
||||
void RemoveListener(uint32_t id, const DukValue& value)
|
||||
void RemoveListener(uint32_t id, const JSCallback& value)
|
||||
{
|
||||
auto& listeners = GetListenerList(id);
|
||||
listeners.erase(std::remove(listeners.begin(), listeners.end(), value), listeners.end());
|
||||
std::erase_if(listeners, [&value](const JSCallback& x) {
|
||||
// Note: this might be hacky because I am not sure if/when quickjs will keep these function ptrs stable.
|
||||
return JS_VALUE_GET_PTR(value.callback) == JS_VALUE_GET_PTR(x.callback);
|
||||
});
|
||||
}
|
||||
|
||||
void RemoveAllListeners(uint32_t id)
|
||||
@@ -71,204 +73,61 @@ namespace OpenRCT2::Scripting
|
||||
auto& listeners = GetListenerList(id);
|
||||
listeners.clear();
|
||||
}
|
||||
|
||||
void RemoveAllListeners()
|
||||
{
|
||||
_listeners.clear();
|
||||
}
|
||||
};
|
||||
|
||||
class ScSocketBase
|
||||
inline bool IsLocalhostAddress(std::string_view s)
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<Plugin> _plugin;
|
||||
return s == "localhost" || s == "127.0.0.1" || s == "::";
|
||||
}
|
||||
|
||||
protected:
|
||||
static bool IsLocalhostAddress(std::string_view s)
|
||||
inline bool IsOnWhiteList(std::string_view host)
|
||||
{
|
||||
constexpr char delimiter = ',';
|
||||
size_t start_pos = 0;
|
||||
size_t end_pos = 0;
|
||||
while ((end_pos = Config::Get().plugin.allowedHosts.find(delimiter, start_pos)) != std::string::npos)
|
||||
{
|
||||
return s == "localhost" || s == "127.0.0.1" || s == "::";
|
||||
}
|
||||
|
||||
static bool IsOnWhiteList(std::string_view host)
|
||||
{
|
||||
constexpr char delimiter = ',';
|
||||
size_t start_pos = 0;
|
||||
size_t end_pos = 0;
|
||||
while ((end_pos = Config::Get().plugin.allowedHosts.find(delimiter, start_pos)) != std::string::npos)
|
||||
if (host == Config::Get().plugin.allowedHosts.substr(start_pos, end_pos - start_pos))
|
||||
{
|
||||
if (host == Config::Get().plugin.allowedHosts.substr(start_pos, end_pos - start_pos))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
start_pos = end_pos + 1;
|
||||
return true;
|
||||
}
|
||||
return host
|
||||
== Config::Get().plugin.allowedHosts.substr(start_pos, Config::Get().plugin.allowedHosts.length() - start_pos);
|
||||
start_pos = end_pos + 1;
|
||||
}
|
||||
return host
|
||||
== Config::Get().plugin.allowedHosts.substr(start_pos, Config::Get().plugin.allowedHosts.length() - start_pos);
|
||||
}
|
||||
|
||||
public:
|
||||
ScSocketBase(const std::shared_ptr<Plugin>& plugin)
|
||||
: _plugin(plugin)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~ScSocketBase()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
const std::shared_ptr<Plugin>& GetPlugin() const
|
||||
{
|
||||
return _plugin;
|
||||
}
|
||||
struct SocketDataBase
|
||||
{
|
||||
EventList _eventList;
|
||||
std::unique_ptr<Network::ITcpSocket> _socket;
|
||||
std::shared_ptr<Plugin> _plugin;
|
||||
bool _disposed{};
|
||||
|
||||
virtual ~SocketDataBase() = default;
|
||||
virtual void Update() = 0;
|
||||
|
||||
virtual void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool IsDisposed() const = 0;
|
||||
virtual void Dispose() = 0;
|
||||
};
|
||||
|
||||
class ScSocket final : public ScSocketBase
|
||||
struct SocketData final : SocketDataBase
|
||||
{
|
||||
private:
|
||||
static constexpr uint32_t EVENT_NONE = std::numeric_limits<uint32_t>::max();
|
||||
static constexpr uint32_t EVENT_CLOSE = 0;
|
||||
static constexpr uint32_t EVENT_DATA = 1;
|
||||
static constexpr uint32_t EVENT_CONNECT_ONCE = 2;
|
||||
static constexpr uint32_t EVENT_ERROR = 3;
|
||||
|
||||
EventList _eventList;
|
||||
std::unique_ptr<Network::ITcpSocket> _socket;
|
||||
bool _disposed{};
|
||||
bool _connecting{};
|
||||
bool _wasConnected{};
|
||||
|
||||
public:
|
||||
ScSocket(const std::shared_ptr<Plugin>& plugin)
|
||||
: ScSocketBase(plugin)
|
||||
~SocketData() override
|
||||
{
|
||||
}
|
||||
|
||||
ScSocket(const std::shared_ptr<Plugin>& plugin, std::unique_ptr<Network::ITcpSocket>&& socket)
|
||||
: ScSocketBase(plugin)
|
||||
, _socket(std::move(socket))
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
ScSocket* destroy(const DukValue& error)
|
||||
{
|
||||
CloseSocket();
|
||||
return this;
|
||||
}
|
||||
|
||||
ScSocket* setNoDelay(bool noDelay)
|
||||
{
|
||||
if (_socket != nullptr)
|
||||
{
|
||||
_socket->SetNoDelay(noDelay);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
ScSocket* connect(uint16_t port, const std::string& host, const DukValue& callback)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
if (_socket != nullptr)
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Socket has already been created.");
|
||||
}
|
||||
else if (_disposed)
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Socket is disposed.");
|
||||
}
|
||||
else if (_connecting)
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Socket is already connecting.");
|
||||
}
|
||||
else if (!IsLocalhostAddress(host) && !IsOnWhiteList(host))
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "For security reasons, only connecting to localhost is allowed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_socket = Network::CreateTcpSocket();
|
||||
try
|
||||
{
|
||||
_socket->ConnectAsync(host, port);
|
||||
_eventList.AddListener(EVENT_CONNECT_ONCE, callback);
|
||||
_connecting = true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, e.what());
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
ScSocket* end(const DukValue& data)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Socket is disposed.");
|
||||
}
|
||||
else if (_socket != nullptr)
|
||||
{
|
||||
if (data.type() == DukValue::Type::STRING)
|
||||
{
|
||||
write(data.as_string());
|
||||
_socket->Finish();
|
||||
}
|
||||
else
|
||||
{
|
||||
_socket->Finish();
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Only sending strings is currently supported.");
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
bool write(const std::string& data)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Socket is disposed.");
|
||||
}
|
||||
else if (_socket != nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto sentBytes = _socket->SendData(data.c_str(), data.size());
|
||||
return sentBytes != data.size();
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ScSocket* on(const std::string& eventType, const DukValue& callback)
|
||||
{
|
||||
auto eventId = GetEventType(eventType);
|
||||
if (eventId != EVENT_NONE)
|
||||
{
|
||||
_eventList.AddListener(eventId, callback);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
ScSocket* off(const std::string& eventType, const DukValue& callback)
|
||||
{
|
||||
auto eventId = GetEventType(eventType);
|
||||
if (eventId != EVENT_NONE)
|
||||
{
|
||||
_eventList.RemoveListener(eventId, callback);
|
||||
}
|
||||
return this;
|
||||
SocketData::Dispose();
|
||||
}
|
||||
|
||||
void CloseSocket()
|
||||
@@ -283,22 +142,22 @@ namespace OpenRCT2::Scripting
|
||||
RaiseOnClose(false);
|
||||
}
|
||||
}
|
||||
_eventList.RemoveAllListeners();
|
||||
}
|
||||
|
||||
void RaiseOnClose(bool hadError)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
_eventList.Raise(EVENT_CLOSE, GetPlugin(), { ToDuk(ctx, hadError) }, false);
|
||||
JSContext* ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
_eventList.Raise(EVENT_CLOSE, _plugin, { JS_NewBool(ctx, hadError) }, false);
|
||||
}
|
||||
|
||||
void RaiseOnData(const std::string& data)
|
||||
void RaiseOnData(std::string_view data)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto ctx = scriptEngine.GetContext();
|
||||
_eventList.Raise(EVENT_DATA, GetPlugin(), { ToDuk(ctx, data) }, false);
|
||||
JSContext* ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
_eventList.Raise(EVENT_DATA, _plugin, { JSFromStdString(ctx, data) }, false);
|
||||
}
|
||||
|
||||
uint32_t GetEventType(std::string_view name)
|
||||
static uint32_t GetEventType(std::string_view name)
|
||||
{
|
||||
if (name == "close")
|
||||
return EVENT_CLOSE;
|
||||
@@ -309,7 +168,6 @@ namespace OpenRCT2::Scripting
|
||||
return EVENT_NONE;
|
||||
}
|
||||
|
||||
public:
|
||||
void Update() override
|
||||
{
|
||||
if (_disposed)
|
||||
@@ -324,22 +182,21 @@ namespace OpenRCT2::Scripting
|
||||
{
|
||||
_connecting = false;
|
||||
_wasConnected = true;
|
||||
_eventList.Raise(EVENT_CONNECT_ONCE, GetPlugin(), {}, false);
|
||||
_eventList.Raise(EVENT_CONNECT_ONCE, _plugin, {}, false);
|
||||
_eventList.RemoveAllListeners(EVENT_CONNECT_ONCE);
|
||||
}
|
||||
else if (status == Network::SocketStatus::closed)
|
||||
{
|
||||
_connecting = false;
|
||||
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto ctx = scriptEngine.GetContext();
|
||||
JSContext* ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto err = _socket->GetError();
|
||||
if (err == nullptr)
|
||||
{
|
||||
err = "";
|
||||
}
|
||||
auto dukErr = ToDuk(ctx, std::string_view(err));
|
||||
_eventList.Raise(EVENT_ERROR, GetPlugin(), { dukErr }, true);
|
||||
auto jsErr = JSFromStdString(ctx, std::string_view(err));
|
||||
_eventList.Raise(EVENT_ERROR, _plugin, { jsErr }, true);
|
||||
}
|
||||
}
|
||||
else if (status == Network::SocketStatus::connected)
|
||||
@@ -350,7 +207,7 @@ namespace OpenRCT2::Scripting
|
||||
switch (result)
|
||||
{
|
||||
case Network::ReadPacket::success:
|
||||
RaiseOnData(std::string(buffer, bytesRead));
|
||||
RaiseOnData(std::string_view(buffer, bytesRead));
|
||||
break;
|
||||
case Network::ReadPacket::noData:
|
||||
break;
|
||||
@@ -373,129 +230,235 @@ namespace OpenRCT2::Scripting
|
||||
CloseSocket();
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
bool IsDisposed() const override
|
||||
{
|
||||
return _disposed;
|
||||
}
|
||||
|
||||
static void Register(duk_context* ctx)
|
||||
{
|
||||
dukglue_register_method(ctx, &ScSocket::destroy, "destroy");
|
||||
dukglue_register_method(ctx, &ScSocket::setNoDelay, "setNoDelay");
|
||||
dukglue_register_method(ctx, &ScSocket::connect, "connect");
|
||||
dukglue_register_method(ctx, &ScSocket::end, "end");
|
||||
dukglue_register_method(ctx, &ScSocket::write, "write");
|
||||
dukglue_register_method(ctx, &ScSocket::on, "on");
|
||||
dukglue_register_method(ctx, &ScSocket::off, "off");
|
||||
}
|
||||
};
|
||||
|
||||
class ScListener final : public ScSocketBase
|
||||
class ScSocket;
|
||||
extern ScSocket gScSocket;
|
||||
class ScSocket final : public ScBase
|
||||
{
|
||||
private:
|
||||
static constexpr uint32_t EVENT_NONE = std::numeric_limits<uint32_t>::max();
|
||||
static constexpr uint32_t EVENT_CONNECTION = 0;
|
||||
|
||||
EventList _eventList;
|
||||
std::unique_ptr<Network::ITcpSocket> _socket;
|
||||
std::vector<std::shared_ptr<ScSocket>> _scClientSockets;
|
||||
bool _disposed{};
|
||||
|
||||
bool listening_get()
|
||||
static SocketData* GetData(JSValue thisVal)
|
||||
{
|
||||
if (_socket != nullptr)
|
||||
return gScSocket.GetOpaque<SocketData*>(thisVal);
|
||||
}
|
||||
|
||||
static JSValue destroy(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto data = GetData(thisVal);
|
||||
if (!data)
|
||||
return JS_ThrowInternalError(ctx, "Invalid socket");
|
||||
|
||||
data->CloseSocket();
|
||||
return JS_DupValue(ctx, thisVal);
|
||||
}
|
||||
|
||||
static JSValue setNoDelay(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_BOOL(noDelay, ctx, argv[0]);
|
||||
|
||||
auto data = GetData(thisVal);
|
||||
if (!data)
|
||||
return JS_ThrowInternalError(ctx, "Invalid socket");
|
||||
|
||||
if (data->_socket != nullptr)
|
||||
{
|
||||
return _socket->GetStatus() == Network::SocketStatus::listening;
|
||||
data->_socket->SetNoDelay(noDelay);
|
||||
}
|
||||
return false;
|
||||
return JS_DupValue(ctx, thisVal);
|
||||
}
|
||||
|
||||
ScListener* close()
|
||||
static JSValue connect(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
CloseSocket();
|
||||
return this;
|
||||
}
|
||||
JS_UNPACK_UINT32(port, ctx, argv[0]);
|
||||
if (port & 0xFFFF0000)
|
||||
return JS_ThrowRangeError(ctx, "Invalid port.");
|
||||
JS_UNPACK_STR(host, ctx, argv[1]);
|
||||
|
||||
ScListener* listen(int32_t port, const DukValue& dukHost)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
if (_disposed)
|
||||
auto data = GetData(thisVal);
|
||||
if (!data)
|
||||
return JS_ThrowInternalError(ctx, "Invalid socket");
|
||||
|
||||
if (data->_socket != nullptr)
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Socket is disposed.");
|
||||
JS_ThrowPlainError(ctx, "Socket has already been created.");
|
||||
}
|
||||
else if (data->_disposed)
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "Socket is disposed.");
|
||||
}
|
||||
else if (data->_connecting)
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "Socket is already connecting.");
|
||||
}
|
||||
else if (!IsLocalhostAddress(host) && !IsOnWhiteList(host))
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "For security reasons, only connecting to localhost is allowed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_socket == nullptr)
|
||||
data->_socket = Network::CreateTcpSocket();
|
||||
try
|
||||
{
|
||||
_socket = Network::CreateTcpSocket();
|
||||
data->_socket->ConnectAsync(host, port);
|
||||
if (JS_IsFunction(ctx, argv[2]))
|
||||
{
|
||||
data->_eventList.AddListener(SocketData::EVENT_CONNECT_ONCE, JSCallback(ctx, argv[2]));
|
||||
}
|
||||
data->_connecting = true;
|
||||
}
|
||||
|
||||
if (_socket->GetStatus() == Network::SocketStatus::listening)
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Server is already listening.");
|
||||
JS_ThrowInternalError(ctx, "%s", e.what());
|
||||
}
|
||||
}
|
||||
return JS_DupValue(ctx, thisVal);
|
||||
}
|
||||
|
||||
static JSValue end(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto data = GetData(thisVal);
|
||||
if (!data)
|
||||
return JS_ThrowInternalError(ctx, "Invalid socket");
|
||||
|
||||
if (data->_disposed)
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "Socket is disposed.");
|
||||
}
|
||||
else if (data->_socket != nullptr)
|
||||
{
|
||||
if (JS_IsString(argv[0]))
|
||||
{
|
||||
if (JSValue res = write(ctx, thisVal, argc, argv); JS_IsException(res))
|
||||
{
|
||||
return res;
|
||||
}
|
||||
data->_socket->Finish();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dukHost.type() == DukValue::Type::STRING)
|
||||
{
|
||||
auto host = dukHost.as_string();
|
||||
if (IsLocalhostAddress(host) || IsOnWhiteList(host))
|
||||
{
|
||||
try
|
||||
{
|
||||
_socket->Listen(host, port);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, e.what());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "For security reasons, only binding to localhost is allowed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_socket->Listen("127.0.0.1", port);
|
||||
}
|
||||
data->_socket->Finish();
|
||||
JS_ThrowPlainError(ctx, "Only sending strings is currently supported.");
|
||||
}
|
||||
}
|
||||
return this;
|
||||
return JS_DupValue(ctx, thisVal);
|
||||
}
|
||||
|
||||
ScListener* on(const std::string& eventType, const DukValue& callback)
|
||||
static JSValue write(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto eventId = GetEventType(eventType);
|
||||
if (eventId != EVENT_NONE)
|
||||
JS_UNPACK_STR(str, ctx, argv[0]);
|
||||
|
||||
auto data = GetData(thisVal);
|
||||
if (!data)
|
||||
return JS_ThrowInternalError(ctx, "Invalid socket");
|
||||
|
||||
if (data->_disposed)
|
||||
{
|
||||
_eventList.AddListener(eventId, callback);
|
||||
JS_ThrowPlainError(ctx, "Socket is disposed.");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
ScListener* off(const std::string& eventType, const DukValue& callback)
|
||||
{
|
||||
auto eventId = GetEventType(eventType);
|
||||
if (eventId != EVENT_NONE)
|
||||
else if (data->_socket != nullptr)
|
||||
{
|
||||
_eventList.RemoveListener(eventId, callback);
|
||||
try
|
||||
{
|
||||
auto sentBytes = data->_socket->SendData(str.c_str(), str.size());
|
||||
return JS_NewBool(ctx, sentBytes != str.size());
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
return JS_NewBool(ctx, false);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
return JS_NewBool(ctx, false);
|
||||
}
|
||||
|
||||
uint32_t GetEventType(std::string_view name)
|
||||
static JSValue on(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
if (name == "connection")
|
||||
return EVENT_CONNECTION;
|
||||
return EVENT_NONE;
|
||||
JS_UNPACK_STR(eventType, ctx, argv[0]);
|
||||
JS_UNPACK_CALLBACK(callback, ctx, argv[1]);
|
||||
|
||||
auto data = GetData(thisVal);
|
||||
if (!data)
|
||||
return JS_ThrowInternalError(ctx, "Invalid socket");
|
||||
|
||||
auto eventId = SocketData::GetEventType(eventType);
|
||||
if (eventId != SocketData::EVENT_NONE)
|
||||
{
|
||||
data->_eventList.AddListener(eventId, callback);
|
||||
}
|
||||
return JS_DupValue(ctx, thisVal);
|
||||
}
|
||||
|
||||
static JSValue off(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_STR(eventType, ctx, argv[0]);
|
||||
JS_UNPACK_CALLBACK(callback, ctx, argv[1]);
|
||||
|
||||
auto data = GetData(thisVal);
|
||||
if (!data)
|
||||
return JS_ThrowInternalError(ctx, "Invalid socket");
|
||||
|
||||
auto eventId = SocketData::GetEventType(eventType);
|
||||
if (eventId != SocketData::EVENT_NONE)
|
||||
{
|
||||
data->_eventList.RemoveListener(eventId, callback);
|
||||
}
|
||||
return JS_DupValue(ctx, thisVal);
|
||||
}
|
||||
|
||||
public:
|
||||
ScListener(const std::shared_ptr<Plugin>& plugin)
|
||||
: ScSocketBase(plugin)
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CFUNC_DEF("destroy", 1, ScSocket::destroy), JS_CFUNC_DEF("setNoDelay", 1, ScSocket::setNoDelay),
|
||||
JS_CFUNC_DEF("connect", 3, ScSocket::connect), JS_CFUNC_DEF("end", 1, ScSocket::end),
|
||||
JS_CFUNC_DEF("write", 1, ScSocket::write), JS_CFUNC_DEF("on", 2, ScSocket::on),
|
||||
JS_CFUNC_DEF("off", 2, ScSocket::off),
|
||||
};
|
||||
|
||||
JSValue New(JSContext* ctx, const std::shared_ptr<Plugin>& plugin)
|
||||
{
|
||||
// unique ptr is used to avoid static analyzer false positives.
|
||||
auto data = std::make_unique<SocketData>();
|
||||
data->_plugin = plugin;
|
||||
GetContext()->GetScriptEngine().AddSocket(data.get());
|
||||
return MakeWithOpaque(ctx, funcs, data.release());
|
||||
}
|
||||
|
||||
JSValue New(JSContext* ctx, const std::shared_ptr<Plugin>& plugin, std::unique_ptr<Network::ITcpSocket>&& socket)
|
||||
{
|
||||
auto data = std::make_unique<SocketData>();
|
||||
data->_plugin = plugin;
|
||||
data->_socket = std::move(socket);
|
||||
GetContext()->GetScriptEngine().AddSocket(data.get());
|
||||
return MakeWithOpaque(ctx, funcs, data.release());
|
||||
}
|
||||
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "Socket", Finalize);
|
||||
}
|
||||
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
SocketData* data = gScSocket.GetOpaque<SocketData*>(thisVal);
|
||||
if (data)
|
||||
{
|
||||
// Note the game context pointer can be null if the game is shutting down,
|
||||
// but all sockets should have been cleaned up by then.
|
||||
IContext* gameContext = GetContext();
|
||||
assert(gameContext != nullptr);
|
||||
gameContext->GetScriptEngine().RemoveSocket(data);
|
||||
data->Dispose();
|
||||
delete data;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ListenerData final : SocketDataBase
|
||||
{
|
||||
static constexpr uint32_t EVENT_NONE = std::numeric_limits<uint32_t>::max();
|
||||
static constexpr uint32_t EVENT_CONNECTION = 0;
|
||||
|
||||
~ListenerData() override
|
||||
{
|
||||
ListenerData::Dispose();
|
||||
}
|
||||
|
||||
void Update() override
|
||||
@@ -514,13 +477,9 @@ namespace OpenRCT2::Scripting
|
||||
// Default to using Nagle's algorithm like node.js does
|
||||
client->SetNoDelay(false);
|
||||
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto clientSocket = std::make_shared<ScSocket>(GetPlugin(), std::move(client));
|
||||
scriptEngine.AddSocket(clientSocket);
|
||||
|
||||
auto ctx = scriptEngine.GetContext();
|
||||
auto dukClientSocket = GetObjectAsDukValue(ctx, clientSocket);
|
||||
_eventList.Raise(EVENT_CONNECTION, GetPlugin(), { dukClientSocket }, false);
|
||||
JSContext* ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto clientSocket = gScSocket.New(ctx, _plugin, std::move(client));
|
||||
_eventList.Raise(EVENT_CONNECTION, _plugin, { clientSocket }, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -532,6 +491,7 @@ namespace OpenRCT2::Scripting
|
||||
_socket->Close();
|
||||
_socket = nullptr;
|
||||
}
|
||||
_eventList.RemoveAllListeners();
|
||||
}
|
||||
|
||||
void Dispose() override
|
||||
@@ -539,19 +499,172 @@ namespace OpenRCT2::Scripting
|
||||
CloseSocket();
|
||||
_disposed = true;
|
||||
}
|
||||
};
|
||||
|
||||
bool IsDisposed() const override
|
||||
class ScListener;
|
||||
extern ScListener gScListener;
|
||||
class ScListener final : public ScBase
|
||||
{
|
||||
static ListenerData* GetData(JSValue thisVal)
|
||||
{
|
||||
return _disposed;
|
||||
return gScListener.GetOpaque<ListenerData*>(thisVal);
|
||||
}
|
||||
|
||||
static void Register(duk_context* ctx)
|
||||
static JSValue listening_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScListener::listening_get, nullptr, "listening");
|
||||
dukglue_register_method(ctx, &ScListener::close, "close");
|
||||
dukglue_register_method(ctx, &ScListener::listen, "listen");
|
||||
dukglue_register_method(ctx, &ScListener::on, "on");
|
||||
dukglue_register_method(ctx, &ScListener::off, "off");
|
||||
auto data = GetData(thisVal);
|
||||
if (!data)
|
||||
return JS_ThrowInternalError(ctx, "Invalid listener");
|
||||
|
||||
if (data->_socket != nullptr)
|
||||
{
|
||||
return JS_NewBool(ctx, data->_socket->GetStatus() == Network::SocketStatus::listening);
|
||||
}
|
||||
return JS_NewBool(ctx, false);
|
||||
}
|
||||
|
||||
static JSValue close(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto data = GetData(thisVal);
|
||||
if (!data)
|
||||
return JS_ThrowInternalError(ctx, "Invalid listener");
|
||||
|
||||
data->CloseSocket();
|
||||
return JS_DupValue(ctx, thisVal);
|
||||
}
|
||||
|
||||
static JSValue listen(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_UINT32(port, ctx, argv[0]);
|
||||
if (port & 0xFFFF0000)
|
||||
return JS_ThrowRangeError(ctx, "Invalid port.");
|
||||
|
||||
auto data = GetData(thisVal);
|
||||
if (!data)
|
||||
return JS_ThrowInternalError(ctx, "Invalid listener");
|
||||
|
||||
if (data->_disposed)
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "Socket is disposed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data->_socket == nullptr)
|
||||
{
|
||||
data->_socket = Network::CreateTcpSocket();
|
||||
}
|
||||
|
||||
if (data->_socket->GetStatus() == Network::SocketStatus::listening)
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "Server is already listening.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (JS_IsString(argv[1]))
|
||||
{
|
||||
std::string host = JSToStdString(ctx, argv[1]);
|
||||
if (IsLocalhostAddress(host) || IsOnWhiteList(host))
|
||||
{
|
||||
try
|
||||
{
|
||||
data->_socket->Listen(host, port);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "%s", e.what());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
JS_ThrowPlainError(ctx, "For security reasons, only binding to localhost is allowed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data->_socket->Listen("127.0.0.1", port);
|
||||
}
|
||||
}
|
||||
}
|
||||
return JS_DupValue(ctx, thisVal);
|
||||
}
|
||||
|
||||
static JSValue on(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_STR(eventType, ctx, argv[0]);
|
||||
JS_UNPACK_CALLBACK(callback, ctx, argv[1]);
|
||||
|
||||
auto data = GetData(thisVal);
|
||||
if (!data)
|
||||
return JS_ThrowInternalError(ctx, "Invalid listener");
|
||||
|
||||
auto eventId = GetEventType(eventType);
|
||||
if (eventId != ListenerData::EVENT_NONE)
|
||||
{
|
||||
data->_eventList.AddListener(eventId, callback);
|
||||
}
|
||||
return JS_DupValue(ctx, thisVal);
|
||||
}
|
||||
|
||||
static JSValue off(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_STR(eventType, ctx, argv[0]);
|
||||
JS_UNPACK_CALLBACK(callback, ctx, argv[1]);
|
||||
|
||||
auto data = GetData(thisVal);
|
||||
if (!data)
|
||||
return JS_ThrowInternalError(ctx, "Invalid listener");
|
||||
|
||||
auto eventId = GetEventType(eventType);
|
||||
if (eventId != ListenerData::EVENT_NONE)
|
||||
{
|
||||
data->_eventList.RemoveListener(eventId, callback);
|
||||
}
|
||||
return JS_DupValue(ctx, thisVal);
|
||||
}
|
||||
|
||||
static uint32_t GetEventType(std::string_view name)
|
||||
{
|
||||
if (name == "connection")
|
||||
return ListenerData::EVENT_CONNECTION;
|
||||
return ListenerData::EVENT_NONE;
|
||||
}
|
||||
|
||||
public:
|
||||
JSValue New(JSContext* ctx, const std::shared_ptr<Plugin>& plugin)
|
||||
{
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("listening", ScListener::listening_get, nullptr),
|
||||
JS_CFUNC_DEF("close", 0, ScListener::close),
|
||||
JS_CFUNC_DEF("listen", 2, ScListener::listen),
|
||||
JS_CFUNC_DEF("on", 2, ScListener::on),
|
||||
JS_CFUNC_DEF("off", 2, ScListener::off),
|
||||
};
|
||||
|
||||
// unique ptr is used to avoid static analyzer false positives.
|
||||
auto data = std::make_unique<ListenerData>();
|
||||
data->_plugin = plugin;
|
||||
GetContext()->GetScriptEngine().AddSocket(data.get());
|
||||
return MakeWithOpaque(ctx, funcs, data.release());
|
||||
}
|
||||
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "Listener", Finalize);
|
||||
}
|
||||
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
ListenerData* data = gScListener.GetOpaque<ListenerData*>(thisVal);
|
||||
if (data)
|
||||
{
|
||||
// Note the game context pointer can be null if the game is shutting down,
|
||||
// but all sockets should have been cleaned up by then.
|
||||
IContext* gameContext = GetContext();
|
||||
assert(gameContext != nullptr);
|
||||
gameContext->GetScriptEngine().RemoveSocket(data);
|
||||
data->Dispose();
|
||||
delete data;
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "../../../core/EnumMap.hpp"
|
||||
#include "../../../object/ObjectTypes.h"
|
||||
#include "ScObject.hpp"
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
#include "../../../Context.h"
|
||||
#include "../../../object/ObjectRepository.h"
|
||||
#include "../../Duktape.hpp"
|
||||
#include "../../ScriptEngine.h"
|
||||
|
||||
#include <optional>
|
||||
@@ -34,137 +33,166 @@ namespace OpenRCT2::Scripting
|
||||
return values[EnumValue(sourceGame)];
|
||||
}
|
||||
|
||||
class ScInstalledObject
|
||||
class ScInstalledObject;
|
||||
extern ScInstalledObject gScInstalledObject;
|
||||
|
||||
class ScInstalledObject final : public ScBase
|
||||
{
|
||||
protected:
|
||||
size_t _index{};
|
||||
private:
|
||||
struct InstalledObjectData
|
||||
{
|
||||
size_t _index{};
|
||||
};
|
||||
|
||||
public:
|
||||
ScInstalledObject(size_t index)
|
||||
: _index(index)
|
||||
void Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "InstalledObject", Finalize);
|
||||
}
|
||||
|
||||
static void Register(duk_context* ctx)
|
||||
JSValue New(JSContext* ctx, size_t index)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScInstalledObject::path_get, nullptr, "path");
|
||||
dukglue_register_property(ctx, &ScInstalledObject::generation_get, nullptr, "generation");
|
||||
dukglue_register_property(ctx, &ScInstalledObject::identifier_get, nullptr, "identifier");
|
||||
dukglue_register_property(ctx, &ScInstalledObject::type_get, nullptr, "type");
|
||||
dukglue_register_property(ctx, &ScInstalledObject::sourceGames_get, nullptr, "sourceGames");
|
||||
dukglue_register_property(ctx, &ScInstalledObject::legacyIdentifier_get, nullptr, "legacyIdentifier");
|
||||
dukglue_register_property(ctx, &ScInstalledObject::authors_get, nullptr, "authors");
|
||||
dukglue_register_property(ctx, &ScInstalledObject::name_get, nullptr, "name");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("path", ScInstalledObject::path_get, nullptr),
|
||||
JS_CGETSET_DEF("generation", ScInstalledObject::generation_get, nullptr),
|
||||
JS_CGETSET_DEF("identifier", ScInstalledObject::identifier_get, nullptr),
|
||||
JS_CGETSET_DEF("type", ScInstalledObject::type_get, nullptr),
|
||||
JS_CGETSET_DEF("sourceGames", ScInstalledObject::sourceGames_get, nullptr),
|
||||
JS_CGETSET_DEF("legacyIdentifier", ScInstalledObject::legacyIdentifier_get, nullptr),
|
||||
JS_CGETSET_DEF("authors", ScInstalledObject::authors_get, nullptr),
|
||||
JS_CGETSET_DEF("name", ScInstalledObject::name_get, nullptr),
|
||||
};
|
||||
return MakeWithOpaque(ctx, funcs, new InstalledObjectData{ index });
|
||||
}
|
||||
|
||||
private:
|
||||
std::string path_get() const
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
auto installedObject = GetInstalledObject();
|
||||
if (installedObject != nullptr)
|
||||
{
|
||||
return installedObject->Path;
|
||||
}
|
||||
return {};
|
||||
InstalledObjectData* data = gScInstalledObject.GetOpaque<InstalledObjectData*>(thisVal);
|
||||
if (data)
|
||||
delete data;
|
||||
}
|
||||
|
||||
std::string generation_get() const
|
||||
static JSValue path_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto installedObject = GetInstalledObject();
|
||||
auto installedObject = GetInstalledObject(thisVal);
|
||||
if (installedObject != nullptr)
|
||||
{
|
||||
return JSFromStdString(ctx, installedObject->Path);
|
||||
}
|
||||
return JSFromStdString(ctx, "");
|
||||
}
|
||||
|
||||
static JSValue generation_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto installedObject = GetInstalledObject(thisVal);
|
||||
if (installedObject != nullptr)
|
||||
{
|
||||
if (installedObject->Generation == ObjectGeneration::DAT)
|
||||
return "dat";
|
||||
return JSFromStdString(ctx, "dat");
|
||||
else
|
||||
return "json";
|
||||
return JSFromStdString(ctx, "json");
|
||||
}
|
||||
return {};
|
||||
return JSFromStdString(ctx, "");
|
||||
}
|
||||
|
||||
std::vector<std::string> sourceGames_get() const
|
||||
static JSValue sourceGames_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
auto installedObject = GetInstalledObject();
|
||||
auto installedObject = GetInstalledObject(thisVal);
|
||||
|
||||
JSValue array = JS_NewArray(ctx);
|
||||
if (installedObject != nullptr)
|
||||
{
|
||||
int64_t index = 0;
|
||||
for (const auto& sourceGame : installedObject->Sources)
|
||||
{
|
||||
result.push_back(std::string(ObjectSourceGameToString(sourceGame)));
|
||||
JSValue sourceGameStr = JSFromStdString(ctx, std::string(ObjectSourceGameToString(sourceGame)));
|
||||
JS_SetPropertyInt64(ctx, array, index++, sourceGameStr);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return array;
|
||||
}
|
||||
|
||||
std::string type_get() const
|
||||
static JSValue type_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto installedObject = GetInstalledObject();
|
||||
auto installedObject = GetInstalledObject(thisVal);
|
||||
if (installedObject != nullptr)
|
||||
{
|
||||
return std::string(objectTypeToString(installedObject->Type));
|
||||
return JSFromStdString(ctx, std::string(objectTypeToString(installedObject->Type)));
|
||||
}
|
||||
return {};
|
||||
return JSFromStdString(ctx, "");
|
||||
}
|
||||
|
||||
std::string identifier_get() const
|
||||
static JSValue identifier_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto installedObject = GetInstalledObject();
|
||||
auto installedObject = GetInstalledObject(thisVal);
|
||||
if (installedObject != nullptr)
|
||||
{
|
||||
if (installedObject->Generation == ObjectGeneration::DAT)
|
||||
{
|
||||
return ObjectEntryDescriptor(installedObject->ObjectEntry).ToString();
|
||||
return JSFromStdString(ctx, ObjectEntryDescriptor(installedObject->ObjectEntry).ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
return installedObject->Identifier;
|
||||
return JSFromStdString(ctx, installedObject->Identifier);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
return JSFromStdString(ctx, "");
|
||||
}
|
||||
|
||||
DukValue legacyIdentifier_get() const
|
||||
static JSValue legacyIdentifier_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto installedObject = GetInstalledObject();
|
||||
auto installedObject = GetInstalledObject(thisVal);
|
||||
if (installedObject != nullptr)
|
||||
{
|
||||
if (!installedObject->ObjectEntry.IsEmpty())
|
||||
{
|
||||
return ToDuk(ctx, installedObject->ObjectEntry.GetName());
|
||||
auto str = installedObject->ObjectEntry.GetName();
|
||||
if (str.find('\0') != std::string::npos)
|
||||
str = {};
|
||||
return JSFromStdString(ctx, str);
|
||||
}
|
||||
}
|
||||
return ToDuk(ctx, nullptr);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
std::vector<std::string> authors_get() const
|
||||
static JSValue authors_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto installedObject = GetInstalledObject();
|
||||
auto installedObject = GetInstalledObject(thisVal);
|
||||
|
||||
JSValue array = JS_NewArray(ctx);
|
||||
if (installedObject != nullptr)
|
||||
{
|
||||
return installedObject->Authors;
|
||||
uint32_t index = 0;
|
||||
for (const auto& author : installedObject->Authors)
|
||||
{
|
||||
JSValue authorStr = JSFromStdString(ctx, author);
|
||||
JS_SetPropertyInt64(ctx, array, index++, authorStr);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
return array;
|
||||
}
|
||||
|
||||
std::string name_get() const
|
||||
static JSValue name_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto installedObject = GetInstalledObject();
|
||||
auto installedObject = GetInstalledObject(thisVal);
|
||||
if (installedObject != nullptr)
|
||||
{
|
||||
return installedObject->Name;
|
||||
return JSFromStdString(ctx, installedObject->Name);
|
||||
}
|
||||
return {};
|
||||
return JSFromStdString(ctx, "");
|
||||
}
|
||||
|
||||
const ObjectRepositoryItem* GetInstalledObject() const
|
||||
static const ObjectRepositoryItem* GetInstalledObject(JSValue thisVal)
|
||||
{
|
||||
size_t index = gScInstalledObject.GetOpaque<InstalledObjectData*>(thisVal)->_index;
|
||||
auto context = GetContext();
|
||||
auto& objectRepository = context->GetObjectRepository();
|
||||
auto numObjects = objectRepository.GetNumObjects();
|
||||
if (_index < numObjects)
|
||||
if (index < numObjects)
|
||||
{
|
||||
auto* objects = objectRepository.GetObjects();
|
||||
return &objects[_index];
|
||||
return &objects[index];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,69 +15,78 @@
|
||||
#include "../../../object/ObjectList.h"
|
||||
#include "../../../ride/RideData.h"
|
||||
#include "../../../windows/Intent.h"
|
||||
#include "../../Duktape.hpp"
|
||||
#include "../../ScriptEngine.h"
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Scripting;
|
||||
|
||||
void ScObjectManager::Register(duk_context* ctx)
|
||||
void ScObjectManager::Register(JSContext* ctx)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScObjectManager::installedObjects_get, nullptr, "installedObjects");
|
||||
dukglue_register_method(ctx, &ScObjectManager::installedObject_get, "getInstalledObject");
|
||||
dukglue_register_method(ctx, &ScObjectManager::load, "load");
|
||||
dukglue_register_method(ctx, &ScObjectManager::unload, "unload");
|
||||
dukglue_register_method(ctx, &ScObjectManager::getObject, "getObject");
|
||||
dukglue_register_method(ctx, &ScObjectManager::getAllObjects, "getAllObjects");
|
||||
RegisterBaseStr(ctx, "ObjectManager");
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<ScInstalledObject>> ScObjectManager::installedObjects_get() const
|
||||
JSValue ScObjectManager::New(JSContext* ctx)
|
||||
{
|
||||
std::vector<std::shared_ptr<ScInstalledObject>> result;
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("installedObjects", ScObjectManager::installedObjects_get, nullptr),
|
||||
JS_CFUNC_DEF("getInstalledObject", 1, ScObjectManager::getInstalledObject),
|
||||
JS_CFUNC_DEF("load", 2, ScObjectManager::load),
|
||||
JS_CFUNC_DEF("unload", 1, ScObjectManager::unload),
|
||||
JS_CFUNC_DEF("getObject", 2, ScObjectManager::getObject),
|
||||
JS_CFUNC_DEF("getAllObjects", 1, ScObjectManager::getAllObjects),
|
||||
};
|
||||
return MakeWithOpaque(ctx, funcs, nullptr);
|
||||
}
|
||||
|
||||
JSValue ScObjectManager::installedObjects_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
JSValue result = JS_NewArray(ctx);
|
||||
|
||||
auto context = GetContext();
|
||||
auto& objectManager = context->GetObjectRepository();
|
||||
auto count = objectManager.GetNumObjects();
|
||||
for (size_t i = 0; i < count; i++)
|
||||
auto count = static_cast<int64_t>(objectManager.GetNumObjects());
|
||||
for (int64_t i = 0; i < count; i++)
|
||||
{
|
||||
auto installedObject = std::make_shared<ScInstalledObject>(i);
|
||||
result.push_back(installedObject);
|
||||
JSValue installedObject = gScInstalledObject.New(ctx, i);
|
||||
JS_SetPropertyInt64(ctx, result, i, installedObject);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<ScInstalledObject> ScObjectManager::installedObject_get(const std::string& identifier) const
|
||||
JSValue ScObjectManager::getInstalledObject(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_STR(identifier, ctx, argv[0]);
|
||||
|
||||
auto context = GetContext();
|
||||
auto& objectRepository = context->GetObjectRepository();
|
||||
auto object = objectRepository.FindObject(identifier);
|
||||
return object != nullptr ? std::make_shared<ScInstalledObject>(object->Id) : nullptr;
|
||||
return object != nullptr ? gScInstalledObject.New(ctx, object->Id) : JS_NULL;
|
||||
}
|
||||
|
||||
DukValue ScObjectManager::load(const DukValue& p1, const DukValue& p2)
|
||||
JSValue ScObjectManager::load(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto context = GetContext();
|
||||
auto& scriptEngine = context->GetScriptEngine();
|
||||
auto& objectRepository = context->GetObjectRepository();
|
||||
auto& objectManager = context->GetObjectManager();
|
||||
auto ctx = scriptEngine.GetContext();
|
||||
|
||||
if (p1.is_array())
|
||||
if (JS_IsArray(argv[0]))
|
||||
{
|
||||
// load(identifiers)
|
||||
JS_UNPACK_ARRAY(array, ctx, argv[0]);
|
||||
std::vector<ObjectEntryDescriptor> descriptors;
|
||||
for (const auto& item : p1.as_array())
|
||||
{
|
||||
if (item.type() != DukValue::STRING)
|
||||
throw DukException() << "Expected string for 'identifier'.";
|
||||
int64_t length;
|
||||
JS_GetLength(ctx, array, &length);
|
||||
|
||||
const auto& identifier = item.as_string();
|
||||
for (int64_t i = 0; i < length; i++)
|
||||
{
|
||||
JSValue item = JS_GetPropertyInt64(ctx, array, i);
|
||||
JS_UNPACK_STR(identifier, ctx, item);
|
||||
descriptors.push_back(ObjectEntryDescriptor::Parse(identifier));
|
||||
}
|
||||
|
||||
duk_push_array(ctx);
|
||||
duk_uarridx_t index = 0;
|
||||
JSValue result = JS_NewArray(ctx);
|
||||
int64_t index = 0;
|
||||
for (const auto& descriptor : descriptors)
|
||||
{
|
||||
auto obj = objectManager.LoadObject(descriptor);
|
||||
@@ -85,38 +94,31 @@ DukValue ScObjectManager::load(const DukValue& p1, const DukValue& p2)
|
||||
{
|
||||
MarkAsResearched(obj);
|
||||
auto objIndex = objectManager.GetLoadedObjectEntryIndex(obj);
|
||||
auto scLoadedObject = CreateScObject(scriptEngine.GetContext(), obj->GetObjectType(), objIndex);
|
||||
scLoadedObject.push();
|
||||
duk_put_prop_index(ctx, -2, index);
|
||||
auto scLoadedObject = CreateScObject(ctx, obj->GetObjectType(), objIndex);
|
||||
JS_SetPropertyInt64(ctx, result, index, scLoadedObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_push_null(ctx);
|
||||
duk_put_prop_index(ctx, -2, index);
|
||||
JS_SetPropertyInt64(ctx, result, index, JS_NULL);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
RefreshResearchedItems();
|
||||
return DukValue::take_from_stack(ctx);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// load(identifier, index?)
|
||||
if (p1.type() != DukValue::STRING)
|
||||
throw DukException() << "Expected string for 'identifier'.";
|
||||
|
||||
const auto& identifier = p1.as_string();
|
||||
JS_UNPACK_STR(identifier, ctx, argv[0]);
|
||||
auto descriptor = ObjectEntryDescriptor::Parse(identifier);
|
||||
|
||||
auto installedObject = objectRepository.FindObject(descriptor);
|
||||
if (installedObject != nullptr)
|
||||
{
|
||||
if (p2.type() != DukValue::UNDEFINED)
|
||||
if (argc > 1 && !JS_IsUndefined(argv[1]))
|
||||
{
|
||||
if (p2.type() != DukValue::NUMBER)
|
||||
throw DukException() << "Expected number for 'index'.";
|
||||
JS_UNPACK_UINT32(index, ctx, argv[1]);
|
||||
|
||||
auto index = static_cast<size_t>(p2.as_uint());
|
||||
auto limit = getObjectTypeLimit(installedObject->Type);
|
||||
if (index < limit)
|
||||
{
|
||||
@@ -131,7 +133,7 @@ DukValue ScObjectManager::load(const DukValue& p1, const DukValue& p2)
|
||||
MarkAsResearched(obj);
|
||||
RefreshResearchedItems();
|
||||
auto objIndex = objectManager.GetLoadedObjectEntryIndex(obj);
|
||||
return CreateScObject(scriptEngine.GetContext(), obj->GetObjectType(), objIndex);
|
||||
return CreateScObject(ctx, obj->GetObjectType(), objIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,30 +145,28 @@ DukValue ScObjectManager::load(const DukValue& p1, const DukValue& p2)
|
||||
MarkAsResearched(obj);
|
||||
RefreshResearchedItems();
|
||||
auto objIndex = objectManager.GetLoadedObjectEntryIndex(obj);
|
||||
return CreateScObject(scriptEngine.GetContext(), obj->GetObjectType(), objIndex);
|
||||
return CreateScObject(ctx, obj->GetObjectType(), objIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ToDuk(ctx, nullptr);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
void ScObjectManager::unload(const DukValue& p1, const DukValue& p2)
|
||||
JSValue ScObjectManager::unload(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
// TODO: shouldn't this throw JS_THROW_IF_GAME_STATE_NOT_MUTABLE()?
|
||||
auto context = GetContext();
|
||||
auto& objectManager = context->GetObjectManager();
|
||||
|
||||
if (p1.type() == DukValue::STRING)
|
||||
if (JS_IsString(argv[0]))
|
||||
{
|
||||
const auto& szP1 = p1.as_string();
|
||||
JS_UNPACK_STR(szP1, ctx, argv[0]);
|
||||
auto objType = objectTypeFromString(szP1);
|
||||
if (objType != ObjectType::none)
|
||||
{
|
||||
// unload(type, index)
|
||||
if (p2.type() != DukValue::NUMBER)
|
||||
throw DukException() << "'index' is invalid.";
|
||||
|
||||
auto objIndex = p2.as_uint();
|
||||
JS_UNPACK_UINT32(objIndex, ctx, argv[1]);
|
||||
auto obj = objectManager.GetLoadedObject(objType, objIndex);
|
||||
if (obj != nullptr)
|
||||
{
|
||||
@@ -179,27 +179,35 @@ void ScObjectManager::unload(const DukValue& p1, const DukValue& p2)
|
||||
objectManager.UnloadObjects({ ObjectEntryDescriptor::Parse(szP1) });
|
||||
}
|
||||
}
|
||||
else if (p1.is_array())
|
||||
else if (JS_IsArray(argv[0]))
|
||||
{
|
||||
// unload(identifiers)
|
||||
auto identifiers = p1.as_array();
|
||||
JS_UNPACK_ARRAY(array, ctx, argv[0]);
|
||||
int64_t length;
|
||||
JS_GetLength(ctx, array, &length);
|
||||
|
||||
std::vector<ObjectEntryDescriptor> descriptors;
|
||||
for (const auto& identifier : identifiers)
|
||||
for (int64_t i = 0; i < length; i++)
|
||||
{
|
||||
if (identifier.type() == DukValue::STRING)
|
||||
JSValue item = JS_GetPropertyInt64(ctx, array, i);
|
||||
if (JS_IsString(item))
|
||||
{
|
||||
descriptors.push_back(ObjectEntryDescriptor::Parse(identifier.as_string()));
|
||||
JS_UNPACK_STR(identifier, ctx, item);
|
||||
descriptors.push_back(ObjectEntryDescriptor::Parse(identifier));
|
||||
}
|
||||
}
|
||||
objectManager.UnloadObjects(descriptors);
|
||||
}
|
||||
auto intent = Intent(INTENT_ACTION_REFRESH_SCENERY);
|
||||
ContextBroadcastIntent(&intent);
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
DukValue ScObjectManager::getObject(const std::string& typez, int32_t index) const
|
||||
JSValue ScObjectManager::getObject(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
JS_UNPACK_STR(typez, ctx, argv[0]);
|
||||
JS_UNPACK_INT32(index, ctx, argv[1]);
|
||||
auto& objManager = GetContext()->GetObjectManager();
|
||||
|
||||
auto type = objectTypeFromString(typez);
|
||||
@@ -213,35 +221,35 @@ DukValue ScObjectManager::getObject(const std::string& typez, int32_t index) con
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Invalid object type.");
|
||||
return JS_ThrowPlainError(ctx, "Invalid object type.");
|
||||
}
|
||||
return ToDuk(ctx, nullptr);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
std::vector<DukValue> ScObjectManager::getAllObjects(const std::string& typez) const
|
||||
JSValue ScObjectManager::getAllObjects(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto& objManager = GetContext()->GetObjectManager();
|
||||
JS_UNPACK_STR(typez, ctx, argv[0]);
|
||||
|
||||
std::vector<DukValue> result;
|
||||
auto& objManager = GetContext()->GetObjectManager();
|
||||
auto type = objectTypeFromString(typez);
|
||||
|
||||
if (type != ObjectType::none)
|
||||
{
|
||||
auto count = getObjectEntryGroupCount(type);
|
||||
for (auto i = 0u; i < count; i++)
|
||||
JSValue result = JS_NewArray(ctx);
|
||||
size_t count = getObjectEntryGroupCount(type);
|
||||
int64_t resultIndex = 0;
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
auto obj = objManager.GetLoadedObject(type, i);
|
||||
if (obj != nullptr)
|
||||
{
|
||||
result.push_back(CreateScObject(ctx, type, i));
|
||||
JSValue scObj = CreateScObject(ctx, type, static_cast<int32_t>(i));
|
||||
JS_SetPropertyInt64(ctx, result, resultIndex++, scObj);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Invalid object type.");
|
||||
}
|
||||
return result;
|
||||
return JS_ThrowPlainError(ctx, "Invalid object type.");
|
||||
}
|
||||
|
||||
void ScObjectManager::MarkAsResearched(const Object* object)
|
||||
@@ -270,26 +278,42 @@ void ScObjectManager::RefreshResearchedItems()
|
||||
gSilentResearch = false;
|
||||
}
|
||||
|
||||
DukValue ScObjectManager::CreateScObject(duk_context* ctx, ObjectType type, int32_t index)
|
||||
JSValue ScObjectManager::CreateScObject(JSContext* ctx, ObjectType type, int32_t index)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ObjectType::ride:
|
||||
return GetObjectAsDukValue(ctx, std::make_shared<ScRideObject>(type, index));
|
||||
{
|
||||
return ScRideObject::New(ctx, type, index);
|
||||
}
|
||||
case ObjectType::smallScenery:
|
||||
return GetObjectAsDukValue(ctx, std::make_shared<ScSmallSceneryObject>(type, index));
|
||||
{
|
||||
return ScSmallSceneryObject::New(ctx, type, index);
|
||||
}
|
||||
case ObjectType::largeScenery:
|
||||
return GetObjectAsDukValue(ctx, std::make_shared<ScLargeSceneryObject>(type, index));
|
||||
{
|
||||
return ScLargeSceneryObject::New(ctx, type, index);
|
||||
}
|
||||
case ObjectType::walls:
|
||||
return GetObjectAsDukValue(ctx, std::make_shared<ScWallObject>(type, index));
|
||||
{
|
||||
return ScWallObject::New(ctx, type, index);
|
||||
}
|
||||
case ObjectType::pathAdditions:
|
||||
return GetObjectAsDukValue(ctx, std::make_shared<ScFootpathAdditionObject>(type, index));
|
||||
{
|
||||
return ScFootpathAdditionObject::New(ctx, type, index);
|
||||
}
|
||||
case ObjectType::banners:
|
||||
return GetObjectAsDukValue(ctx, std::make_shared<ScBannerObject>(type, index));
|
||||
{
|
||||
return ScBannerObject::New(ctx, type, index);
|
||||
}
|
||||
case ObjectType::sceneryGroup:
|
||||
return GetObjectAsDukValue(ctx, std::make_shared<ScSceneryGroupObject>(type, index));
|
||||
{
|
||||
return ScSceneryGroupObject::New(ctx, type, index);
|
||||
}
|
||||
default:
|
||||
return GetObjectAsDukValue(ctx, std::make_shared<ScObject>(type, index));
|
||||
{
|
||||
return ScObject::New(ctx, type, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "../../Duktape.hpp"
|
||||
#include "../../ScriptEngine.h"
|
||||
#include "ScInstalledObject.hpp"
|
||||
#include "ScObject.hpp"
|
||||
@@ -20,24 +19,28 @@
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScObjectManager
|
||||
class ScObjectManager;
|
||||
extern ScObjectManager gScObjectManager;
|
||||
|
||||
class ScObjectManager final : public ScBase
|
||||
{
|
||||
public:
|
||||
static void Register(duk_context* ctx);
|
||||
void Register(JSContext* ctx);
|
||||
JSValue New(JSContext* ctx);
|
||||
|
||||
std::vector<std::shared_ptr<ScInstalledObject>> installedObjects_get() const;
|
||||
std::shared_ptr<ScInstalledObject> installedObject_get(const std::string& identifier) const;
|
||||
|
||||
DukValue load(const DukValue& p1, const DukValue& p2);
|
||||
void unload(const DukValue& p1, const DukValue& p2);
|
||||
|
||||
DukValue getObject(const std::string& typez, int32_t index) const;
|
||||
std::vector<DukValue> getAllObjects(const std::string& typez) const;
|
||||
static JSValue getObject(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
static JSValue getAllObjects(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
|
||||
private:
|
||||
static JSValue installedObjects_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue getInstalledObject(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
|
||||
static JSValue load(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
static JSValue unload(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
|
||||
static void MarkAsResearched(const Object* object);
|
||||
static void RefreshResearchedItems();
|
||||
static DukValue CreateScObject(duk_context* ctx, ObjectType type, int32_t index);
|
||||
static JSValue CreateScObject(JSContext* ctx, ObjectType type, int32_t index);
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,197 +9,144 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
#include "../../../Context.h"
|
||||
#include "../../../ride/Ride.h"
|
||||
#include "../../ScriptEngine.h"
|
||||
#include "../object/ScObject.hpp"
|
||||
#include "ScRideStation.hpp"
|
||||
|
||||
#include "../../../Context.h"
|
||||
#include "../../../ride/Ride.h"
|
||||
#include "../../Duktape.hpp"
|
||||
#include "../../ScriptEngine.h"
|
||||
#include "../object/ScObject.hpp"
|
||||
#include "ScRideStation.hpp"
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const TrackColour& value)
|
||||
|
||||
inline JSValue ToJSValue(JSContext* ctx, const VehicleColour& value)
|
||||
{
|
||||
DukObject obj(ctx);
|
||||
obj.Set("main", EnumValue(value.main));
|
||||
obj.Set("additional", EnumValue(value.additional));
|
||||
obj.Set("supports", EnumValue(value.supports));
|
||||
return obj.Take();
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "body", JS_NewInt32(ctx, EnumValue(value.Body)));
|
||||
JS_SetPropertyStr(ctx, obj, "trim", JS_NewInt32(ctx, EnumValue(value.Trim)));
|
||||
JS_SetPropertyStr(ctx, obj, "ternary", JS_NewInt32(ctx, EnumValue(value.Tertiary)));
|
||||
JS_SetPropertyStr(ctx, obj, "tertiary", JS_NewInt32(ctx, EnumValue(value.Tertiary)));
|
||||
return obj;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline TrackColour FromDuk(const DukValue& s)
|
||||
{
|
||||
TrackColour result{};
|
||||
result.main = static_cast<Drawing::Colour>(AsOrDefault(s["main"], 0));
|
||||
result.additional = static_cast<Drawing::Colour>(AsOrDefault(s["additional"], 0));
|
||||
result.supports = static_cast<Drawing::Colour>(AsOrDefault(s["supports"], 0));
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const VehicleColour& value)
|
||||
{
|
||||
DukObject obj(ctx);
|
||||
obj.Set("body", EnumValue(value.Body));
|
||||
obj.Set("trim", EnumValue(value.Trim));
|
||||
obj.Set("ternary", EnumValue(value.Tertiary));
|
||||
obj.Set("tertiary", EnumValue(value.Tertiary));
|
||||
return obj.Take();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline VehicleColour FromDuk(const DukValue& s)
|
||||
inline VehicleColour JSToVehicleColours(JSContext* ctx, JSValue val)
|
||||
{
|
||||
VehicleColour result{};
|
||||
result.Body = static_cast<Drawing::Colour>(AsOrDefault(s["body"], 0));
|
||||
result.Trim = static_cast<Drawing::Colour>(AsOrDefault(s["trim"], 0));
|
||||
result.Tertiary = static_cast<Drawing::Colour>(AsOrDefault(s["ternary"], 0));
|
||||
result.Tertiary = static_cast<Drawing::Colour>(AsOrDefault<int32_t>(s["tertiary"], EnumValue(result.Tertiary)));
|
||||
result.Body = static_cast<Drawing::Colour>(AsOrDefault(ctx, val, "body", 0));
|
||||
result.Trim = static_cast<Drawing::Colour>(AsOrDefault(ctx, val, "trim", 0));
|
||||
result.Tertiary = static_cast<Drawing::Colour>(AsOrDefault(ctx, val, "ternary", 0));
|
||||
result.Tertiary = static_cast<Drawing::Colour>(
|
||||
AsOrDefault(ctx, val, "tertiary", static_cast<uint32_t>(result.Tertiary)));
|
||||
return result;
|
||||
}
|
||||
|
||||
class ScRide
|
||||
inline JSValue ToJSValue(JSContext* ctx, const TrackColour& value)
|
||||
{
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "main", JS_NewInt32(ctx, EnumValue(value.main)));
|
||||
JS_SetPropertyStr(ctx, obj, "additional", JS_NewInt32(ctx, EnumValue(value.additional)));
|
||||
JS_SetPropertyStr(ctx, obj, "supports", JS_NewInt32(ctx, EnumValue(value.supports)));
|
||||
return obj;
|
||||
}
|
||||
|
||||
inline TrackColour JSToTrackColour(JSContext* ctx, JSValue val)
|
||||
{
|
||||
TrackColour result{};
|
||||
result.main = static_cast<Drawing::Colour>(AsOrDefault(ctx, val, "main", 0));
|
||||
result.additional = static_cast<Drawing::Colour>(AsOrDefault(ctx, val, "additional", 0));
|
||||
result.supports = static_cast<Drawing::Colour>(AsOrDefault(ctx, val, "supports", 0));
|
||||
return result;
|
||||
}
|
||||
|
||||
class ScRide;
|
||||
extern ScRide gScRide;
|
||||
|
||||
class ScRide final : public ScBase
|
||||
{
|
||||
private:
|
||||
RideId _rideId = RideId::GetNull();
|
||||
struct RideData
|
||||
{
|
||||
RideId _rideId = RideId::GetNull();
|
||||
};
|
||||
|
||||
public:
|
||||
ScRide(RideId rideId);
|
||||
void Register(JSContext* ctx);
|
||||
JSValue New(JSContext* ctx, RideId rideId);
|
||||
|
||||
private:
|
||||
int32_t id_get() const;
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal);
|
||||
static RideData* GetRideData(JSValue thisVal);
|
||||
static Ride* GetRide(JSValue thisVal);
|
||||
|
||||
std::shared_ptr<ScRideObject> object_get();
|
||||
|
||||
int32_t type_get() const;
|
||||
|
||||
std::string classification_get() const;
|
||||
|
||||
std::string name_get() const;
|
||||
void name_set(std::string value);
|
||||
|
||||
std::string status_get() const;
|
||||
|
||||
uint32_t flags_get() const;
|
||||
|
||||
void flags_set(uint32_t value);
|
||||
|
||||
uint8_t mode_get() const;
|
||||
|
||||
void mode_set(uint8_t value);
|
||||
|
||||
uint8_t departFlags_get() const;
|
||||
|
||||
void departFlags_set(uint8_t value);
|
||||
|
||||
uint8_t minimumWaitingTime_get() const;
|
||||
|
||||
void minimumWaitingTime_set(uint8_t value);
|
||||
|
||||
uint8_t maximumWaitingTime_get() const;
|
||||
|
||||
void maximumWaitingTime_set(uint8_t value);
|
||||
|
||||
std::vector<uint16_t> vehicles_get() const;
|
||||
|
||||
std::vector<DukValue> vehicleColours_get() const;
|
||||
|
||||
void vehicleColours_set(const std::vector<DukValue>& value);
|
||||
|
||||
std::vector<DukValue> colourSchemes_get() const;
|
||||
|
||||
void colourSchemes_set(const std::vector<DukValue>& value);
|
||||
|
||||
ObjectEntryIndex stationStyle_get() const;
|
||||
|
||||
void stationStyle_set(ObjectEntryIndex value);
|
||||
|
||||
ObjectEntryIndex music_get() const;
|
||||
|
||||
void music_set(ObjectEntryIndex value);
|
||||
|
||||
std::vector<std::shared_ptr<ScRideStation>> stations_get() const;
|
||||
|
||||
std::vector<int32_t> price_get() const;
|
||||
|
||||
void price_set(const std::vector<int32_t>& value);
|
||||
|
||||
int32_t excitement_get() const;
|
||||
void excitement_set(int32_t value);
|
||||
|
||||
int32_t intensity_get() const;
|
||||
void intensity_set(int32_t value);
|
||||
|
||||
int32_t nausea_get() const;
|
||||
void nausea_set(int32_t value);
|
||||
|
||||
int32_t totalCustomers_get() const;
|
||||
void totalCustomers_set(int32_t value);
|
||||
|
||||
int32_t buildDate_get() const;
|
||||
void buildDate_set(int32_t value);
|
||||
|
||||
int32_t age_get() const;
|
||||
|
||||
money64 runningCost_get() const;
|
||||
void runningCost_set(money64 value);
|
||||
|
||||
int32_t totalProfit_get() const;
|
||||
void totalProfit_set(int32_t value);
|
||||
|
||||
uint8_t inspectionInterval_get() const;
|
||||
void inspectionInterval_set(uint8_t value);
|
||||
|
||||
DukValue value_get() const;
|
||||
|
||||
void value_set(const DukValue& value);
|
||||
|
||||
uint8_t downtime_get() const;
|
||||
|
||||
uint8_t liftHillSpeed_get() const;
|
||||
void lifthillSpeed_set(uint8_t value);
|
||||
|
||||
uint8_t maxLiftHillSpeed_get() const;
|
||||
uint8_t minLiftHillSpeed_get() const;
|
||||
|
||||
uint8_t satisfaction_get() const;
|
||||
|
||||
double maxSpeed_get() const;
|
||||
|
||||
double averageSpeed_get() const;
|
||||
|
||||
int32_t rideTime_get() const;
|
||||
|
||||
double rideLength_get() const;
|
||||
|
||||
double maxPositiveVerticalGs_get() const;
|
||||
|
||||
double maxNegativeVerticalGs_get() const;
|
||||
|
||||
double maxLateralGs_get() const;
|
||||
|
||||
double totalAirTime_get() const;
|
||||
|
||||
uint8_t numDrops_get() const;
|
||||
|
||||
uint8_t numLiftHills_get() const;
|
||||
|
||||
double highestDropHeight_get() const;
|
||||
|
||||
Ride* GetRide() const;
|
||||
|
||||
void SetBreakdown(const std::string& breakDown);
|
||||
|
||||
void FixBreakdown();
|
||||
|
||||
std::string getBreakdown() const;
|
||||
|
||||
public:
|
||||
static void Register(duk_context* ctx);
|
||||
static JSValue id_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue object_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue type_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue classification_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue name_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue name_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue status_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue flags_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue flags_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue mode_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue mode_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue departFlags_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue departFlags_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue minimumWaitingTime_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue minimumWaitingTime_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue maximumWaitingTime_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue maximumWaitingTime_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue vehicles_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue vehicleColours_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue vehicleColours_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue colourSchemes_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue colourSchemes_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue stationStyle_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue stationStyle_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue music_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue music_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue stations_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue price_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue price_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue excitement_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue excitement_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue intensity_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue intensity_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue nausea_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue nausea_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue totalCustomers_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue totalCustomers_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue buildDate_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue buildDate_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue age_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue runningCost_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue runningCost_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue totalProfit_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue totalProfit_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue inspectionInterval_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue inspectionInterval_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue value_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue value_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue downtime_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue liftHillSpeed_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue liftHillSpeed_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue maxLiftHillSpeed_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue minLiftHillSpeed_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue satisfaction_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue maxSpeed_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue averageSpeed_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue rideTime_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue rideLength_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue maxPositiveVerticalGs_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue maxNegativeVerticalGs_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue maxLateralGs_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue totalAirTime_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue numDrops_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue numLiftHills_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue highestDropHeight_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue breakdown_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue setBreakdown(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
static JSValue fixBreakdown(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
|
||||
@@ -13,121 +13,140 @@
|
||||
|
||||
#include "../../../Context.h"
|
||||
#include "../../../ride/Ride.h"
|
||||
#include "../../Duktape.hpp"
|
||||
#include "../../ScriptEngine.h"
|
||||
#include "../object/ScObject.hpp"
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
ScRideStation::ScRideStation(RideId rideId, StationIndex stationIndex)
|
||||
: _rideId(rideId)
|
||||
, _stationIndex(stationIndex)
|
||||
void ScRideStation::Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "RideStation", Finalize);
|
||||
}
|
||||
|
||||
void ScRideStation::Register(duk_context* ctx)
|
||||
JSValue ScRideStation::New(JSContext* ctx, RideId rideId, StationIndex stationIndex)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScRideStation::start_get, &ScRideStation::start_set, "start");
|
||||
dukglue_register_property(ctx, &ScRideStation::length_get, &ScRideStation::length_set, "length");
|
||||
dukglue_register_property(ctx, &ScRideStation::entrance_get, &ScRideStation::entrance_set, "entrance");
|
||||
dukglue_register_property(ctx, &ScRideStation::exit_get, &ScRideStation::exit_set, "exit");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("start", ScRideStation::start_get, ScRideStation::start_set),
|
||||
JS_CGETSET_DEF("length", ScRideStation::length_get, ScRideStation::length_set),
|
||||
JS_CGETSET_DEF("entrance", ScRideStation::entrance_get, ScRideStation::entrance_set),
|
||||
JS_CGETSET_DEF("exit", ScRideStation::exit_get, ScRideStation::exit_set),
|
||||
};
|
||||
return MakeWithOpaque(ctx, funcs, new RideStationData{ rideId, stationIndex });
|
||||
}
|
||||
|
||||
DukValue ScRideStation::start_get() const
|
||||
void ScRideStation::Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto station = GetRideStation();
|
||||
if (station != nullptr)
|
||||
{
|
||||
auto start = CoordsXYZ(station->Start, station->GetBaseZ());
|
||||
return ToDuk(ctx, start);
|
||||
}
|
||||
return ToDuk(ctx, nullptr);
|
||||
RideStationData* data = GetRideStationData(thisVal);
|
||||
if (data)
|
||||
delete data;
|
||||
}
|
||||
|
||||
void ScRideStation::start_set(const DukValue& value)
|
||||
ScRideStation::RideStationData* ScRideStation::GetRideStationData(JSValue thisVal)
|
||||
{
|
||||
auto station = GetRideStation();
|
||||
if (station != nullptr)
|
||||
{
|
||||
auto start = FromDuk<CoordsXYZ>(value);
|
||||
station->Start = { start.x, start.y };
|
||||
station->SetBaseZ(start.z);
|
||||
}
|
||||
return gScRideStation.GetOpaque<RideStationData*>(thisVal);
|
||||
}
|
||||
|
||||
int32_t ScRideStation::length_get() const
|
||||
RideStation* ScRideStation::GetRideStation(JSValue thisVal)
|
||||
{
|
||||
auto station = GetRideStation();
|
||||
if (station != nullptr)
|
||||
{
|
||||
return station->Length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ScRideStation::length_set(int32_t value)
|
||||
{
|
||||
auto station = GetRideStation();
|
||||
if (station != nullptr)
|
||||
{
|
||||
station->Length = value;
|
||||
}
|
||||
}
|
||||
|
||||
DukValue ScRideStation::entrance_get() const
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto station = GetRideStation();
|
||||
if (station != nullptr)
|
||||
{
|
||||
return ToDuk(ctx, station->Entrance.ToCoordsXYZD());
|
||||
}
|
||||
return ToDuk(ctx, nullptr);
|
||||
}
|
||||
|
||||
void ScRideStation::entrance_set(const DukValue& value)
|
||||
{
|
||||
auto station = GetRideStation();
|
||||
if (station != nullptr)
|
||||
{
|
||||
station->Entrance = FromDuk<CoordsXYZD>(value);
|
||||
}
|
||||
}
|
||||
|
||||
DukValue ScRideStation::exit_get() const
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto station = GetRideStation();
|
||||
if (station != nullptr)
|
||||
{
|
||||
return ToDuk(ctx, station->Exit.ToCoordsXYZD());
|
||||
}
|
||||
return ToDuk(ctx, nullptr);
|
||||
}
|
||||
|
||||
void ScRideStation::exit_set(const DukValue& value)
|
||||
{
|
||||
auto station = GetRideStation();
|
||||
if (station != nullptr)
|
||||
{
|
||||
station->Exit = FromDuk<CoordsXYZD>(value);
|
||||
}
|
||||
}
|
||||
|
||||
RideStation* ScRideStation::GetRideStation() const
|
||||
{
|
||||
auto ride = GetRide(_rideId);
|
||||
RideStationData* data = GetRideStationData(thisVal);
|
||||
auto ride = ::GetRide(data->_rideId);
|
||||
if (ride != nullptr)
|
||||
{
|
||||
if (_stationIndex.ToUnderlying() < std::size(ride->getStations()))
|
||||
if (data->_stationIndex.ToUnderlying() < std::size(ride->getStations()))
|
||||
{
|
||||
return &ride->getStation(_stationIndex);
|
||||
return &ride->getStation(data->_stationIndex);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSValue ScRideStation::start_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto station = GetRideStation(thisVal);
|
||||
if (station != nullptr)
|
||||
{
|
||||
auto start = CoordsXYZ(station->Start, station->GetBaseZ());
|
||||
return ToJSValue(ctx, start);
|
||||
}
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
JSValue ScRideStation::start_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
|
||||
auto station = GetRideStation(thisVal);
|
||||
if (station != nullptr)
|
||||
{
|
||||
auto start = JSToCoordsXYZ(ctx, value);
|
||||
station->Start = { start.x, start.y };
|
||||
station->SetBaseZ(start.z);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue ScRideStation::length_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto station = GetRideStation(thisVal);
|
||||
return JS_NewInt32(ctx, station != nullptr ? station->Length : 0);
|
||||
}
|
||||
|
||||
JSValue ScRideStation::length_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
JS_UNPACK_INT32(valueInt, ctx, value);
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
|
||||
auto station = GetRideStation(thisVal);
|
||||
if (station != nullptr)
|
||||
{
|
||||
station->Length = valueInt;
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue ScRideStation::entrance_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto station = GetRideStation(thisVal);
|
||||
if (station != nullptr)
|
||||
{
|
||||
return ToJSValue(ctx, station->Entrance.ToCoordsXYZD());
|
||||
}
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
JSValue ScRideStation::entrance_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
|
||||
auto station = GetRideStation(thisVal);
|
||||
if (station != nullptr)
|
||||
{
|
||||
station->Entrance = JSToCoordsXYZD(ctx, value);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue ScRideStation::exit_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto station = GetRideStation(thisVal);
|
||||
if (station != nullptr)
|
||||
{
|
||||
return ToJSValue(ctx, station->Exit.ToCoordsXYZD());
|
||||
}
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
JSValue ScRideStation::exit_set(JSContext* ctx, JSValue thisVal, JSValue value)
|
||||
{
|
||||
JS_THROW_IF_GAME_STATE_NOT_MUTABLE();
|
||||
|
||||
auto station = GetRideStation(thisVal);
|
||||
if (station != nullptr)
|
||||
{
|
||||
station->Exit = JSToCoordsXYZD(ctx, value);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
#endif
|
||||
|
||||
@@ -13,39 +13,39 @@
|
||||
|
||||
#include "../../../Context.h"
|
||||
#include "../../../ride/Ride.h"
|
||||
#include "../../Duktape.hpp"
|
||||
#include "../../ScriptEngine.h"
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScRideStation
|
||||
class ScRideStation;
|
||||
extern ScRideStation gScRideStation;
|
||||
|
||||
class ScRideStation final : public ScBase
|
||||
{
|
||||
private:
|
||||
RideId _rideId = RideId::GetNull();
|
||||
StationIndex _stationIndex{};
|
||||
struct RideStationData
|
||||
{
|
||||
RideId _rideId = RideId::GetNull();
|
||||
StationIndex _stationIndex{};
|
||||
};
|
||||
|
||||
public:
|
||||
ScRideStation(RideId rideId, StationIndex stationIndex);
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
void Register(JSContext* ctx);
|
||||
JSValue New(JSContext* ctx, RideId rideId, StationIndex stationIndex);
|
||||
|
||||
private:
|
||||
DukValue start_get() const;
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal);
|
||||
static RideStationData* GetRideStationData(JSValue thisVal);
|
||||
static RideStation* GetRideStation(JSValue thisVal);
|
||||
|
||||
void start_set(const DukValue& value);
|
||||
|
||||
int32_t length_get() const;
|
||||
|
||||
void length_set(int32_t value);
|
||||
|
||||
DukValue entrance_get() const;
|
||||
|
||||
void entrance_set(const DukValue& value);
|
||||
|
||||
DukValue exit_get() const;
|
||||
|
||||
void exit_set(const DukValue& value);
|
||||
|
||||
RideStation* GetRideStation() const;
|
||||
static JSValue start_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue start_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue length_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue length_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue entrance_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue entrance_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
static JSValue exit_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue exit_set(JSContext* ctx, JSValue thisVal, JSValue value);
|
||||
};
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -25,105 +25,116 @@
|
||||
using namespace OpenRCT2::Scripting;
|
||||
using namespace OpenRCT2::TrackMetadata;
|
||||
|
||||
std::shared_ptr<ScTrackIterator> ScTrackIterator::FromElement(const CoordsXY& position, int32_t elementIndex)
|
||||
JSValue ScTrackIterator::FromElement(JSContext* ctx, const CoordsXY& position, int32_t elementIndex)
|
||||
{
|
||||
auto el = MapGetNthElementAt(position, elementIndex);
|
||||
if (el == nullptr)
|
||||
return nullptr;
|
||||
return JS_NULL;
|
||||
auto origin = GetTrackSegmentOrigin(CoordsXYE(position, el));
|
||||
if (!origin)
|
||||
return nullptr;
|
||||
return JS_NULL;
|
||||
|
||||
auto trackEl = el->AsTrack();
|
||||
return std::make_shared<ScTrackIterator>(*origin, trackEl->GetTrackType(), trackEl->GetRideIndex());
|
||||
return gScTrackIterator.New(ctx, *origin, trackEl->GetTrackType());
|
||||
}
|
||||
|
||||
ScTrackIterator::ScTrackIterator(const CoordsXYZD& position, OpenRCT2::TrackElemType type, RideId ride)
|
||||
: _position(position)
|
||||
, _type(type)
|
||||
, _ride(ride)
|
||||
void ScTrackIterator::Register(JSContext* ctx)
|
||||
{
|
||||
RegisterBaseStr(ctx, "TrackIterator", Finalize);
|
||||
}
|
||||
|
||||
void ScTrackIterator::Register(duk_context* ctx)
|
||||
JSValue ScTrackIterator::New(JSContext* ctx, const CoordsXYZD& position, OpenRCT2::TrackElemType type)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScTrackIterator::position_get, nullptr, "position");
|
||||
dukglue_register_property(ctx, &ScTrackIterator::segment_get, nullptr, "segment");
|
||||
dukglue_register_property(ctx, &ScTrackIterator::previousPosition_get, nullptr, "previousPosition");
|
||||
dukglue_register_property(ctx, &ScTrackIterator::nextPosition_get, nullptr, "nextPosition");
|
||||
dukglue_register_method(ctx, &ScTrackIterator::previous, "previous");
|
||||
dukglue_register_method(ctx, &ScTrackIterator::next, "next");
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("position", ScTrackIterator::position_get, nullptr),
|
||||
JS_CGETSET_DEF("segment", ScTrackIterator::segment_get, nullptr),
|
||||
JS_CGETSET_DEF("previousPosition", ScTrackIterator::previousPosition_get, nullptr),
|
||||
JS_CGETSET_DEF("nextPosition", ScTrackIterator::nextPosition_get, nullptr),
|
||||
JS_CFUNC_DEF("previous", 0, ScTrackIterator::previous),
|
||||
JS_CFUNC_DEF("next", 0, ScTrackIterator::next),
|
||||
};
|
||||
return MakeWithOpaque(ctx, funcs, new TrackIteratorData{ position, type });
|
||||
}
|
||||
|
||||
DukValue ScTrackIterator::position_get() const
|
||||
void ScTrackIterator::Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto ctx = scriptEngine.GetContext();
|
||||
return ToDuk(ctx, _position);
|
||||
TrackIteratorData* data = GetTrackIteratorData(thisVal);
|
||||
if (data)
|
||||
delete data;
|
||||
}
|
||||
|
||||
DukValue ScTrackIterator::segment_get() const
|
||||
ScTrackIterator::TrackIteratorData* ScTrackIterator::GetTrackIteratorData(JSValue thisVal)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto ctx = scriptEngine.GetContext();
|
||||
|
||||
if (_type >= TrackElemType::count)
|
||||
return ToDuk(ctx, nullptr);
|
||||
|
||||
return GetObjectAsDukValue(ctx, std::make_shared<ScTrackSegment>(_type));
|
||||
return gScTrackIterator.GetOpaque<TrackIteratorData*>(thisVal);
|
||||
}
|
||||
|
||||
DukValue ScTrackIterator::previousPosition_get() const
|
||||
JSValue ScTrackIterator::position_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto ctx = scriptEngine.GetContext();
|
||||
auto* data = GetTrackIteratorData(thisVal);
|
||||
return ToJSValue(ctx, data->_position);
|
||||
}
|
||||
|
||||
auto& ted = GetTrackElementDescriptor(_type);
|
||||
JSValue ScTrackIterator::segment_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto* data = GetTrackIteratorData(thisVal);
|
||||
|
||||
if (data->_type >= TrackElemType::count)
|
||||
return JS_NULL;
|
||||
|
||||
return gScTrackSegment.New(ctx, data->_type);
|
||||
}
|
||||
|
||||
JSValue ScTrackIterator::previousPosition_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto* data = GetTrackIteratorData(thisVal);
|
||||
|
||||
auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
const auto& seq0 = ted.sequenceData.sequences[0].clearance;
|
||||
auto pos = _position + CoordsXYZ(seq0.x, seq0.y, seq0.z);
|
||||
auto pos = data->_position + CoordsXYZ(seq0.x, seq0.y, seq0.z);
|
||||
|
||||
auto el = MapGetTrackElementAtOfTypeSeq(pos, _type, 0);
|
||||
auto el = MapGetTrackElementAtOfTypeSeq(pos, data->_type, 0);
|
||||
if (el == nullptr)
|
||||
return ToDuk(ctx, nullptr);
|
||||
return JS_NULL;
|
||||
|
||||
auto posEl = CoordsXYE(pos.x, pos.y, reinterpret_cast<TileElement*>(el));
|
||||
TrackBeginEnd tbe{};
|
||||
trackBlockGetPrevious(posEl, &tbe);
|
||||
CoordsXYZD result(tbe.end_x, tbe.end_y, tbe.begin_z, tbe.begin_direction);
|
||||
return ToDuk(ctx, result);
|
||||
return ToJSValue(ctx, result);
|
||||
}
|
||||
|
||||
DukValue ScTrackIterator::nextPosition_get() const
|
||||
JSValue ScTrackIterator::nextPosition_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto ctx = scriptEngine.GetContext();
|
||||
auto* data = GetTrackIteratorData(thisVal);
|
||||
|
||||
auto& ted = GetTrackElementDescriptor(_type);
|
||||
auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
const auto& seq0 = ted.sequenceData.sequences[0].clearance;
|
||||
auto pos = _position + CoordsXYZ(seq0.x, seq0.y, seq0.z);
|
||||
auto pos = data->_position + CoordsXYZ(seq0.x, seq0.y, seq0.z);
|
||||
|
||||
auto el = MapGetTrackElementAtOfTypeSeq(pos, _type, 0);
|
||||
auto el = MapGetTrackElementAtOfTypeSeq(pos, data->_type, 0);
|
||||
if (el == nullptr)
|
||||
return ToDuk(ctx, nullptr);
|
||||
return JS_NULL;
|
||||
|
||||
auto posEl = CoordsXYE(_position.x, _position.y, reinterpret_cast<TileElement*>(el));
|
||||
auto posEl = CoordsXYE(data->_position.x, data->_position.y, reinterpret_cast<TileElement*>(el));
|
||||
CoordsXYE next;
|
||||
int32_t z{};
|
||||
int32_t direction{};
|
||||
trackBlockGetNext(&posEl, &next, &z, &direction);
|
||||
CoordsXYZD result(next.x, next.y, z, direction);
|
||||
return ToDuk(ctx, result);
|
||||
return ToJSValue(ctx, result);
|
||||
}
|
||||
|
||||
bool ScTrackIterator::previous()
|
||||
JSValue ScTrackIterator::previous(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto& ted = GetTrackElementDescriptor(_type);
|
||||
const auto& seq0 = ted.sequenceData.sequences[0].clearance;
|
||||
auto pos = _position + CoordsXYZ(seq0.x, seq0.y, seq0.z);
|
||||
auto* data = GetTrackIteratorData(thisVal);
|
||||
|
||||
auto el = MapGetTrackElementAtOfTypeSeq(pos, _type, 0);
|
||||
auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
const auto& seq0 = ted.sequenceData.sequences[0].clearance;
|
||||
auto pos = data->_position + CoordsXYZ(seq0.x, seq0.y, seq0.z);
|
||||
|
||||
auto el = MapGetTrackElementAtOfTypeSeq(pos, data->_type, 0);
|
||||
if (el == nullptr)
|
||||
return false;
|
||||
return JS_NewBool(ctx, false);
|
||||
|
||||
auto posEl = CoordsXYE(pos.x, pos.y, reinterpret_cast<TileElement*>(el));
|
||||
TrackBeginEnd tbe{};
|
||||
@@ -133,25 +144,27 @@ bool ScTrackIterator::previous()
|
||||
auto origin = GetTrackSegmentOrigin(prev);
|
||||
if (origin)
|
||||
{
|
||||
_position = *origin;
|
||||
_type = prev.element->AsTrack()->GetTrackType();
|
||||
return true;
|
||||
data->_position = *origin;
|
||||
data->_type = prev.element->AsTrack()->GetTrackType();
|
||||
return JS_NewBool(ctx, true);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return JS_NewBool(ctx, false);
|
||||
}
|
||||
|
||||
bool ScTrackIterator::next()
|
||||
JSValue ScTrackIterator::next(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
auto& ted = GetTrackElementDescriptor(_type);
|
||||
auto* data = GetTrackIteratorData(thisVal);
|
||||
|
||||
auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
const auto& seq0 = ted.sequenceData.sequences[0].clearance;
|
||||
auto pos = _position + CoordsXYZ(seq0.x, seq0.y, seq0.z);
|
||||
auto pos = data->_position + CoordsXYZ(seq0.x, seq0.y, seq0.z);
|
||||
|
||||
auto el = MapGetTrackElementAtOfTypeSeq(pos, _type, 0);
|
||||
auto el = MapGetTrackElementAtOfTypeSeq(pos, data->_type, 0);
|
||||
if (el == nullptr)
|
||||
return false;
|
||||
return JS_NewBool(ctx, false);
|
||||
|
||||
auto posEl = CoordsXYE(_position.x, _position.y, reinterpret_cast<TileElement*>(el));
|
||||
auto posEl = CoordsXYE(data->_position.x, data->_position.y, reinterpret_cast<TileElement*>(el));
|
||||
CoordsXYE next;
|
||||
int32_t z{};
|
||||
int32_t direction{};
|
||||
@@ -160,12 +173,12 @@ bool ScTrackIterator::next()
|
||||
auto origin = GetTrackSegmentOrigin(next);
|
||||
if (origin)
|
||||
{
|
||||
_position = *origin;
|
||||
_type = next.element->AsTrack()->GetTrackType();
|
||||
return true;
|
||||
data->_position = *origin;
|
||||
data->_type = next.element->AsTrack()->GetTrackType();
|
||||
return JS_NewBool(ctx, true);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return JS_NewBool(ctx, false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,34 +12,40 @@
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "../../../Identifiers.h"
|
||||
#include "../../Duktape.hpp"
|
||||
#include "../../ScriptEngine.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
class ScTrackIterator
|
||||
class ScTrackIterator;
|
||||
extern ScTrackIterator gScTrackIterator;
|
||||
|
||||
class ScTrackIterator final : public ScBase
|
||||
{
|
||||
private:
|
||||
CoordsXYZD _position;
|
||||
TrackElemType _type;
|
||||
[[maybe_unused]] RideId _ride;
|
||||
struct TrackIteratorData
|
||||
{
|
||||
CoordsXYZD _position;
|
||||
TrackElemType _type;
|
||||
};
|
||||
|
||||
public:
|
||||
static std::shared_ptr<ScTrackIterator> FromElement(const CoordsXY& position, int32_t elementIndex);
|
||||
static void Register(duk_context* ctx);
|
||||
|
||||
ScTrackIterator(const CoordsXYZD& position, TrackElemType type, RideId ride);
|
||||
static JSValue FromElement(JSContext* ctx, const CoordsXY& position, int32_t elementIndex);
|
||||
void Register(JSContext* ctx);
|
||||
JSValue New(JSContext* ctx, const CoordsXYZD& position, TrackElemType type);
|
||||
|
||||
private:
|
||||
DukValue position_get() const;
|
||||
DukValue segment_get() const;
|
||||
DukValue previousPosition_get() const;
|
||||
DukValue nextPosition_get() const;
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal);
|
||||
static TrackIteratorData* GetTrackIteratorData(JSValue thisVal);
|
||||
|
||||
bool previous();
|
||||
bool next();
|
||||
static JSValue position_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue segment_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue previousPosition_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue nextPosition_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
static JSValue previous(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
static JSValue next(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
};
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -12,191 +12,231 @@
|
||||
#include "ScTrackSegment.h"
|
||||
|
||||
#include "../../../Context.h"
|
||||
#include "../../../core/EnumMap.hpp"
|
||||
#include "../../../ride/TrackData.h"
|
||||
#include "../../../ride/Vehicle.h"
|
||||
#include "../../../ride/ted/TrackElementDescriptor.h"
|
||||
#include "../../ScriptEngine.h"
|
||||
#include "../../ScriptUtil.hpp"
|
||||
|
||||
using namespace OpenRCT2::Scripting;
|
||||
using namespace OpenRCT2::TrackMetadata;
|
||||
|
||||
ScTrackSegment::ScTrackSegment(OpenRCT2::TrackElemType type)
|
||||
: _type(type)
|
||||
static JSValue VehicleInfoToJSValue(JSContext* ctx, const VehicleInfo& value)
|
||||
{
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, value.x));
|
||||
JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, value.y));
|
||||
JS_SetPropertyStr(ctx, obj, "z", JS_NewInt32(ctx, value.z));
|
||||
JS_SetPropertyStr(ctx, obj, "yaw", JS_NewInt32(ctx, value.yaw));
|
||||
JS_SetPropertyStr(ctx, obj, "pitch", JS_NewInt32(ctx, EnumValue(value.pitch)));
|
||||
JS_SetPropertyStr(ctx, obj, "roll", JS_NewInt32(ctx, EnumValue(value.roll)));
|
||||
return obj;
|
||||
}
|
||||
|
||||
void ScTrackSegment::Register(duk_context* ctx)
|
||||
void ScTrackSegment::Register(JSContext* ctx)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScTrackSegment::type_get, nullptr, "type");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::description_get, nullptr, "description");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::elements_get, nullptr, "elements");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::beginDirection_get, nullptr, "beginDirection");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::endDirection_get, nullptr, "endDirection");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::beginSlope_get, nullptr, "beginSlope");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::endSlope_get, nullptr, "endSlope");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::beginBank_get, nullptr, "beginBank");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::endBank_get, nullptr, "endBank");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::beginZ_get, nullptr, "beginZ");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::endZ_get, nullptr, "endZ");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::endX_get, nullptr, "endX");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::endY_get, nullptr, "endY");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::length_get, nullptr, "length");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::nextCurveElement_get, nullptr, "nextSuggestedSegment");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::previousCurveElement_get, nullptr, "previousSuggestedSegment");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::getMirrorElement, nullptr, "mirrorSegment");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::getAlternativeElement, nullptr, "alternateTypeSegment");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::getPriceModifier, nullptr, "priceModifier");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::getTrackGroup, nullptr, "trackGroup");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::getTrackCurvature, nullptr, "turnDirection");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::getTrackPitchDirection, nullptr, "slopeDirection");
|
||||
|
||||
dukglue_register_property(
|
||||
ctx, &ScTrackSegment::getTrackFlag<TrackElementFlag::onlyUnderwater>, nullptr, "onlyAllowedUnderwater");
|
||||
dukglue_register_property(
|
||||
ctx, &ScTrackSegment::getTrackFlag<TrackElementFlag::onlyAboveGround>, nullptr, "onlyAllowedAboveGround");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::getTrackFlag<TrackElementFlag::allowLiftHill>, nullptr, "allowsChainLift");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::getTrackFlag<TrackElementFlag::banked>, nullptr, "isBanked");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::getTrackFlag<TrackElementFlag::inversionToNormal>, nullptr, "isInversion");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::getTrackFlag<TrackElementFlag::isSteepUp>, nullptr, "isSteepUp");
|
||||
dukglue_register_property(
|
||||
ctx, &ScTrackSegment::getTrackFlag<TrackElementFlag::startsAtHalfHeight>, nullptr, "startsHalfHeightUp");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::getTrackFlag<TrackElementFlag::isGolfHole>, nullptr, "countsAsInversion");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::getTrackFlag<TrackElementFlag::turnBanked>, nullptr, "isBankedTurn");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::getTrackFlag<TrackElementFlag::turnSloped>, nullptr, "isSlopedTurn");
|
||||
dukglue_register_property(ctx, &ScTrackSegment::getTrackFlag<TrackElementFlag::helix>, nullptr, "isHelix");
|
||||
dukglue_register_property(
|
||||
ctx, &ScTrackSegment::getTrackFlag<TrackElementFlag::normalToInversion>, nullptr, "countsAsInversion");
|
||||
|
||||
dukglue_register_method(ctx, &ScTrackSegment::getSubpositionLength, "getSubpositionLength");
|
||||
dukglue_register_method(ctx, &ScTrackSegment::getSubpositions, "getSubpositions");
|
||||
RegisterBaseStr(ctx, "TrackSegment", Finalize);
|
||||
}
|
||||
|
||||
int32_t ScTrackSegment::type_get() const
|
||||
JSValue ScTrackSegment::New(JSContext* ctx, OpenRCT2::TrackElemType type)
|
||||
{
|
||||
return EnumValue(_type);
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("type", ScTrackSegment::type_get, nullptr),
|
||||
JS_CGETSET_DEF("description", ScTrackSegment::description_get, nullptr),
|
||||
JS_CGETSET_DEF("elements", ScTrackSegment::elements_get, nullptr),
|
||||
JS_CGETSET_DEF("beginDirection", ScTrackSegment::beginDirection_get, nullptr),
|
||||
JS_CGETSET_DEF("endDirection", ScTrackSegment::endDirection_get, nullptr),
|
||||
JS_CGETSET_DEF("beginSlope", ScTrackSegment::beginSlope_get, nullptr),
|
||||
JS_CGETSET_DEF("endSlope", ScTrackSegment::endSlope_get, nullptr),
|
||||
JS_CGETSET_DEF("beginBank", ScTrackSegment::beginBank_get, nullptr),
|
||||
JS_CGETSET_DEF("endBank", ScTrackSegment::endBank_get, nullptr),
|
||||
JS_CGETSET_DEF("beginZ", ScTrackSegment::beginZ_get, nullptr),
|
||||
JS_CGETSET_DEF("endZ", ScTrackSegment::endZ_get, nullptr),
|
||||
JS_CGETSET_DEF("endX", ScTrackSegment::endX_get, nullptr),
|
||||
JS_CGETSET_DEF("endY", ScTrackSegment::endY_get, nullptr),
|
||||
JS_CGETSET_DEF("length", ScTrackSegment::length_get, nullptr),
|
||||
JS_CGETSET_DEF("nextSuggestedSegment", ScTrackSegment::nextCurveElement_get, nullptr),
|
||||
JS_CGETSET_DEF("previousSuggestedSegment", ScTrackSegment::previousCurveElement_get, nullptr),
|
||||
JS_CGETSET_DEF("mirrorSegment", ScTrackSegment::getMirrorElement, nullptr),
|
||||
JS_CGETSET_DEF("alternateTypeSegment", ScTrackSegment::getAlternativeElement, nullptr),
|
||||
JS_CGETSET_DEF("priceModifier", ScTrackSegment::getPriceModifier, nullptr),
|
||||
JS_CGETSET_DEF("trackGroup", ScTrackSegment::getTrackGroup, nullptr),
|
||||
JS_CGETSET_DEF("turnDirection", ScTrackSegment::getTrackCurvature, nullptr),
|
||||
JS_CGETSET_DEF("slopeDirection", ScTrackSegment::getTrackPitchDirection, nullptr),
|
||||
JS_CGETSET_DEF("onlyAllowedUnderwater", ScTrackSegment::getTrackFlag<TrackElementFlag::onlyUnderwater>, nullptr),
|
||||
JS_CGETSET_DEF("onlyAllowedAboveGround", ScTrackSegment::getTrackFlag<TrackElementFlag::onlyAboveGround>, nullptr),
|
||||
JS_CGETSET_DEF("allowsChainLift", ScTrackSegment::getTrackFlag<TrackElementFlag::allowLiftHill>, nullptr),
|
||||
JS_CGETSET_DEF("isBanked", ScTrackSegment::getTrackFlag<TrackElementFlag::banked>, nullptr),
|
||||
JS_CGETSET_DEF("isInversion", ScTrackSegment::getTrackFlag<TrackElementFlag::inversionToNormal>, nullptr),
|
||||
JS_CGETSET_DEF("isSteepUp", ScTrackSegment::getTrackFlag<TrackElementFlag::isSteepUp>, nullptr),
|
||||
JS_CGETSET_DEF("startsHalfHeightUp", ScTrackSegment::getTrackFlag<TrackElementFlag::startsAtHalfHeight>, nullptr),
|
||||
JS_CGETSET_DEF("countsAsGolfHole", ScTrackSegment::getTrackFlag<TrackElementFlag::isGolfHole>, nullptr),
|
||||
JS_CGETSET_DEF("isBankedTurn", ScTrackSegment::getTrackFlag<TrackElementFlag::turnBanked>, nullptr),
|
||||
JS_CGETSET_DEF("isSlopedTurn", ScTrackSegment::getTrackFlag<TrackElementFlag::turnSloped>, nullptr),
|
||||
JS_CGETSET_DEF("isHelix", ScTrackSegment::getTrackFlag<TrackElementFlag::helix>, nullptr),
|
||||
JS_CGETSET_DEF("countsAsInversion", ScTrackSegment::getTrackFlag<TrackElementFlag::normalToInversion>, nullptr),
|
||||
JS_CFUNC_DEF("getSubpositionLength", 2, ScTrackSegment::getSubpositionLength),
|
||||
JS_CFUNC_DEF("getSubpositions", 2, ScTrackSegment::getSubpositions),
|
||||
};
|
||||
return MakeWithOpaque(ctx, funcs, new TrackSegmentData{ type });
|
||||
}
|
||||
|
||||
std::string ScTrackSegment::description_get() const
|
||||
void ScTrackSegment::Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
return LanguageGetString(ted.description);
|
||||
TrackSegmentData* data = GetTrackSegmentData(thisVal);
|
||||
if (data)
|
||||
delete data;
|
||||
}
|
||||
|
||||
int32_t ScTrackSegment::beginZ_get() const
|
||||
ScTrackSegment::TrackSegmentData* ScTrackSegment::GetTrackSegmentData(JSValue thisVal)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
return ted.coordinates.zBegin;
|
||||
return gScTrackSegment.GetOpaque<TrackSegmentData*>(thisVal);
|
||||
}
|
||||
|
||||
int32_t ScTrackSegment::beginDirection_get() const
|
||||
JSValue ScTrackSegment::type_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
return ted.coordinates.rotationBegin;
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
return JS_NewInt32(ctx, EnumValue(data->_type));
|
||||
}
|
||||
|
||||
int32_t ScTrackSegment::beginSlope_get() const
|
||||
JSValue ScTrackSegment::description_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
return EnumValue(ted.definition.pitchStart);
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
return JS_NewString(ctx, LanguageGetString(ted.description));
|
||||
}
|
||||
|
||||
int32_t ScTrackSegment::beginBank_get() const
|
||||
JSValue ScTrackSegment::beginZ_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
return EnumValue(ted.definition.rollStart);
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
return JS_NewInt32(ctx, ted.coordinates.zBegin);
|
||||
}
|
||||
|
||||
int32_t ScTrackSegment::endX_get() const
|
||||
JSValue ScTrackSegment::beginDirection_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
return ted.coordinates.x;
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
return JS_NewInt32(ctx, ted.coordinates.rotationBegin);
|
||||
}
|
||||
|
||||
int32_t ScTrackSegment::endY_get() const
|
||||
JSValue ScTrackSegment::beginSlope_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
return ted.coordinates.y;
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
return JS_NewInt32(ctx, EnumValue(ted.definition.pitchStart));
|
||||
}
|
||||
|
||||
int32_t ScTrackSegment::endZ_get() const
|
||||
JSValue ScTrackSegment::beginBank_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
return ted.coordinates.zEnd;
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
return JS_NewInt32(ctx, EnumValue(ted.definition.rollStart));
|
||||
}
|
||||
|
||||
int32_t ScTrackSegment::endDirection_get() const
|
||||
JSValue ScTrackSegment::endX_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
return ted.coordinates.rotationEnd;
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
return JS_NewInt32(ctx, ted.coordinates.x);
|
||||
}
|
||||
|
||||
int32_t ScTrackSegment::endSlope_get() const
|
||||
JSValue ScTrackSegment::endY_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
return EnumValue(ted.definition.pitchEnd);
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
return JS_NewInt32(ctx, ted.coordinates.y);
|
||||
}
|
||||
|
||||
int32_t ScTrackSegment::endBank_get() const
|
||||
JSValue ScTrackSegment::endZ_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
return EnumValue(ted.definition.rollEnd);
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
return JS_NewInt32(ctx, ted.coordinates.zEnd);
|
||||
}
|
||||
|
||||
int32_t ScTrackSegment::length_get() const
|
||||
JSValue ScTrackSegment::endDirection_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
return ted.pieceLength;
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
return JS_NewInt32(ctx, ted.coordinates.rotationEnd);
|
||||
}
|
||||
|
||||
DukValue ScTrackSegment::elements_get() const
|
||||
JSValue ScTrackSegment::endSlope_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
auto ctx = scriptEngine.GetContext();
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
return JS_NewUint32(ctx, EnumValue(ted.definition.pitchEnd));
|
||||
}
|
||||
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
JSValue ScTrackSegment::endBank_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
return JS_NewUint32(ctx, EnumValue(ted.definition.rollEnd));
|
||||
}
|
||||
|
||||
duk_push_array(ctx);
|
||||
JSValue ScTrackSegment::length_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
return JS_NewUint32(ctx, ted.pieceLength);
|
||||
}
|
||||
|
||||
duk_uarridx_t index = 0;
|
||||
for (uint8_t i = 0; i < ted.sequenceData.numSequences; i++)
|
||||
JSValue ScTrackSegment::elements_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
|
||||
JSValue result = JS_NewArray(ctx);
|
||||
|
||||
for (int64_t i = 0; i < ted.sequenceData.numSequences; i++)
|
||||
{
|
||||
auto& block = ted.sequenceData.sequences[i].clearance;
|
||||
duk_push_object(ctx);
|
||||
duk_push_number(ctx, block.x);
|
||||
duk_put_prop_string(ctx, -2, "x");
|
||||
duk_push_number(ctx, block.y);
|
||||
duk_put_prop_string(ctx, -2, "y");
|
||||
duk_push_number(ctx, block.z);
|
||||
duk_put_prop_string(ctx, -2, "z");
|
||||
|
||||
duk_put_prop_index(ctx, -2, index);
|
||||
index++;
|
||||
JSValue element = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, element, "x", JS_NewInt32(ctx, block.x));
|
||||
JS_SetPropertyStr(ctx, element, "y", JS_NewInt32(ctx, block.y));
|
||||
JS_SetPropertyStr(ctx, element, "z", JS_NewInt32(ctx, block.z));
|
||||
JS_SetPropertyInt64(ctx, result, i, element);
|
||||
}
|
||||
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
uint16_t ScTrackSegment::getSubpositionLength(uint8_t trackSubposition, uint8_t direction) const
|
||||
{
|
||||
return VehicleGetMoveInfoSize(static_cast<VehicleTrackSubposition>(trackSubposition), _type, direction);
|
||||
}
|
||||
|
||||
std::vector<DukValue> ScTrackSegment::getSubpositions(uint8_t trackSubposition, uint8_t direction) const
|
||||
{
|
||||
const auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
const uint16_t size = getSubpositionLength(trackSubposition, direction);
|
||||
const uint16_t typeAndDirection = (EnumValue(_type) << 2) | (direction & 3);
|
||||
|
||||
std::vector<DukValue> result;
|
||||
|
||||
for (auto idx = 0; idx < size; idx++)
|
||||
{
|
||||
result.push_back(ToDuk<VehicleInfo>(ctx, gTrackVehicleInfo[trackSubposition][typeAndDirection]->info[idx]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static DukValue _trackCurveToString(duk_context* ctx, TrackCurve curve)
|
||||
JSValue ScTrackSegment::getSubpositionLength(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_UINT32(trackSubposition, ctx, argv[0]);
|
||||
JS_UNPACK_UINT32(direction, ctx, argv[1]);
|
||||
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
uint16_t length = VehicleGetMoveInfoSize(
|
||||
static_cast<VehicleTrackSubposition>(trackSubposition), data->_type, static_cast<uint8_t>(direction));
|
||||
return JS_NewUint32(ctx, length);
|
||||
}
|
||||
|
||||
JSValue ScTrackSegment::getSubpositions(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv)
|
||||
{
|
||||
JS_UNPACK_UINT32(trackSubposition, ctx, argv[0]);
|
||||
JS_UNPACK_UINT32(direction, ctx, argv[1]);
|
||||
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const uint16_t size = VehicleGetMoveInfoSize(
|
||||
static_cast<VehicleTrackSubposition>(trackSubposition), data->_type, static_cast<uint8_t>(direction));
|
||||
const uint16_t typeAndDirection = (EnumValue(data->_type) << 2) | (direction & 3);
|
||||
|
||||
JSValue result = JS_NewArray(ctx);
|
||||
|
||||
for (int64_t idx = 0; idx < size; idx++)
|
||||
{
|
||||
JSValue subposition = VehicleInfoToJSValue(ctx, gTrackVehicleInfo[trackSubposition][typeAndDirection]->info[idx]);
|
||||
JS_SetPropertyInt64(ctx, result, idx, subposition);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static JSValue _trackCurveToString(JSContext* ctx, TrackCurve curve)
|
||||
{
|
||||
static const EnumMap<TrackCurve> map(
|
||||
{
|
||||
@@ -211,92 +251,100 @@ static DukValue _trackCurveToString(duk_context* ctx, TrackCurve curve)
|
||||
{ "right_large", TrackCurve::rightLarge },
|
||||
});
|
||||
|
||||
u8string text = u8string(map[curve]);
|
||||
return ToDuk<std::string>(ctx, text);
|
||||
const auto text = std::string(map[curve]);
|
||||
return JSFromStdString(ctx, text);
|
||||
}
|
||||
|
||||
DukValue ScTrackSegment::nextCurveElement_get() const
|
||||
JSValue ScTrackSegment::nextCurveElement_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
|
||||
auto nextInChain = ted.curveChain.next;
|
||||
if (nextInChain.isTrackType)
|
||||
return ToDuk<int32_t>(ctx, EnumValue(nextInChain.trackType));
|
||||
return JS_NewInt32(ctx, EnumValue(nextInChain.trackType));
|
||||
|
||||
return _trackCurveToString(ctx, nextInChain.curve);
|
||||
}
|
||||
|
||||
DukValue ScTrackSegment::previousCurveElement_get() const
|
||||
JSValue ScTrackSegment::previousCurveElement_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
|
||||
auto previousInChain = ted.curveChain.previous;
|
||||
if (previousInChain.isTrackType)
|
||||
return ToDuk<int32_t>(ctx, EnumValue(previousInChain.trackType));
|
||||
return JS_NewInt32(ctx, EnumValue(previousInChain.trackType));
|
||||
|
||||
return _trackCurveToString(ctx, previousInChain.curve);
|
||||
}
|
||||
|
||||
DukValue ScTrackSegment::getMirrorElement() const
|
||||
JSValue ScTrackSegment::getMirrorElement(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
if (ted.mirrorElement == TrackElemType::none)
|
||||
return ToDuk(ctx, nullptr);
|
||||
return ToDuk<int32_t>(ctx, EnumValue(ted.mirrorElement));
|
||||
return JS_NULL;
|
||||
return JS_NewInt32(ctx, EnumValue(ted.mirrorElement));
|
||||
}
|
||||
|
||||
DukValue ScTrackSegment::getAlternativeElement() const
|
||||
JSValue ScTrackSegment::getAlternativeElement(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
if (ted.alternativeType == TrackElemType::none)
|
||||
return ToDuk(ctx, nullptr);
|
||||
return ToDuk<int32_t>(ctx, EnumValue(ted.alternativeType));
|
||||
return JS_NULL;
|
||||
return JS_NewInt32(ctx, EnumValue(ted.alternativeType));
|
||||
}
|
||||
|
||||
int32_t ScTrackSegment::getPriceModifier() const
|
||||
JSValue ScTrackSegment::getPriceModifier(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
|
||||
return ted.priceModifier;
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
return JS_NewInt32(ctx, ted.priceModifier);
|
||||
}
|
||||
|
||||
template<TrackElementFlag flag>
|
||||
bool ScTrackSegment::getTrackFlag() const
|
||||
JSValue ScTrackSegment::getTrackFlag(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
|
||||
return ted.flags.has(flag);
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
return JS_NewBool(ctx, ted.flags.has(flag));
|
||||
}
|
||||
|
||||
int32_t ScTrackSegment::getTrackGroup() const
|
||||
JSValue ScTrackSegment::getTrackGroup(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
|
||||
return EnumValue(ted.definition.group);
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
return JS_NewInt32(ctx, EnumValue(ted.definition.group));
|
||||
}
|
||||
|
||||
std::string ScTrackSegment::getTrackCurvature() const
|
||||
JSValue ScTrackSegment::getTrackCurvature(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
std::string curvature;
|
||||
if (ted.flags.has(TrackElementFlag::turnLeft))
|
||||
return "left";
|
||||
if (ted.flags.has(TrackElementFlag::turnRight))
|
||||
return "right";
|
||||
return "straight";
|
||||
curvature = "left";
|
||||
else if (ted.flags.has(TrackElementFlag::turnRight))
|
||||
curvature = "right";
|
||||
else
|
||||
curvature = "straight";
|
||||
return JSFromStdString(ctx, curvature);
|
||||
}
|
||||
|
||||
std::string ScTrackSegment::getTrackPitchDirection() const
|
||||
JSValue ScTrackSegment::getTrackPitchDirection(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(_type);
|
||||
const auto* data = GetTrackSegmentData(thisVal);
|
||||
const auto& ted = GetTrackElementDescriptor(data->_type);
|
||||
std::string pitch;
|
||||
if (ted.flags.has(TrackElementFlag::up))
|
||||
return "up";
|
||||
if (ted.flags.has(TrackElementFlag::down))
|
||||
return "down";
|
||||
return "flat";
|
||||
pitch = "up";
|
||||
else if (ted.flags.has(TrackElementFlag::down))
|
||||
pitch = "down";
|
||||
else
|
||||
pitch = "flat";
|
||||
return JSFromStdString(ctx, pitch);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
#include "../../Duktape.hpp"
|
||||
#include "../../ScriptEngine.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -23,56 +23,51 @@ namespace OpenRCT2::TrackMetadata
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
template<>
|
||||
inline DukValue ToDuk(duk_context* ctx, const VehicleInfo& value)
|
||||
{
|
||||
DukObject dukSubposition(ctx);
|
||||
dukSubposition.Set("x", value.x);
|
||||
dukSubposition.Set("y", value.y);
|
||||
dukSubposition.Set("z", value.z);
|
||||
dukSubposition.Set("yaw", value.yaw);
|
||||
dukSubposition.Set("pitch", EnumValue(value.pitch));
|
||||
dukSubposition.Set("roll", EnumValue(value.roll));
|
||||
return dukSubposition.Take();
|
||||
}
|
||||
class ScTrackSegment;
|
||||
extern ScTrackSegment gScTrackSegment;
|
||||
|
||||
class ScTrackSegment
|
||||
class ScTrackSegment final : public ScBase
|
||||
{
|
||||
private:
|
||||
TrackElemType _type;
|
||||
struct TrackSegmentData
|
||||
{
|
||||
TrackElemType _type;
|
||||
};
|
||||
|
||||
public:
|
||||
ScTrackSegment(TrackElemType type);
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
void Register(JSContext* ctx);
|
||||
JSValue New(JSContext* ctx, TrackElemType type);
|
||||
|
||||
private:
|
||||
int32_t type_get() const;
|
||||
std::string description_get() const;
|
||||
int32_t beginZ_get() const;
|
||||
int32_t beginDirection_get() const;
|
||||
int32_t beginSlope_get() const;
|
||||
int32_t beginBank_get() const;
|
||||
int32_t endX_get() const;
|
||||
int32_t endY_get() const;
|
||||
int32_t endZ_get() const;
|
||||
int32_t endDirection_get() const;
|
||||
int32_t endSlope_get() const;
|
||||
int32_t endBank_get() const;
|
||||
int32_t length_get() const;
|
||||
DukValue elements_get() const;
|
||||
uint16_t getSubpositionLength(uint8_t trackSubposition, uint8_t direction) const;
|
||||
std::vector<DukValue> getSubpositions(uint8_t trackSubposition, uint8_t direction) const;
|
||||
DukValue nextCurveElement_get() const;
|
||||
DukValue previousCurveElement_get() const;
|
||||
DukValue getMirrorElement() const;
|
||||
DukValue getAlternativeElement() const;
|
||||
int32_t getPriceModifier() const;
|
||||
int32_t getTrackGroup() const;
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal);
|
||||
static TrackSegmentData* GetTrackSegmentData(JSValue thisVal);
|
||||
|
||||
static JSValue type_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue description_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue beginZ_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue beginDirection_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue beginSlope_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue beginBank_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue endX_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue endY_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue endZ_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue endDirection_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue endSlope_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue endBank_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue length_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue elements_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue getSubpositionLength(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
static JSValue getSubpositions(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv);
|
||||
static JSValue nextCurveElement_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue previousCurveElement_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue getMirrorElement(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue getAlternativeElement(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue getPriceModifier(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue getTrackGroup(JSContext* ctx, JSValue thisVal);
|
||||
template<TrackMetadata::TrackElementFlag flag>
|
||||
bool getTrackFlag() const;
|
||||
std::string getTrackCurvature() const;
|
||||
std::string getTrackPitchDirection() const;
|
||||
static JSValue getTrackFlag(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue getTrackCurvature(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue getTrackPitchDirection(JSContext* ctx, JSValue thisVal);
|
||||
};
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -16,74 +16,84 @@
|
||||
#include "../../../localisation/Formatting.h"
|
||||
#include "../../../management/Award.h"
|
||||
#include "../../../windows/Intent.h"
|
||||
#include "../../Duktape.hpp"
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
ScAward::ScAward(size_t index)
|
||||
: _index(index)
|
||||
extern ScAward gScAward;
|
||||
|
||||
using OpaqueAwardData = struct
|
||||
{
|
||||
size_t index;
|
||||
};
|
||||
|
||||
JSValue ScAward::New(JSContext* ctx, size_t index)
|
||||
{
|
||||
return MakeWithOpaque(ctx, funcs, new OpaqueAwardData{ index });
|
||||
}
|
||||
|
||||
void ScAward::Register(duk_context* ctx)
|
||||
void ScAward::Register(JSContext* ctx)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScAward::type_get, nullptr, "type");
|
||||
dukglue_register_property(ctx, &ScAward::text_get, nullptr, "text");
|
||||
dukglue_register_property(ctx, &ScAward::positive_get, nullptr, "positive");
|
||||
dukglue_register_property(ctx, &ScAward::imageId_get, nullptr, "imageId");
|
||||
dukglue_register_property(ctx, &ScAward::monthsRemaining_get, nullptr, "monthsRemaining");
|
||||
RegisterBaseStr(ctx, "Award", Finalize);
|
||||
}
|
||||
|
||||
Award* ScAward::GetAward() const
|
||||
void ScAward::Finalize(JSRuntime* rt, JSValue thisVal)
|
||||
{
|
||||
return &getGameState().park.currentAwards[_index];
|
||||
OpaqueAwardData* data = gScAward.GetOpaque<OpaqueAwardData*>(thisVal);
|
||||
if (data)
|
||||
delete data;
|
||||
}
|
||||
|
||||
std::string ScAward::type_get() const
|
||||
Award* ScAward::GetAward(JSValue thisVal)
|
||||
{
|
||||
auto award = GetAward();
|
||||
OpaqueAwardData* data = gScAward.GetOpaque<OpaqueAwardData*>(thisVal);
|
||||
return &getGameState().park.currentAwards[data->index];
|
||||
}
|
||||
|
||||
JSValue ScAward::type_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto award = GetAward(thisVal);
|
||||
if (award == nullptr)
|
||||
return {};
|
||||
return JSFromStdString(ctx, {});
|
||||
|
||||
return AwardTypeToString(award->Type).value_or(std::string());
|
||||
return JSFromStdString(ctx, AwardTypeToString(award->Type).value_or(std::string()));
|
||||
}
|
||||
|
||||
std::string ScAward::text_get() const
|
||||
JSValue ScAward::text_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto award = GetAward();
|
||||
auto award = GetAward(thisVal);
|
||||
if (award == nullptr)
|
||||
return {};
|
||||
return JSFromStdString(ctx, {});
|
||||
|
||||
Formatter ft{};
|
||||
ft.Add<StringId>(AwardGetText(award->Type));
|
||||
return FormatStringIDLegacy(STR_STRINGID, ft.Data());
|
||||
return JSFromStdString(ctx, FormatStringIDLegacy(STR_STRINGID, ft.Data()));
|
||||
}
|
||||
|
||||
uint16_t ScAward::monthsRemaining_get() const
|
||||
JSValue ScAward::monthsRemaining_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto award = GetAward();
|
||||
auto award = GetAward(thisVal);
|
||||
if (award == nullptr)
|
||||
return {};
|
||||
return JS_NewInt32(ctx, {});
|
||||
|
||||
return award->Time;
|
||||
return JS_NewInt32(ctx, award->Time);
|
||||
}
|
||||
|
||||
bool ScAward::positive_get() const
|
||||
JSValue ScAward::positive_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto award = GetAward();
|
||||
auto award = GetAward(thisVal);
|
||||
if (award == nullptr)
|
||||
return {};
|
||||
return JS_NewBool(ctx, {});
|
||||
|
||||
return AwardIsPositive(award->Type);
|
||||
return JS_NewBool(ctx, AwardIsPositive(award->Type));
|
||||
}
|
||||
|
||||
uint32_t ScAward::imageId_get() const
|
||||
JSValue ScAward::imageId_get(JSContext* ctx, JSValue thisVal)
|
||||
{
|
||||
auto award = GetAward();
|
||||
auto award = GetAward(thisVal);
|
||||
if (award == nullptr)
|
||||
return {};
|
||||
return JS_NewUint32(ctx, {});
|
||||
|
||||
return AwardGetSprite(award->Type);
|
||||
return JS_NewUint32(ctx, AwardGetSprite(award->Type));
|
||||
}
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
|
||||
#include "../../../Context.h"
|
||||
#include "../../../management/Award.h"
|
||||
#include "../../Duktape.hpp"
|
||||
#include "../../ScriptEngine.h"
|
||||
|
||||
#include <quickjs.h>
|
||||
#include <string>
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
@@ -60,24 +60,31 @@ namespace OpenRCT2::Scripting
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
class ScAward
|
||||
class ScAward;
|
||||
extern ScAward gScAward;
|
||||
|
||||
class ScAward final : public ScBase
|
||||
{
|
||||
private:
|
||||
size_t _index{};
|
||||
|
||||
public:
|
||||
ScAward(size_t index);
|
||||
|
||||
static void Register(duk_context* ctx);
|
||||
JSValue New(JSContext* ctx, size_t index);
|
||||
void Register(JSContext* ctx);
|
||||
|
||||
private:
|
||||
Award* GetAward() const;
|
||||
static void Finalize(JSRuntime* rt, JSValue thisVal);
|
||||
static Award* GetAward(JSValue thisVal);
|
||||
|
||||
std::string type_get() const;
|
||||
std::string text_get() const;
|
||||
uint16_t monthsRemaining_get() const;
|
||||
bool positive_get() const;
|
||||
uint32_t imageId_get() const;
|
||||
static JSValue type_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue text_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue monthsRemaining_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue positive_get(JSContext* ctx, JSValue thisVal);
|
||||
static JSValue imageId_get(JSContext* ctx, JSValue thisVal);
|
||||
|
||||
static constexpr JSCFunctionListEntry funcs[] = {
|
||||
JS_CGETSET_DEF("type", &ScAward::type_get, nullptr), JS_CGETSET_DEF("text", &ScAward::text_get, nullptr),
|
||||
JS_CGETSET_DEF("positive", &ScAward::positive_get, nullptr),
|
||||
JS_CGETSET_DEF("imageId", &ScAward::imageId_get, nullptr),
|
||||
JS_CGETSET_DEF("monthsRemaining", &ScAward::monthsRemaining_get, nullptr)
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user