1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-19 04:53:12 +01:00

Implement tile selection API

This commit is contained in:
Ted John
2020-05-01 17:04:37 +01:00
parent 32eb7071a1
commit 0ca52e58fd
4 changed files with 282 additions and 0 deletions

View File

@@ -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.
*/

View File

@@ -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 <openrct2/common.h>
# include <openrct2/scripting/Duktape.hpp>
# include <openrct2/world/Map.h>
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<CoordsXY> GetCoordsXY(const DukValue& dukCoords)
{
std::optional<CoordsXY> 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<MapRange> GetMapRange(const DukValue& dukMapRange)
{
std::optional<MapRange> 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

View File

@@ -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<ScViewport>(WC_MAIN_WINDOW);
}
std::shared_ptr<ScTileSelection> tileSelection_get() const
{
return std::make_shared<ScTileSelection>(_scriptEngine.GetContext());
}
std::shared_ptr<ScWindow> 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");

View File

@@ -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<ScUi>(scriptEngine), "ui");
ScTileSelection::Register(ctx);
ScUi::Register(ctx);
ScViewport::Register(ctx);
ScWidget::Register(ctx);