diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 5dc75f276b..86711e844e 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -990,7 +990,19 @@ declare global { } type EntityType = - "car" | "duck" | "peep" | "steam_particle" | "money_effect" | "crashed_vehicle_particle" | "explosion_cloud" | "crash_splash" | "explosion_flare" | "jumping_fountain_water" | "balloon" | "jumping_fountain_snow"; + "balloon" | + "car" | + "crash_splash" | + "crashed_vehicle_particle" | + "duck" | + "explosion_cloud" | + "explosion_flare" | + "jumping_fountain_snow" | + "jumping_fountain_water" | + "litter" | + "money_effect" | + "peep" | + "steam_particle"; /** * Represents an object "entity" on the map that can typically moves and has a sub-tile coordinate. @@ -1733,7 +1745,7 @@ declare global { readonly windows: number; readonly mainViewport: Viewport; readonly tileSelection: TileSelection; - readonly tool: Tool; + readonly tool: Tool | null; getWindow(id: number): Window; getWindow(classification: string): Window; @@ -1754,6 +1766,19 @@ declare global { */ showTextInput(desc: TextInputDesc): void; + /** + * Shows the window for loading or saving a file and calls the given callback when a file + * is selected. + * @param desc The parameters for the file browse window. + */ + showFileBrowse(desc: FileBrowseDesc): void; + + /** + * Shows the scenario select window and calls the given callback when a scenario is + * selected. + */ + showScenarioSelect(desc: ScenarioSelectDesc): 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. @@ -1794,6 +1819,59 @@ declare global { callback: (value: string) => void; } + /** + * Parameters for the file browse window. + */ + interface FileBrowseDesc { + /** + * Whether to browse a file for loading or saving. Saving will prompt the user + * before overwriting a file. + */ + type: 'load'; + + /** + * The type of file to browse for. + */ + fileType: 'game' | 'heightmap'; + + /** + * The pre-selected file to load by default if the user clicks OK. + */ + defaultPath?: string; + + /** + * The function to call when the user has selected a file. + */ + callback: (path: string) => void; + } + + /** + * Parameters for the scenario select window. + */ + interface ScenarioSelectDesc { + /** + * The function to call when the user has selected a scenario. + */ + callback: (scenario: ScenarioFile) => void; + } + + /** + * Represents an installed scenario's path and metadata. + */ + interface ScenarioFile { + id: number; + category: 'beginner' | 'challenging' | 'expert' | 'real' | 'other' | 'dlc' | 'build_your_own'; + sourceGame: 'rct1' | 'rct1_aa' | 'rct1_ll' | 'rct2' | 'rct2_ww' | 'rct2_tt' | 'real' | 'other'; + path: string; + internalName: string; + name: string; + details: string; + highscore: { + name: string; + companyValue: number; + } + } + interface TileSelection { range: MapRange; tiles: CoordsXY[]; @@ -1821,11 +1899,11 @@ declare global { id: string; cursor?: CursorType; - onStart: () => void; - onDown: (e: ToolEventArgs) => void; - onMove: (e: ToolEventArgs) => void; - onUp: (e: ToolEventArgs) => void; - onFinish: () => void; + onStart?: () => void; + onDown?: (e: ToolEventArgs) => void; + onMove?: (e: ToolEventArgs) => void; + onUp?: (e: ToolEventArgs) => void; + onFinish?: () => void; } type CursorType = @@ -1861,10 +1939,14 @@ declare global { * Represents the type of a widget, e.g. button or label. */ type WidgetType = - "button" | "checkbox" | "colourpicker" | "dropdown" | "groupbox" | "label" | "listview" | "spinner" | "textbox" | "viewport"; + "button" | "checkbox" | "colourpicker" | "dropdown" | "groupbox" | + "label" | "listview" | "spinner" | "textbox" | "viewport"; - interface Widget { - type: WidgetType; + type Widget = + ButtonWidget | CheckboxWidget | ColourPickerWidget | DropdownWidget | GroupBoxWidget | + LabelWidget | ListView | SpinnerWidget | TextBoxWidget | ViewportWidget; + + interface WidgetBase { x: number; y: number; width: number; @@ -1875,7 +1957,8 @@ declare global { isVisible?: boolean; } - interface ButtonWidget extends Widget { + interface ButtonWidget extends WidgetBase { + type: 'button'; /** * Whether the button has a 3D border. * By default, text buttons have borders and image buttons do not but it can be overridden. @@ -1884,29 +1967,37 @@ declare global { image?: number; isPressed?: boolean; text?: string; - onClick: () => void; + onClick?: () => void; } - interface CheckboxWidget extends Widget { - text: string; - isChecked: boolean; - onChange: (isChecked: boolean) => void; + interface CheckboxWidget extends WidgetBase { + type: 'checkbox'; + text?: string; + isChecked?: boolean; + onChange?: (isChecked: boolean) => void; } - interface ColourPickerWidget extends Widget { - colour: number; - onChange: (colour: number) => void; + interface ColourPickerWidget extends WidgetBase { + type: 'colourpicker'; + colour?: number; + onChange?: (colour: number) => void; } - interface DropdownWidget extends Widget { - items: string[]; - selectedIndex: number; - onChange: (index: number) => void; + interface DropdownWidget extends WidgetBase { + type: 'dropdown'; + items?: string[]; + selectedIndex?: number; + onChange?: (index: number) => void; } - interface LabelWidget extends Widget { - text: string; - onChange: (index: number) => void; + interface GroupBoxWidget extends WidgetBase { + type: 'groupbox'; + } + + interface LabelWidget extends WidgetBase { + type: 'label'; + text?: string; + onChange?: (index: number) => void; } type SortOrder = "none" | "ascending" | "descending"; @@ -1936,7 +2027,8 @@ declare global { column: number; } - interface ListView extends Widget { + interface ListView extends WidgetBase { + type: 'listview'; scrollbars?: ScrollbarType; isStriped?: boolean; showColumnHeaders?: boolean; @@ -1950,20 +2042,23 @@ declare global { onClick?: (item: number, column: number) => void; } - interface SpinnerWidget extends Widget { - text: string; - onDecrement: () => void; - onIncrement: () => void; + interface SpinnerWidget extends WidgetBase { + type: 'spinner'; + text?: string; + onDecrement?: () => void; + onIncrement?: () => void; } - interface TextBoxWidget extends Widget { - text: string; - maxLength: number; - onChange: (text: string) => void; + interface TextBoxWidget extends WidgetBase { + type: 'textbox'; + text?: string; + maxLength?: number; + onChange?: (text: string) => void; } - interface ViewportWidget extends Widget { - viewport: Viewport + interface ViewportWidget extends WidgetBase { + type: 'viewport'; + viewport?: Viewport } interface Window { diff --git a/src/openrct2-ui/scripting/ScWidget.hpp b/src/openrct2-ui/scripting/ScWidget.hpp index 372531b814..74c1c0cc26 100644 --- a/src/openrct2-ui/scripting/ScWidget.hpp +++ b/src/openrct2-ui/scripting/ScWidget.hpp @@ -350,6 +350,7 @@ namespace OpenRCT2::Scripting } } + protected: std::string text_get() const { if (IsCustomWindow()) @@ -362,6 +363,7 @@ namespace OpenRCT2::Scripting } return ""; } + void text_set(std::string value) { auto w = GetWindow(); @@ -371,20 +373,6 @@ namespace OpenRCT2::Scripting } } - std::shared_ptr viewport_get() const - { - auto w = GetWindow(); - if (w != nullptr && IsCustomWindow()) - { - auto widget = GetWidget(); - if (widget != nullptr && widget->type == WindowWidgetType::Viewport) - { - return std::make_shared(w->classification, w->number); - } - } - return {}; - } - public: static void Register(duk_context* ctx) { @@ -397,10 +385,6 @@ namespace OpenRCT2::Scripting dukglue_register_property(ctx, &ScWidget::height_get, &ScWidget::height_set, "height"); dukglue_register_property(ctx, &ScWidget::isDisabled_get, &ScWidget::isDisabled_set, "isDisabled"); dukglue_register_property(ctx, &ScWidget::isVisible_get, &ScWidget::isVisible_set, "isVisible"); - - // No so common - dukglue_register_property(ctx, &ScWidget::text_get, &ScWidget::text_set, "text"); - dukglue_register_property(ctx, &ScWidget::viewport_get, nullptr, "viewport"); } protected: @@ -641,6 +625,36 @@ namespace OpenRCT2::Scripting } }; + class ScGroupBoxWidget : public ScWidget + { + public: + ScGroupBoxWidget(rct_windowclass c, rct_windownumber n, rct_widgetindex widgetIndex) + : ScWidget(c, n, widgetIndex) + { + } + + static void Register(duk_context* ctx) + { + dukglue_set_base_class(ctx); + dukglue_register_property(ctx, &ScGroupBoxWidget::text_get, &ScGroupBoxWidget::text_set, "text"); + } + }; + + class ScLabelWidget : public ScWidget + { + public: + ScLabelWidget(rct_windowclass c, rct_windownumber n, rct_widgetindex widgetIndex) + : ScWidget(c, n, widgetIndex) + { + } + + static void Register(duk_context* ctx) + { + dukglue_set_base_class(ctx); + dukglue_register_property(ctx, &ScLabelWidget::text_get, &ScLabelWidget::text_set, "text"); + } + }; + class ScListViewWidget : public ScWidget { public: @@ -832,6 +846,21 @@ namespace OpenRCT2::Scripting } }; + class ScSpinnerWidget : public ScWidget + { + public: + ScSpinnerWidget(rct_windowclass c, rct_windownumber n, rct_widgetindex widgetIndex) + : ScWidget(c, n, widgetIndex) + { + } + + static void Register(duk_context* ctx) + { + dukglue_set_base_class(ctx); + dukglue_register_property(ctx, &ScSpinnerWidget::text_get, &ScSpinnerWidget::text_set, "text"); + } + }; + class ScTextBoxWidget : public ScWidget { public: @@ -867,6 +896,36 @@ namespace OpenRCT2::Scripting } }; + class ScViewportWidget : public ScWidget + { + public: + ScViewportWidget(rct_windowclass c, rct_windownumber n, rct_widgetindex widgetIndex) + : ScWidget(c, n, widgetIndex) + { + } + + static void Register(duk_context* ctx) + { + dukglue_set_base_class(ctx); + dukglue_register_property(ctx, &ScViewportWidget::viewport_get, nullptr, "viewport"); + } + + private: + std::shared_ptr viewport_get() const + { + auto w = GetWindow(); + if (w != nullptr && IsCustomWindow()) + { + auto widget = GetWidget(); + if (widget != nullptr && widget->type == WindowWidgetType::Viewport) + { + return std::make_shared(w->classification, w->number); + } + } + return {}; + } + }; + inline DukValue ScWidget::ToDukValue(duk_context* ctx, rct_window* w, rct_widgetindex widgetIndex) { const auto& widget = w->widgets[widgetIndex]; @@ -884,10 +943,18 @@ namespace OpenRCT2::Scripting return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); case WindowWidgetType::DropdownMenu: return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); + case WindowWidgetType::Groupbox: + return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); + case WindowWidgetType::Label: + return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); case WindowWidgetType::Scroll: return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); + case WindowWidgetType::Spinner: + return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); case WindowWidgetType::TextBox: return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); + case WindowWidgetType::Viewport: + return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); default: return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); } diff --git a/src/openrct2-ui/scripting/UiExtensions.cpp b/src/openrct2-ui/scripting/UiExtensions.cpp index 21947810b5..0361b58f1d 100644 --- a/src/openrct2-ui/scripting/UiExtensions.cpp +++ b/src/openrct2-ui/scripting/UiExtensions.cpp @@ -31,13 +31,18 @@ void UiScriptExtensions::Extend(ScriptEngine& scriptEngine) ScTool::Register(ctx); ScUi::Register(ctx); ScViewport::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); ScWindow::Register(ctx); InitialiseCustomMenuItems(scriptEngine); diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp index 2598b54e78..a0a30963f1 100644 --- a/src/openrct2/scripting/ScriptEngine.cpp +++ b/src/openrct2/scripting/ScriptEngine.cpp @@ -718,8 +718,15 @@ DukValue ScriptEngine::ExecutePluginCall( void ScriptEngine::LogPluginInfo(const std::shared_ptr& plugin, std::string_view message) { - const auto& pluginName = plugin->GetMetadata().Name; - _console.WriteLine("[" + pluginName + "] " + std::string(message)); + if (plugin == nullptr) + { + _console.WriteLine(std::string(message)); + } + else + { + const auto& pluginName = plugin->GetMetadata().Name; + _console.WriteLine("[" + pluginName + "] " + std::string(message)); + } } void ScriptEngine::AddNetworkPlugin(std::string_view code)