From 0ca52e58fd25c49b871e135a625ee9056ddb9708 Mon Sep 17 00:00:00 2001 From: Ted John Date: Fri, 1 May 2020 17:04:37 +0100 Subject: [PATCH] Implement tile selection API --- distribution/openrct2.d.ts | 94 +++++++++ src/openrct2-ui/scripting/ScTileSelection.hpp | 179 ++++++++++++++++++ src/openrct2-ui/scripting/ScUi.hpp | 7 + src/openrct2-ui/scripting/UiExtensions.cpp | 2 + 4 files changed, 282 insertions(+) create mode 100644 src/openrct2-ui/scripting/ScTileSelection.hpp diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 798635f3a2..8a2e80e6f5 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -71,6 +71,14 @@ declare global { z: number; } + /** + * A rectangular area specified using two coordinates. + */ + interface MapRange { + leftTop: Coord2; + rightBottom: Coord2; + } + /** * Represents information about the plugin such as type, name, author and version. * It also includes the entry point. @@ -710,6 +718,7 @@ declare global { readonly height: number; readonly windows: number; readonly mainViewport: Viewport; + readonly tileSelection: TileSelection; getWindow(id: number): Window; getWindow(classification: string): Window; @@ -717,9 +726,94 @@ declare global { closeWindows(classification: string, id?: number): void; closeAllWindows(): void; + /** + * Begins a new tool session. The cursor will change to the style specified by the + * given tool descriptor and cursor events will be provided. + * @param tool The properties and event handlers for the tool. + */ + activateTool(tool: ToolDesc): void; + registerMenuItem(text: string, callback: () => void): void; } + interface TileSelection { + range: MapRange; + tiles: Coord2[]; + } + + /** + * The type of tool. + * Raw will provide only the screen coordinates of where the cursor is. + * Tile will provide a coordinates of the tile the cursor is over. + * Interact will provide information about the ride or entity the cursor is over. + */ + type ToolType = "raw" | "tile" | "interact"; + + type InteractionType = + "none" | + "surface" | + "sprite" | + "ride" | + "water" | + "scenery" | + "footpath" | + "footpath_addition" | + "park" | + "wall" | + "large_scenery" | + "label" | + "banner"; + + interface ToolEventArgs { + kind: InteractionType; + screenCoords: Coord2; + mapCoords?: Coord3; + tileElementIndex?: number; + entity?: Entity; + } + + /** + * Describes the properties and event handlers for a custom tool. + */ + interface ToolDesc { + type: ToolType; + cursor: CursorType; + + onDown: (e: ToolEventArgs) => void; + onMove: (e: ToolEventArgs) => void; + onUp: (e: ToolEventArgs) => void; + onFinish: () => void; + } + + type CursorType = + "arrow" | + "blank" | + "up_arrow" | + "up_down_arrow" | + "hand_point" | + "zzz" | + "diagonal_arrows" | + "picker" | + "tree_down" | + "fountain_down" | + "statue_down" | + "bench_down" | + "cross_hair" | + "bin_down" | + "lamppost_down" | + "fence_down" | + "flower_down" | + "path_down" | + "dig_down" | + "water_down" | + "house_down" | + "volcano_down" | + "walk_down" | + "paint_down" | + "entrance_down" | + "hand_open" | + "hand_closed"; + /** * Represents the type of a widget, e.g. button or label. */ diff --git a/src/openrct2-ui/scripting/ScTileSelection.hpp b/src/openrct2-ui/scripting/ScTileSelection.hpp new file mode 100644 index 0000000000..84d5472a63 --- /dev/null +++ b/src/openrct2-ui/scripting/ScTileSelection.hpp @@ -0,0 +1,179 @@ +/***************************************************************************** + * Copyright (c) 2014-2020 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 +# include +# include + +namespace OpenRCT2::Scripting +{ + class ScTileSelection + { + private: + duk_context* _ctx{}; + + public: + ScTileSelection(duk_context* ctx) + : _ctx(ctx) + { + } + + DukValue range_get() const + { + if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE) + { + DukObject range(_ctx); + + DukObject leftTop(_ctx); + leftTop.Set("x", gMapSelectPositionA.x); + leftTop.Set("y", gMapSelectPositionA.y); + range.Set("leftTop", leftTop.Take()); + + DukObject rightBottom(_ctx); + rightBottom.Set("x", gMapSelectPositionB.x); + rightBottom.Set("y", gMapSelectPositionB.y); + range.Set("rightBottom", rightBottom.Take()); + return range.Take(); + } + else + { + duk_push_null(_ctx); + return DukValue::take_from_stack(_ctx); + } + } + + void range_set(DukValue value) + { + if (value.type() == DukValue::Type::OBJECT) + { + auto range = GetMapRange(value); + if (range) + { + gMapSelectPositionA.x = range->GetLeft(); + gMapSelectPositionA.y = range->GetTop(); + gMapSelectPositionB.x = range->GetRight(); + gMapSelectPositionB.y = range->GetBottom(); + gMapSelectType = MAP_SELECT_TYPE_FULL; + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + } + } + else + { + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + } + map_invalidate_selection_rect(); + } + + DukValue tiles_get() const + { + duk_push_array(_ctx); + if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE_CONSTRUCT) + { + duk_uarridx_t index = 0; + for (const auto& tile : gMapSelectionTiles) + { + 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); + index++; + } + } + return DukValue::take_from_stack(_ctx); + } + + void tiles_set(DukValue value) + { + gMapSelectionTiles.clear(); + if (value.is_array()) + { + 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)) + { + auto dukElement = DukValue::take_from_stack(_ctx); + auto coords = GetCoordsXY(dukElement); + if (coords) + { + gMapSelectionTiles.push_back(*coords); + } + } + } + duk_pop(_ctx); + } + + if (gMapSelectionTiles.empty()) + { + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN; + } + else + { + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + } + map_invalidate_map_selection_tiles(); + } + + 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"); + } + + private: + static std::optional GetCoordsXY(const DukValue& dukCoords) + { + std::optional result; + if (dukCoords.type() == DukValue::Type::OBJECT) + { + auto dukX = dukCoords["x"]; + if (dukX.type() == DukValue::Type::NUMBER) + { + auto dukY = dukCoords["y"]; + if (dukY.type() == DukValue::Type::NUMBER) + { + CoordsXY coords; + coords.x = dukX.as_int(); + coords.y = dukY.as_int(); + result = coords; + } + } + } + return result; + } + + static std::optional GetMapRange(const DukValue& dukMapRange) + { + std::optional result; + if (dukMapRange.type() == DukValue::Type::OBJECT) + { + auto leftTop = GetCoordsXY(dukMapRange["leftTop"]); + if (leftTop.has_value()) + { + auto rightBottom = GetCoordsXY(dukMapRange["rightBottom"]); + if (rightBottom.has_value()) + { + result = MapRange(leftTop->x, leftTop->y, rightBottom->x, rightBottom->y); + } + } + } + return result; + } + }; +} // namespace OpenRCT2::Scripting + +#endif diff --git a/src/openrct2-ui/scripting/ScUi.hpp b/src/openrct2-ui/scripting/ScUi.hpp index d1b2f5094c..eda29c5826 100644 --- a/src/openrct2-ui/scripting/ScUi.hpp +++ b/src/openrct2-ui/scripting/ScUi.hpp @@ -12,6 +12,7 @@ #ifdef ENABLE_SCRIPTING # include "CustomMenu.h" +# include "ScTileSelection.hpp" # include "ScViewport.hpp" # include "ScWindow.hpp" @@ -64,6 +65,11 @@ namespace OpenRCT2::Scripting return std::make_shared(WC_MAIN_WINDOW); } + std::shared_ptr tileSelection_get() const + { + return std::make_shared(_scriptEngine.GetContext()); + } + std::shared_ptr openWindow(DukValue desc) { using namespace OpenRCT2::Ui::Windows; @@ -142,6 +148,7 @@ namespace OpenRCT2::Scripting 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_method(ctx, &ScUi::openWindow, "openWindow"); dukglue_register_method(ctx, &ScUi::closeWindows, "closeWindows"); dukglue_register_method(ctx, &ScUi::closeAllWindows, "closeAllWindows"); diff --git a/src/openrct2-ui/scripting/UiExtensions.cpp b/src/openrct2-ui/scripting/UiExtensions.cpp index de973d5ef7..1bfd49b26d 100644 --- a/src/openrct2-ui/scripting/UiExtensions.cpp +++ b/src/openrct2-ui/scripting/UiExtensions.cpp @@ -12,6 +12,7 @@ # include "UiExtensions.h" # include "CustomMenu.h" +# include "ScTileSelection.hpp" # include "ScUi.hpp" # include "ScWidget.hpp" # include "ScWindow.hpp" @@ -26,6 +27,7 @@ void UiScriptExtensions::Extend(ScriptEngine& scriptEngine) dukglue_register_global(ctx, std::make_shared(scriptEngine), "ui"); + ScTileSelection::Register(ctx); ScUi::Register(ctx); ScViewport::Register(ctx); ScWidget::Register(ctx);