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

3869 lines
149 KiB
C++
Raw Normal View History

2014-04-11 03:42:39 +02:00
/*****************************************************************************
* Copyright (c) 2014-2024 OpenRCT2 developers
2014-04-11 03:42:39 +02:00
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
2014-04-11 03:42:39 +02:00
*
* OpenRCT2 is licensed under the GNU General Public License version 3.
2014-04-11 03:42:39 +02:00
*****************************************************************************/
2018-06-22 23:21:44 +02:00
#include "../UiContext.h"
#include "../interface/InGameConsole.h"
2020-02-11 22:26:05 +01:00
#include "../scripting/CustomMenu.h"
2017-10-17 22:31:05 +02:00
#include <algorithm>
2018-11-21 23:16:04 +01:00
#include <iterator>
#include <limits>
2018-06-22 23:21:44 +02:00
#include <openrct2-ui/interface/Dropdown.h>
#include <openrct2-ui/interface/LandTool.h>
#include <openrct2-ui/interface/Viewport.h>
#include <openrct2-ui/interface/Widget.h>
#include <openrct2-ui/windows/Window.h>
2017-12-13 13:02:24 +01:00
#include <openrct2/Cheats.h>
2017-11-11 02:57:45 +01:00
#include <openrct2/Context.h>
#include <openrct2/Editor.h>
#include <openrct2/Game.h>
#include <openrct2/GameState.h>
2017-12-12 14:52:57 +01:00
#include <openrct2/Input.h>
2018-06-22 23:21:44 +02:00
#include <openrct2/OpenRCT2.h>
#include <openrct2/ParkImporter.h>
#include <openrct2/Version.h>
Split actions hpp files into separate h and cpp files (#13548) * Split up SmallSceneryPlace/Remove Added undo function for Remove Scenery * Refactor: Balloon and Banner actions hpp=>h/cpp * Refactor: rename all action *.hpp files to *.cpp This is preparation for separation in later commits. Note that without the complete set of commits in this branch, the code will not build. * Refactor Clear, Climate, Custom, and Footpath actions hpp=>h/cpp * VSCode: add src subdirectories to includePath * Refactor Guest actions hpp=>h/cpp * Refactor Land actions hpp=>h/cpp * Refactor LargeScenery actions hpp=>h/cpp * Refactor Load, Maze, Network actions hpp=>h/cpp * Refactor Park actions hpp=>h/cpp * Refactor/style: move private function declarations in actions *.h Previous action .h files included private function declarations with private member variables, before public function declarations. This commit re-orders the header files to the following order: - public member variables - private member variables - public functions - private functions * Refactor Pause action hpp=>h/cpp * Refactor Peep, Place, Player actions hpp=>h/cpp * Refactor Ride actions hpp=>h/cpp * Refactor Scenario, Set*, Sign* actions hpp=>h/cpp * Refactor SmallScenerySetColourAction hpp=>h/cpp * Refactor Staff actions hpp=>h/cpp * Refactor Surface, Tile, Track* actions hpp=>h/cpp * Refactor Wall and Water actions hpp=>h/cpp * Fix various includes and other compile errors Update includes for tests. Move static function declarations to .h files Add explicit includes to various files that were previously implicit (the required header was a nested include in an action hpp file, and the action .h file does not include that header) Move RideSetStatus string enum to the cpp file to avoid unused imports * Xcode: modify project file for actions refactor * Cleanup whitespace and end-of-file newlines Co-authored-by: duncanspumpkin <duncans_pumpkin@hotmail.co.uk>
2020-12-10 07:39:10 +01:00
#include <openrct2/actions/BannerPlaceAction.h>
#include <openrct2/actions/BannerSetColourAction.h>
#include <openrct2/actions/ClearAction.h>
#include <openrct2/actions/FootpathAdditionPlaceAction.h>
#include <openrct2/actions/GameSetSpeedAction.h>
Split actions hpp files into separate h and cpp files (#13548) * Split up SmallSceneryPlace/Remove Added undo function for Remove Scenery * Refactor: Balloon and Banner actions hpp=>h/cpp * Refactor: rename all action *.hpp files to *.cpp This is preparation for separation in later commits. Note that without the complete set of commits in this branch, the code will not build. * Refactor Clear, Climate, Custom, and Footpath actions hpp=>h/cpp * VSCode: add src subdirectories to includePath * Refactor Guest actions hpp=>h/cpp * Refactor Land actions hpp=>h/cpp * Refactor LargeScenery actions hpp=>h/cpp * Refactor Load, Maze, Network actions hpp=>h/cpp * Refactor Park actions hpp=>h/cpp * Refactor/style: move private function declarations in actions *.h Previous action .h files included private function declarations with private member variables, before public function declarations. This commit re-orders the header files to the following order: - public member variables - private member variables - public functions - private functions * Refactor Pause action hpp=>h/cpp * Refactor Peep, Place, Player actions hpp=>h/cpp * Refactor Ride actions hpp=>h/cpp * Refactor Scenario, Set*, Sign* actions hpp=>h/cpp * Refactor SmallScenerySetColourAction hpp=>h/cpp * Refactor Staff actions hpp=>h/cpp * Refactor Surface, Tile, Track* actions hpp=>h/cpp * Refactor Wall and Water actions hpp=>h/cpp * Fix various includes and other compile errors Update includes for tests. Move static function declarations to .h files Add explicit includes to various files that were previously implicit (the required header was a nested include in an action hpp file, and the action .h file does not include that header) Move RideSetStatus string enum to the cpp file to avoid unused imports * Xcode: modify project file for actions refactor * Cleanup whitespace and end-of-file newlines Co-authored-by: duncanspumpkin <duncans_pumpkin@hotmail.co.uk>
2020-12-10 07:39:10 +01:00
#include <openrct2/actions/LandLowerAction.h>
#include <openrct2/actions/LandRaiseAction.h>
#include <openrct2/actions/LandSmoothAction.h>
#include <openrct2/actions/LargeSceneryPlaceAction.h>
#include <openrct2/actions/LargeScenerySetColourAction.h>
#include <openrct2/actions/LoadOrQuitAction.h>
#include <openrct2/actions/PauseToggleAction.h>
#include <openrct2/actions/SmallSceneryPlaceAction.h>
#include <openrct2/actions/SmallScenerySetColourAction.h>
#include <openrct2/actions/SurfaceSetStyleAction.h>
#include <openrct2/actions/WallPlaceAction.h>
#include <openrct2/actions/WallSetColourAction.h>
#include <openrct2/actions/WaterLowerAction.h>
#include <openrct2/actions/WaterRaiseAction.h>
2018-06-22 23:21:44 +02:00
#include <openrct2/audio/audio.h>
#include <openrct2/config/Config.h>
2021-11-25 22:47:24 +01:00
#include <openrct2/entity/Staff.h>
#include <openrct2/interface/Chat.h>
#include <openrct2/interface/InteractiveConsole.h>
2017-11-11 02:57:45 +01:00
#include <openrct2/interface/Screenshot.h>
2021-12-12 00:06:06 +01:00
#include <openrct2/localisation/Formatter.h>
2017-11-11 02:57:45 +01:00
#include <openrct2/network/network.h>
#include <openrct2/object/BannerSceneryEntry.h>
2023-01-26 19:44:42 +01:00
#include <openrct2/object/LargeSceneryEntry.h>
#include <openrct2/object/ObjectEntryManager.h>
#include <openrct2/object/PathAdditionEntry.h>
#include <openrct2/object/SmallSceneryEntry.h>
#include <openrct2/object/WallSceneryEntry.h>
#include <openrct2/paint/VirtualFloor.h>
2018-06-22 23:21:44 +02:00
#include <openrct2/scenario/Scenario.h>
#include <openrct2/ui/UiContext.h>
#include <openrct2/util/Math.hpp>
2017-11-11 02:57:45 +01:00
#include <openrct2/windows/Intent.h>
2018-01-11 10:59:26 +01:00
#include <openrct2/world/Footpath.h>
2018-06-22 23:21:44 +02:00
#include <openrct2/world/Park.h>
2018-01-11 10:59:26 +01:00
#include <openrct2/world/Scenery.h>
2018-05-01 16:33:16 +02:00
#include <openrct2/world/Surface.h>
2018-06-22 23:21:44 +02:00
#include <openrct2/world/Wall.h>
#include <string>
2018-03-11 23:25:34 +01:00
namespace OpenRCT2::Ui::Windows
{
enum
{
WIDX_PAUSE,
WIDX_FILE_MENU,
WIDX_MUTE,
WIDX_ZOOM_OUT,
WIDX_ZOOM_IN,
WIDX_ROTATE,
WIDX_VIEW_MENU,
WIDX_MAP,
WIDX_LAND,
WIDX_WATER,
WIDX_SCENERY,
WIDX_PATH,
WIDX_CONSTRUCT_RIDE,
WIDX_RIDES,
WIDX_PARK,
WIDX_STAFF,
WIDX_GUESTS,
WIDX_CLEAR_SCENERY,
WIDX_FASTFORWARD,
WIDX_CHEATS,
WIDX_DEBUG,
WIDX_FINANCES,
WIDX_RESEARCH,
WIDX_NEWS,
WIDX_NETWORK,
WIDX_CHAT,
WIDX_SEPARATOR,
};
2014-04-11 03:42:39 +02:00
validate_global_widx(WC_TOP_TOOLBAR, WIDX_PAUSE);
validate_global_widx(WC_TOP_TOOLBAR, WIDX_LAND);
validate_global_widx(WC_TOP_TOOLBAR, WIDX_WATER);
validate_global_widx(WC_TOP_TOOLBAR, WIDX_SCENERY);
validate_global_widx(WC_TOP_TOOLBAR, WIDX_PATH);
enum FileMenuDdidx
{
DDIDX_NEW_GAME = 0,
DDIDX_LOAD_GAME = 1,
DDIDX_SAVE_GAME = 2,
DDIDX_SAVE_GAME_AS = 3,
// separator
DDIDX_ABOUT = 5,
DDIDX_OPTIONS = 6,
DDIDX_SCREENSHOT = 7,
DDIDX_GIANT_SCREENSHOT = 8,
// separator
DDIDX_FILE_BUG_ON_GITHUB = 10,
DDIDX_UPDATE_AVAILABLE = 11,
// separator
DDIDX_QUIT_TO_MENU = 13,
DDIDX_EXIT_OPENRCT2 = 14,
};
enum TopToolbarViewMenuDdidx
{
DDIDX_UNDERGROUND_INSIDE = 0,
DDIDX_TRANSPARENT_WATER = 1,
DDIDX_HIDE_BASE = 2,
DDIDX_HIDE_VERTICAL = 3,
// separator
DDIDX_HIDE_RIDES = 5,
DDIDX_HIDE_VEHICLES = 6,
DDIDX_HIDE_VEGETATION = 7,
DDIDX_HIDE_SCENERY = 8,
DDIDX_HIDE_PATHS = 9,
DDIDX_HIDE_SUPPORTS = 10,
DDIDX_HIDE_GUESTS = 11,
DDIDX_HIDE_STAFF = 12,
// separator
DDIDX_LAND_HEIGHTS = 14,
DDIDX_TRACK_HEIGHTS = 15,
DDIDX_PATH_HEIGHTS = 16,
// separator
DDIDX_VIEW_CLIPPING = 18,
DDIDX_HIGHLIGHT_PATH_ISSUES = 19,
// separator
DDIDX_TRANSPARENCY = 21,
TOP_TOOLBAR_VIEW_MENU_COUNT,
};
enum TopToolbarDebugDdidx
{
DDIDX_CONSOLE = 0,
DDIDX_DEBUG_PAINT = 1,
TOP_TOOLBAR_DEBUG_COUNT,
};
enum TopToolbarNetworkDdidx
{
DDIDX_MULTIPLAYER = 0,
DDIDX_MULTIPLAYER_RECONNECT = 1,
TOP_TOOLBAR_NETWORK_COUNT,
};
2015-07-16 19:32:43 +02:00
enum
{
DDIDX_CHEATS,
DDIDX_TILE_INSPECTOR = 1,
DDIDX_OBJECT_SELECTION = 2,
DDIDX_INVENTIONS_LIST = 3,
DDIDX_SCENARIO_OPTIONS = 4,
DDIDX_OBJECTIVE_OPTIONS = 5,
// 6 is a separator
DDIDX_ENABLE_SANDBOX_MODE = 7,
DDIDX_DISABLE_CLEARANCE_CHECKS = 8,
DDIDX_DISABLE_SUPPORT_LIMITS = 9,
TOP_TOOLBAR_CHEATS_COUNT,
};
2015-06-26 23:40:09 +02:00
enum
{
DDIDX_SHOW_MAP,
DDIDX_OPEN_VIEWPORT,
};
enum
{
DDIDX_ROTATE_CLOCKWISE,
DDIDX_ROTATE_ANTI_CLOCKWISE,
};
#pragma region Toolbar_widget_ordering
// clang-format off
// from left to right
static constexpr int32_t left_aligned_widgets_order[] = {
WIDX_PAUSE,
WIDX_FASTFORWARD,
WIDX_FILE_MENU,
WIDX_MUTE,
WIDX_NETWORK,
WIDX_CHAT,
WIDX_CHEATS,
WIDX_DEBUG,
WIDX_SEPARATOR,
WIDX_ZOOM_OUT,
WIDX_ZOOM_IN,
WIDX_ROTATE,
WIDX_VIEW_MENU,
WIDX_MAP,
};
// from right to left
static constexpr int32_t right_aligned_widgets_order[] = {
WIDX_NEWS,
WIDX_GUESTS,
WIDX_STAFF,
WIDX_PARK,
WIDX_RIDES,
WIDX_RESEARCH,
WIDX_FINANCES,
WIDX_SEPARATOR,
WIDX_CONSTRUCT_RIDE,
WIDX_PATH,
WIDX_SCENERY,
WIDX_WATER,
WIDX_LAND,
WIDX_CLEAR_SCENERY,
};
#pragma endregion
static Widget _topToolbarWidgets[] = {
MakeRemapWidget({ 0, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Primary , SPR_TOOLBAR_PAUSE, STR_PAUSE_GAME_TIP ), // Pause
MakeRemapWidget({ 60, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Primary , SPR_TOOLBAR_FILE, STR_DISC_AND_GAME_OPTIONS_TIP ), // File menu
MakeRemapWidget({250, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Primary , SPR_G2_TOOLBAR_MUTE, STR_TOOLBAR_MUTE_TIP ), // Mute
MakeRemapWidget({100, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Secondary , SPR_TOOLBAR_ZOOM_OUT, STR_ZOOM_OUT_TIP ), // Zoom out
MakeRemapWidget({130, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Secondary , SPR_TOOLBAR_ZOOM_IN, STR_ZOOM_IN_TIP ), // Zoom in
MakeRemapWidget({160, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Secondary , SPR_TOOLBAR_ROTATE, STR_ROTATE_TIP ), // Rotate camera
MakeRemapWidget({190, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Secondary , SPR_TOOLBAR_VIEW, STR_VIEW_OPTIONS_TIP ), // Transparency menu
MakeRemapWidget({220, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Secondary , SPR_TOOLBAR_MAP, STR_SHOW_MAP_TIP ), // Map
MakeRemapWidget({267, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Tertiary , SPR_TOOLBAR_LAND, STR_ADJUST_LAND_TIP ), // Land
MakeRemapWidget({297, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Tertiary , SPR_TOOLBAR_WATER, STR_ADJUST_WATER_TIP ), // Water
MakeRemapWidget({327, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Tertiary , SPR_TOOLBAR_SCENERY, STR_PLACE_SCENERY_TIP ), // Scenery
MakeRemapWidget({357, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Tertiary , SPR_TOOLBAR_FOOTPATH, STR_BUILD_FOOTPATH_TIP ), // Path
MakeRemapWidget({387, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Tertiary , SPR_TOOLBAR_CONSTRUCT_RIDE, STR_BUILD_RIDE_TIP ), // Construct ride
MakeRemapWidget({490, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Quaternary, SPR_TOOLBAR_RIDES, STR_RIDES_IN_PARK_TIP ), // Rides
MakeRemapWidget({520, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Quaternary, SPR_TOOLBAR_PARK, STR_PARK_INFORMATION_TIP ), // Park
MakeRemapWidget({550, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Quaternary, SPR_TAB_TOOLBAR, STR_STAFF_TIP ), // Staff
MakeRemapWidget({560, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Quaternary, SPR_TOOLBAR_GUESTS, STR_GUESTS_TIP ), // Guests
MakeRemapWidget({560, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Tertiary , SPR_TOOLBAR_CLEAR_SCENERY, STR_CLEAR_SCENERY_TIP ), // Clear scenery
MakeRemapWidget({ 30, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Primary , SPR_TAB_TOOLBAR, STR_GAME_SPEED_TIP ), // Fast forward
MakeRemapWidget({ 30, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Primary , SPR_TAB_TOOLBAR, STR_CHEATS_TIP ), // Cheats
MakeRemapWidget({ 30, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Primary , SPR_TAB_TOOLBAR, STR_DEBUG_TIP ), // Debug
MakeRemapWidget({ 30, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Quaternary, SPR_TAB_TOOLBAR, STR_SCENARIO_OPTIONS_FINANCIAL_TIP), // Finances
MakeRemapWidget({ 30, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Quaternary, SPR_TAB_TOOLBAR, STR_FINANCES_RESEARCH_TIP ), // Research
MakeRemapWidget({ 30, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Quaternary, SPR_TAB_TOOLBAR, STR_SHOW_RECENT_MESSAGES_TIP ), // News
MakeRemapWidget({ 30, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Primary , SPR_G2_TOOLBAR_MULTIPLAYER, STR_SHOW_MULTIPLAYER_STATUS_TIP ), // Network
MakeRemapWidget({ 30, 0}, {30, TOP_TOOLBAR_HEIGHT + 1}, WindowWidgetType::TrnBtn, WindowColour::Primary , SPR_TAB_TOOLBAR, STR_TOOLBAR_CHAT_TIP ), // Chat
MakeWidget ({ 0, 0}, {10, 1}, WindowWidgetType::Empty, WindowColour::Primary ), // Artificial widget separator
kWidgetsEnd,
2014-04-11 03:42:39 +02:00
};
// clang-format on
static void ScenarioSelectCallback(const utf8* path);
2014-04-11 03:42:39 +02:00
class TopToolbar final : public Window
{
private:
bool _landToolBlocked{ false };
uint8_t _unkF64F0E{ 0 };
int16_t _unkF64F0A{ 0 };
void InitViewMenu(Widget& widget);
void ViewMenuDropdown(int16_t dropdownIndex);
void InitMapMenu(Widget& widget);
void MapMenuDropdown(int16_t dropdownIndex);
void InitFastforwardMenu(Widget& widget);
2014-04-11 03:42:39 +02:00
void FastforwardMenuDropdown(int16_t dropdownIndex);
void InitRotateMenu(Widget& widget);
void RotateMenuDropdown(int16_t dropdownIndex);
void InitCheatsMenu(Widget& widget);
void CheatsMenuDropdown(int16_t dropdownIndex);
void InitDebugMenu(Widget& widget);
void DebugMenuDropdown(int16_t dropdownIndex);
void InitNetworkMenu(Widget& widget);
void NetworkMenuDropdown(int16_t dropdownIndex);
/**
*
* rct2: 0x0066CCE7
*/
void ToggleFootpathWindow()
2023-02-14 22:19:42 +01:00
{
if (WindowFindByClass(WindowClass::Footpath) == nullptr)
2023-02-14 22:19:42 +01:00
{
ContextOpenWindow(WindowClass::Footpath);
}
else
{
ToolCancel();
WindowCloseByClass(WindowClass::Footpath);
2023-02-14 22:19:42 +01:00
}
}
2023-02-14 22:19:42 +01:00
/**
*
* rct2: 0x0066CD54
*/
void ToggleLandWindow(WidgetIndex widgetIndex)
2018-06-22 23:21:44 +02:00
{
if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) && gCurrentToolWidget.window_classification == WindowClass::TopToolbar
&& gCurrentToolWidget.widget_index == WIDX_LAND)
{
ToolCancel();
}
else
{
_landToolBlocked = false;
ShowGridlines();
ToolSet(*this, widgetIndex, Tool::DigDown);
InputSetFlag(INPUT_FLAG_6, true);
ContextOpenWindow(WindowClass::Land);
}
}
/**
*
* rct2: 0x0066CD0C
*/
void ToggleClearSceneryWindow(WidgetIndex widgetIndex)
{
if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE) && gCurrentToolWidget.window_classification == WindowClass::TopToolbar
&& gCurrentToolWidget.widget_index == WIDX_CLEAR_SCENERY))
{
ToolCancel();
}
else
{
ShowGridlines();
ToolSet(*this, widgetIndex, Tool::Crosshair);
InputSetFlag(INPUT_FLAG_6, true);
ContextOpenWindow(WindowClass::ClearScenery);
}
}
/**
*
* rct2: 0x0066CD9C
*/
void ToggleWaterWindow(WidgetIndex widgetIndex)
2023-02-14 22:19:42 +01:00
{
if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) && gCurrentToolWidget.window_classification == WindowClass::TopToolbar
&& gCurrentToolWidget.widget_index == WIDX_WATER)
{
ToolCancel();
}
else
{
_landToolBlocked = false;
ShowGridlines();
ToolSet(*this, widgetIndex, Tool::WaterDown);
InputSetFlag(INPUT_FLAG_6, true);
ContextOpenWindow(WindowClass::Water);
}
2023-02-14 22:19:42 +01:00
}
/**
*
* rct2: 0x0068E213
*/
void ToolUpdateSceneryClear(const ScreenCoordsXY& screenPos)
2018-06-22 23:21:44 +02:00
{
if (!ToolUpdateLandPaint(screenPos))
2018-06-22 23:21:44 +02:00
return;
auto action = GetClearAction();
auto result = GameActions::Query(&action);
auto cost = (result.Error == GameActions::Status::Ok ? result.Cost : kMoney64Undefined);
if (gClearSceneryCost != cost)
2023-02-14 22:19:42 +01:00
{
gClearSceneryCost = cost;
WindowInvalidateByClass(WindowClass::ClearScenery);
2023-02-14 22:19:42 +01:00
}
2018-06-22 23:21:44 +02:00
}
int8_t ToolUpdateLandPaint(const ScreenCoordsXY& screenPos)
2017-11-22 23:06:56 +01:00
{
uint8_t state_changed = 0;
MapInvalidateSelectionRect();
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
auto mapTile = ScreenGetMapXY(screenPos, nullptr);
2023-02-14 22:19:42 +01:00
if (!mapTile.has_value())
{
if (gClearSceneryCost != kMoney64Undefined)
2023-02-14 22:19:42 +01:00
{
gClearSceneryCost = kMoney64Undefined;
WindowInvalidateByClass(WindowClass::ClearScenery);
2023-02-14 22:19:42 +01:00
}
return state_changed;
2023-02-14 22:19:42 +01:00
}
2018-06-22 23:21:44 +02:00
2023-02-14 22:19:42 +01:00
if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE))
2018-06-22 23:21:44 +02:00
{
2023-02-14 22:19:42 +01:00
gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE;
state_changed++;
}
2019-04-07 10:15:41 +02:00
if (gMapSelectType != MAP_SELECT_TYPE_FULL)
2023-02-14 22:19:42 +01:00
{
gMapSelectType = MAP_SELECT_TYPE_FULL;
2023-02-14 22:19:42 +01:00
state_changed++;
2018-06-22 23:21:44 +02:00
}
2015-04-15 21:52:19 +02:00
int16_t tool_size = std::max<uint16_t>(1, gLandToolSize);
int16_t tool_length = (tool_size - 1) * 32;
// Move to tool bottom left
mapTile->x -= (tool_size - 1) * 16;
mapTile->y -= (tool_size - 1) * 16;
mapTile = mapTile->ToTileStart();
2023-02-14 22:19:42 +01:00
if (gMapSelectPositionA.x != mapTile->x)
{
2023-02-14 22:19:42 +01:00
gMapSelectPositionA.x = mapTile->x;
state_changed++;
}
2023-02-14 22:19:42 +01:00
if (gMapSelectPositionA.y != mapTile->y)
{
2023-02-14 22:19:42 +01:00
gMapSelectPositionA.y = mapTile->y;
state_changed++;
}
2023-02-14 22:19:42 +01:00
mapTile->x += tool_length;
mapTile->y += tool_length;
2023-02-14 22:19:42 +01:00
if (gMapSelectPositionB.x != mapTile->x)
2018-06-22 23:21:44 +02:00
{
2023-02-14 22:19:42 +01:00
gMapSelectPositionB.x = mapTile->x;
state_changed++;
}
2023-02-14 22:19:42 +01:00
if (gMapSelectPositionB.y != mapTile->y)
2018-06-22 23:21:44 +02:00
{
2023-02-14 22:19:42 +01:00
gMapSelectPositionB.y = mapTile->y;
state_changed++;
}
2017-03-13 19:05:13 +01:00
2023-02-14 22:19:42 +01:00
MapInvalidateSelectionRect();
return state_changed;
}
2023-02-14 22:19:42 +01:00
/**
*
* rct2: 0x00664280
*/
void ToolUpdateLand(const ScreenCoordsXY& screenPos)
2018-06-22 23:21:44 +02:00
{
const bool mapCtrlPressed = InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z);
MapInvalidateSelectionRect();
if (gCurrentToolId == Tool::UpDownArrow)
2018-06-22 23:21:44 +02:00
{
if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE))
return;
money64 lower_cost = SelectionLowerLand(0);
money64 raise_cost = SelectionRaiseLand(0);
if (gLandToolRaiseCost != raise_cost || gLandToolLowerCost != lower_cost)
{
gLandToolRaiseCost = raise_cost;
gLandToolLowerCost = lower_cost;
WindowInvalidateByClass(WindowClass::Land);
}
return;
}
int16_t tool_size = gLandToolSize;
std::optional<CoordsXY> mapTile;
uint8_t side{};
2018-06-22 23:21:44 +02:00
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
if (tool_size == 1)
{
int32_t selectionType;
// Get selection type and map coordinates from mouse x,y position
ScreenPosToMapPos(screenPos, &selectionType);
mapTile = ScreenGetMapXYSide(screenPos, &side);
if (!mapTile.has_value())
{
money64 lower_cost = kMoney64Undefined;
money64 raise_cost = kMoney64Undefined;
2018-06-22 23:21:44 +02:00
if (gLandToolRaiseCost != raise_cost || gLandToolLowerCost != lower_cost)
{
gLandToolRaiseCost = raise_cost;
gLandToolLowerCost = lower_cost;
WindowInvalidateByClass(WindowClass::Land);
}
return;
}
2018-06-22 23:21:44 +02:00
uint8_t state_changed = 0;
2018-06-22 23:21:44 +02:00
if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE))
{
gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE;
state_changed++;
}
2018-06-22 23:21:44 +02:00
if (gMapSelectType != selectionType)
{
gMapSelectType = selectionType;
state_changed++;
}
2023-02-14 22:19:42 +01:00
if ((gMapSelectType != MAP_SELECT_TYPE_EDGE_0 + (side & 0xFF)) && mapCtrlPressed)
{
gMapSelectType = MAP_SELECT_TYPE_EDGE_0 + (side & 0xFF);
state_changed++;
}
2018-06-22 23:21:44 +02:00
if (gMapSelectPositionA.x != mapTile->x)
{
gMapSelectPositionA.x = mapTile->x;
state_changed++;
}
2018-06-22 23:21:44 +02:00
if (gMapSelectPositionA.y != mapTile->y)
{
gMapSelectPositionA.y = mapTile->y;
state_changed++;
}
if (gMapSelectPositionB.x != mapTile->x)
{
gMapSelectPositionB.x = mapTile->x;
state_changed++;
}
if (gMapSelectPositionB.y != mapTile->y)
{
gMapSelectPositionB.y = mapTile->y;
state_changed++;
}
MapInvalidateSelectionRect();
if (!state_changed)
return;
money64 lower_cost = SelectionLowerLand(0);
money64 raise_cost = SelectionRaiseLand(0);
if (gLandToolRaiseCost != raise_cost || gLandToolLowerCost != lower_cost)
{
gLandToolRaiseCost = raise_cost;
gLandToolLowerCost = lower_cost;
WindowInvalidateByClass(WindowClass::Land);
}
2023-02-14 22:19:42 +01:00
return;
}
// Get map coordinates and the side of the tile that is being hovered over
mapTile = ScreenGetMapXYSide(screenPos, &side);
if (!mapTile.has_value())
2023-02-14 22:19:42 +01:00
{
money64 lower_cost = kMoney64Undefined;
money64 raise_cost = kMoney64Undefined;
if (gLandToolRaiseCost != raise_cost || gLandToolLowerCost != lower_cost)
{
gLandToolRaiseCost = raise_cost;
gLandToolLowerCost = lower_cost;
WindowInvalidateByClass(WindowClass::Land);
}
return;
}
uint8_t state_changed = 0;
if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE))
2018-06-22 23:21:44 +02:00
{
gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE;
state_changed++;
}
2023-02-14 22:19:42 +01:00
if (gMapSelectType != MAP_SELECT_TYPE_FULL)
{
gMapSelectType = MAP_SELECT_TYPE_FULL;
state_changed++;
}
2023-02-14 22:19:42 +01:00
if ((gMapSelectType != MAP_SELECT_TYPE_EDGE_0 + (side & 0xFF)) && mapCtrlPressed)
{
gMapSelectType = MAP_SELECT_TYPE_EDGE_0 + (side & 0xFF);
state_changed++;
}
if (tool_size == 0)
tool_size = 1;
int16_t tool_length = (tool_size - 1) * 32;
// Decide on shape of the brush for bigger selection size
switch (gMapSelectType)
{
case MAP_SELECT_TYPE_EDGE_0:
case MAP_SELECT_TYPE_EDGE_2:
// Line
mapTile->y -= (tool_size - 1) * 16;
mapTile->y = mapTile->ToTileStart().y;
break;
case MAP_SELECT_TYPE_EDGE_1:
case MAP_SELECT_TYPE_EDGE_3:
// Line
mapTile->x -= (tool_size - 1) * 16;
mapTile->x = mapTile->ToTileStart().x;
break;
default:
// Move to tool bottom left
mapTile->x -= (tool_size - 1) * 16;
mapTile->y -= (tool_size - 1) * 16;
mapTile = mapTile->ToTileStart();
break;
}
if (gMapSelectPositionA.x != mapTile->x)
{
gMapSelectPositionA.x = mapTile->x;
state_changed++;
}
if (gMapSelectPositionA.y != mapTile->y)
{
gMapSelectPositionA.y = mapTile->y;
state_changed++;
}
// Go to other side
switch (gMapSelectType)
{
case MAP_SELECT_TYPE_EDGE_0:
case MAP_SELECT_TYPE_EDGE_2:
// Line
mapTile->y += tool_length;
gMapSelectType = MAP_SELECT_TYPE_FULL;
break;
case MAP_SELECT_TYPE_EDGE_1:
case MAP_SELECT_TYPE_EDGE_3:
// Line
mapTile->x += tool_length;
gMapSelectType = MAP_SELECT_TYPE_FULL;
break;
default:
mapTile->x += tool_length;
mapTile->y += tool_length;
break;
}
if (gMapSelectPositionB.x != mapTile->x)
{
gMapSelectPositionB.x = mapTile->x;
state_changed++;
}
if (gMapSelectPositionB.y != mapTile->y)
{
gMapSelectPositionB.y = mapTile->y;
state_changed++;
}
2020-11-15 14:58:36 +01:00
MapInvalidateSelectionRect();
if (!state_changed)
return;
2020-11-15 14:58:36 +01:00
money64 lower_cost = SelectionLowerLand(0);
money64 raise_cost = SelectionRaiseLand(0);
2020-11-15 14:58:36 +01:00
if (gLandToolRaiseCost != raise_cost || gLandToolLowerCost != lower_cost)
{
gLandToolRaiseCost = raise_cost;
gLandToolLowerCost = lower_cost;
WindowInvalidateByClass(WindowClass::Land);
}
}
/**
*
* rct2: 0x006E6BDC
*/
void ToolUpdateWater(const ScreenCoordsXY& screenPos)
2020-11-15 14:58:36 +01:00
{
MapInvalidateSelectionRect();
if (gCurrentToolId == Tool::UpDownArrow)
2023-02-14 22:19:42 +01:00
{
if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE))
2023-02-14 22:19:42 +01:00
return;
auto waterLowerAction = WaterLowerAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y });
auto waterRaiseAction = WaterRaiseAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y });
auto res = GameActions::Query(&waterLowerAction);
money64 lowerCost = res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined;
res = GameActions::Query(&waterRaiseAction);
money64 raiseCost = res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined;
if (gWaterToolRaiseCost != raiseCost || gWaterToolLowerCost != lowerCost)
2023-02-14 22:19:42 +01:00
{
gWaterToolRaiseCost = raiseCost;
gWaterToolLowerCost = lowerCost;
WindowInvalidateByClass(WindowClass::Water);
2023-02-14 22:19:42 +01:00
}
return;
}
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
2020-11-15 14:58:36 +01:00
auto info = GetMapCoordinatesFromPos(
screenPos, EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Water));
if (info.SpriteType == ViewportInteractionItem::None)
{
if (gWaterToolRaiseCost != kMoney64Undefined || gWaterToolLowerCost != kMoney64Undefined)
2023-02-14 22:19:42 +01:00
{
gWaterToolRaiseCost = kMoney64Undefined;
gWaterToolLowerCost = kMoney64Undefined;
WindowInvalidateByClass(WindowClass::Water);
2023-02-14 22:19:42 +01:00
}
return;
}
2020-11-15 14:58:36 +01:00
auto mapTile = info.Loc.ToTileCentre();
2020-11-15 14:58:36 +01:00
uint8_t state_changed = 0;
2020-11-15 14:58:36 +01:00
if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE))
{
gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE;
state_changed++;
2018-06-22 23:21:44 +02:00
}
if (gMapSelectType != MAP_SELECT_TYPE_FULL_WATER)
2023-02-14 22:19:42 +01:00
{
gMapSelectType = MAP_SELECT_TYPE_FULL_WATER;
state_changed++;
}
int16_t tool_size = std::max<uint16_t>(1, gLandToolSize);
int16_t tool_length = (tool_size - 1) * 32;
2020-11-15 14:58:36 +01:00
// Move to tool bottom left
mapTile.x -= (tool_size - 1) * 16;
mapTile.y -= (tool_size - 1) * 16;
mapTile.x &= 0xFFE0;
mapTile.y &= 0xFFE0;
if (gMapSelectPositionA.x != mapTile.x)
{
2023-02-14 22:19:42 +01:00
gMapSelectPositionA.x = mapTile.x;
state_changed++;
}
if (gMapSelectPositionA.y != mapTile.y)
{
2023-02-14 22:19:42 +01:00
gMapSelectPositionA.y = mapTile.y;
state_changed++;
}
mapTile.x += tool_length;
mapTile.y += tool_length;
if (gMapSelectPositionB.x != mapTile.x)
{
2023-02-14 22:19:42 +01:00
gMapSelectPositionB.x = mapTile.x;
state_changed++;
}
if (gMapSelectPositionB.y != mapTile.y)
{
2023-02-14 22:19:42 +01:00
gMapSelectPositionB.y = mapTile.y;
state_changed++;
}
2020-11-15 14:58:36 +01:00
MapInvalidateSelectionRect();
if (!state_changed)
return;
2020-11-15 14:58:36 +01:00
auto waterLowerAction = WaterLowerAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y });
auto waterRaiseAction = WaterRaiseAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y });
2020-11-15 14:58:36 +01:00
auto res = GameActions::Query(&waterLowerAction);
money64 lowerCost = res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined;
2020-11-15 14:58:36 +01:00
res = GameActions::Query(&waterRaiseAction);
money64 raiseCost = res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined;
2015-04-18 13:17:44 +02:00
if (gWaterToolRaiseCost != raiseCost || gWaterToolLowerCost != lowerCost)
{
gWaterToolRaiseCost = raiseCost;
gWaterToolLowerCost = lowerCost;
WindowInvalidateByClass(WindowClass::Water);
2023-02-14 22:19:42 +01:00
}
}
/**
*
* rct2: 0x006E287B
*/
void ToolUpdateScenery(const ScreenCoordsXY& screenPos)
{
MapInvalidateSelectionRect();
MapInvalidateMapSelectionTiles();
if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off)
2023-02-14 22:19:42 +01:00
{
VirtualFloorInvalidate();
}
2020-03-13 14:44:34 +01:00
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT;
2020-03-13 14:44:34 +01:00
if (gWindowSceneryPaintEnabled)
return;
if (gWindowSceneryEyedropperEnabled)
return;
2020-03-13 14:44:34 +01:00
const auto selection = WindowSceneryGetTabSelection();
if (selection.IsUndefined())
{
SceneryRemoveGhostToolPlacement();
return;
}
2020-03-13 14:44:34 +01:00
money64 cost = 0;
2020-03-13 14:44:34 +01:00
switch (selection.SceneryType)
{
case SCENERY_TYPE_SMALL:
2023-02-14 22:19:42 +01:00
{
CoordsXY mapTile = {};
uint8_t quadrant;
Direction rotation;
2020-03-13 14:44:34 +01:00
Sub6E1F34SmallScenery(screenPos, selection.EntryIndex, mapTile, &quadrant, &rotation);
2020-03-13 14:44:34 +01:00
if (mapTile.IsNull())
{
SceneryRemoveGhostToolPlacement();
return;
}
2020-11-15 14:58:36 +01:00
gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE;
if (gWindowSceneryScatterEnabled)
{
uint16_t cluster_size = (gWindowSceneryScatterSize - 1) * COORDS_XY_STEP;
gMapSelectPositionA.x = mapTile.x - cluster_size / 2;
gMapSelectPositionA.y = mapTile.y - cluster_size / 2;
gMapSelectPositionB.x = mapTile.x + cluster_size / 2;
gMapSelectPositionB.y = mapTile.y + cluster_size / 2;
if (gWindowSceneryScatterSize % 2 == 0)
{
gMapSelectPositionB.x += COORDS_XY_STEP;
gMapSelectPositionB.y += COORDS_XY_STEP;
}
}
else
{
gMapSelectPositionA.x = mapTile.x;
gMapSelectPositionA.y = mapTile.y;
gMapSelectPositionB.x = mapTile.x;
gMapSelectPositionB.y = mapTile.y;
}
2020-03-13 14:44:34 +01:00
auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry<SmallSceneryEntry>(selection.EntryIndex);
2020-03-13 14:44:34 +01:00
gMapSelectType = MAP_SELECT_TYPE_FULL;
if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE) && !gWindowSceneryScatterEnabled)
{
gMapSelectType = MAP_SELECT_TYPE_QUARTER_0 + (quadrant ^ 2);
}
2021-09-15 22:22:15 +02:00
MapInvalidateSelectionRect();
// If no change in ghost placement
if ((gSceneryGhostType & SCENERY_GHOST_FLAG_0) && mapTile == gSceneryGhostPosition && quadrant == _unkF64F0E
&& gSceneryPlaceZ == _unkF64F0A && gSceneryPlaceObject.SceneryType == SCENERY_TYPE_SMALL
&& gSceneryPlaceObject.EntryIndex == selection.EntryIndex)
{
return;
}
2023-02-14 22:19:42 +01:00
SceneryRemoveGhostToolPlacement();
2020-03-13 14:44:34 +01:00
_unkF64F0E = quadrant;
_unkF64F0A = gSceneryPlaceZ;
uint8_t attemptsLeft = 1;
if (gSceneryPlaceZ != 0 && gSceneryShiftPressed)
{
attemptsLeft = 20;
}
for (; attemptsLeft != 0; attemptsLeft--)
{
cost = TryPlaceGhostSmallScenery(
{ mapTile, gSceneryPlaceZ, rotation }, quadrant, selection.EntryIndex, gWindowSceneryPrimaryColour,
gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour);
if (cost != kMoney64Undefined)
break;
gSceneryPlaceZ += 8;
}
gSceneryPlaceCost = cost;
break;
}
case SCENERY_TYPE_PATH_ITEM:
{
CoordsXY mapTile = {};
int32_t z;
2018-06-22 23:21:44 +02:00
Sub6E1F34PathItem(screenPos, selection.EntryIndex, mapTile, &z);
2018-06-22 23:21:44 +02:00
if (mapTile.IsNull())
{
SceneryRemoveGhostToolPlacement();
return;
}
2018-06-22 23:21:44 +02:00
gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE;
gMapSelectPositionA.x = mapTile.x;
gMapSelectPositionA.y = mapTile.y;
gMapSelectPositionB.x = mapTile.x;
gMapSelectPositionB.y = mapTile.y;
gMapSelectType = MAP_SELECT_TYPE_FULL;
2018-06-22 23:21:44 +02:00
MapInvalidateSelectionRect();
2018-06-22 23:21:44 +02:00
// If no change in ghost placement
if ((gSceneryGhostType & SCENERY_GHOST_FLAG_1) && mapTile == gSceneryGhostPosition
&& z == gSceneryGhostPosition.z)
{
return;
}
SceneryRemoveGhostToolPlacement();
2018-06-22 23:21:44 +02:00
cost = TryPlaceGhostPathAddition({ mapTile, z }, selection.EntryIndex);
gSceneryPlaceCost = cost;
break;
2018-06-22 23:21:44 +02:00
}
case SCENERY_TYPE_WALL:
{
CoordsXY mapTile = {};
uint8_t edge;
2018-06-22 23:21:44 +02:00
Sub6E1F34Wall(screenPos, selection.EntryIndex, mapTile, &edge);
2020-03-13 14:44:34 +01:00
if (mapTile.IsNull())
{
SceneryRemoveGhostToolPlacement();
return;
}
2018-06-22 23:21:44 +02:00
gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE;
gMapSelectPositionA.x = mapTile.x;
gMapSelectPositionA.y = mapTile.y;
gMapSelectPositionB.x = mapTile.x;
gMapSelectPositionB.y = mapTile.y;
gMapSelectType = MAP_SELECT_TYPE_EDGE_0 + edge;
2020-03-13 14:44:34 +01:00
MapInvalidateSelectionRect();
// If no change in ghost placement
if ((gSceneryGhostType & SCENERY_GHOST_FLAG_2) && mapTile == gSceneryGhostPosition
&& edge == gSceneryGhostWallRotation && gSceneryPlaceZ == _unkF64F0A)
{
return;
}
2019-03-25 20:20:31 +01:00
SceneryRemoveGhostToolPlacement();
gSceneryGhostWallRotation = edge;
_unkF64F0A = gSceneryPlaceZ;
uint8_t attemptsLeft = 1;
if (gSceneryPlaceZ != 0 && gSceneryShiftPressed)
{
attemptsLeft = 20;
}
cost = 0;
for (; attemptsLeft != 0; attemptsLeft--)
{
cost = TryPlaceGhostWall(
{ mapTile, gSceneryPlaceZ }, edge, selection.EntryIndex, gWindowSceneryPrimaryColour,
gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour);
if (cost != kMoney64Undefined)
break;
gSceneryPlaceZ += 8;
}
2023-02-14 22:19:42 +01:00
gSceneryPlaceCost = cost;
break;
}
case SCENERY_TYPE_LARGE:
{
CoordsXY mapTile = {};
Direction direction;
Sub6E1F34LargeScenery(screenPos, selection.EntryIndex, mapTile, &direction);
if (mapTile.IsNull())
{
SceneryRemoveGhostToolPlacement();
return;
}
auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry<LargeSceneryEntry>(selection.EntryIndex);
gMapSelectionTiles.clear();
for (auto* tile = sceneryEntry->tiles;
tile->x_offset != static_cast<int16_t>(static_cast<uint16_t>(0xFFFF)); tile++)
{
CoordsXY tileLocation = { tile->x_offset, tile->y_offset };
auto rotatedTileCoords = tileLocation.Rotate(direction);
rotatedTileCoords.x += mapTile.x;
rotatedTileCoords.y += mapTile.y;
gMapSelectionTiles.push_back(rotatedTileCoords);
}
gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT;
MapInvalidateMapSelectionTiles();
// If no change in ghost placement
if ((gSceneryGhostType & SCENERY_GHOST_FLAG_3) && mapTile == gSceneryGhostPosition
&& gSceneryPlaceZ == _unkF64F0A && gSceneryPlaceObject.SceneryType == SCENERY_TYPE_LARGE
&& gSceneryPlaceObject.EntryIndex == selection.EntryIndex)
{
return;
}
SceneryRemoveGhostToolPlacement();
gSceneryPlaceObject.SceneryType = SCENERY_TYPE_LARGE;
gSceneryPlaceObject.EntryIndex = selection.EntryIndex;
_unkF64F0A = gSceneryPlaceZ;
uint8_t attemptsLeft = 1;
if (gSceneryPlaceZ != 0 && gSceneryShiftPressed)
{
attemptsLeft = 20;
}
cost = 0;
for (; attemptsLeft != 0; attemptsLeft--)
{
cost = TryPlaceGhostLargeScenery(
{ mapTile, gSceneryPlaceZ, direction }, selection.EntryIndex, gWindowSceneryPrimaryColour,
gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour);
if (cost != kMoney64Undefined)
break;
gSceneryPlaceZ += COORDS_Z_STEP;
}
gSceneryPlaceCost = cost;
break;
}
case SCENERY_TYPE_BANNER:
{
CoordsXY mapTile = {};
Direction direction;
int32_t z;
Sub6E1F34Banner(screenPos, selection.EntryIndex, mapTile, &z, &direction);
if (mapTile.IsNull())
{
SceneryRemoveGhostToolPlacement();
return;
}
gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE;
gMapSelectPositionA.x = mapTile.x;
gMapSelectPositionA.y = mapTile.y;
gMapSelectPositionB.x = mapTile.x;
gMapSelectPositionB.y = mapTile.y;
gMapSelectType = MAP_SELECT_TYPE_FULL;
MapInvalidateSelectionRect();
// If no change in ghost placement
if ((gSceneryGhostType & SCENERY_GHOST_FLAG_4) && mapTile == gSceneryGhostPosition
&& z == gSceneryGhostPosition.z && direction == gSceneryPlaceRotation)
{
return;
}
SceneryRemoveGhostToolPlacement();
cost = TryPlaceGhostBanner({ mapTile, z, direction }, selection.EntryIndex);
gSceneryPlaceCost = cost;
break;
}
}
}
/**
*
* rct2: 0x006644DD
*/
money64 SelectionRaiseLand(uint8_t flag)
{
int32_t centreX = (gMapSelectPositionA.x + gMapSelectPositionB.x) / 2;
int32_t centreY = (gMapSelectPositionA.y + gMapSelectPositionB.y) / 2;
centreX += 16;
centreY += 16;
if (gLandMountainMode)
{
auto landSmoothAction = LandSmoothAction(
{ centreX, centreY },
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y },
gMapSelectType, false);
auto res = (flag & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landSmoothAction)
: GameActions::Query(&landSmoothAction);
return res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined;
}
auto landRaiseAction = LandRaiseAction(
{ centreX, centreY },
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, gMapSelectType);
auto res = (flag & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landRaiseAction)
: GameActions::Query(&landRaiseAction);
return res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined;
2023-02-14 22:19:42 +01:00
}
/**
*
* rct2: 0x006645B3
*/
money64 SelectionLowerLand(uint8_t flag)
{
int32_t centreX = (gMapSelectPositionA.x + gMapSelectPositionB.x) / 2;
int32_t centreY = (gMapSelectPositionA.y + gMapSelectPositionB.y) / 2;
centreX += 16;
centreY += 16;
if (gLandMountainMode)
2023-02-14 22:19:42 +01:00
{
auto landSmoothAction = LandSmoothAction(
{ centreX, centreY },
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y },
gMapSelectType, true);
auto res = (flag & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landSmoothAction)
: GameActions::Query(&landSmoothAction);
return res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined;
2023-02-14 22:19:42 +01:00
}
auto landLowerAction = LandLowerAction(
{ centreX, centreY },
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, gMapSelectType);
auto res = (flag & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landLowerAction)
: GameActions::Query(&landLowerAction);
return res.Error == GameActions::Status::Ok ? res.Cost : kMoney64Undefined;
}
/**
* part of window_top_toolbar_tool_drag(0x0066CB4E)
* rct2: 0x00664454
*/
void LandToolDrag(const ScreenCoordsXY& screenPos)
{
auto* window = WindowFindFromPoint(screenPos);
if (window == nullptr)
return;
WidgetIndex widget_index = WindowFindWidgetFromPoint(*window, screenPos);
if (widget_index == -1)
return;
const auto& widget = window->widgets[widget_index];
if (widget.type != WindowWidgetType::Viewport)
return;
const auto* selectedViewport = window->viewport;
if (selectedViewport == nullptr)
2023-02-14 22:19:42 +01:00
return;
int16_t tile_height = selectedViewport->zoom.ApplyInversedTo(-16);
int32_t y_diff = screenPos.y - gInputDragLast.y;
if (y_diff <= tile_height)
{
gInputDragLast.y += tile_height;
SelectionRaiseLand(GAME_COMMAND_FLAG_APPLY);
gLandToolRaiseCost = kMoney64Undefined;
gLandToolLowerCost = kMoney64Undefined;
}
else if (y_diff >= -tile_height)
{
gInputDragLast.y -= tile_height;
SelectionLowerLand(GAME_COMMAND_FLAG_APPLY);
2019-03-04 17:57:01 +01:00
gLandToolRaiseCost = kMoney64Undefined;
gLandToolLowerCost = kMoney64Undefined;
}
2023-02-14 22:19:42 +01:00
}
/**
* part of window_top_toolbar_tool_drag(0x0066CB4E)
* rct2: 0x006E6D4B
*/
void WaterToolDrag(const ScreenCoordsXY& screenPos)
2023-02-14 22:19:42 +01:00
{
auto* window = WindowFindFromPoint(screenPos);
if (window == nullptr || window->viewport == nullptr)
return;
2023-02-14 22:19:42 +01:00
int16_t dx = window->viewport->zoom.ApplyInversedTo(-16);
auto offsetPos = screenPos - ScreenCoordsXY{ 0, gInputDragLast.y };
if (offsetPos.y <= dx)
2023-02-14 22:19:42 +01:00
{
gInputDragLast.y += dx;
auto waterRaiseAction = WaterRaiseAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y });
GameActions::Execute(&waterRaiseAction);
gWaterToolRaiseCost = kMoney64Undefined;
gWaterToolLowerCost = kMoney64Undefined;
return;
2023-02-14 22:19:42 +01:00
}
dx = -dx;
if (offsetPos.y >= dx)
2023-02-14 22:19:42 +01:00
{
gInputDragLast.y += dx;
auto waterLowerAction = WaterLowerAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y });
GameActions::Execute(&waterLowerAction);
gWaterToolRaiseCost = kMoney64Undefined;
gWaterToolLowerCost = kMoney64Undefined;
return;
2023-02-14 22:19:42 +01:00
}
}
/**
*
* rct2: 0x006E24F6
* On failure returns kMoney64Undefined
* On success places ghost scenery and returns cost to place proper
*/
money64 TryPlaceGhostSmallScenery(
CoordsXYZD loc, uint8_t quadrant, ObjectEntryIndex entryIndex, colour_t primaryColour, colour_t secondaryColour,
colour_t tertiaryColour)
{
SceneryRemoveGhostToolPlacement();
// 6e252b
auto smallSceneryPlaceAction = SmallSceneryPlaceAction(
loc, quadrant, entryIndex, primaryColour, secondaryColour, tertiaryColour);
smallSceneryPlaceAction.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED);
auto res = GameActions::Execute(&smallSceneryPlaceAction);
if (res.Error != GameActions::Status::Ok)
return kMoney64Undefined;
const auto placementData = res.GetData<SmallSceneryPlaceActionResult>();
gSceneryPlaceRotation = loc.direction;
gSceneryPlaceObject.SceneryType = SCENERY_TYPE_SMALL;
gSceneryPlaceObject.EntryIndex = entryIndex;
gSceneryGhostPosition = { loc, placementData.BaseHeight };
gSceneryQuadrant = placementData.SceneryQuadrant;
if (placementData.GroundFlags & ELEMENT_IS_UNDERGROUND)
2023-02-14 22:19:42 +01:00
{
// Set underground on
ViewportSetVisibility(ViewportVisibility::UndergroundViewGhostOn);
}
else
{
// Set underground off
ViewportSetVisibility(ViewportVisibility::UndergroundViewGhostOff);
}
gSceneryGhostType |= SCENERY_GHOST_FLAG_0;
return res.Cost;
}
money64 TryPlaceGhostPathAddition(CoordsXYZ loc, ObjectEntryIndex entryIndex)
{
SceneryRemoveGhostToolPlacement();
// 6e265b
auto footpathAdditionPlaceAction = FootpathAdditionPlaceAction(loc, entryIndex);
footpathAdditionPlaceAction.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED);
footpathAdditionPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) {
if (result->Error != GameActions::Status::Ok)
{
return;
2023-02-14 22:19:42 +01:00
}
gSceneryGhostPosition = loc;
gSceneryGhostType |= SCENERY_GHOST_FLAG_1;
});
auto res = GameActions::Execute(&footpathAdditionPlaceAction);
if (res.Error != GameActions::Status::Ok)
return kMoney64Undefined;
return res.Cost;
}
money64 TryPlaceGhostWall(
CoordsXYZ loc, uint8_t edge, ObjectEntryIndex entryIndex, colour_t primaryColour, colour_t secondaryColour,
colour_t tertiaryColour)
{
SceneryRemoveGhostToolPlacement();
// 6e26b0
auto wallPlaceAction = WallPlaceAction(entryIndex, loc, edge, primaryColour, secondaryColour, tertiaryColour);
wallPlaceAction.SetFlags(
GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND);
wallPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) {
if (result->Error != GameActions::Status::Ok)
return;
const auto placementData = result->GetData<WallPlaceActionResult>();
gSceneryGhostPosition = { loc, placementData.BaseHeight };
gSceneryGhostWallRotation = edge;
gSceneryGhostType |= SCENERY_GHOST_FLAG_2;
});
auto res = GameActions::Execute(&wallPlaceAction);
if (res.Error != GameActions::Status::Ok)
return kMoney64Undefined;
return res.Cost;
}
money64 TryPlaceGhostLargeScenery(
CoordsXYZD loc, ObjectEntryIndex entryIndex, colour_t primaryColour, colour_t secondaryColour,
colour_t tertiaryColour)
{
SceneryRemoveGhostToolPlacement();
// 6e25a7
auto sceneryPlaceAction = LargeSceneryPlaceAction(loc, entryIndex, primaryColour, secondaryColour, tertiaryColour);
sceneryPlaceAction.SetFlags(
GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND);
auto res = GameActions::Execute(&sceneryPlaceAction);
if (res.Error != GameActions::Status::Ok)
return kMoney64Undefined;
const auto placementData = res.GetData<LargeSceneryPlaceActionResult>();
gSceneryPlaceRotation = loc.direction;
gSceneryGhostPosition = { loc, placementData.firstTileHeight };
if (placementData.GroundFlags & ELEMENT_IS_UNDERGROUND)
{
// Set underground on
ViewportSetVisibility(ViewportVisibility::UndergroundViewGhostOn);
2023-02-14 22:19:42 +01:00
}
else
{
// Set underground off
ViewportSetVisibility(ViewportVisibility::UndergroundViewGhostOff);
}
gSceneryGhostType |= SCENERY_GHOST_FLAG_3;
return res.Cost;
2023-02-14 22:19:42 +01:00
}
money64 TryPlaceGhostBanner(CoordsXYZD loc, ObjectEntryIndex entryIndex)
{
SceneryRemoveGhostToolPlacement();
// 6e2612
auto primaryColour = gWindowSceneryPrimaryColour;
auto bannerPlaceAction = BannerPlaceAction(loc, entryIndex, primaryColour);
bannerPlaceAction.SetFlags(
GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND);
auto res = GameActions::Execute(&bannerPlaceAction);
if (res.Error != GameActions::Status::Ok)
return kMoney64Undefined;
gSceneryGhostPosition = loc;
gSceneryGhostPosition.z += PATH_HEIGHT_STEP;
gSceneryPlaceRotation = loc.direction;
gSceneryGhostType |= SCENERY_GHOST_FLAG_4;
return res.Cost;
}
/**
*
* rct2: 0x006E3158
*/
void RepaintSceneryToolDown(const ScreenCoordsXY& screenCoords, WidgetIndex widgetIndex)
2023-02-14 22:19:42 +01:00
{
auto flag = EnumsToFlags(
ViewportInteractionItem::Scenery, ViewportInteractionItem::Wall, ViewportInteractionItem::LargeScenery,
ViewportInteractionItem::Banner);
auto info = GetMapCoordinatesFromPos(screenCoords, flag);
switch (info.SpriteType)
2023-02-14 22:19:42 +01:00
{
case ViewportInteractionItem::Scenery:
{
auto* sceneryEntry = info.Element->AsSmallScenery()->GetEntry();
// If can't repaint
if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR | SMALL_SCENERY_FLAG_HAS_GLASS))
return;
uint8_t quadrant = info.Element->AsSmallScenery()->GetSceneryQuadrant();
auto repaintScenery = SmallScenerySetColourAction(
{ info.Loc, info.Element->GetBaseZ() }, quadrant, info.Element->AsSmallScenery()->GetEntryIndex(),
gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour);
GameActions::Execute(&repaintScenery);
break;
}
case ViewportInteractionItem::Wall:
2023-02-14 22:19:42 +01:00
{
auto* scenery_entry = info.Element->AsWall()->GetEntry();
// If can't repaint
if (!(scenery_entry->flags & (WALL_SCENERY_HAS_PRIMARY_COLOUR | WALL_SCENERY_HAS_GLASS)))
return;
auto repaintScenery = WallSetColourAction(
{ info.Loc, info.Element->GetBaseZ(), info.Element->GetDirection() }, gWindowSceneryPrimaryColour,
gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour);
GameActions::Execute(&repaintScenery);
break;
2023-02-14 22:19:42 +01:00
}
case ViewportInteractionItem::LargeScenery:
2023-02-14 22:19:42 +01:00
{
auto* sceneryEntry = info.Element->AsLargeScenery()->GetEntry();
// If can't repaint
if (!(sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR))
return;
auto repaintScenery = LargeScenerySetColourAction(
{ info.Loc, info.Element->GetBaseZ(), info.Element->GetDirection() },
info.Element->AsLargeScenery()->GetSequenceIndex(), gWindowSceneryPrimaryColour,
gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour);
GameActions::Execute(&repaintScenery);
break;
2023-02-14 22:19:42 +01:00
}
case ViewportInteractionItem::Banner:
2023-02-14 22:19:42 +01:00
{
auto banner = info.Element->AsBanner()->GetBanner();
if (banner != nullptr)
{
auto* bannerEntry = OpenRCT2::ObjectManager::GetObjectEntry<BannerSceneryEntry>(banner->type);
if (bannerEntry->flags & BANNER_ENTRY_FLAG_HAS_PRIMARY_COLOUR)
{
auto repaintScenery = BannerSetColourAction(
{ info.Loc, info.Element->GetBaseZ(), info.Element->AsBanner()->GetPosition() },
gWindowSceneryPrimaryColour);
GameActions::Execute(&repaintScenery);
}
}
break;
2023-02-14 22:19:42 +01:00
}
default:
return;
2023-02-14 22:19:42 +01:00
}
}
void SceneryEyedropperToolDown(const ScreenCoordsXY& screenCoords, WidgetIndex widgetIndex)
{
auto flag = EnumsToFlags(
ViewportInteractionItem::Scenery, ViewportInteractionItem::Wall, ViewportInteractionItem::LargeScenery,
ViewportInteractionItem::Banner, ViewportInteractionItem::PathAddition);
auto info = GetMapCoordinatesFromPos(screenCoords, flag);
switch (info.SpriteType)
2023-02-14 22:19:42 +01:00
{
case ViewportInteractionItem::Scenery:
{
SmallSceneryElement* sceneryElement = info.Element->AsSmallScenery();
auto entryIndex = sceneryElement->GetEntryIndex();
auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry<SmallSceneryEntry>(entryIndex);
if (sceneryEntry != nullptr)
{
WindowScenerySetSelectedItem(
{ SCENERY_TYPE_SMALL, entryIndex }, sceneryElement->GetPrimaryColour(),
sceneryElement->GetSecondaryColour(), sceneryElement->GetTertiaryColour(),
sceneryElement->GetDirectionWithOffset(GetCurrentRotation()));
}
break;
}
case ViewportInteractionItem::Wall:
2023-02-14 22:19:42 +01:00
{
auto entryIndex = info.Element->AsWall()->GetEntryIndex();
auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry<WallSceneryEntry>(entryIndex);
2023-02-14 22:19:42 +01:00
if (sceneryEntry != nullptr)
{
WindowScenerySetSelectedItem(
{ SCENERY_TYPE_WALL, entryIndex }, info.Element->AsWall()->GetPrimaryColour(),
info.Element->AsWall()->GetSecondaryColour(), info.Element->AsWall()->GetTertiaryColour(),
std::nullopt);
2023-02-14 22:19:42 +01:00
}
break;
2023-02-14 22:19:42 +01:00
}
case ViewportInteractionItem::LargeScenery:
2023-02-14 22:19:42 +01:00
{
auto entryIndex = info.Element->AsLargeScenery()->GetEntryIndex();
auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry<LargeSceneryEntry>(entryIndex);
if (sceneryEntry != nullptr)
{
WindowScenerySetSelectedItem(
{ SCENERY_TYPE_LARGE, entryIndex }, info.Element->AsLargeScenery()->GetPrimaryColour(),
info.Element->AsLargeScenery()->GetSecondaryColour(), std::nullopt,
(GetCurrentRotation() + info.Element->GetDirection()) & 3);
}
break;
2023-02-14 22:19:42 +01:00
}
case ViewportInteractionItem::Banner:
{
auto banner = info.Element->AsBanner()->GetBanner();
if (banner != nullptr)
{
auto sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry<BannerSceneryEntry>(banner->type);
if (sceneryEntry != nullptr)
{
WindowScenerySetSelectedItem(
{ SCENERY_TYPE_BANNER, banner->type }, std::nullopt, std::nullopt, std::nullopt, std::nullopt);
}
}
break;
}
case ViewportInteractionItem::PathAddition:
{
auto entryIndex = info.Element->AsPath()->GetAdditionEntryIndex();
auto* pathAdditionEntry = OpenRCT2::ObjectManager::GetObjectEntry<PathAdditionEntry>(entryIndex);
if (pathAdditionEntry != nullptr)
{
WindowScenerySetSelectedItem(
{ SCENERY_TYPE_PATH_ITEM, entryIndex }, std::nullopt, std::nullopt, std::nullopt, std::nullopt);
}
break;
}
default:
break;
2023-02-14 22:19:42 +01:00
}
}
void Sub6E1F34UpdateScreenCoordsAndButtonsPressed(bool canRaiseItem, ScreenCoordsXY& screenPos)
2023-02-14 22:19:42 +01:00
{
if (!canRaiseItem && !GetGameState().Cheats.DisableSupportLimits)
{
gSceneryCtrlPressed = false;
gSceneryShiftPressed = false;
}
else
2023-02-14 22:19:42 +01:00
{
if (!gSceneryCtrlPressed)
2023-02-14 22:19:42 +01:00
{
if (InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z))
{
// CTRL pressed
constexpr auto flag = EnumsToFlags(
ViewportInteractionItem::Terrain, ViewportInteractionItem::Ride, ViewportInteractionItem::Scenery,
ViewportInteractionItem::Footpath, ViewportInteractionItem::Wall,
ViewportInteractionItem::LargeScenery);
auto info = GetMapCoordinatesFromPos(screenPos, flag);
if (info.SpriteType != ViewportInteractionItem::None)
{
gSceneryCtrlPressed = true;
gSceneryCtrlPressZ = info.Element->GetBaseZ();
}
}
}
else
{
if (!(InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z)))
{
// CTRL not pressed
gSceneryCtrlPressed = false;
}
}
2023-02-14 22:19:42 +01:00
if (!gSceneryShiftPressed)
{
if (InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_SHIFT_Z))
2023-02-14 22:19:42 +01:00
{
// SHIFT pressed
gSceneryShiftPressed = true;
gSceneryShiftPressX = screenPos.x;
gSceneryShiftPressY = screenPos.y;
gSceneryShiftPressZOffset = 0;
2023-02-14 22:19:42 +01:00
}
}
else
2023-02-14 22:19:42 +01:00
{
if (InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_SHIFT_Z))
{
// SHIFT pressed
gSceneryShiftPressZOffset = (gSceneryShiftPressY - screenPos.y + 4);
// Scale delta by zoom to match mouse position.
auto* mainWnd = WindowGetMain();
if (mainWnd != nullptr && mainWnd->viewport != nullptr)
{
gSceneryShiftPressZOffset = mainWnd->viewport->zoom.ApplyTo(gSceneryShiftPressZOffset);
}
gSceneryShiftPressZOffset = Floor2(gSceneryShiftPressZOffset, 8);
screenPos.x = gSceneryShiftPressX;
screenPos.y = gSceneryShiftPressY;
}
else
{
// SHIFT not pressed
gSceneryShiftPressed = false;
}
2023-02-14 22:19:42 +01:00
}
}
}
void Sub6E1F34SmallScenery(
const ScreenCoordsXY& sourceScreenPos, ObjectEntryIndex sceneryIndex, CoordsXY& gridPos, uint8_t* outQuadrant,
Direction* outRotation)
{
auto* w = WindowFindByClass(WindowClass::Scenery);
if (w == nullptr)
{
gridPos.SetNull();
return;
}
auto screenPos = sourceScreenPos;
uint16_t maxPossibleHeight = ZoomLevel::max().ApplyTo(
std::numeric_limits<decltype(TileElement::BaseHeight)>::max() - 32);
bool can_raise_item = false;
const auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry<SmallSceneryEntry>(sceneryIndex);
if (sceneryEntry == nullptr)
{
gridPos.SetNull();
return;
}
maxPossibleHeight -= sceneryEntry->height;
if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_STACKABLE))
2023-02-14 22:19:42 +01:00
{
can_raise_item = true;
2023-02-14 22:19:42 +01:00
}
Sub6E1F34UpdateScreenCoordsAndButtonsPressed(can_raise_item, screenPos);
// Small scenery
if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE))
2023-02-14 22:19:42 +01:00
{
uint8_t quadrant = 0;
// If CTRL not pressed
if (!gSceneryCtrlPressed)
2023-02-14 22:19:42 +01:00
{
auto gridCoords = ScreenGetMapXYQuadrant(screenPos, &quadrant);
if (!gridCoords.has_value())
2023-02-14 22:19:42 +01:00
{
gridPos.SetNull();
return;
2023-02-14 22:19:42 +01:00
}
gridPos = gridCoords.value();
gSceneryPlaceZ = 0;
// If SHIFT pressed
if (gSceneryShiftPressed)
{
auto* surfaceElement = MapGetSurfaceElementAt(gridPos);
if (surfaceElement == nullptr)
{
gridPos.SetNull();
return;
}
int16_t z = (surfaceElement->GetBaseZ()) & 0xFFF0;
z += gSceneryShiftPressZOffset;
z = std::clamp<int16_t>(z, 16, maxPossibleHeight);
gSceneryPlaceZ = z;
}
2023-02-14 22:19:42 +01:00
}
else
{
int16_t z = gSceneryCtrlPressZ;
auto mapCoords = ScreenGetMapXYQuadrantWithZ(screenPos, z, &quadrant);
if (!mapCoords.has_value())
{
gridPos.SetNull();
return;
}
gridPos = mapCoords.value();
// If SHIFT pressed
if (gSceneryShiftPressed)
{
z += gSceneryShiftPressZOffset;
}
z = std::clamp<int16_t>(z, 16, maxPossibleHeight);
gSceneryPlaceZ = z;
2023-02-14 22:19:42 +01:00
}
if (gridPos.IsNull())
return;
uint8_t rotation = gWindowSceneryRotation;
if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ROTATABLE))
{
rotation = UtilRand() & 0xFF;
}
rotation -= GetCurrentRotation();
rotation &= 0x3;
if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off)
{
VirtualFloorSetHeight(gSceneryPlaceZ);
}
2018-06-22 23:21:44 +02:00
*outQuadrant = quadrant ^ 2;
*outRotation = rotation;
2018-06-22 23:21:44 +02:00
return;
}
2018-06-22 23:21:44 +02:00
2023-02-14 22:19:42 +01:00
// If CTRL not pressed
if (!gSceneryCtrlPressed)
{
constexpr auto flag = EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Water);
auto info = GetMapCoordinatesFromPos(screenPos, flag);
gridPos = info.Loc;
if (info.SpriteType == ViewportInteractionItem::None)
2023-02-14 22:19:42 +01:00
{
gridPos.SetNull();
return;
}
2018-06-22 23:21:44 +02:00
// If CTRL and SHIFT not pressed
2023-02-14 22:19:42 +01:00
gSceneryPlaceZ = 0;
2018-06-22 23:21:44 +02:00
2023-02-14 22:19:42 +01:00
// If SHIFT pressed
if (gSceneryShiftPressed)
{
auto surfaceElement = MapGetSurfaceElementAt(gridPos);
2023-02-14 22:19:42 +01:00
if (surfaceElement == nullptr)
{
gridPos.SetNull();
return;
}
2023-02-14 22:19:42 +01:00
int16_t z = (surfaceElement->GetBaseZ()) & 0xFFF0;
z += gSceneryShiftPressZOffset;
2023-02-14 22:19:42 +01:00
z = std::clamp<int16_t>(z, 16, maxPossibleHeight);
2023-02-14 22:19:42 +01:00
gSceneryPlaceZ = z;
}
}
else
{
int16_t z = gSceneryCtrlPressZ;
auto coords = ScreenGetMapXYWithZ(screenPos, z);
if (coords.has_value())
{
gridPos = *coords;
}
else
2023-02-14 22:19:42 +01:00
{
gridPos.SetNull();
}
// If SHIFT pressed
if (gSceneryShiftPressed)
{
z += gSceneryShiftPressZOffset;
}
2023-02-14 22:19:42 +01:00
z = std::clamp<int16_t>(z, 16, maxPossibleHeight);
2023-02-14 22:19:42 +01:00
gSceneryPlaceZ = z;
}
2023-02-14 22:19:42 +01:00
if (gridPos.IsNull())
return;
gridPos = gridPos.ToTileStart();
2023-02-14 22:19:42 +01:00
uint8_t rotation = gWindowSceneryRotation;
2023-02-14 22:19:42 +01:00
if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ROTATABLE))
{
rotation = UtilRand() & 0xFF;
}
2023-02-14 22:19:42 +01:00
rotation -= GetCurrentRotation();
rotation &= 0x3;
2023-02-14 22:19:42 +01:00
if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off)
{
VirtualFloorSetHeight(gSceneryPlaceZ);
}
*outQuadrant = 0;
2023-02-14 22:19:42 +01:00
*outRotation = rotation;
}
void Sub6E1F34PathItem(
const ScreenCoordsXY& sourceScreenPos, ObjectEntryIndex sceneryIndex, CoordsXY& gridPos, int32_t* outZ)
{
auto* w = WindowFindByClass(WindowClass::Scenery);
if (w == nullptr)
{
gridPos.SetNull();
return;
}
auto screenPos = sourceScreenPos;
Sub6E1F34UpdateScreenCoordsAndButtonsPressed(false, screenPos);
// Path bits
constexpr auto flag = EnumsToFlags(ViewportInteractionItem::Footpath, ViewportInteractionItem::PathAddition);
2023-02-14 22:19:42 +01:00
auto info = GetMapCoordinatesFromPos(screenPos, flag);
gridPos = info.Loc;
2023-02-14 22:19:42 +01:00
if (info.SpriteType == ViewportInteractionItem::None)
{
2023-02-14 22:19:42 +01:00
gridPos.SetNull();
return;
}
if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off)
2018-06-22 23:21:44 +02:00
{
VirtualFloorSetHeight(gSceneryPlaceZ);
}
2023-02-14 22:19:42 +01:00
*outZ = info.Element->GetBaseZ();
}
2023-02-14 22:19:42 +01:00
void Sub6E1F34Wall(
const ScreenCoordsXY& sourceScreenPos, ObjectEntryIndex sceneryIndex, CoordsXY& gridPos, uint8_t* outEdges)
{
auto* w = WindowFindByClass(WindowClass::Scenery);
2023-02-14 22:19:42 +01:00
if (w == nullptr)
{
gridPos.SetNull();
return;
}
2023-02-14 22:19:42 +01:00
auto screenPos = sourceScreenPos;
uint16_t maxPossibleHeight = ZoomLevel::max().ApplyTo(
std::numeric_limits<decltype(TileElement::BaseHeight)>::max() - 32);
auto* wallEntry = OpenRCT2::ObjectManager::GetObjectEntry<WallSceneryEntry>(sceneryIndex);
if (wallEntry != nullptr)
2018-06-22 23:21:44 +02:00
{
maxPossibleHeight -= wallEntry->height;
2018-06-22 23:21:44 +02:00
}
Sub6E1F34UpdateScreenCoordsAndButtonsPressed(true, screenPos);
// Walls
uint8_t edge;
// If CTRL not pressed
if (!gSceneryCtrlPressed)
{
auto gridCoords = ScreenGetMapXYSide(screenPos, &edge);
if (!gridCoords.has_value())
{
gridPos.SetNull();
return;
}
gridPos = gridCoords.value();
gSceneryPlaceZ = 0;
2023-02-14 22:19:42 +01:00
// If SHIFT pressed
if (gSceneryShiftPressed)
{
auto* surfaceElement = MapGetSurfaceElementAt(gridPos);
2023-02-14 22:19:42 +01:00
if (surfaceElement == nullptr)
{
gridPos.SetNull();
return;
}
2023-02-14 22:19:42 +01:00
int16_t z = (surfaceElement->GetBaseZ()) & 0xFFF0;
z += gSceneryShiftPressZOffset;
z = std::clamp<int16_t>(z, 16, maxPossibleHeight);
gSceneryPlaceZ = z;
}
}
else
2018-06-22 23:21:44 +02:00
{
int16_t z = gSceneryCtrlPressZ;
auto mapCoords = ScreenGetMapXYSideWithZ(screenPos, z, &edge);
if (!mapCoords.has_value())
2023-02-14 22:19:42 +01:00
{
gridPos.SetNull();
return;
}
gridPos = mapCoords.value();
2018-06-22 23:21:44 +02:00
// If SHIFT pressed
if (gSceneryShiftPressed)
{
z += gSceneryShiftPressZOffset;
}
2023-02-14 22:19:42 +01:00
z = std::clamp<int16_t>(z, 16, maxPossibleHeight);
2023-02-14 22:19:42 +01:00
gSceneryPlaceZ = z;
2018-06-22 23:21:44 +02:00
}
if (gridPos.IsNull())
return;
if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off)
2018-06-22 23:21:44 +02:00
{
VirtualFloorSetHeight(gSceneryPlaceZ);
2018-06-22 23:21:44 +02:00
}
*outEdges = edge;
2023-02-14 22:19:42 +01:00
}
void Sub6E1F34LargeScenery(
const ScreenCoordsXY& sourceScreenPos, ObjectEntryIndex sceneryIndex, CoordsXY& gridPos, Direction* outDirection)
2023-02-14 22:19:42 +01:00
{
auto* w = WindowFindByClass(WindowClass::Scenery);
if (w == nullptr)
{
gridPos.SetNull();
return;
}
auto screenPos = sourceScreenPos;
uint16_t maxPossibleHeight = ZoomLevel::max().ApplyTo(
std::numeric_limits<decltype(TileElement::BaseHeight)>::max() - 32);
auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry<LargeSceneryEntry>(sceneryIndex);
if (sceneryEntry)
{
int16_t maxClearZ = 0;
for (int32_t i = 0; sceneryEntry->tiles[i].x_offset != -1; ++i)
{
maxClearZ = std::max<int16_t>(maxClearZ, sceneryEntry->tiles[i].z_clearance);
}
maxPossibleHeight = std::max(0, maxPossibleHeight - maxClearZ);
}
Sub6E1F34UpdateScreenCoordsAndButtonsPressed(true, screenPos);
// Large scenery
// If CTRL not pressed
if (!gSceneryCtrlPressed)
{
const CoordsXY mapCoords = ViewportInteractionGetTileStartAtCursor(screenPos);
gridPos = mapCoords;
if (gridPos.IsNull())
return;
gSceneryPlaceZ = 0;
// If SHIFT pressed
if (gSceneryShiftPressed)
{
auto* surfaceElement = MapGetSurfaceElementAt(gridPos);
if (surfaceElement == nullptr)
{
gridPos.SetNull();
return;
}
int16_t z = (surfaceElement->GetBaseZ()) & 0xFFF0;
z += gSceneryShiftPressZOffset;
2015-05-28 20:33:09 +02:00
z = std::clamp<int16_t>(z, 16, maxPossibleHeight);
gSceneryPlaceZ = z;
}
}
else
{
int16_t z = gSceneryCtrlPressZ;
auto coords = ScreenGetMapXYWithZ(screenPos, z);
if (coords.has_value())
{
gridPos = *coords;
}
else
2023-02-14 22:19:42 +01:00
{
gridPos.SetNull();
}
// If SHIFT pressed
if (gSceneryShiftPressed)
{
z += gSceneryShiftPressZOffset;
}
2023-02-14 22:19:42 +01:00
z = std::clamp<int16_t>(z, 16, maxPossibleHeight);
gSceneryPlaceZ = z;
}
if (gridPos.IsNull())
return;
2019-03-04 17:57:01 +01:00
gridPos = gridPos.ToTileStart();
2021-09-15 22:22:15 +02:00
Direction rotation = gWindowSceneryRotation;
rotation -= GetCurrentRotation();
rotation &= 0x3;
if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off)
{
VirtualFloorSetHeight(gSceneryPlaceZ);
}
*outDirection = rotation;
2023-02-14 22:19:42 +01:00
}
void Sub6E1F34Banner(
const ScreenCoordsXY& sourceScreenPos, ObjectEntryIndex sceneryIndex, CoordsXY& gridPos, int32_t* outZ,
Direction* outDirection)
{
auto* w = WindowFindByClass(WindowClass::Scenery);
2019-03-04 17:57:01 +01:00
if (w == nullptr)
{
gridPos.SetNull();
return;
}
2021-09-15 22:22:15 +02:00
auto screenPos = sourceScreenPos;
Sub6E1F34UpdateScreenCoordsAndButtonsPressed(false, screenPos);
// Banner
constexpr auto flag = EnumsToFlags(ViewportInteractionItem::Footpath, ViewportInteractionItem::PathAddition);
auto info = GetMapCoordinatesFromPos(screenPos, flag);
gridPos = info.Loc;
if (info.SpriteType == ViewportInteractionItem::None)
{
gridPos.SetNull();
return;
}
uint8_t rotation = gWindowSceneryRotation;
rotation -= GetCurrentRotation();
rotation &= 0x3;
auto z = info.Element->GetBaseZ();
if (info.Element->AsPath()->IsSloped())
{
if (rotation != DirectionReverse(info.Element->AsPath()->GetSlopeDirection()))
{
z += (2 * COORDS_Z_STEP);
}
}
if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off)
2023-02-14 22:19:42 +01:00
{
VirtualFloorSetHeight(gSceneryPlaceZ);
2023-02-14 22:19:42 +01:00
}
*outDirection = rotation;
*outZ = z;
2023-02-14 22:19:42 +01:00
}
/**
*
* rct2: 0x006E2CC6
*/
void SceneryToolDown(const ScreenCoordsXY& screenCoords, WidgetIndex widgetIndex)
2023-02-14 22:19:42 +01:00
{
SceneryRemoveGhostToolPlacement();
if (gWindowSceneryPaintEnabled & 1)
{
RepaintSceneryToolDown(screenCoords, widgetIndex);
return;
}
if (gWindowSceneryEyedropperEnabled)
{
SceneryEyedropperToolDown(screenCoords, widgetIndex);
return;
}
2023-02-14 22:19:42 +01:00
auto selectedTab = WindowSceneryGetTabSelection();
uint8_t sceneryType = selectedTab.SceneryType;
uint16_t selectedScenery = selectedTab.EntryIndex;
CoordsXY gridPos;
2023-02-14 22:19:42 +01:00
switch (sceneryType)
2023-02-14 22:19:42 +01:00
{
case SCENERY_TYPE_SMALL:
{
uint8_t quadrant;
Direction rotation;
Sub6E1F34SmallScenery(screenCoords, selectedScenery, gridPos, &quadrant, &rotation);
if (gridPos.IsNull())
return;
2023-02-14 22:19:42 +01:00
int32_t quantity = 1;
bool isCluster = gWindowSceneryScatterEnabled
&& (NetworkGetMode() != NETWORK_MODE_CLIENT
|| NetworkCanPerformCommand(NetworkGetCurrentPlayerGroupIndex(), -2));
2023-02-14 22:19:42 +01:00
if (isCluster)
2023-02-14 22:19:42 +01:00
{
switch (gWindowSceneryScatterDensity)
{
case ScatterToolDensity::LowDensity:
quantity = gWindowSceneryScatterSize;
break;
2023-02-14 22:19:42 +01:00
case ScatterToolDensity::MediumDensity:
quantity = gWindowSceneryScatterSize * 2;
break;
2023-02-14 22:19:42 +01:00
case ScatterToolDensity::HighDensity:
quantity = gWindowSceneryScatterSize * 3;
break;
}
2023-02-14 22:19:42 +01:00
}
bool forceError = true;
for (int32_t q = 0; q < quantity; q++)
{
int32_t zCoordinate = gSceneryPlaceZ;
auto* sceneryEntry = OpenRCT2::ObjectManager::GetObjectEntry<SmallSceneryEntry>(selectedScenery);
2023-02-14 22:19:42 +01:00
int16_t cur_grid_x = gridPos.x;
int16_t cur_grid_y = gridPos.y;
2023-02-14 22:19:42 +01:00
if (isCluster)
2023-02-14 22:19:42 +01:00
{
if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE))
{
quadrant = UtilRand() & 3;
}
int16_t grid_x_offset = (UtilRand() % gWindowSceneryScatterSize) - (gWindowSceneryScatterSize / 2);
int16_t grid_y_offset = (UtilRand() % gWindowSceneryScatterSize) - (gWindowSceneryScatterSize / 2);
if (gWindowSceneryScatterSize % 2 == 0)
{
grid_x_offset += 1;
grid_y_offset += 1;
}
cur_grid_x += grid_x_offset * COORDS_XY_STEP;
cur_grid_y += grid_y_offset * COORDS_XY_STEP;
if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ROTATABLE))
{
gSceneryPlaceRotation = (gSceneryPlaceRotation + 1) & 3;
}
2023-02-14 22:19:42 +01:00
}
uint8_t zAttemptRange = 1;
if (gSceneryPlaceZ != 0 && gSceneryShiftPressed)
2023-02-14 22:19:42 +01:00
{
zAttemptRange = 20;
2023-02-14 22:19:42 +01:00
}
auto success = GameActions::Status::Unknown;
// Try find a valid z coordinate
for (; zAttemptRange != 0; zAttemptRange--)
2023-02-14 22:19:42 +01:00
{
auto smallSceneryPlaceAction = SmallSceneryPlaceAction(
{ cur_grid_x, cur_grid_y, gSceneryPlaceZ, gSceneryPlaceRotation }, quadrant, selectedScenery,
gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour);
auto res = GameActions::Query(&smallSceneryPlaceAction);
success = res.Error;
if (res.Error == GameActions::Status::Ok)
{
break;
}
if (res.Error == GameActions::Status::InsufficientFunds)
{
break;
}
if (zAttemptRange != 1)
{
gSceneryPlaceZ += 8;
}
2023-02-14 22:19:42 +01:00
}
// Actually place
if (success == GameActions::Status::Ok || ((q + 1 == quantity) && forceError))
{
auto smallSceneryPlaceAction = SmallSceneryPlaceAction(
{ cur_grid_x, cur_grid_y, gSceneryPlaceZ, gSceneryPlaceRotation }, quadrant, selectedScenery,
gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour);
smallSceneryPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) {
if (result->Error == GameActions::Status::Ok)
{
OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position);
}
});
auto res = GameActions::Execute(&smallSceneryPlaceAction);
if (res.Error == GameActions::Status::Ok)
{
forceError = false;
}
if (res.Error == GameActions::Status::InsufficientFunds)
{
break;
}
}
gSceneryPlaceZ = zCoordinate;
}
break;
}
case SCENERY_TYPE_PATH_ITEM:
{
int32_t z;
Sub6E1F34PathItem(screenCoords, selectedScenery, gridPos, &z);
if (gridPos.IsNull())
return;
auto footpathAdditionPlaceAction = FootpathAdditionPlaceAction({ gridPos, z }, selectedScenery);
footpathAdditionPlaceAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) {
if (result->Error != GameActions::Status::Ok)
{
return;
}
OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position);
});
auto res = GameActions::Execute(&footpathAdditionPlaceAction);
break;
}
case SCENERY_TYPE_WALL:
{
uint8_t edges;
Sub6E1F34Wall(screenCoords, selectedScenery, gridPos, &edges);
if (gridPos.IsNull())
return;
uint8_t zAttemptRange = 1;
if (gSceneryPlaceZ != 0 && gSceneryShiftPressed)
{
zAttemptRange = 20;
2023-02-14 22:19:42 +01:00
}
for (; zAttemptRange != 0; zAttemptRange--)
{
auto wallPlaceAction = WallPlaceAction(
selectedScenery, { gridPos, gSceneryPlaceZ }, edges, gWindowSceneryPrimaryColour,
gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour);
auto res = GameActions::Query(&wallPlaceAction);
2023-02-14 22:19:42 +01:00
if (res.Error == GameActions::Status::Ok)
{
break;
}
if (const auto* message = std::get_if<StringId>(&res.ErrorMessage))
2023-02-14 22:19:42 +01:00
{
if (*message == STR_NOT_ENOUGH_CASH_REQUIRES || *message == STR_CAN_ONLY_BUILD_THIS_ON_WATER)
{
break;
}
2023-02-14 22:19:42 +01:00
}
2023-02-14 22:19:42 +01:00
if (zAttemptRange != 1)
{
gSceneryPlaceZ += 8;
}
}
auto wallPlaceAction = WallPlaceAction(
selectedScenery, { gridPos, gSceneryPlaceZ }, edges, gWindowSceneryPrimaryColour,
gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour);
2023-02-14 22:19:42 +01:00
wallPlaceAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) {
if (result->Error == GameActions::Status::Ok)
2023-02-14 22:19:42 +01:00
{
OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position);
2023-02-14 22:19:42 +01:00
}
});
auto res = GameActions::Execute(&wallPlaceAction);
break;
2023-02-14 22:19:42 +01:00
}
case SCENERY_TYPE_LARGE:
2023-02-14 22:19:42 +01:00
{
Direction direction;
Sub6E1F34LargeScenery(screenCoords, selectedScenery, gridPos, &direction);
if (gridPos.IsNull())
return;
2023-02-14 22:19:42 +01:00
uint8_t zAttemptRange = 1;
if (gSceneryPlaceZ != 0 && gSceneryShiftPressed)
2023-02-14 22:19:42 +01:00
{
zAttemptRange = 20;
2023-02-14 22:19:42 +01:00
}
for (; zAttemptRange != 0; zAttemptRange--)
2023-02-14 22:19:42 +01:00
{
CoordsXYZD loc = { gridPos, gSceneryPlaceZ, direction };
auto sceneryPlaceAction = LargeSceneryPlaceAction(
loc, selectedScenery, gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour,
gWindowSceneryTertiaryColour);
auto res = GameActions::Query(&sceneryPlaceAction);
if (res.Error == GameActions::Status::Ok)
2023-02-14 22:19:42 +01:00
{
break;
}
if (const auto* message = std::get_if<StringId>(&res.ErrorMessage))
{
if (*message == STR_NOT_ENOUGH_CASH_REQUIRES || *message == STR_CAN_ONLY_BUILD_THIS_ON_WATER)
{
break;
}
}
2023-02-14 22:19:42 +01:00
if (zAttemptRange != 1)
{
gSceneryPlaceZ += 8;
}
2023-02-14 22:19:42 +01:00
}
CoordsXYZD loc = { gridPos, gSceneryPlaceZ, direction };
auto sceneryPlaceAction = LargeSceneryPlaceAction(
loc, selectedScenery, gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour,
gWindowSceneryTertiaryColour);
sceneryPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) {
if (result->Error == GameActions::Status::Ok)
2023-02-14 22:19:42 +01:00
{
OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position);
2023-02-14 22:19:42 +01:00
}
else
{
OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::Error, { loc.x, loc.y, gSceneryPlaceZ });
}
});
auto res = GameActions::Execute(&sceneryPlaceAction);
break;
}
case SCENERY_TYPE_BANNER:
{
int32_t z;
Direction direction;
Sub6E1F34Banner(screenCoords, selectedScenery, gridPos, &z, &direction);
if (gridPos.IsNull())
return;
2023-02-14 22:19:42 +01:00
CoordsXYZD loc{ gridPos, z, direction };
auto primaryColour = gWindowSceneryPrimaryColour;
auto bannerPlaceAction = BannerPlaceAction(loc, selectedScenery, primaryColour);
bannerPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) {
if (result->Error == GameActions::Status::Ok)
{
auto data = result->GetData<BannerPlaceActionResult>();
OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, result->Position);
ContextOpenDetailWindow(WD_BANNER, data.bannerId.ToUnderlying());
}
});
GameActions::Execute(&bannerPlaceAction);
break;
2023-02-14 22:19:42 +01:00
}
}
}
ClearAction GetClearAction()
{
auto range = MapRange(gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y);
ClearableItems itemsToClear = 0;
if (gClearSmallScenery)
itemsToClear |= CLEARABLE_ITEMS::SCENERY_SMALL;
if (gClearLargeScenery)
itemsToClear |= CLEARABLE_ITEMS::SCENERY_LARGE;
if (gClearFootpath)
itemsToClear |= CLEARABLE_ITEMS::SCENERY_FOOTPATH;
return ClearAction(range, itemsToClear);
}
2023-02-14 22:19:42 +01:00
public:
void OnMouseUp(WidgetIndex widgetIndex) override
{
WindowBase* mainWindow;
2023-02-14 22:19:42 +01:00
switch (widgetIndex)
{
case WIDX_PAUSE:
if (NetworkGetMode() != NETWORK_MODE_CLIENT)
2023-02-14 22:19:42 +01:00
{
auto pauseToggleAction = PauseToggleAction();
GameActions::Execute(&pauseToggleAction);
2023-02-14 22:19:42 +01:00
}
break;
case WIDX_ZOOM_OUT:
if ((mainWindow = WindowGetMain()) != nullptr)
WindowZoomOut(*mainWindow, false);
break;
case WIDX_ZOOM_IN:
if ((mainWindow = WindowGetMain()) != nullptr)
WindowZoomIn(*mainWindow, false);
break;
case WIDX_CLEAR_SCENERY:
ToggleClearSceneryWindow(WIDX_CLEAR_SCENERY);
break;
case WIDX_LAND:
ToggleLandWindow(WIDX_LAND);
break;
case WIDX_WATER:
ToggleWaterWindow(WIDX_WATER);
break;
case WIDX_SCENERY:
if (!ToolSet(*this, WIDX_SCENERY, Tool::Arrow))
2023-02-14 22:19:42 +01:00
{
InputSetFlag(INPUT_FLAG_6, true);
ContextOpenWindow(WindowClass::Scenery);
2023-02-14 22:19:42 +01:00
}
break;
case WIDX_PATH:
ToggleFootpathWindow();
break;
case WIDX_CONSTRUCT_RIDE:
ContextOpenWindow(WindowClass::ConstructRide);
break;
case WIDX_RIDES:
ContextOpenWindow(WindowClass::RideList);
break;
case WIDX_PARK:
ContextOpenWindow(WindowClass::ParkInformation);
break;
case WIDX_STAFF:
ContextOpenWindow(WindowClass::StaffList);
break;
case WIDX_GUESTS:
ContextOpenWindow(WindowClass::GuestList);
break;
case WIDX_FINANCES:
ContextOpenWindow(WindowClass::Finances);
break;
case WIDX_RESEARCH:
ContextOpenWindow(WindowClass::Research);
break;
case WIDX_NEWS:
ContextOpenWindow(WindowClass::RecentNews);
break;
case WIDX_MUTE:
OpenRCT2::Audio::ToggleAllSounds();
break;
case WIDX_CHAT:
if (ChatAvailable())
2023-02-14 22:19:42 +01:00
{
ChatToggle();
2023-02-14 22:19:42 +01:00
}
else
{
ContextShowError(STR_CHAT_UNAVAILABLE, STR_NONE, {});
}
break;
2023-02-14 22:19:42 +01:00
}
}
void OnMouseDown(WidgetIndex widgetIndex) override
2023-02-14 22:19:42 +01:00
{
int32_t numItems = 0;
Widget& widget = widgets[widgetIndex];
2023-02-14 22:19:42 +01:00
switch (widgetIndex)
{
case WIDX_FILE_MENU:
if (gScreenFlags & (SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER))
{
gDropdownItems[numItems++].Format = STR_ABOUT;
gDropdownItems[numItems++].Format = STR_OPTIONS;
gDropdownItems[numItems++].Format = STR_SCREENSHOT;
gDropdownItems[numItems++].Format = STR_GIANT_SCREENSHOT;
gDropdownItems[numItems++].Format = STR_EMPTY;
gDropdownItems[numItems++].Format = STR_FILE_BUG_ON_GITHUB;
2023-02-14 22:19:42 +01:00
if (OpenRCT2::GetContext()->HasNewVersionInfo())
gDropdownItems[numItems++].Format = STR_UPDATE_AVAILABLE;
2023-02-14 22:19:42 +01:00
gDropdownItems[numItems++].Format = STR_EMPTY;
2023-02-14 22:19:42 +01:00
if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER)
gDropdownItems[numItems++].Format = STR_QUIT_ROLLERCOASTER_DESIGNER;
else
gDropdownItems[numItems++].Format = STR_QUIT_TRACK_DESIGNS_MANAGER;
2023-02-14 22:19:42 +01:00
gDropdownItems[numItems++].Format = STR_EXIT_OPENRCT2;
}
else if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR)
{
gDropdownItems[numItems++].Format = STR_LOAD_LANDSCAPE;
gDropdownItems[numItems++].Format = STR_SAVE_LANDSCAPE;
gDropdownItems[numItems++].Format = STR_EMPTY;
gDropdownItems[numItems++].Format = STR_ABOUT;
gDropdownItems[numItems++].Format = STR_OPTIONS;
gDropdownItems[numItems++].Format = STR_SCREENSHOT;
gDropdownItems[numItems++].Format = STR_GIANT_SCREENSHOT;
gDropdownItems[numItems++].Format = STR_EMPTY;
gDropdownItems[numItems++].Format = STR_FILE_BUG_ON_GITHUB;
if (OpenRCT2::GetContext()->HasNewVersionInfo())
gDropdownItems[numItems++].Format = STR_UPDATE_AVAILABLE;
gDropdownItems[numItems++].Format = STR_EMPTY;
gDropdownItems[numItems++].Format = STR_QUIT_SCENARIO_EDITOR;
gDropdownItems[numItems++].Format = STR_EXIT_OPENRCT2;
}
2023-02-14 22:19:42 +01:00
else
{
gDropdownItems[numItems++].Format = STR_NEW_GAME;
gDropdownItems[numItems++].Format = STR_LOAD_GAME;
gDropdownItems[numItems++].Format = STR_SAVE_GAME;
gDropdownItems[numItems++].Format = STR_SAVE_GAME_AS;
gDropdownItems[numItems++].Format = STR_EMPTY;
gDropdownItems[numItems++].Format = STR_ABOUT;
gDropdownItems[numItems++].Format = STR_OPTIONS;
gDropdownItems[numItems++].Format = STR_SCREENSHOT;
gDropdownItems[numItems++].Format = STR_GIANT_SCREENSHOT;
gDropdownItems[numItems++].Format = STR_EMPTY;
gDropdownItems[numItems++].Format = STR_FILE_BUG_ON_GITHUB;
if (OpenRCT2::GetContext()->HasNewVersionInfo())
gDropdownItems[numItems++].Format = STR_UPDATE_AVAILABLE;
gDropdownItems[numItems++].Format = STR_EMPTY;
gDropdownItems[numItems++].Format = STR_QUIT_TO_MENU;
gDropdownItems[numItems++].Format = STR_EXIT_OPENRCT2;
}
2023-02-14 22:19:42 +01:00
WindowDropdownShowText(
{ windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[0] | 0x80,
Dropdown::Flag::StayOpen, numItems);
break;
case WIDX_CHEATS:
InitCheatsMenu(widget);
break;
case WIDX_VIEW_MENU:
InitViewMenu(widget);
break;
case WIDX_MAP:
InitMapMenu(widget);
break;
case WIDX_FASTFORWARD:
InitFastforwardMenu(widget);
break;
case WIDX_ROTATE:
InitRotateMenu(widget);
break;
case WIDX_DEBUG:
InitDebugMenu(widget);
break;
case WIDX_NETWORK:
InitNetworkMenu(widget);
break;
}
2023-02-14 22:19:42 +01:00
}
void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override
2023-02-14 22:19:42 +01:00
{
if (selectedIndex == -1)
{
return;
}
switch (widgetIndex)
{
case WIDX_FILE_MENU:
2023-02-14 22:19:42 +01:00
// New game is only available in the normal game. Skip one position to avoid incorrect mappings in the menus
// of the other modes.
if (gScreenFlags & (SCREEN_FLAGS_SCENARIO_EDITOR))
selectedIndex += 1;
2023-02-14 22:19:42 +01:00
// Quicksave is only available in the normal game. Skip one position to avoid incorrect mappings in the
// menus of the other modes.
if (gScreenFlags & (SCREEN_FLAGS_SCENARIO_EDITOR) && selectedIndex > DDIDX_LOAD_GAME)
selectedIndex += 1;
2023-02-14 22:19:42 +01:00
// Track designer and track designs manager start with About, not Load/save
if (gScreenFlags & (SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER))
selectedIndex += DDIDX_ABOUT;
2023-02-14 22:19:42 +01:00
// The "Update available" menu item is only available when there is one
if (selectedIndex >= DDIDX_UPDATE_AVAILABLE && !OpenRCT2::GetContext()->HasNewVersionInfo())
selectedIndex += 1;
2023-02-14 22:19:42 +01:00
switch (selectedIndex)
2023-02-14 22:19:42 +01:00
{
case DDIDX_NEW_GAME:
2023-02-14 22:19:42 +01:00
{
auto intent = Intent(WindowClass::ScenarioSelect);
intent.PutExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast<void*>(ScenarioSelectCallback));
2023-02-14 22:19:42 +01:00
ContextOpenIntent(&intent);
break;
2023-02-14 22:19:42 +01:00
}
case DDIDX_LOAD_GAME:
2023-02-14 22:19:42 +01:00
{
auto loadOrQuitAction = LoadOrQuitAction(LoadOrQuitModes::OpenSavePrompt);
GameActions::Execute(&loadOrQuitAction);
break;
}
case DDIDX_SAVE_GAME:
ToolCancel();
SaveGame();
break;
case DDIDX_SAVE_GAME_AS:
if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR)
{
auto intent = Intent(WindowClass::Loadsave);
intent.PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_LANDSCAPE);
intent.PutExtra(INTENT_EXTRA_PATH, GetGameState().ScenarioName);
ContextOpenIntent(&intent);
}
else
{
ToolCancel();
SaveGameAs();
}
break;
case DDIDX_ABOUT:
ContextOpenWindow(WindowClass::About);
break;
case DDIDX_OPTIONS:
ContextOpenWindow(WindowClass::Options);
break;
case DDIDX_SCREENSHOT:
gScreenshotCountdown = 10;
break;
case DDIDX_GIANT_SCREENSHOT:
ScreenshotGiant();
break;
case DDIDX_FILE_BUG_ON_GITHUB:
{
std::string url = "https://github.com/OpenRCT2/OpenRCT2/issues/new?"
"assignees=&labels=bug&template=bug_report.yaml";
// Automatically fill the "OpenRCT2 build" input
auto versionStr = String::URLEncode(gVersionInfoFull);
url.append("&f299dd2a20432827d99b648f73eb4649b23f8ec98d158d6f82b81e43196ee36b=" + versionStr);
OpenRCT2::GetContext()->GetUiContext()->OpenURL(url);
2023-02-14 22:19:42 +01:00
}
break;
case DDIDX_UPDATE_AVAILABLE:
ContextOpenWindowView(WV_NEW_VERSION_INFO);
break;
case DDIDX_QUIT_TO_MENU:
{
WindowCloseByClass(WindowClass::ManageTrackDesign);
WindowCloseByClass(WindowClass::TrackDeletePrompt);
auto loadOrQuitAction = LoadOrQuitAction(
LoadOrQuitModes::OpenSavePrompt, PromptMode::SaveBeforeQuit);
GameActions::Execute(&loadOrQuitAction);
break;
}
case DDIDX_EXIT_OPENRCT2:
ContextQuit();
break;
}
break;
case WIDX_CHEATS:
CheatsMenuDropdown(selectedIndex);
break;
case WIDX_VIEW_MENU:
ViewMenuDropdown(selectedIndex);
break;
case WIDX_MAP:
MapMenuDropdown(selectedIndex);
break;
case WIDX_FASTFORWARD:
FastforwardMenuDropdown(selectedIndex);
break;
case WIDX_ROTATE:
RotateMenuDropdown(selectedIndex);
break;
case WIDX_DEBUG:
DebugMenuDropdown(selectedIndex);
break;
case WIDX_NETWORK:
NetworkMenuDropdown(selectedIndex);
break;
}
}
void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override
{
switch (widgetIndex)
{
case WIDX_CLEAR_SCENERY:
ToolUpdateSceneryClear(screenCoords);
break;
case WIDX_LAND:
if (gLandPaintMode)
ToolUpdateLandPaint(screenCoords);
else
ToolUpdateLand(screenCoords);
break;
case WIDX_WATER:
ToolUpdateWater(screenCoords);
break;
case WIDX_SCENERY:
ToolUpdateScenery(screenCoords);
break;
#ifdef ENABLE_SCRIPTING
default:
auto& customTool = OpenRCT2::Scripting::ActiveCustomTool;
if (customTool)
2023-02-14 22:19:42 +01:00
{
customTool->OnUpdate(screenCoords);
2023-02-14 22:19:42 +01:00
}
break;
#endif
}
}
void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override
{
switch (widgetIndex)
{
case WIDX_CLEAR_SCENERY:
if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)
2023-02-14 22:19:42 +01:00
{
auto action = GetClearAction();
GameActions::Execute(&action);
gCurrentToolId = Tool::Crosshair;
2023-02-14 22:19:42 +01:00
}
break;
case WIDX_LAND:
if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)
{
auto surfaceSetStyleAction = SurfaceSetStyleAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y },
gLandToolTerrainSurface, gLandToolTerrainEdge);
GameActions::Execute(&surfaceSetStyleAction);
gCurrentToolId = Tool::UpDownArrow;
}
else
{
_landToolBlocked = true;
}
break;
case WIDX_WATER:
if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)
{
gCurrentToolId = Tool::UpDownArrow;
}
else
{
_landToolBlocked = true;
}
break;
case WIDX_SCENERY:
SceneryToolDown(screenCoords, widgetIndex);
break;
#ifdef ENABLE_SCRIPTING
default:
auto& customTool = OpenRCT2::Scripting::ActiveCustomTool;
if (customTool)
{
customTool->OnDown(screenCoords);
}
break;
#endif
}
2023-02-14 22:19:42 +01:00
}
void OnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override
2023-02-14 22:19:42 +01:00
{
switch (widgetIndex)
{
case WIDX_CLEAR_SCENERY:
if (WindowFindByClass(WindowClass::Error) == nullptr && (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE))
{
auto action = GetClearAction();
GameActions::Execute(&action);
gCurrentToolId = Tool::Crosshair;
}
break;
case WIDX_LAND:
// Custom setting to only change land style instead of raising or lowering land
if (gLandPaintMode)
{
if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)
{
auto surfaceSetStyleAction = SurfaceSetStyleAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y },
gLandToolTerrainSurface, gLandToolTerrainEdge);
GameActions::Execute(&surfaceSetStyleAction);
// The tool is set to 12 here instead of 3 so that the dragging cursor is not the elevation change
// cursor
gCurrentToolId = Tool::Crosshair;
}
}
else
{
if (!_landToolBlocked)
{
LandToolDrag(screenCoords);
}
}
break;
case WIDX_WATER:
if (!_landToolBlocked)
{
WaterToolDrag(screenCoords);
}
break;
case WIDX_SCENERY:
if (gWindowSceneryPaintEnabled & 1)
SceneryToolDown(screenCoords, widgetIndex);
if (gWindowSceneryEyedropperEnabled)
SceneryToolDown(screenCoords, widgetIndex);
break;
2023-02-14 22:19:42 +01:00
#ifdef ENABLE_SCRIPTING
default:
auto& customTool = OpenRCT2::Scripting::ActiveCustomTool;
if (customTool)
{
customTool->OnDrag(screenCoords);
}
break;
2023-02-14 22:19:42 +01:00
#endif
}
2023-02-14 22:19:42 +01:00
}
void OnToolUp(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override
2023-02-14 22:19:42 +01:00
{
_landToolBlocked = false;
switch (widgetIndex)
{
case WIDX_LAND:
MapInvalidateSelectionRect();
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
gCurrentToolId = Tool::DigDown;
break;
case WIDX_WATER:
MapInvalidateSelectionRect();
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
gCurrentToolId = Tool::WaterDown;
break;
case WIDX_CLEAR_SCENERY:
MapInvalidateSelectionRect();
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
2023-02-14 22:19:42 +01:00
gCurrentToolId = Tool::Crosshair;
break;
#ifdef ENABLE_SCRIPTING
default:
auto& customTool = OpenRCT2::Scripting::ActiveCustomTool;
if (customTool)
{
customTool->OnUp(screenCoords);
}
break;
#endif
}
}
void OnToolAbort(WidgetIndex widgetIndex) override
{
switch (widgetIndex)
{
case WIDX_LAND:
case WIDX_WATER:
case WIDX_CLEAR_SCENERY:
HideGridlines();
break;
#ifdef ENABLE_SCRIPTING
default:
auto& customTool = OpenRCT2::Scripting::ActiveCustomTool;
if (customTool)
{
customTool->OnAbort();
customTool = {};
}
break;
#endif
}
}
void OnPrepareDraw() override
{
int32_t x, widgetIndex, widgetWidth, firstAlignment;
Widget* widget;
// Enable / disable buttons
widgets[WIDX_PAUSE].type = WindowWidgetType::TrnBtn;
widgets[WIDX_FILE_MENU].type = WindowWidgetType::TrnBtn;
widgets[WIDX_ZOOM_OUT].type = WindowWidgetType::TrnBtn;
widgets[WIDX_ZOOM_IN].type = WindowWidgetType::TrnBtn;
widgets[WIDX_ROTATE].type = WindowWidgetType::TrnBtn;
widgets[WIDX_VIEW_MENU].type = WindowWidgetType::TrnBtn;
widgets[WIDX_MAP].type = WindowWidgetType::TrnBtn;
widgets[WIDX_MUTE].type = WindowWidgetType::TrnBtn;
widgets[WIDX_CHAT].type = WindowWidgetType::TrnBtn;
widgets[WIDX_LAND].type = WindowWidgetType::TrnBtn;
widgets[WIDX_WATER].type = WindowWidgetType::TrnBtn;
widgets[WIDX_SCENERY].type = WindowWidgetType::TrnBtn;
widgets[WIDX_PATH].type = WindowWidgetType::TrnBtn;
widgets[WIDX_CONSTRUCT_RIDE].type = WindowWidgetType::TrnBtn;
widgets[WIDX_RIDES].type = WindowWidgetType::TrnBtn;
widgets[WIDX_PARK].type = WindowWidgetType::TrnBtn;
widgets[WIDX_STAFF].type = WindowWidgetType::TrnBtn;
widgets[WIDX_GUESTS].type = WindowWidgetType::TrnBtn;
widgets[WIDX_CLEAR_SCENERY].type = WindowWidgetType::TrnBtn;
widgets[WIDX_FINANCES].type = WindowWidgetType::TrnBtn;
widgets[WIDX_RESEARCH].type = WindowWidgetType::TrnBtn;
widgets[WIDX_FASTFORWARD].type = WindowWidgetType::TrnBtn;
widgets[WIDX_CHEATS].type = WindowWidgetType::TrnBtn;
widgets[WIDX_DEBUG].type = gConfigGeneral.DebuggingTools ? WindowWidgetType::TrnBtn : WindowWidgetType::Empty;
widgets[WIDX_NEWS].type = WindowWidgetType::TrnBtn;
widgets[WIDX_NETWORK].type = WindowWidgetType::TrnBtn;
if (!gConfigInterface.ToolbarShowMute)
widgets[WIDX_MUTE].type = WindowWidgetType::Empty;
if (!gConfigInterface.ToolbarShowChat)
widgets[WIDX_CHAT].type = WindowWidgetType::Empty;
2023-02-14 22:19:42 +01:00
if (!gConfigInterface.ToolbarShowResearch)
widgets[WIDX_RESEARCH].type = WindowWidgetType::Empty;
if (!gConfigInterface.ToolbarShowCheats)
widgets[WIDX_CHEATS].type = WindowWidgetType::Empty;
if (!gConfigInterface.ToolbarShowNews)
widgets[WIDX_NEWS].type = WindowWidgetType::Empty;
if (!gConfigInterface.ToolbarShowZoom)
{
widgets[WIDX_ZOOM_IN].type = WindowWidgetType::Empty;
widgets[WIDX_ZOOM_OUT].type = WindowWidgetType::Empty;
}
if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR || gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)
{
widgets[WIDX_PAUSE].type = WindowWidgetType::Empty;
}
if ((GetGameState().Park.Flags & PARK_FLAGS_NO_MONEY) || !gConfigInterface.ToolbarShowFinances)
widgets[WIDX_FINANCES].type = WindowWidgetType::Empty;
if (gScreenFlags & SCREEN_FLAGS_EDITOR)
{
widgets[WIDX_PARK].type = WindowWidgetType::Empty;
widgets[WIDX_STAFF].type = WindowWidgetType::Empty;
widgets[WIDX_GUESTS].type = WindowWidgetType::Empty;
widgets[WIDX_FINANCES].type = WindowWidgetType::Empty;
widgets[WIDX_RESEARCH].type = WindowWidgetType::Empty;
widgets[WIDX_NEWS].type = WindowWidgetType::Empty;
widgets[WIDX_NETWORK].type = WindowWidgetType::Empty;
auto& gameState = GetGameState();
if (gameState.EditorStep != EditorStep::LandscapeEditor)
2023-02-14 22:19:42 +01:00
{
widgets[WIDX_LAND].type = WindowWidgetType::Empty;
widgets[WIDX_WATER].type = WindowWidgetType::Empty;
2023-02-14 22:19:42 +01:00
}
if (gameState.EditorStep != EditorStep::RollercoasterDesigner)
2023-02-14 22:19:42 +01:00
{
widgets[WIDX_RIDES].type = WindowWidgetType::Empty;
widgets[WIDX_CONSTRUCT_RIDE].type = WindowWidgetType::Empty;
widgets[WIDX_FASTFORWARD].type = WindowWidgetType::Empty;
2023-02-14 22:19:42 +01:00
}
if (gameState.EditorStep != EditorStep::LandscapeEditor
&& gameState.EditorStep != EditorStep::RollercoasterDesigner)
2023-02-14 22:19:42 +01:00
{
widgets[WIDX_MAP].type = WindowWidgetType::Empty;
widgets[WIDX_SCENERY].type = WindowWidgetType::Empty;
widgets[WIDX_PATH].type = WindowWidgetType::Empty;
widgets[WIDX_CLEAR_SCENERY].type = WindowWidgetType::Empty;
widgets[WIDX_ZOOM_OUT].type = WindowWidgetType::Empty;
widgets[WIDX_ZOOM_IN].type = WindowWidgetType::Empty;
widgets[WIDX_ROTATE].type = WindowWidgetType::Empty;
widgets[WIDX_VIEW_MENU].type = WindowWidgetType::Empty;
2023-02-14 22:19:42 +01:00
}
}
switch (NetworkGetMode())
{
case NETWORK_MODE_NONE:
widgets[WIDX_NETWORK].type = WindowWidgetType::Empty;
widgets[WIDX_CHAT].type = WindowWidgetType::Empty;
break;
case NETWORK_MODE_CLIENT:
widgets[WIDX_PAUSE].type = WindowWidgetType::Empty;
[[fallthrough]];
case NETWORK_MODE_SERVER:
widgets[WIDX_FASTFORWARD].type = WindowWidgetType::Empty;
break;
}
// Align left hand side toolbar buttons
firstAlignment = 1;
x = 0;
for (size_t i = 0; i < std::size(left_aligned_widgets_order); ++i)
{
widgetIndex = left_aligned_widgets_order[i];
widget = &widgets[widgetIndex];
if (widget->type == WindowWidgetType::Empty && widgetIndex != WIDX_SEPARATOR)
continue;
if (firstAlignment && widgetIndex == WIDX_SEPARATOR)
continue;
widgetWidth = widget->width();
widget->left = x;
x += widgetWidth;
widget->right = x;
x += 1;
firstAlignment = 0;
}
// Align right hand side toolbar buttons if necessary
int32_t screenWidth = ContextGetWidth();
firstAlignment = 1;
x = std::max(640, screenWidth);
for (size_t i = 0; i < std::size(right_aligned_widgets_order); ++i)
{
widgetIndex = right_aligned_widgets_order[i];
widget = &widgets[widgetIndex];
if (widget->type == WindowWidgetType::Empty && widgetIndex != WIDX_SEPARATOR)
continue;
if (firstAlignment && widgetIndex == WIDX_SEPARATOR)
continue;
widgetWidth = widget->width();
x -= 1;
widget->right = x;
x -= widgetWidth;
widget->left = x;
firstAlignment = 0;
}
// Footpath button pressed down
if (WindowFindByClass(WindowClass::Footpath) == nullptr)
pressed_widgets &= ~(1uLL << WIDX_PATH);
else
pressed_widgets |= (1uLL << WIDX_PATH);
if (gGamePaused & GAME_PAUSED_NORMAL)
pressed_widgets |= (1uLL << WIDX_PAUSE);
else
pressed_widgets &= ~(1uLL << WIDX_PAUSE);
if (!OpenRCT2::Audio::gGameSoundsOff)
widgets[WIDX_MUTE].image = ImageId(SPR_G2_TOOLBAR_MUTE, FilterPaletteID::PaletteNull);
else
widgets[WIDX_MUTE].image = ImageId(SPR_G2_TOOLBAR_UNMUTE, FilterPaletteID::PaletteNull);
// Set map button to the right image.
if (widgets[WIDX_MAP].type != WindowWidgetType::Empty)
{
static constexpr uint32_t _imageIdByRotation[] = {
SPR_G2_MAP_NORTH,
SPR_G2_MAP_WEST,
SPR_G2_MAP_SOUTH,
SPR_G2_MAP_EAST,
};
uint32_t mapImageId = _imageIdByRotation[GetCurrentRotation()];
widgets[WIDX_MAP].image = ImageId(mapImageId, FilterPaletteID::PaletteNull);
}
// Zoomed out/in disable. Not sure where this code is in the original.
const auto* mainWindow = WindowGetMain();
if (mainWindow == nullptr || mainWindow->viewport == nullptr)
{
LOG_ERROR("mainWindow or mainWindow->viewport is null!");
return;
}
if (mainWindow->viewport->zoom == ZoomLevel::min())
{
disabled_widgets |= (1uLL << WIDX_ZOOM_IN);
}
else if (mainWindow->viewport->zoom >= ZoomLevel::max())
{
disabled_widgets |= (1uLL << WIDX_ZOOM_OUT);
}
else
{
disabled_widgets &= ~((1uLL << WIDX_ZOOM_IN) | (1uLL << WIDX_ZOOM_OUT));
}
2023-02-14 22:19:42 +01:00
}
void OnDraw(DrawPixelInfo& dpi) override
2023-02-14 22:19:42 +01:00
{
const auto& gameState = GetGameState();
int32_t imgId;
2023-02-14 22:19:42 +01:00
WindowDrawWidgets(*this, dpi);
ScreenCoordsXY screenPos{};
// Draw staff button image (setting masks to the staff colours)
if (widgets[WIDX_STAFF].type != WindowWidgetType::Empty)
{
screenPos = { windowPos.x + widgets[WIDX_STAFF].left, windowPos.y + widgets[WIDX_STAFF].top };
imgId = SPR_TOOLBAR_STAFF;
if (WidgetIsPressed(*this, WIDX_STAFF))
imgId++;
GfxDrawSprite(dpi, ImageId(imgId, gameState.StaffHandymanColour, gameState.StaffMechanicColour), screenPos);
}
// Draw fast forward button
if (widgets[WIDX_FASTFORWARD].type != WindowWidgetType::Empty)
{
screenPos = { windowPos.x + widgets[WIDX_FASTFORWARD].left + 0,
windowPos.y + widgets[WIDX_FASTFORWARD].top + 0 };
if (WidgetIsPressed(*this, WIDX_FASTFORWARD))
screenPos.y++;
GfxDrawSprite(dpi, ImageId(SPR_G2_FASTFORWARD), screenPos + ScreenCoordsXY{ 6, 3 });
for (int32_t i = 0; i < gGameSpeed && gGameSpeed <= 4; i++)
2023-02-14 22:19:42 +01:00
{
GfxDrawSprite(dpi, ImageId(SPR_G2_SPEED_ARROW), screenPos + ScreenCoordsXY{ 5 + i * 5, 15 });
2023-02-14 22:19:42 +01:00
}
for (int32_t i = 0; i < 3 && i < gGameSpeed - 4 && gGameSpeed >= 5; i++)
{
GfxDrawSprite(dpi, ImageId(SPR_G2_HYPER_ARROW), screenPos + ScreenCoordsXY{ 5 + i * 6, 15 });
}
}
// Draw cheats button
if (widgets[WIDX_CHEATS].type != WindowWidgetType::Empty)
{
screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_CHEATS].left - 1, widgets[WIDX_CHEATS].top - 1 };
if (WidgetIsPressed(*this, WIDX_CHEATS))
screenPos.y++;
GfxDrawSprite(dpi, ImageId(SPR_G2_SANDBOX), screenPos);
// Draw an overlay if clearance checks are disabled
if (GetGameState().Cheats.DisableClearanceChecks)
2023-02-14 22:19:42 +01:00
{
auto colour = static_cast<colour_t>(EnumValue(COLOUR_DARK_ORANGE) | EnumValue(COLOUR_FLAG_OUTLINE));
DrawTextBasic(
dpi, screenPos + ScreenCoordsXY{ 26, 2 }, STR_OVERLAY_CLEARANCE_CHECKS_DISABLED, {},
{ colour, TextAlignment::RIGHT });
2023-02-14 22:19:42 +01:00
}
}
// Draw chat button
if (widgets[WIDX_CHAT].type != WindowWidgetType::Empty)
{
screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_CHAT].left, widgets[WIDX_CHAT].top - 2 };
if (WidgetIsPressed(*this, WIDX_CHAT))
screenPos.y++;
GfxDrawSprite(dpi, ImageId(SPR_G2_CHAT), screenPos);
}
// Draw debug button
if (widgets[WIDX_DEBUG].type != WindowWidgetType::Empty)
{
screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_DEBUG].left, widgets[WIDX_DEBUG].top - 1 };
if (WidgetIsPressed(*this, WIDX_DEBUG))
screenPos.y++;
GfxDrawSprite(dpi, ImageId(SPR_TAB_GEARS_0), screenPos);
}
// Draw research button
if (widgets[WIDX_RESEARCH].type != WindowWidgetType::Empty)
{
screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_RESEARCH].left - 1, widgets[WIDX_RESEARCH].top };
if (WidgetIsPressed(*this, WIDX_RESEARCH))
screenPos.y++;
GfxDrawSprite(dpi, ImageId(SPR_TAB_FINANCES_RESEARCH_0), screenPos);
}
// Draw finances button
if (widgets[WIDX_FINANCES].type != WindowWidgetType::Empty)
{
screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_FINANCES].left + 3, widgets[WIDX_FINANCES].top + 1 };
if (WidgetIsPressed(*this, WIDX_FINANCES))
screenPos.y++;
GfxDrawSprite(dpi, ImageId(SPR_FINANCE), screenPos);
}
// Draw news button
if (widgets[WIDX_NEWS].type != WindowWidgetType::Empty)
{
screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_NEWS].left + 3, widgets[WIDX_NEWS].top + 0 };
if (WidgetIsPressed(*this, WIDX_NEWS))
screenPos.y++;
GfxDrawSprite(dpi, ImageId(SPR_G2_TAB_NEWS), screenPos);
}
// Draw network button
if (widgets[WIDX_NETWORK].type != WindowWidgetType::Empty)
{
screenPos = windowPos + ScreenCoordsXY{ widgets[WIDX_NETWORK].left + 3, widgets[WIDX_NETWORK].top + 0 };
if (WidgetIsPressed(*this, WIDX_NETWORK))
screenPos.y++;
// Draw (de)sync icon.
imgId = (NetworkIsDesynchronised() ? SPR_G2_MULTIPLAYER_DESYNC : SPR_G2_MULTIPLAYER_SYNC);
GfxDrawSprite(dpi, ImageId(imgId), screenPos + ScreenCoordsXY{ 3, 11 });
// Draw number of players.
auto ft = Formatter();
ft.Add<int32_t>(NetworkGetNumVisiblePlayers());
auto colour = static_cast<colour_t>(EnumValue(COLOUR_WHITE) | EnumValue(COLOUR_FLAG_OUTLINE));
DrawTextBasic(dpi, screenPos + ScreenCoordsXY{ 23, 1 }, STR_COMMA16, ft, { colour, TextAlignment::RIGHT });
}
2023-02-14 22:19:42 +01:00
}
};
static void ScenarioSelectCallback(const utf8* path)
{
WindowCloseByClass(WindowClass::EditorObjectSelection);
GameNotifyMapChange();
GetContext()->LoadParkFromFile(path, false, true);
GameLoadScripts();
GameNotifyMapChanged();
}
/**
* Creates the main game top toolbar window.
* rct2: 0x0066B485 (part of 0x0066B3E8)
*/
WindowBase* TopToolbarOpen()
2018-06-22 23:21:44 +02:00
{
TopToolbar* window = WindowCreate<TopToolbar>(
WindowClass::TopToolbar, ScreenCoordsXY(0, 0), ContextGetWidth(), TOP_TOOLBAR_HEIGHT + 1,
WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_BACKGROUND);
window->widgets = _topToolbarWidgets;
WindowInitScrollWidgets(*window);
return window;
}
/**
*
* rct2: 0x0066D104
*/
bool LandToolIsActive()
{
if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)))
return false;
if (gCurrentToolWidget.window_classification != WindowClass::TopToolbar)
return false;
if (gCurrentToolWidget.widget_index != WIDX_LAND)
return false;
return true;
}
/**
*
* rct2: 0x0066D125
*/
bool ClearSceneryToolIsActive()
{
if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)))
return false;
if (gCurrentToolWidget.window_classification != WindowClass::TopToolbar)
return false;
if (gCurrentToolWidget.widget_index != WIDX_CLEAR_SCENERY)
return false;
return true;
}
/**
*
* rct2: 0x0066D125
*/
bool WaterToolIsActive()
{
if (!(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)))
return false;
if (gCurrentToolWidget.window_classification != WindowClass::TopToolbar)
return false;
if (gCurrentToolWidget.widget_index != WIDX_WATER)
return false;
return true;
}
void TopToolbar::InitViewMenu(Widget& widget)
{
using namespace Dropdown;
constexpr ItemExt items[] = {
ToggleOption(DDIDX_UNDERGROUND_INSIDE, STR_UNDERGROUND_VIEW),
ToggleOption(DDIDX_TRANSPARENT_WATER, STR_VIEWPORT_TRANSPARENT_WATER),
ToggleOption(DDIDX_HIDE_BASE, STR_REMOVE_BASE_LAND),
ToggleOption(DDIDX_HIDE_VERTICAL, STR_REMOVE_VERTICAL_FACES),
Separator(),
ToggleOption(DDIDX_HIDE_RIDES, STR_SEE_THROUGH_RIDES),
ToggleOption(DDIDX_HIDE_VEHICLES, STR_SEE_THROUGH_VEHICLES),
ToggleOption(DDIDX_HIDE_VEGETATION, STR_SEE_THROUGH_VEGETATION),
ToggleOption(DDIDX_HIDE_SCENERY, STR_SEE_THROUGH_SCENERY),
ToggleOption(DDIDX_HIDE_PATHS, STR_SEE_THROUGH_PATHS),
ToggleOption(DDIDX_HIDE_SUPPORTS, STR_SEE_THROUGH_SUPPORTS),
ToggleOption(DDIDX_HIDE_GUESTS, STR_SEE_THROUGH_GUESTS),
ToggleOption(DDIDX_HIDE_STAFF, STR_SEE_THROUGH_STAFF),
Separator(),
ToggleOption(DDIDX_LAND_HEIGHTS, STR_HEIGHT_MARKS_ON_LAND),
ToggleOption(DDIDX_TRACK_HEIGHTS, STR_HEIGHT_MARKS_ON_RIDE_TRACKS),
ToggleOption(DDIDX_PATH_HEIGHTS, STR_HEIGHT_MARKS_ON_PATHS),
Separator(),
ToggleOption(DDIDX_VIEW_CLIPPING, STR_VIEW_CLIPPING_MENU),
ToggleOption(DDIDX_HIGHLIGHT_PATH_ISSUES, STR_HIGHLIGHT_PATH_ISSUES_MENU),
Separator(),
ToggleOption(DDIDX_TRANSPARENCY, STR_TRANSPARENCY_OPTIONS),
};
static_assert(ItemIDsMatchIndices(items));
SetItems(items);
WindowDropdownShowText(
{ windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[1] | 0x80, 0,
TOP_TOOLBAR_VIEW_MENU_COUNT);
// Set checkmarks
auto* mainViewport = WindowGetMain()->viewport;
if (mainViewport->flags & VIEWPORT_FLAG_UNDERGROUND_INSIDE)
Dropdown::SetChecked(DDIDX_UNDERGROUND_INSIDE, true);
if (gConfigGeneral.TransparentWater)
Dropdown::SetChecked(DDIDX_TRANSPARENT_WATER, true);
if (mainViewport->flags & VIEWPORT_FLAG_HIDE_BASE)
Dropdown::SetChecked(DDIDX_HIDE_BASE, true);
if (mainViewport->flags & VIEWPORT_FLAG_HIDE_VERTICAL)
Dropdown::SetChecked(DDIDX_HIDE_VERTICAL, true);
if (mainViewport->flags & VIEWPORT_FLAG_HIDE_RIDES)
Dropdown::SetChecked(DDIDX_HIDE_RIDES, true);
if (mainViewport->flags & VIEWPORT_FLAG_HIDE_VEHICLES)
Dropdown::SetChecked(DDIDX_HIDE_VEHICLES, true);
if (mainViewport->flags & VIEWPORT_FLAG_HIDE_VEGETATION)
Dropdown::SetChecked(DDIDX_HIDE_VEGETATION, true);
if (mainViewport->flags & VIEWPORT_FLAG_HIDE_SCENERY)
Dropdown::SetChecked(DDIDX_HIDE_SCENERY, true);
if (mainViewport->flags & VIEWPORT_FLAG_HIDE_PATHS)
Dropdown::SetChecked(DDIDX_HIDE_PATHS, true);
if (mainViewport->flags & VIEWPORT_FLAG_HIDE_SUPPORTS)
Dropdown::SetChecked(DDIDX_HIDE_SUPPORTS, true);
if (mainViewport->flags & VIEWPORT_FLAG_HIDE_GUESTS)
Dropdown::SetChecked(DDIDX_HIDE_GUESTS, true);
if (mainViewport->flags & VIEWPORT_FLAG_HIDE_STAFF)
Dropdown::SetChecked(DDIDX_HIDE_STAFF, true);
if (mainViewport->flags & VIEWPORT_FLAG_LAND_HEIGHTS)
Dropdown::SetChecked(DDIDX_LAND_HEIGHTS, true);
if (mainViewport->flags & VIEWPORT_FLAG_TRACK_HEIGHTS)
Dropdown::SetChecked(DDIDX_TRACK_HEIGHTS, true);
if (mainViewport->flags & VIEWPORT_FLAG_PATH_HEIGHTS)
Dropdown::SetChecked(DDIDX_PATH_HEIGHTS, true);
if (mainViewport->flags & VIEWPORT_FLAG_CLIP_VIEW)
Dropdown::SetChecked(DDIDX_VIEW_CLIPPING, true);
if (mainViewport->flags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES)
Dropdown::SetChecked(DDIDX_HIGHLIGHT_PATH_ISSUES, true);
gDropdownDefaultIndex = DDIDX_UNDERGROUND_INSIDE;
// Opaque water relies on RCT1 sprites.
if (!IsCsgLoaded())
{
Dropdown::SetDisabled(DDIDX_TRANSPARENT_WATER, true);
2023-02-14 22:19:42 +01:00
}
}
void TopToolbar::ViewMenuDropdown(int16_t dropdownIndex)
2018-06-22 23:21:44 +02:00
{
auto* w = WindowGetMain();
if (w != nullptr)
2023-02-14 22:19:42 +01:00
{
switch (dropdownIndex)
{
case DDIDX_UNDERGROUND_INSIDE:
w->viewport->flags ^= VIEWPORT_FLAG_UNDERGROUND_INSIDE;
break;
case DDIDX_TRANSPARENT_WATER:
gConfigGeneral.TransparentWater ^= 1;
ConfigSaveDefault();
break;
case DDIDX_HIDE_BASE:
w->viewport->flags ^= VIEWPORT_FLAG_HIDE_BASE;
break;
case DDIDX_HIDE_VERTICAL:
w->viewport->flags ^= VIEWPORT_FLAG_HIDE_VERTICAL;
break;
case DDIDX_HIDE_RIDES:
w->viewport->flags ^= VIEWPORT_FLAG_HIDE_RIDES;
break;
case DDIDX_HIDE_VEHICLES:
w->viewport->flags ^= VIEWPORT_FLAG_HIDE_VEHICLES;
break;
case DDIDX_HIDE_VEGETATION:
w->viewport->flags ^= VIEWPORT_FLAG_HIDE_VEGETATION;
break;
case DDIDX_HIDE_SCENERY:
w->viewport->flags ^= VIEWPORT_FLAG_HIDE_SCENERY;
break;
case DDIDX_HIDE_PATHS:
w->viewport->flags ^= VIEWPORT_FLAG_HIDE_PATHS;
break;
case DDIDX_HIDE_SUPPORTS:
w->viewport->flags ^= VIEWPORT_FLAG_HIDE_SUPPORTS;
break;
case DDIDX_HIDE_GUESTS:
w->viewport->flags ^= VIEWPORT_FLAG_HIDE_GUESTS;
break;
case DDIDX_HIDE_STAFF:
w->viewport->flags ^= VIEWPORT_FLAG_HIDE_STAFF;
break;
case DDIDX_LAND_HEIGHTS:
w->viewport->flags ^= VIEWPORT_FLAG_LAND_HEIGHTS;
break;
case DDIDX_TRACK_HEIGHTS:
w->viewport->flags ^= VIEWPORT_FLAG_TRACK_HEIGHTS;
break;
case DDIDX_PATH_HEIGHTS:
w->viewport->flags ^= VIEWPORT_FLAG_PATH_HEIGHTS;
break;
case DDIDX_VIEW_CLIPPING:
if (WindowFindByClass(WindowClass::ViewClipping) == nullptr)
{
ContextOpenWindow(WindowClass::ViewClipping);
}
else
{
// If window is already open, toggle the view clipping on/off
w->viewport->flags ^= VIEWPORT_FLAG_CLIP_VIEW;
}
break;
case DDIDX_HIGHLIGHT_PATH_ISSUES:
w->viewport->flags ^= VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES;
break;
case DDIDX_TRANSPARENCY:
ContextOpenWindow(WindowClass::Transparency);
break;
default:
return;
}
w->Invalidate();
2023-02-14 22:19:42 +01:00
}
}
2015-05-19 00:16:43 +02:00
void TopToolbar::InitMapMenu(Widget& widget)
2020-02-11 22:26:05 +01:00
{
auto i = 0;
gDropdownItems[i++].Format = STR_SHORTCUT_SHOW_MAP;
gDropdownItems[i++].Format = STR_EXTRA_VIEWPORT;
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && GetGameState().EditorStep == EditorStep::LandscapeEditor)
2020-02-11 22:26:05 +01:00
{
gDropdownItems[i++].Format = STR_MAPGEN_WINDOW_TITLE;
2023-02-14 22:19:42 +01:00
}
#ifdef ENABLE_SCRIPTING
const auto& customMenuItems = OpenRCT2::Scripting::CustomMenuItems;
if (!customMenuItems.empty())
2023-02-14 22:19:42 +01:00
{
gDropdownItems[i++].Format = STR_EMPTY;
for (const auto& item : customMenuItems)
{
if (item.Kind == OpenRCT2::Scripting::CustomToolbarMenuItemKind::Standard)
{
gDropdownItems[i].Format = STR_STRING;
auto sz = item.Text.c_str();
std::memcpy(&gDropdownItems[i].Args, &sz, sizeof(const char*));
i++;
}
}
2023-02-14 22:19:42 +01:00
}
#endif
2023-02-14 22:19:42 +01:00
WindowDropdownShowText(
{ windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[1] | 0x80, 0, i);
gDropdownDefaultIndex = DDIDX_SHOW_MAP;
}
2023-02-14 22:19:42 +01:00
void TopToolbar::MapMenuDropdown(int16_t dropdownIndex)
{
int32_t customStartIndex = 3;
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && GetGameState().EditorStep == EditorStep::LandscapeEditor)
2023-02-14 22:19:42 +01:00
{
customStartIndex++;
}
2020-02-11 22:26:05 +01:00
if (dropdownIndex < customStartIndex)
{
switch (dropdownIndex)
2023-02-14 22:19:42 +01:00
{
case 0:
ContextOpenWindow(WindowClass::Map);
break;
case 1:
ContextOpenWindow(WindowClass::Viewport);
break;
case 2:
ContextOpenWindow(WindowClass::Mapgen);
break;
2023-02-14 22:19:42 +01:00
}
}
else
{
#ifdef ENABLE_SCRIPTING
const auto& customMenuItems = OpenRCT2::Scripting::CustomMenuItems;
auto customIndex = static_cast<size_t>(dropdownIndex - customStartIndex);
size_t i = 0;
for (const auto& item : customMenuItems)
2023-02-14 22:19:42 +01:00
{
if (item.Kind == OpenRCT2::Scripting::CustomToolbarMenuItemKind::Standard)
{
if (i == customIndex)
{
item.Invoke();
break;
}
i++;
}
2023-02-14 22:19:42 +01:00
}
#endif
2023-02-14 22:19:42 +01:00
}
}
2023-02-14 22:19:42 +01:00
void TopToolbar::InitFastforwardMenu(Widget& widget)
{
int32_t num_items = 4;
gDropdownItems[0].Format = STR_TOGGLE_OPTION;
gDropdownItems[1].Format = STR_TOGGLE_OPTION;
gDropdownItems[2].Format = STR_TOGGLE_OPTION;
gDropdownItems[3].Format = STR_TOGGLE_OPTION;
if (gConfigGeneral.DebuggingTools)
2020-02-11 22:26:05 +01:00
{
gDropdownItems[4].Format = STR_EMPTY;
gDropdownItems[5].Format = STR_TOGGLE_OPTION;
gDropdownItems[5].Args = STR_SPEED_HYPER;
num_items = 6;
2020-02-11 22:26:05 +01:00
}
2023-02-14 22:19:42 +01:00
gDropdownItems[0].Args = STR_SPEED_NORMAL;
gDropdownItems[1].Args = STR_SPEED_QUICK;
gDropdownItems[2].Args = STR_SPEED_FAST;
gDropdownItems[3].Args = STR_SPEED_TURBO;
WindowDropdownShowText(
{ windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[0] | 0x80, 0, num_items);
2020-02-11 22:26:05 +01:00
// Set checkmarks
if (gGameSpeed <= 4)
2023-02-14 22:19:42 +01:00
{
Dropdown::SetChecked(gGameSpeed - 1, true);
2023-02-14 22:19:42 +01:00
}
if (gGameSpeed == 8)
2023-02-14 22:19:42 +01:00
{
Dropdown::SetChecked(5, true);
2023-02-14 22:19:42 +01:00
}
if (gConfigGeneral.DebuggingTools)
2023-02-14 22:19:42 +01:00
{
gDropdownDefaultIndex = (gGameSpeed == 8 ? 0 : gGameSpeed);
2023-02-14 22:19:42 +01:00
}
else
2018-06-22 23:21:44 +02:00
{
gDropdownDefaultIndex = (gGameSpeed >= 4 ? 0 : gGameSpeed);
}
if (gDropdownDefaultIndex == 4)
2018-06-22 23:21:44 +02:00
{
gDropdownDefaultIndex = 5;
2023-02-14 22:19:42 +01:00
}
}
void TopToolbar::FastforwardMenuDropdown(int16_t dropdownIndex)
{
auto* w = WindowGetMain();
if (w != nullptr)
2023-02-14 22:19:42 +01:00
{
if (dropdownIndex >= 0 && dropdownIndex <= 5)
{
auto newSpeed = dropdownIndex + 1;
if (newSpeed >= 5)
newSpeed = 8;
auto setSpeedAction = GameSetSpeedAction(newSpeed);
GameActions::Execute(&setSpeedAction);
}
}
}
void TopToolbar::InitRotateMenu(Widget& widget)
2023-02-14 22:19:42 +01:00
{
gDropdownItems[0].Format = STR_ROTATE_CLOCKWISE;
gDropdownItems[1].Format = STR_ROTATE_ANTI_CLOCKWISE;
WindowDropdownShowText(
{ windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[1] | 0x80, 0, 2);
gDropdownDefaultIndex = DDIDX_ROTATE_CLOCKWISE;
}
2023-02-14 22:19:42 +01:00
void TopToolbar::RotateMenuDropdown(int16_t dropdownIndex)
{
if (dropdownIndex == 0)
2023-02-14 22:19:42 +01:00
{
ViewportRotateAll(1);
2023-02-14 22:19:42 +01:00
}
else if (dropdownIndex == 1)
2023-02-14 22:19:42 +01:00
{
ViewportRotateAll(-1);
2023-02-14 22:19:42 +01:00
}
}
void TopToolbar::InitCheatsMenu(Widget& widget)
{
using namespace Dropdown;
constexpr ItemExt items[] = {
ToggleOption(DDIDX_CHEATS, STR_CHEAT_TITLE),
ToggleOption(DDIDX_TILE_INSPECTOR, STR_DEBUG_DROPDOWN_TILE_INSPECTOR),
ToggleOption(DDIDX_OBJECT_SELECTION, STR_DEBUG_DROPDOWN_OBJECT_SELECTION),
ToggleOption(DDIDX_INVENTIONS_LIST, STR_DEBUG_DROPDOWN_INVENTIONS_LIST),
ToggleOption(DDIDX_SCENARIO_OPTIONS, STR_DEBUG_DROPDOWN_SCENARIO_OPTIONS),
ToggleOption(DDIDX_OBJECTIVE_OPTIONS, STR_CHEATS_MENU_OBJECTIVE_OPTIONS),
Separator(),
ToggleOption(DDIDX_ENABLE_SANDBOX_MODE, STR_ENABLE_SANDBOX_MODE),
ToggleOption(DDIDX_DISABLE_CLEARANCE_CHECKS, STR_DISABLE_CLEARANCE_CHECKS),
ToggleOption(DDIDX_DISABLE_SUPPORT_LIMITS, STR_DISABLE_SUPPORT_LIMITS),
};
static_assert(ItemIDsMatchIndices(items));
SetItems(items);
WindowDropdownShowText(
{ windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[0] | 0x80, 0,
TOP_TOOLBAR_CHEATS_COUNT);
// Disable items that are not yet available in multiplayer
if (NetworkGetMode() != NETWORK_MODE_NONE)
2023-02-14 22:19:42 +01:00
{
Dropdown::SetDisabled(DDIDX_OBJECT_SELECTION, true);
Dropdown::SetDisabled(DDIDX_INVENTIONS_LIST, true);
Dropdown::SetDisabled(DDIDX_OBJECTIVE_OPTIONS, true);
2023-02-14 22:19:42 +01:00
}
if (gScreenFlags & SCREEN_FLAGS_EDITOR)
2023-02-14 22:19:42 +01:00
{
Dropdown::SetDisabled(DDIDX_OBJECT_SELECTION, true);
Dropdown::SetDisabled(DDIDX_INVENTIONS_LIST, true);
Dropdown::SetDisabled(DDIDX_SCENARIO_OPTIONS, true);
Dropdown::SetDisabled(DDIDX_OBJECTIVE_OPTIONS, true);
Dropdown::SetDisabled(DDIDX_ENABLE_SANDBOX_MODE, true);
2023-02-14 22:19:42 +01:00
}
if (GetGameState().Cheats.SandboxMode)
2023-02-14 22:19:42 +01:00
{
Dropdown::SetChecked(DDIDX_ENABLE_SANDBOX_MODE, true);
2023-02-14 22:19:42 +01:00
}
if (GetGameState().Cheats.DisableClearanceChecks)
2023-02-14 22:19:42 +01:00
{
Dropdown::SetChecked(DDIDX_DISABLE_CLEARANCE_CHECKS, true);
2023-02-14 22:19:42 +01:00
}
if (GetGameState().Cheats.DisableSupportLimits)
2023-02-14 22:19:42 +01:00
{
Dropdown::SetChecked(DDIDX_DISABLE_SUPPORT_LIMITS, true);
2023-02-14 22:19:42 +01:00
}
2021-05-24 16:30:46 +02:00
gDropdownDefaultIndex = DDIDX_CHEATS;
2021-05-24 16:30:46 +02:00
}
void TopToolbar::CheatsMenuDropdown(int16_t dropdownIndex)
2018-06-22 23:21:44 +02:00
{
switch (dropdownIndex)
{
case DDIDX_CHEATS:
ContextOpenWindow(WindowClass::Cheats);
2018-06-22 23:21:44 +02:00
break;
case DDIDX_TILE_INSPECTOR:
ContextOpenWindow(WindowClass::TileInspector);
2018-06-22 23:21:44 +02:00
break;
case DDIDX_OBJECT_SELECTION:
WindowCloseAll();
ContextOpenWindow(WindowClass::EditorObjectSelection);
2018-06-22 23:21:44 +02:00
break;
case DDIDX_INVENTIONS_LIST:
ContextOpenWindow(WindowClass::EditorInventionList);
2018-06-22 23:21:44 +02:00
break;
case DDIDX_SCENARIO_OPTIONS:
ContextOpenWindow(WindowClass::EditorScenarioOptions);
2022-03-06 16:04:11 +01:00
break;
case DDIDX_OBJECTIVE_OPTIONS:
ContextOpenWindow(WindowClass::EditorObjectiveOptions);
2018-06-22 23:21:44 +02:00
break;
case DDIDX_ENABLE_SANDBOX_MODE:
CheatsSet(CheatType::SandboxMode, !GetGameState().Cheats.SandboxMode);
2018-06-22 23:21:44 +02:00
break;
case DDIDX_DISABLE_CLEARANCE_CHECKS:
CheatsSet(CheatType::DisableClearanceChecks, !GetGameState().Cheats.DisableClearanceChecks);
break;
case DDIDX_DISABLE_SUPPORT_LIMITS:
CheatsSet(CheatType::DisableSupportLimits, !GetGameState().Cheats.DisableSupportLimits);
break;
}
2023-02-14 22:19:42 +01:00
}
void TopToolbar::InitDebugMenu(Widget& widget)
2023-02-14 22:19:42 +01:00
{
gDropdownItems[DDIDX_CONSOLE].Format = STR_TOGGLE_OPTION;
gDropdownItems[DDIDX_CONSOLE].Args = STR_DEBUG_DROPDOWN_CONSOLE;
gDropdownItems[DDIDX_DEBUG_PAINT].Format = STR_TOGGLE_OPTION;
gDropdownItems[DDIDX_DEBUG_PAINT].Args = STR_DEBUG_DROPDOWN_DEBUG_PAINT;
2023-02-14 22:19:42 +01:00
WindowDropdownShowText(
{ windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[0] | 0x80,
Dropdown::Flag::StayOpen, TOP_TOOLBAR_DEBUG_COUNT);
2023-02-14 22:19:42 +01:00
Dropdown::SetChecked(DDIDX_DEBUG_PAINT, WindowFindByClass(WindowClass::DebugPaint) != nullptr);
2023-02-14 22:19:42 +01:00
}
void TopToolbar::DebugMenuDropdown(int16_t dropdownIndex)
2023-02-14 22:19:42 +01:00
{
auto* w = WindowGetMain();
if (w != nullptr)
2023-02-14 22:19:42 +01:00
{
switch (dropdownIndex)
2023-02-14 22:19:42 +01:00
{
case DDIDX_CONSOLE:
2023-02-14 22:19:42 +01:00
{
auto& console = GetInGameConsole();
console.Open();
2023-02-14 22:19:42 +01:00
break;
}
case DDIDX_DEBUG_PAINT:
if (WindowFindByClass(WindowClass::DebugPaint) == nullptr)
{
ContextOpenWindow(WindowClass::DebugPaint);
}
else
{
WindowCloseByClass(WindowClass::DebugPaint);
}
break;
2023-02-14 22:19:42 +01:00
}
}
}
void TopToolbar::InitNetworkMenu(Widget& widget)
2018-06-22 23:21:44 +02:00
{
gDropdownItems[DDIDX_MULTIPLAYER].Format = STR_MULTIPLAYER;
gDropdownItems[DDIDX_MULTIPLAYER_RECONNECT].Format = STR_MULTIPLAYER_RECONNECT;
2023-02-14 22:19:42 +01:00
WindowDropdownShowText(
{ windowPos.x + widget.left, windowPos.y + widget.top }, widget.height() + 1, colours[0] | 0x80, 0,
TOP_TOOLBAR_NETWORK_COUNT);
2023-02-14 22:19:42 +01:00
Dropdown::SetDisabled(DDIDX_MULTIPLAYER_RECONNECT, !NetworkIsDesynchronised());
2015-07-02 18:00:39 +02:00
gDropdownDefaultIndex = DDIDX_MULTIPLAYER;
2023-02-14 22:19:42 +01:00
}
void TopToolbar::NetworkMenuDropdown(int16_t dropdownIndex)
2023-02-14 22:19:42 +01:00
{
auto* w = WindowGetMain();
if (w != nullptr)
2023-02-14 22:19:42 +01:00
{
switch (dropdownIndex)
2023-02-14 22:19:42 +01:00
{
case DDIDX_MULTIPLAYER:
ContextOpenWindow(WindowClass::Multiplayer);
break;
case DDIDX_MULTIPLAYER_RECONNECT:
NetworkReconnect();
break;
2023-02-14 22:19:42 +01:00
}
}
}
} // namespace OpenRCT2::Ui::Windows