Fix #18665, #18559: Add API for getting g2 icons by name

Co-authored-by: Michael Steenbeek <m.o.steenbeek@gmail.com>
This commit is contained in:
spacek531 2022-12-12 15:03:16 -08:00 committed by GitHub
parent 6b8f7afe29
commit 44100234c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 313 additions and 10 deletions

View File

@ -3,6 +3,7 @@
- Feature: [#17782] The Flying Coaster now has access to boosters and can draw outside loops.
- Feature: [#17997] The Log Flume can now draw steep pieces down (if vehicle allows it).
- Feature: [#18312, objects#220, OpenSFX#13] New sound effects for the Hybrid and Single Rail roller coasters.
- Feature: [#18675] [Plugin] Plugins can refer to g2 image icons by name.
- Feature: [objects#173] Add alpine coaster vehicle.
- Feature: [objects#221] Add two extra jungle walls.
- Feature: [objects#225] Add log cabin roofs.

View File

@ -230,6 +230,12 @@ declare global {
*/
getTrackSegment(type: number): TrackSegment | null;
/**
* Gets the image number for the given icon.
* @param iconName The name of the icon.
*/
getIcon(iconName: IconName): number;
/**
* Gets a random integer within the specified range using the game's pseudo-
* random number generator. This is part of the game state and shared across
@ -2537,6 +2543,26 @@ declare global {
type Widget =
ButtonWidget | CheckboxWidget | ColourPickerWidget | CustomWidget | DropdownWidget | GroupBoxWidget |
LabelWidget | ListViewWidget | SpinnerWidget | TextBoxWidget | ViewportWidget;
type IconName = "arrow_down" | "arrow_up" | "chat" | "cheats" | "copy" | "empty" | "eyedropper" |
"fast_forward" | "game_speed_indicator" | "game_speed_indicator_double" | "glassy_recolourable" |
"hide_full" | "hide_partial" | "hide_scenery" | "hide_supports" | "hide_vegetation" | "hide_vehicles" |
"large_scenery" | "legacy_paths" | "link_chain" | "logo" | "logo_text" | "map_east" |
"map_east_pressed" | "map_gen_land" | "map_gen_noise" | "map_gen_trees" | "map_north" |
"map_north_pressed" | "map_south" | "map_south_pressed" | "map_west" | "map_west_pressed" |
"mountain_tool_even" | "mountain_tool_odd" | "multiplayer" | "multiplayer_desync" | "multiplayer_sync" |
"multiplayer_toolbar" | "multiplayer_toolbar_pressed" | "mute" | "mute_pressed" | "news_messages" |
"normal_selection_6x6" | "paste" | "path_railings" | "path_surfaces" | "paths" | "placeholder" |
"rct1_close_off" | "rct1_close_off_pressed" | "rct1_close_on" | "rct1_close_on_pressed"| "rct1_open_off" |
"rct1_open_off_pressed" | "rct1_open_on" | "rct1_open_on_pressed" | "rct1_simulate_off" |
"rct1_simulate_off_pressed" | "rct1_simulate_on" | "rct1_simulate_on_pressed" | "rct1_test_off" |
"rct1_test_off_pressed" | "rct1_test_on" | "rct1_test_on_pressed" | "reload" | "ride_stations" |
"scenery_scatter_high" | "scenery_scatter_low" | "scenery_scatter_medium" | "search" |
"selection_edge_ne" | "selection_edge_nw" | "selection_edge_se" | "selection_edge_sw" |
"server_password" | "sideways_tab" | "sideways_tab_active" | "simulate" | "small_scenery" | "sort" |
"terrain_edges" | "title_play" | "title_restart" | "title_skip" | "title_stop" | "unmute" |
"unmute_pressed" | "view" | "zoom_in" | "zoom_in_background" | "zoom_out" | "zoom_out_background";
interface WidgetBase {
readonly window: Window;
@ -2558,7 +2584,7 @@ declare global {
* By default, text buttons have borders and image buttons do not but it can be overridden.
*/
border: boolean;
image: number;
image: number | IconName;
isPressed: boolean;
text: string;
}
@ -2825,7 +2851,7 @@ declare global {
}
interface WindowTabDesc {
image: number | ImageAnimation;
image: number | ImageAnimation | IconName;
widgets?: WidgetDesc[];
}

View File

@ -107,10 +107,9 @@ namespace OpenRCT2::Ui::Windows
if (result.Type == "button")
{
auto dukImage = desc["image"];
if (dukImage.type() == DukValue::Type::NUMBER)
if (dukImage.type() == DukValue::Type::STRING || dukImage.type() == DukValue::Type::NUMBER)
{
auto img = dukImage.as_uint();
result.Image = ImageId::FromUInt32(img);
result.Image = ImageId::FromUInt32(ImageFromDuk(dukImage));
result.HasBorder = false;
}
else
@ -210,9 +209,9 @@ namespace OpenRCT2::Ui::Windows
{
CustomTabDesc result;
auto dukImage = desc["image"];
if (dukImage.type() == DukValue::Type::NUMBER)
if (dukImage.type() == DukValue::Type::STRING || dukImage.type() == DukValue::Type::NUMBER)
{
result.imageFrameBase = ImageId::FromUInt32(static_cast<uint32_t>(dukImage.as_int()));
result.imageFrameBase = ImageId::FromUInt32(ImageFromDuk(dukImage));
result.imageFrameCount = 0;
result.imageFrameDuration = 0;
}

View File

@ -21,6 +21,7 @@
# include <openrct2/Context.h>
# include <openrct2/common.h>
# include <openrct2/scripting/Duktape.hpp>
# include <openrct2/scripting/IconNames.hpp>
# include <openrct2/scripting/ScriptEngine.h>
namespace OpenRCT2::Scripting
@ -500,16 +501,21 @@ namespace OpenRCT2::Scripting
auto widget = GetWidget();
if (widget != nullptr && widget->type == WindowWidgetType::FlatBtn)
{
if (GetTargetAPIVersion() <= API_VERSION_63_G2_REORDER)
{
return LegacyIconIndex(widget->image);
}
return widget->image;
}
return 0;
}
void image_set(uint32_t value)
void image_set(DukValue value)
{
auto widget = GetWidget();
if (widget != nullptr && widget->type == WindowWidgetType::FlatBtn)
{
widget->image = value;
widget->image = ImageFromDuk(value);
Invalidate();
}
}

View File

@ -486,6 +486,7 @@
<ClInclude Include="scripting\bindings\world\ScParkMessage.hpp" />
<ClInclude Include="scripting\bindings\world\ScTileElement.hpp" />
<ClInclude Include="scripting\Duktape.hpp" />
<ClInclude Include="scripting\IconNames.hpp" />
<ClInclude Include="scripting\HookEngine.h" />
<ClInclude Include="scripting\Plugin.h" />
<ClInclude Include="scripting\bindings\game\ScCheats.hpp" />

View File

@ -465,6 +465,8 @@ namespace OpenRCT2::Scripting
return OBJECT_ENTRY_INDEX_NULL;
}
uint32_t ImageFromDuk(const DukValue& d);
} // namespace OpenRCT2::Scripting
#endif

View File

@ -0,0 +1,241 @@
/*****************************************************************************
* Copyright (c) 2014-2022 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
#include "../core/EnumMap.hpp"
#include "../sprites.h"
#include <map>
#include <string>
namespace OpenRCT2::Scripting
{
// When adding to this map, also add to IconName in openrct2.d.ts
static EnumMap<uint32_t> G2SpriteLookupTable = {
{ "empty", SPR_G2_EMPTY },
{ "placeholder", SPR_G2_PLACEHOLDER },
{ "logo", SPR_G2_LOGO },
{ "logo_text", SPR_G2_TITLE },
{ "fast_forward", SPR_G2_FASTFORWARD },
{ "game_speed_indicator", SPR_G2_SPEED_ARROW },
{ "game_speed_indicator_double", SPR_G2_HYPER_ARROW },
{ "map_gen_land", SPR_G2_TAB_LAND },
{ "zoom_in", SPR_G2_ZOOM_IN },
{ "zoom_in_background", SPR_G2_ZOOM_IN_DISABLED },
{ "zoom_out", SPR_G2_ZOOM_OUT },
{ "zoom_out_background", SPR_G2_ZOOM_OUT_DISABLED },
{ "map_gen_trees", SPR_G2_TAB_TREE },
{ "map_gen_noise", SPR_G2_TAB_PENCIL },
{ "large_scenery", SPR_G2_BUTTON_LARGE_SCENERY },
{ "small_scenery", SPR_G2_BUTTON_TREES },
{ "paths", SPR_G2_BUTTON_FOOTPATH },
{ "rct1_close_off", SPR_G2_RCT1_CLOSE_BUTTON_0 },
{ "rct1_close_off_pressed", SPR_G2_RCT1_CLOSE_BUTTON_1 },
{ "rct1_close_on", SPR_G2_RCT1_CLOSE_BUTTON_2 },
{ "rct1_close_on_pressed", SPR_G2_RCT1_CLOSE_BUTTON_3 },
{ "rct1_test_off", SPR_G2_RCT1_TEST_BUTTON_0 },
{ "rct1_test_off_pressed", SPR_G2_RCT1_TEST_BUTTON_1 },
{ "rct1_test_on", SPR_G2_RCT1_TEST_BUTTON_2 },
{ "rct1_test_on_pressed", SPR_G2_RCT1_TEST_BUTTON_3 },
{ "rct1_open_off", SPR_G2_RCT1_OPEN_BUTTON_0 },
{ "rct1_open_off_pressed", SPR_G2_RCT1_OPEN_BUTTON_1 },
{ "rct1_open_on", SPR_G2_RCT1_OPEN_BUTTON_2 },
{ "rct1_open_on_pressed", SPR_G2_RCT1_OPEN_BUTTON_3 },
{ "title_restart", SPR_G2_TITLE_RESTART },
{ "title_stop", SPR_G2_TITLE_STOP },
{ "title_play", SPR_G2_TITLE_PLAY },
{ "title_skip", SPR_G2_TITLE_SKIP },
{ "cheats", SPR_G2_SANDBOX },
{ "news_messages", SPR_G2_TAB_NEWS },
{ "server_password", SPR_G2_LOCKED },
{ "multiplayer", SPR_G2_MENU_MULTIPLAYER },
{ "sort", SPR_G2_SORT },
{ "copy", SPR_G2_COPY },
{ "paste", SPR_G2_PASTE },
{ "mute", SPR_G2_TOOLBAR_MUTE },
{ "mute_pressed", SPR_G2_TOOLBAR_MUTE_PRESSED },
{ "unmute", SPR_G2_TOOLBAR_UNMUTE },
{ "unmute_pressed", SPR_G2_TOOLBAR_UNMUTE_PRESSED },
{ "search", SPR_G2_SEARCH },
{ "eyedropper", SPR_G2_EYEDROPPER },
{ "chat", SPR_G2_CHAT },
{ "map_north", SPR_G2_MAP_NORTH },
{ "map_north_pressed", SPR_G2_MAP_NORTH_PRESSED },
{ "map_west", SPR_G2_MAP_WEST },
{ "map_west_pressed", SPR_G2_MAP_WEST_PRESSED },
{ "map_south", SPR_G2_MAP_SOUTH },
{ "map_south_pressed", SPR_G2_MAP_SOUTH_PRESSED },
{ "map_east", SPR_G2_MAP_EAST },
{ "map_east_pressed", SPR_G2_MAP_EAST_PRESSED },
{ "multiplayer_toolbar", SPR_G2_TOOLBAR_MULTIPLAYER },
{ "multiplayer_toolbar_pressed", SPR_G2_TOOLBAR_MULTIPLAYER_PRESSED },
{ "multiplayer_sync", SPR_G2_MULTIPLAYER_SYNC },
{ "multiplayer_desync", SPR_G2_MULTIPLAYER_DESYNC },
{ "simulate", SPR_G2_SIMULATE },
{ "rct1_simulate_off", SPR_G2_RCT1_SIMULATE_BUTTON_0 },
{ "rct1_simulate_off_pressed", SPR_G2_RCT1_SIMULATE_BUTTON_1 },
{ "rct1_simulate_on", SPR_G2_RCT1_SIMULATE_BUTTON_2 },
{ "rct1_simulate_on_pressed", SPR_G2_RCT1_SIMULATE_BUTTON_3 },
{ "normal_selection_6x6", SPR_G2_LAND_TOOL_SIZE_6 },
{ "mountain_tool_even", SPR_G2_MOUNTAIN_TOOL_EVEN },
{ "mountain_tool_odd", SPR_G2_MOUNTAIN_TOOL_ODD },
{ "scenery_scatter_low", SPR_G2_SCENERY_SCATTER_LOW },
{ "scenery_scatter_medium", SPR_G2_SCENERY_SCATTER_MEDIUM },
{ "scenery_scatter_high", SPR_G2_SCENERY_SCATTER_HIGH },
{ "view", SPR_G2_VIEW },
{ "path_railings", SPR_G2_PATH_RAILINGS_TAB },
{ "legacy_paths", SPR_G2_LEGACY_PATH_TAB },
{ "path_surfaces", SPR_G2_PATH_SURFACE_TAB },
{ "ride_stations", SPR_G2_RIDE_STATION_TAB },
{ "terrain_edges", SPR_G2_TERRAIN_EDGE_TAB },
{ "hide_vegetation", SPR_G2_BUTTON_HIDE_VEGETATION },
{ "hide_scenery", SPR_G2_BUTTON_HIDE_SCENERY },
{ "hide_vehicles", SPR_G2_BUTTON_HIDE_VEHICLES },
{ "hide_supports", SPR_G2_BUTTON_HIDE_SUPPORTS },
{ "hide_partial", SPR_G2_BUTTON_HIDE_PARTIAL },
{ "hide_full", SPR_G2_BUTTON_HIDE_FULL },
{ "link_chain", SPR_G2_LINK_CHAIN },
{ "sideways_tab", SPR_G2_SIDEWAYS_TAB },
{ "sideways_tab_active", SPR_G2_SIDEWAYS_TAB_ACTIVE },
{ "arrow_up", SPR_G2_ARROW_UP },
{ "arrow_down", SPR_G2_ARROW_DOWN },
{ "reload", SPR_G2_RELOAD },
{ "glassy_recolourable", SPR_G2_SURFACE_GLASSY_RECOLOURABLE },
{ "selection_edge_nw", SPR_G2_SELECTION_EDGE_NW },
{ "selection_edge_ne", SPR_G2_SELECTION_EDGE_NE },
{ "selection_edge_sw", SPR_G2_SELECTION_EDGE_SW },
{ "selection_edge_se", SPR_G2_SELECTION_EDGE_SE },
};
// Maps legacy G2 icons sprite indices to new sprite indices
static std::map<uint32_t, uint32_t> LegacyIconMap{
{ 32248, SPR_G2_EMPTY },
{ 29363, SPR_G2_PLACEHOLDER },
{ 29357, SPR_G2_LOGO },
{ 29358, SPR_G2_TITLE },
{ 29359, SPR_G2_FASTFORWARD },
{ 29360, SPR_G2_SPEED_ARROW },
{ 29361, SPR_G2_HYPER_ARROW },
{ 29362, SPR_G2_TAB_LAND },
{ 29364, SPR_G2_ZOOM_IN },
{ 29365, SPR_G2_ZOOM_IN_DISABLED },
{ 29366, SPR_G2_ZOOM_OUT },
{ 29367, SPR_G2_ZOOM_OUT_DISABLED },
{ 29368, SPR_G2_TAB_TREE },
{ 29369, SPR_G2_TAB_PENCIL },
{ 29370, SPR_G2_BUTTON_LARGE_SCENERY },
{ 29371, SPR_G2_BUTTON_TREES },
{ 29372, SPR_G2_BUTTON_FOOTPATH },
{ 29373, SPR_G2_RCT1_CLOSE_BUTTON_0 },
{ 29374, SPR_G2_RCT1_CLOSE_BUTTON_1 },
{ 29375, SPR_G2_RCT1_CLOSE_BUTTON_2 },
{ 29376, SPR_G2_RCT1_CLOSE_BUTTON_3 },
{ 29377, SPR_G2_RCT1_TEST_BUTTON_0 },
{ 29378, SPR_G2_RCT1_TEST_BUTTON_1 },
{ 29379, SPR_G2_RCT1_TEST_BUTTON_2 },
{ 29380, SPR_G2_RCT1_TEST_BUTTON_3 },
{ 29381, SPR_G2_RCT1_OPEN_BUTTON_0 },
{ 29382, SPR_G2_RCT1_OPEN_BUTTON_1 },
{ 29383, SPR_G2_RCT1_OPEN_BUTTON_2 },
{ 29384, SPR_G2_RCT1_OPEN_BUTTON_3 },
{ 29385, SPR_G2_TITLE_RESTART },
{ 29386, SPR_G2_TITLE_STOP },
{ 29387, SPR_G2_TITLE_PLAY },
{ 29388, SPR_G2_TITLE_SKIP },
{ 29389, SPR_G2_SANDBOX },
{ 29414, SPR_G2_TAB_NEWS },
{ 29415, SPR_G2_LOCKED },
{ 29416, SPR_G2_MENU_MULTIPLAYER },
{ 29433, SPR_G2_SORT },
{ 29434, SPR_G2_COPY },
{ 29435, SPR_G2_PASTE },
{ 29442, SPR_G2_TOOLBAR_MUTE },
{ 29443, SPR_G2_TOOLBAR_MUTE_PRESSED },
{ 29444, SPR_G2_TOOLBAR_UNMUTE },
{ 29445, SPR_G2_TOOLBAR_UNMUTE_PRESSED },
{ 29461, SPR_G2_SEARCH },
{ 29467, SPR_G2_EYEDROPPER },
{ 29468, SPR_G2_CHAT },
{ 29469, SPR_G2_MAP_NORTH },
{ 29470, SPR_G2_MAP_NORTH_PRESSED },
{ 29471, SPR_G2_MAP_WEST },
{ 29472, SPR_G2_MAP_WEST_PRESSED },
{ 29473, SPR_G2_MAP_SOUTH },
{ 29474, SPR_G2_MAP_SOUTH_PRESSED },
{ 29475, SPR_G2_MAP_EAST },
{ 29476, SPR_G2_MAP_EAST_PRESSED },
{ 29477, SPR_G2_TOOLBAR_MULTIPLAYER },
{ 29478, SPR_G2_TOOLBAR_MULTIPLAYER_PRESSED },
{ 29479, SPR_G2_MULTIPLAYER_SYNC },
{ 29480, SPR_G2_MULTIPLAYER_DESYNC },
{ 29481, SPR_G2_SIMULATE },
{ 29482, SPR_G2_RCT1_SIMULATE_BUTTON_0 },
{ 29483, SPR_G2_RCT1_SIMULATE_BUTTON_1 },
{ 29484, SPR_G2_RCT1_SIMULATE_BUTTON_2 },
{ 29485, SPR_G2_RCT1_SIMULATE_BUTTON_3 },
{ 29486, SPR_G2_LAND_TOOL_SIZE_6 },
{ 29487, SPR_G2_MOUNTAIN_TOOL_EVEN },
{ 29488, SPR_G2_MOUNTAIN_TOOL_ODD },
{ 29489, SPR_G2_SCENERY_SCATTER_LOW },
{ 29490, SPR_G2_SCENERY_SCATTER_MEDIUM },
{ 29491, SPR_G2_SCENERY_SCATTER_HIGH },
{ 29494, SPR_G2_VIEW },
{ 29495, SPR_G2_PATH_RAILINGS_TAB },
{ 29496, SPR_G2_LEGACY_PATH_TAB },
{ 29497, SPR_G2_PATH_SURFACE_TAB },
{ 29498, SPR_G2_RIDE_STATION_TAB },
{ 29499, SPR_G2_TERRAIN_EDGE_TAB },
{ 29500, SPR_G2_BUTTON_HIDE_VEGETATION },
{ 29501, SPR_G2_BUTTON_HIDE_SCENERY },
{ 29502, SPR_G2_BUTTON_HIDE_VEHICLES },
{ 29503, SPR_G2_BUTTON_HIDE_SUPPORTS },
{ 29504, SPR_G2_BUTTON_HIDE_PARTIAL },
{ 29505, SPR_G2_BUTTON_HIDE_FULL },
{ 29506, SPR_G2_LINK_CHAIN },
{ 29507, SPR_G2_SIDEWAYS_TAB },
{ 29508, SPR_G2_SIDEWAYS_TAB_ACTIVE },
{ 29509, SPR_G2_ARROW_UP },
{ 29510, SPR_G2_ARROW_DOWN },
{ 29511, SPR_G2_RELOAD },
};
inline uint32_t GetIconByName(const std::string& input)
{
auto result = G2SpriteLookupTable.find(input);
if (result != G2SpriteLookupTable.end())
return result->second;
return SPR_G2_EMPTY;
}
inline uint32_t NewIconIndex(const uint32_t& input)
{
auto result = LegacyIconMap.find(input);
if (result != LegacyIconMap.end())
return result->second;
return input;
}
inline uint32_t LegacyIconIndex(const uint32_t& input)
{
uint32_t result = input;
for (auto& i : LegacyIconMap)
{
if (i.second == input)
{
result = i.first;
break;
}
}
return result;
}
} // namespace OpenRCT2::Scripting

View File

@ -46,10 +46,11 @@ namespace OpenRCT2
namespace OpenRCT2::Scripting
{
static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 64;
static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 65;
// Versions marking breaking changes.
static constexpr int32_t API_VERSION_33_PEEP_DEPRECATION = 33;
static constexpr int32_t API_VERSION_63_G2_REORDER = 63;
# ifndef DISABLE_NETWORK
class ScSocketBase;

View File

@ -19,6 +19,7 @@
# include "../../../scenario/Scenario.h"
# include "../../Duktape.hpp"
# include "../../HookEngine.h"
# include "../../IconNames.hpp"
# include "../../ScriptEngine.h"
# include "../game/ScConfiguration.hpp"
# include "../game/ScDisposable.hpp"
@ -460,6 +461,11 @@ namespace OpenRCT2::Scripting
ClearIntervalOrTimeout(handle);
}
int32_t getIcon(const std::string& iconName)
{
return GetIconByName(iconName);
}
public:
static void Register(duk_context* ctx)
{
@ -482,8 +488,27 @@ namespace OpenRCT2::Scripting
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");
}
};
uint32_t ImageFromDuk(const DukValue& d)
{
uint32_t img{};
if (d.type() == DukValue::Type::NUMBER)
{
img = d.as_uint();
if (GetTargetAPIVersion() <= API_VERSION_63_G2_REORDER)
{
img = NewIconIndex(d.as_uint());
}
}
else if (d.type() == DukValue::Type::STRING)
{
img = GetIconByName(d.as_c_string());
}
return img;
}
} // namespace OpenRCT2::Scripting
#endif

View File

@ -881,6 +881,7 @@ enum
SPR_G2_PLACEHOLDER,
// G2 UI Elements
// When adding UI elements to G2, insert them before fonts and give them a name in src/openrct2/scripting/IconNames.hpp
SPR_G2_LOGO,
SPR_G2_TITLE,