OpenRCT2/src/openrct2-ui/windows/TitleCommandEditor.cpp

841 lines
33 KiB
C++

/*****************************************************************************
* Copyright (c) 2014-2019 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.
*****************************************************************************/
#include "../interface/Theme.h"
#include <iterator>
#include <openrct2-ui/interface/Dropdown.h>
#include <openrct2-ui/interface/Viewport.h>
#include <openrct2-ui/interface/Widget.h>
#include <openrct2-ui/windows/Window.h>
#include <openrct2/Game.h>
#include <openrct2/Input.h>
#include <openrct2/OpenRCT2.h>
#include <openrct2/core/Memory.hpp>
#include <openrct2/drawing/Drawing.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/scenario/ScenarioRepository.h>
#include <openrct2/scenario/ScenarioSources.h>
#include <openrct2/sprites.h>
#include <openrct2/title/TitleSequence.h>
#include <openrct2/util/Util.h>
#include <openrct2/world/Sprite.h>
// clang-format off
struct TITLE_COMMAND_ORDER {
// originally a uint8_t, but the new millisecond wait times require a uint16_t.
uint16_t command;
rct_string_id nameStringId;
rct_string_id descStringId;
};
static TITLE_COMMAND_ORDER _window_title_command_editor_orders[] = {
{ TITLE_SCRIPT_LOAD, STR_TITLE_EDITOR_ACTION_LOAD_SAVE, STR_TITLE_EDITOR_ARGUMENT_SAVEFILE },
{ TITLE_SCRIPT_LOADSC, STR_TITLE_EDITOR_ACTION_LOAD_SCENARIO, STR_TITLE_EDITOR_ARGUMENT_SCENARIO },
{ TITLE_SCRIPT_LOCATION, STR_TITLE_EDITOR_COMMAND_TYPE_LOCATION, STR_TITLE_EDITOR_ARGUMENT_COORDINATES },
{ TITLE_SCRIPT_ROTATE, STR_TITLE_EDITOR_COMMAND_TYPE_ROTATE, STR_TITLE_EDITOR_ARGUMENT_ROTATIONS },
{ TITLE_SCRIPT_ZOOM, STR_TITLE_EDITOR_COMMAND_TYPE_ZOOM, STR_TITLE_EDITOR_ARGUMENT_ZOOM_LEVEL },
{ TITLE_SCRIPT_SPEED, STR_TITLE_EDITOR_COMMAND_TYPE_SPEED, STR_TITLE_EDITOR_ARGUMENT_SPEED },
{ TITLE_SCRIPT_FOLLOW, STR_TITLE_EDITOR_COMMAND_TYPE_FOLLOW, STR_NONE },
{ TITLE_SCRIPT_WAIT, STR_TITLE_EDITOR_COMMAND_TYPE_WAIT, STR_TITLE_EDITOR_ARGUMENT_WAIT_SECONDS },
{ TITLE_SCRIPT_RESTART, STR_TITLE_EDITOR_RESTART, STR_NONE },
{ TITLE_SCRIPT_END, STR_TITLE_EDITOR_END, STR_NONE },
};
#define NUM_COMMANDS std::size(_window_title_command_editor_orders)
enum WINDOW_WATER_WIDGET_IDX {
WIDX_BACKGROUND,
WIDX_TITLE,
WIDX_CLOSE,
WIDX_COMMAND,
WIDX_COMMAND_DROPDOWN,
WIDX_TEXTBOX_FULL,
WIDX_TEXTBOX_X,
WIDX_TEXTBOX_Y,
WIDX_INPUT,
WIDX_INPUT_DROPDOWN,
WIDX_GET,
WIDX_SELECT_SCENARIO,
WIDX_SELECT_SPRITE,
WIDX_VIEWPORT,
WIDX_OKAY,
WIDX_CANCEL
};
constexpr int32_t WW = 200;
constexpr int32_t WH = 120;
constexpr int32_t BY = 32;
constexpr int32_t BY2 = 70;
constexpr int32_t WS = 16;
constexpr int32_t WHA = (WW-WS*2)/2;
static bool _window_title_command_editor_insert;
static int32_t _window_title_command_editor_index;
constexpr size_t BUF_SIZE = 50;
static char textbox1Buffer[BUF_SIZE];
static char textbox2Buffer[BUF_SIZE];
static TitleCommand command = { TITLE_SCRIPT_LOAD, { 0 } };
static TitleSequence * _sequence = nullptr;
static rct_widget window_title_command_editor_widgets[] = {
{ WWT_FRAME, 1, 0, WW-1, 0, WH-1, 0xFFFFFFFF, STR_NONE }, // panel / background
{ WWT_CAPTION, 1, 1, WW-2, 1, 14, STR_TITLE_COMMAND_EDITOR_TITLE, STR_WINDOW_TITLE_TIP }, // title bar
{ WWT_CLOSEBOX, 1, WW-13, WW-3, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, // close x button
{ WWT_DROPDOWN, 1, WS, WW-WS-1, BY, BY+11, STR_NONE, STR_NONE }, // Command dropdown
{ WWT_BUTTON, 1, WW-WS-12, WW-WS-2, BY+1, BY+10, STR_DROPDOWN_GLYPH, STR_NONE },
{ WWT_TEXT_BOX, 1, WS, WW-WS-1, BY2, BY2+11, STR_NONE, STR_NONE }, // full textbox
{ WWT_TEXT_BOX, 1, WS, WS+WHA-4, BY2, BY2+11, STR_NONE, STR_NONE }, // x textbox
{ WWT_TEXT_BOX, 1, WS+WHA+3, WW-WS-1, BY2, BY2+11, STR_NONE, STR_NONE }, // y textbox
{ WWT_DROPDOWN, 1, 16, WW-17, BY2, BY2+11, STR_NONE, STR_NONE }, // Save dropdown
{ WWT_BUTTON, 1, WW-28, WW-18, BY2+1, BY2+10, STR_DROPDOWN_GLYPH, STR_NONE },
{ WWT_BUTTON, 1, WS+WHA+3, WW-WS-1, BY2-14, BY2-3, STR_TITLE_COMMAND_EDITOR_ACTION_GET_LOCATION, STR_NONE }, // Get location/zoom/etc
{ WWT_BUTTON, 1, WS+WHA+12, WW-WS-1, BY2-14, BY2-3, STR_TITLE_COMMAND_EDITOR_ACTION_SELECT_SCENARIO, STR_NONE }, // Select scenario
{ WWT_BUTTON, 1, WS, WW-WS-1, BY2-14, BY2-3, STR_TITLE_COMMAND_EDITOR_SELECT_SPRITE, STR_NONE }, // Select sprite
{ WWT_VIEWPORT, 1, WS, WW-WS-1, BY2, BY2+22, STR_NONE, STR_NONE }, // Viewport
{ WWT_BUTTON, 1, 10, 80, WH-21, WH-10, STR_OK, STR_NONE }, // OKAY
{ WWT_BUTTON, 1, WW-80, WW-10, WH-21, WH-10, STR_CANCEL, STR_NONE }, // Cancel
{ WIDGETS_END },
};
static void window_title_command_editor_close(rct_window * w);
static void window_title_command_editor_mouseup(rct_window * w, rct_widgetindex widgetIndex);
static void window_title_command_editor_mousedown(rct_window * w, rct_widgetindex widgetIndex, rct_widget * widget);
static void window_title_command_editor_dropdown(rct_window * w, rct_widgetindex widgetIndex, int32_t dropdownIndex);
static void window_title_command_editor_update(rct_window * w);
static void window_title_command_editor_tool_down(rct_window * w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
static void window_title_command_editor_invalidate(rct_window * w);
static void window_title_command_editor_paint(rct_window * w, rct_drawpixelinfo * dpi);
static void window_title_command_editor_textinput(rct_window * w, rct_widgetindex widgetIndex, char * text);
static void scenario_select_callback(const utf8 * path);
static int32_t get_command_info_index(int32_t index);
static TITLE_COMMAND_ORDER get_command_info(int32_t index);
static TileCoordsXY get_location();
static uint8_t get_zoom();
static rct_window_event_list window_title_command_editor_events = {
window_title_command_editor_close,
window_title_command_editor_mouseup,
nullptr,
window_title_command_editor_mousedown,
window_title_command_editor_dropdown,
nullptr,
window_title_command_editor_update,
nullptr,
nullptr,
nullptr,
window_title_command_editor_tool_down,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
window_title_command_editor_textinput,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
window_title_command_editor_invalidate,
window_title_command_editor_paint,
nullptr
};
// clang-format on
static void scenario_select_callback(const utf8* path)
{
if (command.Type == TITLE_SCRIPT_LOADSC)
{
const utf8* fileName = path_get_filename(path);
auto scenario = GetScenarioRepository()->GetByFilename(fileName);
safe_strcpy(command.Scenario, scenario->internal_name, sizeof(command.Scenario));
}
}
static int32_t get_command_info_index(int32_t index)
{
for (int32_t i = 0; i < static_cast<int32_t>(NUM_COMMANDS); i++)
{
if (_window_title_command_editor_orders[i].command == index)
return i;
}
return 0;
}
static TITLE_COMMAND_ORDER get_command_info(int32_t index)
{
for (int32_t i = 0; i < static_cast<int32_t>(NUM_COMMANDS); i++)
{
if (_window_title_command_editor_orders[i].command == index)
return _window_title_command_editor_orders[i];
}
return _window_title_command_editor_orders[0];
}
static TileCoordsXY get_location()
{
TileCoordsXY tileCoord = {};
rct_window* w = window_get_main();
if (w != nullptr)
{
int32_t interactionType;
TileElement* tileElement;
CoordsXY mapCoord;
get_map_coordinates_from_pos_window(
w, { w->viewport->view_width / 2, w->viewport->view_height / 2 }, VIEWPORT_INTERACTION_MASK_TERRAIN, mapCoord,
&interactionType, &tileElement, nullptr);
mapCoord.x -= 16;
mapCoord.y -= 16;
tileCoord = TileCoordsXY{ mapCoord };
tileCoord.x++;
tileCoord.y++;
}
return tileCoord;
}
static uint8_t get_zoom()
{
uint8_t zoom = 0;
rct_window* w = window_get_main();
if (w != nullptr)
{
zoom = static_cast<int8_t>(w->viewport->zoom);
}
return zoom;
}
static bool sprite_selector_tool_is_active()
{
if (!(input_test_flag(INPUT_FLAG_TOOL_ACTIVE)))
return false;
if (gCurrentToolWidget.window_classification != WC_TITLE_COMMAND_EDITOR)
return false;
return true;
}
void window_title_command_editor_open(TitleSequence* sequence, int32_t index, bool insert)
{
_sequence = sequence;
// Check if window is already open
if (window_find_by_class(WC_TITLE_COMMAND_EDITOR) != nullptr)
return;
rct_window* window = window_create_centred(
WW, WH, &window_title_command_editor_events, WC_TITLE_COMMAND_EDITOR, WF_STICK_TO_FRONT);
window_title_command_editor_widgets[WIDX_TEXTBOX_FULL].string = textbox1Buffer;
window_title_command_editor_widgets[WIDX_TEXTBOX_X].string = textbox1Buffer;
window_title_command_editor_widgets[WIDX_TEXTBOX_Y].string = textbox2Buffer;
window->widgets = window_title_command_editor_widgets;
window->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_COMMAND) | (1 << WIDX_COMMAND_DROPDOWN) | (1 << WIDX_TEXTBOX_FULL)
| (1 << WIDX_TEXTBOX_X) | (1 << WIDX_TEXTBOX_Y) | (1 << WIDX_INPUT) | (1 << WIDX_INPUT_DROPDOWN) | (1 << WIDX_GET)
| (1 << WIDX_SELECT_SCENARIO) | (1 << WIDX_SELECT_SPRITE) | (1 << WIDX_OKAY) | (1 << WIDX_CANCEL);
window_init_scroll_widgets(window);
rct_widget* const viewportWidget = &window_title_command_editor_widgets[WIDX_VIEWPORT];
viewport_create(
window, window->windowPos + ScreenCoordsXY{ viewportWidget->left + 1, viewportWidget->top + 1 },
viewportWidget->right - viewportWidget->left - 1, viewportWidget->bottom - viewportWidget->top - 1, 0, { 0, 0, 0 }, 0,
SPRITE_INDEX_NULL);
_window_title_command_editor_index = index;
_window_title_command_editor_insert = insert;
if (!insert)
{
command = _sequence->Commands[index];
}
switch (command.Type)
{
case TITLE_SCRIPT_LOAD:
if (command.SaveIndex >= _sequence->NumSaves)
command.SaveIndex = SAVE_INDEX_INVALID;
break;
case TITLE_SCRIPT_LOCATION:
snprintf(textbox1Buffer, BUF_SIZE, "%d", command.X);
snprintf(textbox2Buffer, BUF_SIZE, "%d", command.Y);
break;
case TITLE_SCRIPT_ROTATE:
case TITLE_SCRIPT_ZOOM:
snprintf(textbox1Buffer, BUF_SIZE, "%d", command.Rotations);
break;
case TITLE_SCRIPT_WAIT:
snprintf(textbox1Buffer, BUF_SIZE, "%d", command.Milliseconds);
break;
case TITLE_SCRIPT_FOLLOW:
if (command.SpriteIndex != SPRITE_INDEX_NULL)
{
window_follow_sprite(window, static_cast<size_t>(command.SpriteIndex));
}
break;
}
}
static void window_title_command_editor_close(rct_window* w)
{
if (sprite_selector_tool_is_active())
{
tool_cancel();
}
}
static void window_title_command_editor_mouseup(rct_window* w, rct_widgetindex widgetIndex)
{
switch (widgetIndex)
{
case WIDX_CLOSE:
case WIDX_CANCEL:
window_close(w);
break;
case WIDX_TEXTBOX_FULL:
// The only commands that use TEXTBOX_FULL currently are Wait, Rotate, and Zoom. Rotate and Zoom have single-digit
// maximum values, while Wait has 5-digit maximum values.
if (command.Type == TITLE_SCRIPT_WAIT)
{
window_start_textbox(w, widgetIndex, STR_STRING, textbox1Buffer, 6);
}
else
{
window_start_textbox(w, widgetIndex, STR_STRING, textbox1Buffer, 2);
}
break;
case WIDX_TEXTBOX_X:
window_start_textbox(w, widgetIndex, STR_STRING, textbox1Buffer, 4);
break;
case WIDX_TEXTBOX_Y:
window_start_textbox(w, widgetIndex, STR_STRING, textbox2Buffer, 4);
break;
case WIDX_GET:
if (command.Type == TITLE_SCRIPT_LOCATION)
{
auto tileCoord = get_location();
command.X = static_cast<uint8_t>(tileCoord.x);
command.Y = static_cast<uint8_t>(tileCoord.y);
snprintf(textbox1Buffer, BUF_SIZE, "%d", command.X);
snprintf(textbox2Buffer, BUF_SIZE, "%d", command.Y);
}
else if (command.Type == TITLE_SCRIPT_ZOOM)
{
uint8_t zoom = get_zoom();
command.Zoom = zoom;
snprintf(textbox1Buffer, BUF_SIZE, "%d", command.Zoom);
}
w->Invalidate();
break;
case WIDX_SELECT_SCENARIO:
window_scenarioselect_open(scenario_select_callback, true);
break;
case WIDX_SELECT_SPRITE:
if (!sprite_selector_tool_is_active())
{
tool_set(w, WIDX_BACKGROUND, TOOL_CROSSHAIR);
}
else
{
tool_cancel();
}
break;
case WIDX_OKAY:
if (_window_title_command_editor_insert)
{
size_t insertIndex = _window_title_command_editor_index;
_sequence->NumCommands++;
_sequence->Commands = Memory::ReallocateArray(_sequence->Commands, _sequence->NumCommands);
for (size_t i = _sequence->NumCommands - 1; i > insertIndex; i--)
{
_sequence->Commands[i] = _sequence->Commands[i - 1];
}
_sequence->Commands[insertIndex] = command;
}
else
{
_sequence->Commands[_window_title_command_editor_index] = command;
TitleSequenceSave(_sequence);
}
TitleSequenceSave(_sequence);
rct_window* title_editor_w = window_find_by_class(WC_TITLE_EDITOR);
if (title_editor_w != nullptr)
{
title_editor_w->selected_list_item = _window_title_command_editor_index;
}
window_close(w);
break;
}
}
static void window_title_command_editor_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
{
widget--;
switch (widgetIndex)
{
case WIDX_COMMAND_DROPDOWN:
{
size_t numItems = NUM_COMMANDS;
for (size_t i = 0; i < numItems; i++)
{
gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[i] = _window_title_command_editor_orders[i].nameStringId;
}
window_dropdown_show_text_custom_width(
{ w->windowPos.x + widget->left, w->windowPos.y + widget->top }, widget->bottom - widget->top + 1,
w->colours[1], 0, DROPDOWN_FLAG_STAY_OPEN, numItems, widget->right - widget->left - 3);
dropdown_set_checked(get_command_info_index(command.Type), true);
break;
}
case WIDX_INPUT_DROPDOWN:
if (command.Type == TITLE_SCRIPT_SPEED)
{
int32_t numItems = 4;
for (int32_t i = 0; i < numItems; i++)
{
gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[i] = SpeedNames[i];
}
window_dropdown_show_text_custom_width(
{ w->windowPos.x + widget->left, w->windowPos.y + widget->top }, widget->bottom - widget->top + 1,
w->colours[1], 0, DROPDOWN_FLAG_STAY_OPEN, numItems, widget->right - widget->left - 3);
dropdown_set_checked(command.Speed - 1, true);
}
else if (command.Type == TITLE_SCRIPT_LOAD)
{
int32_t numItems = static_cast<int32_t>(_sequence->NumSaves);
for (int32_t i = 0; i < numItems; i++)
{
gDropdownItemsFormat[i] = STR_OPTIONS_DROPDOWN_ITEM;
gDropdownItemsArgs[i] = reinterpret_cast<uintptr_t>(_sequence->Saves[i]);
}
window_dropdown_show_text_custom_width(
{ w->windowPos.x + widget->left, w->windowPos.y + widget->top }, widget->bottom - widget->top + 1,
w->colours[1], 0, DROPDOWN_FLAG_STAY_OPEN, numItems, widget->right - widget->left - 3);
dropdown_set_checked(command.SaveIndex, true);
}
break;
}
}
static void window_title_command_editor_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
{
if (dropdownIndex == -1)
return;
// Cancel sprite selector tool if it's active
if (sprite_selector_tool_is_active())
{
tool_cancel();
}
switch (widgetIndex)
{
case WIDX_COMMAND_DROPDOWN:
if (command.SpriteIndex != SPRITE_INDEX_NULL)
{
window_unfollow_sprite(w);
}
if (dropdownIndex == get_command_info_index(command.Type))
{
break;
}
command.Type = _window_title_command_editor_orders[dropdownIndex].command;
switch (command.Type)
{
case TITLE_SCRIPT_LOCATION:
{
auto tileCoord = get_location();
command.X = static_cast<uint8_t>(tileCoord.x);
command.Y = static_cast<uint8_t>(tileCoord.y);
snprintf(textbox1Buffer, BUF_SIZE, "%d", command.X);
snprintf(textbox2Buffer, BUF_SIZE, "%d", command.Y);
break;
}
case TITLE_SCRIPT_ROTATE:
command.Rotations = 1;
snprintf(textbox1Buffer, BUF_SIZE, "%d", command.Rotations);
break;
case TITLE_SCRIPT_ZOOM:
command.Zoom = 0;
snprintf(textbox1Buffer, BUF_SIZE, "%d", command.Zoom);
break;
case TITLE_SCRIPT_FOLLOW:
command.SpriteIndex = SPRITE_INDEX_NULL;
command.SpriteName[0] = '\0';
window_unfollow_sprite(w);
w->viewport->flags &= ~VIEWPORT_FOCUS_TYPE_SPRITE;
break;
case TITLE_SCRIPT_SPEED:
command.Speed = 1;
break;
case TITLE_SCRIPT_WAIT:
command.Milliseconds = 10000;
snprintf(textbox1Buffer, BUF_SIZE, "%d", command.Milliseconds);
break;
case TITLE_SCRIPT_LOAD:
command.SaveIndex = 0;
if (command.SaveIndex >= _sequence->NumSaves)
{
command.SaveIndex = 0xFF;
}
break;
case TITLE_SCRIPT_LOADSC:
command.Scenario[0] = '\0';
}
w->Invalidate();
break;
case WIDX_INPUT_DROPDOWN:
switch (command.Type)
{
case TITLE_SCRIPT_SPEED:
if (dropdownIndex != command.Speed - 1)
{
command.Speed = static_cast<uint8_t>(dropdownIndex + 1);
w->Invalidate();
}
break;
case TITLE_SCRIPT_LOAD:
if (dropdownIndex != command.SaveIndex)
{
command.SaveIndex = static_cast<uint8_t>(dropdownIndex);
w->Invalidate();
}
break;
}
break;
}
}
static void window_title_command_editor_textinput(rct_window* w, rct_widgetindex widgetIndex, char* text)
{
char* end;
int32_t value = strtol(widgetIndex != WIDX_TEXTBOX_Y ? textbox1Buffer : textbox2Buffer, &end, 10);
if (value < 0)
value = 0;
// The Wait command is the only one with acceptable values greater than 255.
if (value > 255 && command.Type != TITLE_SCRIPT_WAIT)
value = 255;
switch (widgetIndex)
{
case WIDX_TEXTBOX_FULL:
if (text == nullptr)
{
if (*end == '\0')
{
if (command.Type == TITLE_SCRIPT_WAIT)
{
if (value < 100)
value = 100;
if (value > 65000)
value = 65000;
command.Milliseconds = static_cast<uint16_t>(value);
snprintf(textbox1Buffer, BUF_SIZE, "%d", command.Milliseconds);
}
else
{
// Both Rotate and Zoom have a maximum value of 3, but Rotate has a min value of 1 not 0.
if (value > 3)
value = 3;
if (value < 1 && command.Type == TITLE_SCRIPT_ROTATE)
value = 1;
command.Rotations = static_cast<uint8_t>(value);
snprintf(textbox1Buffer, BUF_SIZE, "%d", command.Rotations);
}
}
w->Invalidate();
}
else
{
safe_strcpy(textbox1Buffer, text, sizeof(textbox1Buffer));
}
break;
case WIDX_TEXTBOX_X:
if (text == nullptr)
{
if (*end == '\0')
{
command.X = static_cast<uint8_t>(value);
}
snprintf(textbox1Buffer, BUF_SIZE, "%d", command.X);
w->Invalidate();
}
else
{
safe_strcpy(textbox1Buffer, text, sizeof(textbox1Buffer));
}
break;
case WIDX_TEXTBOX_Y:
if (text == nullptr)
{
if (*end == '\0')
{
command.Y = static_cast<uint8_t>(value);
}
snprintf(textbox2Buffer, BUF_SIZE, "%d", command.Y);
w->Invalidate();
}
else
{
safe_strcpy(textbox2Buffer, text, sizeof(textbox2Buffer));
}
break;
}
}
static void window_title_command_editor_update(rct_window* w)
{
if (gCurrentTextBox.window.classification == w->classification && gCurrentTextBox.window.number == w->number)
{
window_update_textbox_caret();
widget_invalidate(w, gCurrentTextBox.widget_index);
}
}
static void window_title_command_editor_tool_down(
rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
{
viewport_interaction_info info;
viewport_interaction_get_item_left(screenCoords, &info);
if (info.type == VIEWPORT_INTERACTION_ITEM_SPRITE)
{
uint16_t spriteIndex = info.sprite->generic.sprite_index;
uint16_t spriteIdentifier = info.sprite->generic.sprite_identifier;
bool validSprite = false;
if (info.sprite->IsPeep())
{
validSprite = true;
auto peep = GET_PEEP(spriteIndex);
if (peep != nullptr)
{
uint8_t formatArgs[32]{};
peep->FormatNameTo(formatArgs);
format_string(command.SpriteName, USER_STRING_MAX_LENGTH, STR_STRINGID, &peep->id);
}
}
else if (spriteIdentifier == SPRITE_IDENTIFIER_VEHICLE)
{
validSprite = true;
auto vehicle = GET_VEHICLE(spriteIndex);
if (vehicle != nullptr)
{
auto ride = get_ride(vehicle->ride);
if (ride != nullptr)
{
uint8_t formatArgs[32]{};
ride->FormatNameTo(formatArgs);
format_string(command.SpriteName, USER_STRING_MAX_LENGTH, STR_STRINGID, formatArgs);
}
}
}
else if (spriteIdentifier == SPRITE_IDENTIFIER_LITTER)
{
Litter* litter = &(get_sprite(spriteIndex)->litter);
if (litter->type < std::size(litterNames))
{
validSprite = true;
format_string(command.SpriteName, USER_STRING_MAX_LENGTH, litterNames[litter->type], nullptr);
}
}
else if (spriteIdentifier == SPRITE_IDENTIFIER_MISC)
{
if (info.sprite->IsBalloon())
{
validSprite = true;
format_string(command.SpriteName, USER_STRING_MAX_LENGTH, STR_SHOP_ITEM_SINGULAR_BALLOON, nullptr);
}
else if (info.sprite->IsDuck())
{
validSprite = true;
format_string(command.SpriteName, USER_STRING_MAX_LENGTH, STR_DUCK, nullptr);
}
}
if (validSprite)
{
command.SpriteIndex = spriteIndex;
window_follow_sprite(w, static_cast<size_t>(command.SpriteIndex));
tool_cancel();
w->Invalidate();
}
}
}
static void window_title_command_editor_invalidate(rct_window* w)
{
colour_scheme_update_by_class(w, WC_TITLE_EDITOR);
window_title_command_editor_widgets[WIDX_TEXTBOX_FULL].type = WWT_EMPTY;
window_title_command_editor_widgets[WIDX_TEXTBOX_X].type = WWT_EMPTY;
window_title_command_editor_widgets[WIDX_TEXTBOX_Y].type = WWT_EMPTY;
window_title_command_editor_widgets[WIDX_INPUT].type = WWT_EMPTY;
window_title_command_editor_widgets[WIDX_INPUT_DROPDOWN].type = WWT_EMPTY;
window_title_command_editor_widgets[WIDX_GET].type = WWT_EMPTY;
window_title_command_editor_widgets[WIDX_SELECT_SCENARIO].type = WWT_EMPTY;
window_title_command_editor_widgets[WIDX_SELECT_SPRITE].type = WWT_EMPTY;
window_title_command_editor_widgets[WIDX_VIEWPORT].type = WWT_EMPTY;
switch (command.Type)
{
case TITLE_SCRIPT_LOAD:
case TITLE_SCRIPT_SPEED:
window_title_command_editor_widgets[WIDX_INPUT].type = WWT_DROPDOWN;
window_title_command_editor_widgets[WIDX_INPUT_DROPDOWN].type = WWT_BUTTON;
break;
case TITLE_SCRIPT_LOADSC:
window_title_command_editor_widgets[WIDX_INPUT].type = WWT_DROPDOWN;
window_title_command_editor_widgets[WIDX_SELECT_SCENARIO].type = WWT_BUTTON;
break;
case TITLE_SCRIPT_LOCATION:
window_title_command_editor_widgets[WIDX_TEXTBOX_X].type = WWT_TEXT_BOX;
window_title_command_editor_widgets[WIDX_TEXTBOX_Y].type = WWT_TEXT_BOX;
window_title_command_editor_widgets[WIDX_GET].type = WWT_BUTTON;
break;
case TITLE_SCRIPT_ROTATE:
case TITLE_SCRIPT_WAIT:
window_title_command_editor_widgets[WIDX_TEXTBOX_FULL].type = WWT_TEXT_BOX;
break;
case TITLE_SCRIPT_ZOOM:
window_title_command_editor_widgets[WIDX_GET].type = WWT_BUTTON;
window_title_command_editor_widgets[WIDX_TEXTBOX_FULL].type = WWT_TEXT_BOX;
break;
case TITLE_SCRIPT_FOLLOW:
window_title_command_editor_widgets[WIDX_SELECT_SPRITE].type = WWT_BUTTON;
window_title_command_editor_widgets[WIDX_VIEWPORT].type = WWT_VIEWPORT;
// Draw button pressed while the tool is active
if (sprite_selector_tool_is_active())
w->pressed_widgets |= (1 << WIDX_SELECT_SPRITE);
else
w->pressed_widgets &= ~(1 << WIDX_SELECT_SPRITE);
break;
}
if ((gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) == SCREEN_FLAGS_TITLE_DEMO)
{
w->disabled_widgets |= (1 << WIDX_GET) | (1 << WIDX_SELECT_SPRITE);
window_title_command_editor_widgets[WIDX_SELECT_SPRITE].tooltip = STR_TITLE_COMMAND_EDITOR_SELECT_SPRITE_TOOLTIP;
}
else
{
w->disabled_widgets &= ~((1 << WIDX_GET) | (1 << WIDX_SELECT_SPRITE));
window_title_command_editor_widgets[WIDX_SELECT_SPRITE].tooltip = STR_NONE;
}
}
static void window_title_command_editor_paint(rct_window* w, rct_drawpixelinfo* dpi)
{
window_draw_widgets(w, dpi);
TITLE_COMMAND_ORDER command_info = get_command_info(command.Type);
// "Command:" label
gfx_draw_string_left(
dpi, STR_TITLE_COMMAND_EDITOR_COMMAND_LABEL, nullptr, w->colours[1], w->windowPos.x + WS, w->windowPos.y + BY - 14);
// Command dropdown name
gfx_draw_string_left_clipped(
dpi, command_info.nameStringId, nullptr, w->colours[1], w->windowPos.x + w->widgets[WIDX_COMMAND].left + 1,
w->windowPos.y + w->widgets[WIDX_COMMAND].top,
w->widgets[WIDX_COMMAND_DROPDOWN].left - w->widgets[WIDX_COMMAND].left - 4);
// Label (e.g. "Location:")
gfx_draw_string_left(
dpi, command_info.descStringId, nullptr, w->colours[1], w->windowPos.x + WS, w->windowPos.y + BY2 - 14);
if (command.Type == TITLE_SCRIPT_SPEED)
{
gfx_draw_string_left_clipped(
dpi, SpeedNames[command.Speed - 1], nullptr, w->colours[1], w->windowPos.x + w->widgets[WIDX_INPUT].left + 1,
w->windowPos.y + w->widgets[WIDX_INPUT].top,
w->widgets[WIDX_INPUT_DROPDOWN].left - w->widgets[WIDX_INPUT].left - 4);
}
if (command.Type == TITLE_SCRIPT_FOLLOW)
{
uint8_t colour = COLOUR_BLACK;
rct_string_id spriteString = STR_TITLE_COMMAND_EDITOR_FORMAT_SPRITE_NAME;
if (command.SpriteIndex != SPRITE_INDEX_NULL)
{
window_draw_viewport(dpi, w);
Formatter::Common().Add<uintptr_t>(reinterpret_cast<uintptr_t>(command.SpriteName));
}
else
{
colour = w->colours[1];
spriteString = STR_TITLE_COMMAND_EDITOR_FOLLOW_NO_SPRITE;
}
gfx_set_dirty_blocks(
w->windowPos.x + w->widgets[WIDX_VIEWPORT].left, w->windowPos.y + w->widgets[WIDX_VIEWPORT].top,
w->windowPos.x + w->widgets[WIDX_VIEWPORT].right, w->windowPos.y + w->widgets[WIDX_VIEWPORT].bottom);
gfx_draw_string_left_clipped(
dpi, spriteString, gCommonFormatArgs, colour, w->windowPos.x + w->widgets[WIDX_VIEWPORT].left + 2,
w->windowPos.y + w->widgets[WIDX_VIEWPORT].top + 1,
w->widgets[WIDX_VIEWPORT].right - w->widgets[WIDX_VIEWPORT].left - 2);
}
else if (command.Type == TITLE_SCRIPT_LOAD)
{
if (command.SaveIndex == SAVE_INDEX_INVALID)
{
gfx_draw_string_left_clipped(
dpi, STR_TITLE_COMMAND_EDITOR_NO_SAVE_SELECTED, nullptr, w->colours[1],
w->windowPos.x + w->widgets[WIDX_INPUT].left + 1, w->windowPos.y + w->widgets[WIDX_INPUT].top,
w->widgets[WIDX_INPUT_DROPDOWN].left - w->widgets[WIDX_INPUT].left - 4);
}
else
{
Formatter::Common().Add<uintptr_t>(reinterpret_cast<uintptr_t>(_sequence->Saves[command.SaveIndex]));
gfx_draw_string_left_clipped(
dpi, STR_STRING, gCommonFormatArgs, w->colours[1], w->windowPos.x + w->widgets[WIDX_INPUT].left + 1,
w->windowPos.y + w->widgets[WIDX_INPUT].top,
w->widgets[WIDX_INPUT_DROPDOWN].left - w->widgets[WIDX_INPUT].left - 4);
}
}
else if (command.Type == TITLE_SCRIPT_LOADSC)
{
if (command.Scenario[0] == '\0')
{
gfx_draw_string_left_clipped(
dpi, STR_TITLE_COMMAND_EDITOR_NO_SCENARIO_SELECTED, nullptr, w->colours[1],
w->windowPos.x + w->widgets[WIDX_INPUT].left + 1, w->windowPos.y + w->widgets[WIDX_INPUT].top,
w->widgets[WIDX_INPUT_DROPDOWN].left - w->widgets[WIDX_INPUT].left - 4);
}
else
{
const char* name = "";
rct_string_id nameString = STR_STRING;
auto scenario = GetScenarioRepository()->GetByInternalName(command.Scenario);
if (scenario != nullptr)
{
name = scenario->name;
}
else
{
nameString = STR_TITLE_COMMAND_EDITOR_MISSING_SCENARIO;
}
Formatter::Common().Add<const char*>(name);
gfx_draw_string_left_clipped(
dpi, nameString, gCommonFormatArgs, w->colours[1], w->windowPos.x + w->widgets[WIDX_INPUT].left + 1,
w->windowPos.y + w->widgets[WIDX_INPUT].top,
w->widgets[WIDX_INPUT_DROPDOWN].left - w->widgets[WIDX_INPUT].left - 4);
}
}
}