1 /*****************************************************************************
2 * Copyright (c) 2014-2020 OpenRCT2 developers
3 *
4 * For a complete list of all authors, please refer to contributors.md
5 * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6 *
7 * OpenRCT2 is licensed under the GNU General Public License version 3.
8 *****************************************************************************/
9
10 #include <algorithm>
11 #include <iterator>
12 #include <openrct2-ui/interface/Dropdown.h>
13 #include <openrct2-ui/interface/Widget.h>
14 #include <openrct2-ui/windows/Window.h>
15 #include <openrct2/Game.h>
16 #include <openrct2/Input.h>
17 #include <openrct2/actions/TileModifyAction.h>
18 #include <openrct2/common.h>
19 #include <openrct2/core/Guard.hpp>
20 #include <openrct2/localisation/Localisation.h>
21 #include <openrct2/localisation/StringIds.h>
22 #include <openrct2/object/TerrainEdgeObject.h>
23 #include <openrct2/object/TerrainSurfaceObject.h>
24 #include <openrct2/ride/RideData.h>
25 #include <openrct2/ride/Track.h>
26 #include <openrct2/sprites.h>
27 #include <openrct2/windows/tile_inspector.h>
28 #include <openrct2/world/Banner.h>
29 #include <openrct2/world/Footpath.h>
30 #include <openrct2/world/LargeScenery.h>
31 #include <openrct2/world/Park.h>
32 #include <openrct2/world/Scenery.h>
33 #include <openrct2/world/SmallScenery.h>
34 #include <openrct2/world/Surface.h>
35 #include <openrct2/world/TileInspector.h>
36
37 static constexpr const rct_string_id EntranceTypeStringIds[] = {
38 STR_TILE_INSPECTOR_ENTRANCE_TYPE_RIDE_ENTRANCE,
39 STR_TILE_INSPECTOR_ENTRANCE_TYPE_RIDE_EXIT,
40 STR_TILE_INSPECTOR_ENTRANCE_TYPE_PARK_ENTRANC,
41 };
42
43 static constexpr const rct_string_id ParkEntrancePartStringIds[] = {
44 STR_TILE_INSPECTOR_ENTRANCE_MIDDLE,
45 STR_TILE_INSPECTOR_ENTRANCE_LEFT,
46 STR_TILE_INSPECTOR_ENTRANCE_RIGHT,
47 };
48
49 static constexpr const rct_string_id WallSlopeStringIds[] = {
50 STR_TILE_INSPECTOR_WALL_FLAT,
51 STR_TILE_INSPECTOR_WALL_SLOPED_LEFT,
52 STR_TILE_INSPECTOR_WALL_SLOPED_RIGHT,
53 STR_TILE_INSPECTOR_WALL_ANIMATION_FRAME,
54 };
55
56 enum WINDOW_TILE_INSPECTOR_WIDGET_IDX
57 {
58 WIDX_BACKGROUND,
59 WIDX_TITLE,
60 WIDX_CLOSE,
61 WIDX_LIST,
62 WIDX_SPINNER_X,
63 WIDX_SPINNER_X_INCREASE,
64 WIDX_SPINNER_X_DECREASE,
65 WIDX_SPINNER_Y,
66 WIDX_SPINNER_Y_INCREASE,
67 WIDX_SPINNER_Y_DECREASE,
68 WIDX_BUTTON_CORRUPT,
69 WIDX_BUTTON_REMOVE,
70 WIDX_BUTTON_MOVE_UP,
71 WIDX_BUTTON_MOVE_DOWN,
72 WIDX_BUTTON_ROTATE,
73 WIDX_BUTTON_SORT,
74 WIDX_BUTTON_PASTE,
75 WIDX_BUTTON_COPY,
76 WIDX_COLUMN_TYPE,
77 WIDX_COLUMN_BASEHEIGHT,
78 WIDX_COLUMN_CLEARANCEHEIGHT,
79 WIDX_COLUMN_DIRECTION,
80 WIDX_COLUMN_GHOSTFLAG,
81 WIDX_COLUMN_LASTFLAG,
82 WIDX_GROUPBOX_DETAILS,
83 WIDX_GROUPBOX_PROPERTIES,
84
85 PAGE_WIDGETS,
86
87 // Surface
88 WIDX_SURFACE_SPINNER_HEIGHT = PAGE_WIDGETS,
89 WIDX_SURFACE_SPINNER_HEIGHT_INCREASE,
90 WIDX_SURFACE_SPINNER_HEIGHT_DECREASE,
91 WIDX_SURFACE_BUTTON_REMOVE_FENCES,
92 WIDX_SURFACE_BUTTON_RESTORE_FENCES,
93 WIDX_SURFACE_CHECK_CORNER_N,
94 WIDX_SURFACE_CHECK_CORNER_E,
95 WIDX_SURFACE_CHECK_CORNER_S,
96 WIDX_SURFACE_CHECK_CORNER_W,
97 WIDX_SURFACE_CHECK_DIAGONAL,
98
99 // Path
100 WIDX_PATH_SPINNER_HEIGHT = PAGE_WIDGETS,
101 WIDX_PATH_SPINNER_HEIGHT_INCREASE,
102 WIDX_PATH_SPINNER_HEIGHT_DECREASE,
103 WIDX_PATH_CHECK_BROKEN,
104 WIDX_PATH_CHECK_SLOPED,
105 WIDX_PATH_CHECK_EDGE_NE, // Note: This is NOT named after the world orientation, but after the way
106 WIDX_PATH_CHECK_EDGE_E, // it looks in the window (top corner is north). Their order is important,
107 WIDX_PATH_CHECK_EDGE_SE, // as this is the same order paths use for their corners / edges.
108 WIDX_PATH_CHECK_EDGE_S, // N
109 WIDX_PATH_CHECK_EDGE_SW, // NW-------NE
110 WIDX_PATH_CHECK_EDGE_W, // W ------------- E
111 WIDX_PATH_CHECK_EDGE_NW, // SW-------SE
112 WIDX_PATH_CHECK_EDGE_N, // S
113
114 // Track
115 WIDX_TRACK_CHECK_APPLY_TO_ALL = PAGE_WIDGETS,
116 WIDX_TRACK_SPINNER_HEIGHT,
117 WIDX_TRACK_SPINNER_HEIGHT_INCREASE,
118 WIDX_TRACK_SPINNER_HEIGHT_DECREASE,
119 WIDX_TRACK_CHECK_CHAIN_LIFT,
120 WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED,
121 WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE,
122
123 // Scenery
124 WIDX_SCENERY_SPINNER_HEIGHT = PAGE_WIDGETS,
125 WIDX_SCENERY_SPINNER_HEIGHT_INCREASE,
126 WIDX_SCENERY_SPINNER_HEIGHT_DECREASE,
127 WIDX_SCENERY_CHECK_QUARTER_N,
128 WIDX_SCENERY_CHECK_QUARTER_E,
129 WIDX_SCENERY_CHECK_QUARTER_S,
130 WIDX_SCENERY_CHECK_QUARTER_W,
131 WIDX_SCENERY_CHECK_COLLISION_N,
132 WIDX_SCENERY_CHECK_COLLISION_E,
133 WIDX_SCENERY_CHECK_COLLISION_S,
134 WIDX_SCENERY_CHECK_COLLISION_W,
135
136 // Entrance
137 WIDX_ENTRANCE_SPINNER_HEIGHT = PAGE_WIDGETS,
138 WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE,
139 WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE,
140 WIDX_ENTRANCE_BUTTON_MAKE_USABLE,
141
142 // Wall
143 WIDX_WALL_SPINNER_HEIGHT = PAGE_WIDGETS,
144 WIDX_WALL_SPINNER_HEIGHT_INCREASE,
145 WIDX_WALL_SPINNER_HEIGHT_DECREASE,
146 WIDX_WALL_DROPDOWN_SLOPE,
147 WIDX_WALL_DROPDOWN_SLOPE_BUTTON,
148 WIDX_WALL_SPINNER_ANIMATION_FRAME,
149 WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE,
150 WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE,
151
152 // Large
153 WIDX_LARGE_SCENERY_SPINNER_HEIGHT = PAGE_WIDGETS,
154 WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE,
155 WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE,
156
157 // Banner
158 WIDX_BANNER_SPINNER_HEIGHT = PAGE_WIDGETS,
159 WIDX_BANNER_SPINNER_HEIGHT_INCREASE,
160 WIDX_BANNER_SPINNER_HEIGHT_DECREASE,
161 WIDX_BANNER_CHECK_BLOCK_NE,
162 WIDX_BANNER_CHECK_BLOCK_SE,
163 WIDX_BANNER_CHECK_BLOCK_SW,
164 WIDX_BANNER_CHECK_BLOCK_NW,
165
166 // Corrupt
167 WIDX_CORRUPT_SPINNER_HEIGHT = PAGE_WIDGETS,
168 WIDX_CORRUPT_SPINNER_HEIGHT_INCREASE,
169 WIDX_CORRUPT_SPINNER_HEIGHT_DECREASE,
170 WIDX_CORRUPT_BUTTON_CLAMP,
171 };
172
173 static constexpr const rct_string_id WINDOW_TITLE = STR_TILE_INSPECTOR_TITLE;
174
175 // Window sizes
176 static constexpr const int32_t WW = 400;
177 static constexpr const int32_t WH = 170;
178 constexpr int32_t MIN_WW = WW;
179 constexpr int32_t MAX_WW = WW;
180 constexpr int32_t MIN_WH = 130;
181 constexpr int32_t MAX_WH = 800;
182
183 // Button space for top buttons
184 constexpr auto ToolbarButtonAnchor = ScreenCoordsXY{ WW - 27, 17 };
185 constexpr auto ToolbarButtonSize = ScreenSize{ 24, 24 };
186 constexpr auto ToolbarButtonHalfSize = ScreenSize{ 24, 12 };
187 constexpr auto ToolbarButtonOffsetX = ScreenSize{ -24, 0 };
188
189 // List's column offsets
190 constexpr auto TypeColumnXY = ScreenCoordsXY{ 3, 42 };
191 constexpr auto TypeColumnSize = ScreenSize{ 272, 14 };
192 constexpr auto BaseHeightColumnXY = TypeColumnXY + ScreenSize{ TypeColumnSize.width, 0 };
193 constexpr auto BaseHeightColumnSize = ScreenSize{ 30, 14 };
194 constexpr auto ClearanceHeightColumnXY = BaseHeightColumnXY + ScreenCoordsXY{ BaseHeightColumnSize.width, 0 };
195 constexpr auto ClearanceHeightColumnSize = ScreenSize{ 30, 14 };
196 constexpr auto DirectionColumnXY = ClearanceHeightColumnXY + ScreenCoordsXY{ ClearanceHeightColumnSize.width, 0 };
197 constexpr auto DirectionColumnSize = ScreenSize{ 15, 14 };
198 constexpr auto GhostFlagColumnXY = DirectionColumnXY + ScreenCoordsXY{ DirectionColumnSize.width, 0 };
199 constexpr auto GhostFlagColumnSize = ScreenSize{ 15, 14 };
200 constexpr auto LastFlagColumnXY = GhostFlagColumnXY + ScreenCoordsXY{ GhostFlagColumnSize.width, 0 };
201 constexpr auto LastFlagColumnSize = ScreenSize{ 32, 14 };
202
203 constexpr int32_t PADDING_BOTTOM = 15;
204 constexpr int32_t GROUPBOX_PADDING = 6;
205 constexpr int32_t HORIZONTAL_GROUPBOX_PADDING = 5;
206 constexpr int32_t VERTICAL_GROUPBOX_PADDING = 4;
207 constexpr auto PropertyButtonSize = ScreenSize{ 130, 18 };
208 constexpr auto PropertyFullWidth = ScreenSize{ 370, 18 };
209
PropertyRowCol(ScreenCoordsXY anchor,int32_t row,int32_t column)210 constexpr ScreenCoordsXY PropertyRowCol(ScreenCoordsXY anchor, int32_t row, int32_t column)
211 {
212 return anchor
213 + ScreenCoordsXY{ column * (PropertyButtonSize.width + HORIZONTAL_GROUPBOX_PADDING),
214 row * (PropertyButtonSize.height + VERTICAL_GROUPBOX_PADDING) };
215 }
216
CheckboxGroupOffset(ScreenCoordsXY anchorPoint,int16_t horizontalMultiplier,int16_t verticalMultiplier)217 constexpr ScreenCoordsXY CheckboxGroupOffset(
218 ScreenCoordsXY anchorPoint, int16_t horizontalMultiplier, int16_t verticalMultiplier)
219 {
220 return anchorPoint + ScreenCoordsXY{ 14 * horizontalMultiplier, 7 * verticalMultiplier };
221 }
222
223 // clang-format off
224 // Macros for easily obtaining the top and bottom of a widget inside a properties group box
225 #define GBBT(GROUPTOP, row) ((GROUPTOP) + 14 + row * (PropertyButtonSize.height + VERTICAL_GROUPBOX_PADDING))
226 #define GBBB(GROUPTOP, row) (GBBT((GROUPTOP), row) + PropertyButtonSize.height)
227
228 #define MAIN_TILE_INSPECTOR_WIDGETS \
229 WINDOW_SHIM(WINDOW_TITLE, WW, WH), \
230 MakeWidget({3, 57}, {WW - 6, WH - PADDING_BOTTOM - 58}, WindowWidgetType::Scroll, WindowColour::Secondary, SCROLL_VERTICAL), /* Element list */ \
231 /* X and Y spinners */ \
232 MakeSpinnerWidgets({20, 23}, {51, 12}, WindowWidgetType::Spinner, WindowColour::Secondary), /* Spinner X (3 widgets) */ \
233 MakeSpinnerWidgets({90, 23}, {51, 12}, WindowWidgetType::Spinner, WindowColour::Secondary), /* Spinner Y (3 widgets) */ \
234 /* Top buttons */ \
235 MakeWidget(ToolbarButtonAnchor, ToolbarButtonSize, WindowWidgetType::FlatBtn , WindowColour::Secondary, SPR_MAP, STR_INSERT_CORRUPT_TIP), /* Insert corrupt button */ \
236 MakeWidget(ToolbarButtonAnchor + ToolbarButtonOffsetX * 1, ToolbarButtonSize, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_DEMOLISH, STR_REMOVE_SELECTED_ELEMENT_TIP ), /* Remove button */ \
237 MakeWidget(ToolbarButtonAnchor + ToolbarButtonOffsetX * 2, ToolbarButtonHalfSize, WindowWidgetType::Button, WindowColour::Secondary, STR_UP, STR_MOVE_SELECTED_ELEMENT_UP_TIP), /* Move up */ \
238 MakeWidget(ToolbarButtonAnchor + ToolbarButtonOffsetX * 2 + ScreenSize{0, 12}, ToolbarButtonHalfSize, WindowWidgetType::Button, WindowColour::Secondary, STR_DOWN, STR_MOVE_SELECTED_ELEMENT_DOWN_TIP), /* Move down */ \
239 MakeWidget(ToolbarButtonAnchor + ToolbarButtonOffsetX * 3, ToolbarButtonSize, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_ROTATE_ARROW, STR_ROTATE_SELECTED_ELEMENT_TIP), /* Rotate button */ \
240 MakeWidget(ToolbarButtonAnchor + ToolbarButtonOffsetX * 4, ToolbarButtonSize, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_G2_SORT, STR_TILE_INSPECTOR_SORT_TIP), /* Sort button */ \
241 MakeWidget(ToolbarButtonAnchor + ToolbarButtonOffsetX * 5, ToolbarButtonSize, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_G2_PASTE, STR_TILE_INSPECTOR_PASTE_TIP), /* Paste button */ \
242 MakeWidget(ToolbarButtonAnchor + ToolbarButtonOffsetX * 6, ToolbarButtonSize, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_G2_COPY, STR_TILE_INSPECTOR_COPY_TIP), /* Copy button */ \
243 /* Column headers */ \
244 MakeWidget(TypeColumnXY, TypeColumnSize, WindowWidgetType::TableHeader, WindowColour::Secondary, STR_TILE_INSPECTOR_ELEMENT_TYPE), /* Type */ \
245 MakeWidget(BaseHeightColumnXY, BaseHeightColumnSize, WindowWidgetType::TableHeader, WindowColour::Secondary, STR_TILE_INSPECTOR_BASE_HEIGHT_SHORT, STR_TILE_INSPECTOR_BASE_HEIGHT), /* Base height */ \
246 MakeWidget(ClearanceHeightColumnXY, ClearanceHeightColumnSize, WindowWidgetType::TableHeader, WindowColour::Secondary, STR_TILE_INSPECTOR_CLEARANGE_HEIGHT_SHORT, STR_TILE_INSPECTOR_CLEARANCE_HEIGHT), /* Clearance height */ \
247 MakeWidget(DirectionColumnXY, DirectionColumnSize, WindowWidgetType::TableHeader, WindowColour::Secondary, STR_TILE_INSPECTOR_DIRECTION_SHORT, STR_TILE_INSPECTOR_DIRECTION), /* Direction */ \
248 MakeWidget(GhostFlagColumnXY, GhostFlagColumnSize, WindowWidgetType::TableHeader, WindowColour::Secondary, STR_TILE_INSPECTOR_FLAG_GHOST_SHORT, STR_TILE_INSPECTOR_FLAG_GHOST), /* Ghost flag */ \
249 MakeWidget(LastFlagColumnXY, LastFlagColumnSize, WindowWidgetType::TableHeader, WindowColour::Secondary, STR_TILE_INSPECTOR_FLAG_LAST_SHORT, STR_TILE_INSPECTOR_FLAG_LAST), /* Last of tile flag */ \
250 /* Group boxes */ \
251 MakeWidget({6, 0}, {WW - 12, 0}, WindowWidgetType::Groupbox, WindowColour::Secondary, STR_NONE, STR_NONE ), /* Details group box */ \
252 MakeWidget({6, 0}, {WW - 12, 0}, WindowWidgetType::Groupbox, WindowColour::Secondary, STR_TILE_INSPECTOR_GROUPBOX_PROPERTIES, STR_NONE ) /* Properties group box */
253
254 static rct_widget DefaultWidgets[] = {
255 MAIN_TILE_INSPECTOR_WIDGETS,
256 WIDGETS_END,
257 };
258
259 constexpr int32_t NumSurfaceProperties = 4;
260 constexpr int32_t NumSurfaceDetails = 4;
261 constexpr int32_t SurfacePropertiesHeight = 16 + NumSurfaceProperties * 21;
262 constexpr int32_t SurfaceDetailsHeight = 20 + NumSurfaceDetails * 11;
263 static rct_widget SurfaceWidgets[] = {
264 MAIN_TILE_INSPECTOR_WIDGETS,
265 MakeSpinnerWidgets(PropertyRowCol({ 12, 0 }, 0, 1), PropertyButtonSize, WindowWidgetType::Spinner, WindowColour::Secondary), // WIDX_SURFACE_SPINNER_HEIGHT{,_INCREASE,_DECREASE}
266 MakeWidget(PropertyRowCol({ 12, 0 }, 1, 0), PropertyButtonSize, WindowWidgetType::Button, WindowColour::Secondary, STR_TILE_INSPECTOR_SURFACE_REMOVE_FENCES), // WIDX_SURFACE_BUTTON_REMOVE_FENCES
267 MakeWidget(PropertyRowCol({ 12, 0 }, 1, 1), PropertyButtonSize, WindowWidgetType::Button, WindowColour::Secondary, STR_TILE_INSPECTOR_SURFACE_RESTORE_FENCES), // WIDX_SURFACE_BUTTON_RESTORE_FENCES
268 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 3, 1), 1, 0), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_SURFACE_CHECK_CORNER_N
269 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 3, 1), 2, 1), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_SURFACE_CHECK_CORNER_E
270 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 3, 1), 1, 2), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_SURFACE_CHECK_CORNER_S
271 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 3, 1), 0, 1), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_SURFACE_CHECK_CORNER_W
272 MakeWidget(PropertyRowCol({ 12, 0 }, 4, 0), PropertyFullWidth, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_TILE_INSPECTOR_SURFACE_DIAGONAL), // WIDX_SURFACE_CHECK_DIAGONAL
273 WIDGETS_END,
274 };
275
276 constexpr int32_t NumPathProperties = 5;
277 constexpr int32_t NumPathDetails = 2;
278 constexpr int32_t PathPropertiesHeight = 16 + NumPathProperties * 21;
279 constexpr int32_t PathDetailsHeight = 20 + NumPathDetails * 11;
280 static rct_widget PathWidgets[] = {
281 MAIN_TILE_INSPECTOR_WIDGETS,
282 MakeSpinnerWidgets(PropertyRowCol({ 12, 0 }, 0, 1), PropertyButtonSize, WindowWidgetType::Spinner, WindowColour::Secondary), // WIDX_PATH_SPINNER_HEIGHT{,_INCREASE,_DECREASE}
283 MakeWidget(PropertyRowCol({ 12, 0 }, 1, 0), PropertyFullWidth, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_TILE_INSPECTOR_PATH_BROKEN), // WIDX_PATH_CHECK_BROKEN
284 MakeWidget(PropertyRowCol({ 12, 0 }, 2, 0), PropertyFullWidth, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_TILE_INSPECTOR_PATH_SLOPED), // WIDX_PATH_CHECK_SLOPED
285 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 3, 1), 3, 1), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_PATH_CHECK_EDGE_NE
286 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 3, 1), 4, 2), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_PATH_CHECK_EDGE_E
287 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 3, 1), 3, 3), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_PATH_CHECK_EDGE_SE
288 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 3, 1), 2, 4), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_PATH_CHECK_EDGE_S
289 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 3, 1), 1, 3), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_PATH_CHECK_EDGE_SW
290 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 3, 1), 0, 2), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_PATH_CHECK_EDGE_W
291 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 3, 1), 1, 1), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_PATH_CHECK_EDGE_NW
292 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 3, 1), 2, 0), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_PATH_CHECK_EDGE_N
293 WIDGETS_END,
294 };
295
296 constexpr int32_t NumTrackProperties = 5;
297 constexpr int32_t NumTrackDetails = 7;
298 constexpr int32_t TrackPropertiesHeight = 16 + NumTrackProperties * 21;
299 constexpr int32_t TrackDetailsHeight = 20 + NumTrackDetails * 11;
300 static rct_widget TrackWidgets[] = {
301 MAIN_TILE_INSPECTOR_WIDGETS,
302 MakeWidget(PropertyRowCol({ 12, 0}, 0, 0), PropertyFullWidth, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_TILE_INSPECTOR_TRACK_ENTIRE_TRACK_PIECE), // WIDX_TRACK_CHECK_APPLY_TO_ALL
303 MakeSpinnerWidgets(PropertyRowCol({ 12, 0 }, 1, 1), PropertyButtonSize, WindowWidgetType::Spinner, WindowColour::Secondary), // WIDX_TRACK_SPINNER_HEIGHT{,_INCREASE,_DECREASE}
304 MakeWidget(PropertyRowCol({ 12, 0}, 2, 0), PropertyFullWidth, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_TILE_INSPECTOR_TRACK_CHAIN_LIFT), // WIDX_TRACK_CHECK_CHAIN_LIFT
305 MakeWidget(PropertyRowCol({ 12, 0}, 3, 0), PropertyFullWidth, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_TILE_INSPECTOR_TRACK_BLOCK_BRAKE), // WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED
306 MakeWidget(PropertyRowCol({ 12, 0}, 4, 0), PropertyFullWidth, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_TILE_INSPECTOR_TRACK_IS_INDESTRUCTIBLE), // WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE
307 WIDGETS_END,
308 };
309
310 constexpr int32_t NumSceneryProperties = 4; // The checkbox groups both count for 2 rows
311 constexpr int32_t NumSceneryDetails = 4;
312 constexpr int32_t SceneryPropertiesHeight = 16 + NumSceneryProperties * 21;
313 constexpr int32_t SceneryDetailsHeight = 20 + NumSceneryDetails * 11;
314 static rct_widget SceneryWidgets[] = {
315 MAIN_TILE_INSPECTOR_WIDGETS,
316 MakeSpinnerWidgets(PropertyRowCol({ 12, 0 }, 0, 1), PropertyButtonSize, WindowWidgetType::Spinner, WindowColour::Secondary), // WIDX_SCENERY_SPINNER_HEIGHT{,_INCREASE,_DECREASE}
317 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 1, 1), 1, 0), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_SCENERY_CHECK_QUARTER_N
318 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 1, 1), 2, 1), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_SCENERY_CHECK_QUARTER_E
319 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 1, 1), 1, 2), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_SCENERY_CHECK_QUARTER_S
320 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 1, 1), 0, 1), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_SCENERY_CHECK_QUARTER_W
321 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 2, 1), 1, 0), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_SCENERY_CHECK_COLLISION_N
322 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 2, 1), 2, 1), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_SCENERY_CHECK_COLLISION_E
323 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 2, 1), 1, 2), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_SCENERY_CHECK_COLLISION_S
324 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 2, 1), 0, 1), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_SCENERY_CHECK_COLLISION_W
325 WIDGETS_END,
326 };
327
328 constexpr int32_t NumEntranceProperties = 2;
329 constexpr int32_t NumEntranceDetails = 4;
330 constexpr int32_t EntrancePropertiesHeight = 16 + NumEntranceProperties * 21;
331 constexpr int32_t EntranceDetailsHeight = 20 + NumEntranceDetails * 11;
332 static rct_widget EntranceWidgets[] = {
333 MAIN_TILE_INSPECTOR_WIDGETS,
334 MakeSpinnerWidgets(PropertyRowCol({ 12, 0 }, 0, 1), PropertyButtonSize, WindowWidgetType::Spinner, WindowColour::Secondary), // WIDX_ENTRANCE_SPINNER_HEIGHT{,_INCREASE,_DECREASE}
335 MakeWidget(PropertyRowCol({ 12, 0 }, 1, 0), PropertyButtonSize, WindowWidgetType::Button, WindowColour::Secondary, STR_TILE_INSPECTOR_ENTRANCE_MAKE_USABLE, STR_TILE_INSPECTOR_ENTRANCE_MAKE_USABLE_TIP), // WIDX_ENTRANCE_BUTTON_MAKE_USABLE
336 WIDGETS_END,
337 };
338
339 constexpr int32_t NumWallProperties = 3;
340 constexpr int32_t NumWallDetails = 2;
341 constexpr int32_t WallPropertiesHeight = 16 + NumWallProperties * 21;
342 constexpr int32_t WallDetailsHeight = 20 + NumWallDetails * 11;
343 static rct_widget WallWidgets[] = {
344 MAIN_TILE_INSPECTOR_WIDGETS,
345 MakeSpinnerWidgets(PropertyRowCol({ 12, 0 }, 0, 1), PropertyButtonSize, WindowWidgetType::Spinner, WindowColour::Secondary), // WIDX_WALL_SPINNER_HEIGHT{,_INCREASE,_DECREASE}
346 MakeWidget(PropertyRowCol({ 12, 0 }, 1, 1), PropertyButtonSize, WindowWidgetType::DropdownMenu, WindowColour::Secondary), // WIDX_WALL_DROPDOWN_SLOPE
347 MakeWidget(PropertyRowCol({ 12 + PropertyButtonSize.width - 12, 0 }, 1, 1), { 11, 12}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH), // WIDX_WALL_DROPDOWN_SLOPE_BUTTON
348 MakeSpinnerWidgets(PropertyRowCol({ 12, 0 }, 2, 1), PropertyButtonSize, WindowWidgetType::Spinner, WindowColour::Secondary), // WIDX_WALL_SPINNER_ANIMATION_FRAME{,_INCREASE,_DECREASE}
349 WIDGETS_END,
350 };
351
352 constexpr int32_t NumLargeSceneryProperties = 1;
353 constexpr int32_t NumLargeSceneryDetails = 3;
354 constexpr int32_t LargeSceneryPropertiesHeight = 16 + NumLargeSceneryProperties * 21;
355 constexpr int32_t LargeSceneryDetailsHeight = 20 + NumLargeSceneryDetails * 11;
356 static rct_widget LargeSceneryWidgets[] = {
357 MAIN_TILE_INSPECTOR_WIDGETS,
358 MakeSpinnerWidgets(PropertyRowCol({ 12, 0 }, 0, 1), PropertyButtonSize, WindowWidgetType::Spinner, WindowColour::Secondary), // WIDX_LARGE_SCENERY_SPINNER_HEIGHT{,_INCREASE,_DECREASE}
359 WIDGETS_END,
360 };
361
362 constexpr int32_t NumBannerProperties = 3;
363 constexpr int32_t NumBannerDetails = 1;
364 constexpr int32_t BannerPropertiesHeight = 16 + NumBannerProperties * 21;
365 constexpr int32_t BannerDetailsHeight = 20 + NumBannerDetails * 11;
366 static rct_widget BannerWidgets[] = {
367 MAIN_TILE_INSPECTOR_WIDGETS,
368 MakeSpinnerWidgets(PropertyRowCol({ 12, 0 }, 0, 1), PropertyButtonSize, WindowWidgetType::Spinner, WindowColour::Secondary), // WIDX_BANNER_SPINNER_HEIGHT{,_INCREASE,_DECREASE}
369 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 1, 1), 3, 1), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_BANNER_CHECK_BLOCK_NE
370 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 1, 1), 3, 3), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_BANNER_CHECK_BLOCK_SE
371 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 1, 1), 1, 3), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_BANNER_CHECK_BLOCK_SW
372 MakeWidget(CheckboxGroupOffset(PropertyRowCol({ 12, 0 }, 1, 1), 1, 1), { 12, 12 }, WindowWidgetType::Checkbox, WindowColour::Secondary), // WIDX_BANNER_CHECK_BLOCK_NW
373
374 WIDGETS_END,
375 };
376
377 constexpr int32_t NumCorruptProperties = 2;
378 constexpr int32_t NumCorruptDetails = 0;
379 constexpr int32_t CorruptPropertiesHeight = 16 + NumCorruptProperties * 21;
380 constexpr int32_t CorruptDetailsHeight = 20 + NumCorruptDetails * 11;
381 static rct_widget CorruptWidgets[] = {
382 MAIN_TILE_INSPECTOR_WIDGETS,
383 MakeSpinnerWidgets(PropertyRowCol({ 12, 0 }, 0, 1), PropertyButtonSize, WindowWidgetType::Spinner, WindowColour::Secondary), // WIDX_CORRUPT_SPINNER_HEIGHT
384 MakeWidget(PropertyRowCol({ 12, 0 }, 1, 0), PropertyButtonSize, WindowWidgetType::Button, WindowColour::Secondary,STR_TILE_INSPECTOR_CLAMP_TO_NEXT, STR_TILE_INSPECTOR_CLAMP_TO_NEXT_TIP ), // WIDX_CORRUPT_BUTTON_CLAMP
385 WIDGETS_END,
386 };
387
388 static rct_widget *PageWidgets[] = {
389 DefaultWidgets,
390 SurfaceWidgets,
391 PathWidgets,
392 TrackWidgets,
393 SceneryWidgets,
394 EntranceWidgets,
395 WallWidgets,
396 LargeSceneryWidgets,
397 BannerWidgets,
398 CorruptWidgets,
399 };
400 // clang-format on
401
402 struct TileInspectorGroupboxSettings
403 {
404 // Offsets from the bottom of the window
405 int16_t details_top_offset, details_bottom_offset;
406 int16_t properties_top_offset, properties_bottom_offset;
407 // String to be displayed in the details groupbox
408 rct_string_id string_id;
409 };
410
MakeGroupboxSettings(int16_t detailsHeight,int16_t propertiesHeight,rct_string_id stringId)411 static constexpr TileInspectorGroupboxSettings MakeGroupboxSettings(
412 int16_t detailsHeight, int16_t propertiesHeight, rct_string_id stringId)
413 {
414 TileInspectorGroupboxSettings settings{};
415 decltype(settings.properties_bottom_offset) offsetSum = 0;
416 settings.properties_bottom_offset = (offsetSum += PADDING_BOTTOM);
417 settings.properties_top_offset = (offsetSum += propertiesHeight);
418 settings.details_bottom_offset = (offsetSum += GROUPBOX_PADDING);
419 settings.details_top_offset = (offsetSum += detailsHeight);
420 settings.string_id = stringId;
421 return settings;
422 }
423
424 static constexpr TileInspectorGroupboxSettings PageGroupBoxSettings[] = {
425 MakeGroupboxSettings(SurfaceDetailsHeight, SurfacePropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_SURFACE_INFO),
426 MakeGroupboxSettings(PathDetailsHeight, PathPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_PATH_INFO),
427 MakeGroupboxSettings(TrackDetailsHeight, TrackPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_TRACK_INFO),
428 MakeGroupboxSettings(SceneryDetailsHeight, SceneryPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_SCENERY_INFO),
429 MakeGroupboxSettings(EntranceDetailsHeight, EntrancePropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_ENTRANCE_INFO),
430 MakeGroupboxSettings(WallDetailsHeight, WallPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_WALL_INFO),
431 MakeGroupboxSettings(LargeSceneryDetailsHeight, LargeSceneryPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_BANNER_INFO),
432 MakeGroupboxSettings(BannerDetailsHeight, BannerPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_BANNER_INFO),
433 MakeGroupboxSettings(CorruptDetailsHeight, CorruptPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_CORRUPT_INFO),
434 };
435
436 static constexpr int32_t ViewportInteractionFlags = EnumsToFlags(
437 ViewportInteractionItem::Terrain, ViewportInteractionItem::Entity, ViewportInteractionItem::Ride,
438 ViewportInteractionItem::Scenery, ViewportInteractionItem::Footpath, ViewportInteractionItem::FootpathItem,
439 ViewportInteractionItem::ParkEntrance, ViewportInteractionItem::Wall, ViewportInteractionItem::LargeScenery,
440 ViewportInteractionItem::Banner);
441
442 static int16_t windowTileInspectorHighlightedIndex = -1;
443 static bool windowTileInspectorTileSelected = false;
444 static int32_t windowTileInspectorToolMouseX = 0;
445 static int32_t windowTileInspectorToolMouseY = 0;
446 static bool windowTileInspectorToolCtrlDown = false;
447 static CoordsXY windowTileInspectorToolMap = {};
448 static bool windowTileInspectorApplyToAll = false;
449 static bool windowTileInspectorElementCopied = false;
450 static TileElement tileInspectorCopiedElement;
451
452 static void window_tile_inspector_mouseup(rct_window* w, rct_widgetindex widgetIndex);
453 static void window_tile_inspector_resize(rct_window* w);
454 static void window_tile_inspector_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget);
455 static void window_tile_inspector_update(rct_window* w);
456 static void window_tile_inspector_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex);
457 static void window_tile_inspector_tool_update(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
458 static void window_tile_inspector_update_selected_tile(rct_window* w, const ScreenCoordsXY& screenCoords);
459 static void window_tile_inspector_tool_down(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
460 static void window_tile_inspector_tool_drag(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords);
461 static void window_tile_inspector_scrollgetsize(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height);
462 static void window_tile_inspector_scrollmousedown(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords);
463 static void window_tile_inspector_scrollmouseover(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords);
464 static void window_tile_inspector_invalidate(rct_window* w);
465 static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi);
466 static void window_tile_inspector_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex);
467 static void window_tile_inspector_set_page(rct_window* w, const TileInspectorPage page);
468 static void window_tile_inspector_close(rct_window* w);
469
__anon2a52322c0102(auto& events) 470 static rct_window_event_list TileInspectorWindowEvents([](auto& events) {
471 events.mouse_up = &window_tile_inspector_mouseup;
472 events.resize = &window_tile_inspector_resize;
473 events.mouse_down = &window_tile_inspector_mousedown;
474 events.dropdown = &window_tile_inspector_dropdown;
475 events.update = &window_tile_inspector_update;
476 events.tool_update = &window_tile_inspector_tool_update;
477 events.tool_down = &window_tile_inspector_tool_down;
478 events.tool_drag = &window_tile_inspector_tool_drag;
479 events.get_scroll_size = &window_tile_inspector_scrollgetsize;
480 events.scroll_mousedown = &window_tile_inspector_scrollmousedown;
481 events.scroll_mouseover = &window_tile_inspector_scrollmouseover;
482 events.invalidate = &window_tile_inspector_invalidate;
483 events.paint = &window_tile_inspector_paint;
484 events.scroll_paint = &window_tile_inspector_scrollpaint;
485 events.close = &window_tile_inspector_close;
486 });
487
488 // clang-format off
489 static uint64_t PageEnabledWidgets[] = {
490 (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT),
491 (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_SURFACE_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_SURFACE_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_SURFACE_BUTTON_REMOVE_FENCES) | (1ULL << WIDX_SURFACE_BUTTON_RESTORE_FENCES) | (1ULL << WIDX_SURFACE_CHECK_CORNER_N) | (1ULL << WIDX_SURFACE_CHECK_CORNER_E) | (1ULL << WIDX_SURFACE_CHECK_CORNER_S) | (1ULL << WIDX_SURFACE_CHECK_CORNER_W) | (1ULL << WIDX_SURFACE_CHECK_DIAGONAL),
492 (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_PATH_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_PATH_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_PATH_CHECK_SLOPED) | (1ULL << WIDX_PATH_CHECK_BROKEN) | (1ULL << WIDX_PATH_CHECK_EDGE_N) | (1ULL << WIDX_PATH_CHECK_EDGE_NE) | (1ULL << WIDX_PATH_CHECK_EDGE_E) | (1ULL << WIDX_PATH_CHECK_EDGE_SE) | (1ULL << WIDX_PATH_CHECK_EDGE_S) | (1ULL << WIDX_PATH_CHECK_EDGE_SW) | (1ULL << WIDX_PATH_CHECK_EDGE_W) | (1ULL << WIDX_PATH_CHECK_EDGE_NW),
493 (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_TRACK_CHECK_APPLY_TO_ALL) | (1ULL << WIDX_TRACK_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_TRACK_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_TRACK_CHECK_CHAIN_LIFT) | (1ULL << WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED) | (1ULL << WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE),
494 (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_SCENERY_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_SCENERY_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_SCENERY_CHECK_QUARTER_N) | (1ULL << WIDX_SCENERY_CHECK_QUARTER_E) | (1ULL << WIDX_SCENERY_CHECK_QUARTER_S) | (1ULL << WIDX_SCENERY_CHECK_QUARTER_W) | (1ULL << WIDX_SCENERY_CHECK_COLLISION_N) | (1ULL << WIDX_SCENERY_CHECK_COLLISION_E) | (1ULL << WIDX_SCENERY_CHECK_COLLISION_S) | (1ULL << WIDX_SCENERY_CHECK_COLLISION_W),
495 (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_ENTRANCE_BUTTON_MAKE_USABLE),
496 (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_WALL_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_WALL_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_WALL_DROPDOWN_SLOPE) | (1ULL << WIDX_WALL_DROPDOWN_SLOPE_BUTTON) | (1ULL << WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE) | (1ULL << WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE),
497 (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE),
498 (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_BANNER_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_BANNER_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_BANNER_CHECK_BLOCK_NE) | (1ULL << WIDX_BANNER_CHECK_BLOCK_SE) | (1ULL << WIDX_BANNER_CHECK_BLOCK_SW) | (1ULL << WIDX_BANNER_CHECK_BLOCK_NW),
499 (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_CORRUPT_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_CORRUPT_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_CORRUPT_BUTTON_CLAMP),
500 };
501
502 static uint64_t PageHoldDownWidgets[] = {
503 (1ULL << WIDX_SPINNER_X_INCREASE) | (1ULL << WIDX_SPINNER_X_DECREASE) | (1ULL << WIDX_SPINNER_Y_INCREASE) | (1ULL << WIDX_SPINNER_Y_DECREASE),
504 (1ULL << WIDX_SPINNER_X_INCREASE) | (1ULL << WIDX_SPINNER_X_DECREASE) | (1ULL << WIDX_SPINNER_Y_INCREASE) | (1ULL << WIDX_SPINNER_Y_DECREASE) | (1ULL << WIDX_SURFACE_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_SURFACE_SPINNER_HEIGHT_DECREASE),
505 (1ULL << WIDX_SPINNER_X_INCREASE) | (1ULL << WIDX_SPINNER_X_DECREASE) | (1ULL << WIDX_SPINNER_Y_INCREASE) | (1ULL << WIDX_SPINNER_Y_DECREASE) | (1ULL << WIDX_PATH_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_PATH_SPINNER_HEIGHT_DECREASE),
506 (1ULL << WIDX_SPINNER_X_INCREASE) | (1ULL << WIDX_SPINNER_X_DECREASE) | (1ULL << WIDX_SPINNER_Y_INCREASE) | (1ULL << WIDX_SPINNER_Y_DECREASE) | (1ULL << WIDX_TRACK_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_TRACK_SPINNER_HEIGHT_DECREASE),
507 (1ULL << WIDX_SPINNER_X_INCREASE) | (1ULL << WIDX_SPINNER_X_DECREASE) | (1ULL << WIDX_SPINNER_Y_INCREASE) | (1ULL << WIDX_SPINNER_Y_DECREASE) | (1ULL << WIDX_SCENERY_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_SCENERY_SPINNER_HEIGHT_DECREASE),
508 (1ULL << WIDX_SPINNER_X_INCREASE) | (1ULL << WIDX_SPINNER_X_DECREASE) | (1ULL << WIDX_SPINNER_Y_INCREASE) | (1ULL << WIDX_SPINNER_Y_DECREASE) | (1ULL << WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE),
509 (1ULL << WIDX_SPINNER_X_INCREASE) | (1ULL << WIDX_SPINNER_X_DECREASE) | (1ULL << WIDX_SPINNER_Y_INCREASE) | (1ULL << WIDX_SPINNER_Y_DECREASE) | (1ULL << WIDX_WALL_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_WALL_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE) | (1ULL << WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE),
510 (1ULL << WIDX_SPINNER_X_INCREASE) | (1ULL << WIDX_SPINNER_X_DECREASE) | (1ULL << WIDX_SPINNER_Y_INCREASE) | (1ULL << WIDX_SPINNER_Y_DECREASE) | (1ULL << WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE),
511 (1ULL << WIDX_SPINNER_X_INCREASE) | (1ULL << WIDX_SPINNER_X_DECREASE) | (1ULL << WIDX_SPINNER_Y_INCREASE) | (1ULL << WIDX_SPINNER_Y_DECREASE) | (1ULL << WIDX_BANNER_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_BANNER_SPINNER_HEIGHT_DECREASE),
512 (1ULL << WIDX_SPINNER_X_INCREASE) | (1ULL << WIDX_SPINNER_X_DECREASE) | (1ULL << WIDX_SPINNER_Y_INCREASE) | (1ULL << WIDX_SPINNER_Y_DECREASE) | (1ULL << WIDX_CORRUPT_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_CORRUPT_SPINNER_HEIGHT_DECREASE),
513 };
514
515 static uint64_t PageDisabledWidgets[] = {
516 (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_MOVE_UP) | (1ULL << WIDX_BUTTON_MOVE_DOWN) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY),
517 0,
518 0,
519 0,
520 0,
521 0,
522 0,
523 (1ULL << WIDX_BUTTON_ROTATE),
524 0,
525 (1ULL << WIDX_BUTTON_ROTATE),
526 };
527 // clang-format on
528
window_tile_inspector_open()529 rct_window* window_tile_inspector_open()
530 {
531 rct_window* window;
532
533 // Check if window is already open
534 window = window_bring_to_front_by_class(WC_TILE_INSPECTOR);
535 if (window != nullptr)
536 return window;
537
538 window = WindowCreate(ScreenCoordsXY(0, 29), WW, WH, &TileInspectorWindowEvents, WC_TILE_INSPECTOR, WF_RESIZABLE);
539
540 window_tile_inspector_set_page(window, TileInspectorPage::Default);
541 window->min_width = MIN_WW;
542 window->min_height = MIN_WH;
543 window->max_width = MAX_WW;
544 window->max_height = MAX_WH;
545 windowTileInspectorSelectedIndex = -1;
546 WindowInitScrollWidgets(window);
547
548 windowTileInspectorTileSelected = false;
549
550 tool_set(window, WIDX_BACKGROUND, Tool::Crosshair);
551
552 return window;
553 }
554
window_tile_inspector_clear_clipboard()555 void window_tile_inspector_clear_clipboard()
556 {
557 windowTileInspectorElementCopied = false;
558 }
559
window_tile_inspector_get_selected_element(rct_window * w)560 static TileElement* window_tile_inspector_get_selected_element(rct_window* w)
561 {
562 openrct2_assert(
563 windowTileInspectorSelectedIndex >= 0 && windowTileInspectorSelectedIndex < windowTileInspectorElementCount,
564 "Selected list item out of range");
565 return map_get_first_element_at(windowTileInspectorToolMap) + windowTileInspectorSelectedIndex;
566 }
567
window_tile_inspector_select_element_from_list(rct_window * w,int32_t index)568 static void window_tile_inspector_select_element_from_list(rct_window* w, int32_t index)
569 {
570 if (index < 0 || index >= windowTileInspectorElementCount)
571 {
572 windowTileInspectorSelectedIndex = -1;
573 OpenRCT2::TileInspector::SetSelectedElement(nullptr);
574 }
575 else
576 {
577 windowTileInspectorSelectedIndex = index;
578
579 const TileElement* const tileElement = window_tile_inspector_get_selected_element(w);
580 OpenRCT2::TileInspector::SetSelectedElement(tileElement);
581 }
582
583 w->Invalidate();
584 }
585
window_tile_inspector_load_tile(rct_window * w,TileElement * elementToSelect)586 static void window_tile_inspector_load_tile(rct_window* w, TileElement* elementToSelect)
587 {
588 windowTileInspectorSelectedIndex = -1;
589 w->scrolls[0].v_top = 0;
590
591 TileElement* element = map_get_first_element_at(windowTileInspectorToolMap);
592 int16_t numItems = 0;
593 do
594 {
595 if (element == nullptr)
596 break;
597 if (element == elementToSelect)
598 {
599 windowTileInspectorSelectedIndex = numItems;
600 }
601
602 numItems++;
603 } while (!(element++)->IsLastForTile());
604
605 windowTileInspectorElementCount = numItems;
606
607 w->Invalidate();
608 }
609
window_tile_inspector_insert_corrupt_element(int32_t elementIndex)610 static void window_tile_inspector_insert_corrupt_element(int32_t elementIndex)
611 {
612 openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range");
613 auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnyInsertCorrupt, elementIndex);
614 GameActions::Execute(&modifyTile);
615 }
616
window_tile_inspector_remove_element(int32_t elementIndex)617 static void window_tile_inspector_remove_element(int32_t elementIndex)
618 {
619 openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range");
620 auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnyRemove, elementIndex);
621 GameActions::Execute(&modifyTile);
622 }
623
window_tile_inspector_rotate_element(int32_t elementIndex)624 static void window_tile_inspector_rotate_element(int32_t elementIndex)
625 {
626 openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range");
627 auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnyRotate, elementIndex);
628 GameActions::Execute(&modifyTile);
629 }
630
631 // Swap element with its parent
window_tile_inspector_swap_elements(int16_t first,int16_t second)632 static void window_tile_inspector_swap_elements(int16_t first, int16_t second)
633 {
634 bool firstInRange = first >= 0 && first < windowTileInspectorElementCount;
635 bool secondInRange = second >= 0 && second < windowTileInspectorElementCount;
636 // This might happen if two people are modifying the same tile.
637 if (!firstInRange || !secondInRange)
638 return;
639
640 auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnySwap, first, second);
641 GameActions::Execute(&modifyTile);
642 }
643
window_tile_inspector_sort_elements()644 static void window_tile_inspector_sort_elements()
645 {
646 openrct2_assert(windowTileInspectorTileSelected, "No tile selected");
647 auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnySort);
648 GameActions::Execute(&modifyTile);
649 }
650
window_tile_inspector_copy_element(rct_window * w)651 static void window_tile_inspector_copy_element(rct_window* w)
652 {
653 // Copy value, in case the element gets moved
654 tileInspectorCopiedElement = *window_tile_inspector_get_selected_element(w);
655 windowTileInspectorElementCopied = true;
656 w->Invalidate();
657 }
658
window_tile_inspector_paste_element(rct_window * w)659 static void window_tile_inspector_paste_element(rct_window* w)
660 {
661 auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnyPaste, 0, 0, tileInspectorCopiedElement);
662 GameActions::Execute(&modifyTile);
663 }
664
window_tile_inspector_base_height_offset(int16_t elementIndex,int8_t heightOffset)665 static void window_tile_inspector_base_height_offset(int16_t elementIndex, int8_t heightOffset)
666 {
667 auto modifyTile = TileModifyAction(
668 windowTileInspectorToolMap, TileModifyType::AnyBaseHeightOffset, elementIndex, heightOffset);
669 GameActions::Execute(&modifyTile);
670 }
671
window_tile_inspector_surface_show_park_fences(bool showFences)672 static void window_tile_inspector_surface_show_park_fences(bool showFences)
673 {
674 auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::SurfaceShowParkFences, showFences);
675 GameActions::Execute(&modifyTile);
676 }
677
window_tile_inspector_surface_toggle_corner(int32_t cornerIndex)678 static void window_tile_inspector_surface_toggle_corner(int32_t cornerIndex)
679 {
680 auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::SurfaceToggleCorner, cornerIndex);
681 GameActions::Execute(&modifyTile);
682 }
683
window_tile_inspector_surface_toggle_diagonal()684 static void window_tile_inspector_surface_toggle_diagonal()
685 {
686 auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::SurfaceToggleDiagonal);
687 GameActions::Execute(&modifyTile);
688 }
689
window_tile_inspector_path_set_sloped(int32_t elementIndex,bool sloped)690 static void window_tile_inspector_path_set_sloped(int32_t elementIndex, bool sloped)
691 {
692 auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::PathSetSlope, elementIndex, sloped);
693 GameActions::Execute(&modifyTile);
694 }
695
window_tile_inspector_path_set_broken(int32_t elementIndex,bool broken)696 static void window_tile_inspector_path_set_broken(int32_t elementIndex, bool broken)
697 {
698 auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::PathSetBroken, elementIndex, broken);
699 GameActions::Execute(&modifyTile);
700 }
701
window_tile_inspector_path_toggle_edge(int32_t elementIndex,int32_t cornerIndex)702 static void window_tile_inspector_path_toggle_edge(int32_t elementIndex, int32_t cornerIndex)
703 {
704 openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range");
705 openrct2_assert(cornerIndex >= 0 && cornerIndex < 8, "cornerIndex out of range");
706 auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::PathToggleEdge, elementIndex, cornerIndex);
707 GameActions::Execute(&modifyTile);
708 }
709
window_tile_inspector_entrance_make_usable(int32_t elementIndex)710 static void window_tile_inspector_entrance_make_usable(int32_t elementIndex)
711 {
712 Guard::ArgumentInRange(elementIndex, 0, windowTileInspectorElementCount - 1);
713 auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::EntranceMakeUsable, elementIndex);
714 GameActions::Execute(&modifyTile);
715 }
716
window_tile_inspector_wall_set_slope(int32_t elementIndex,int32_t slopeValue)717 static void window_tile_inspector_wall_set_slope(int32_t elementIndex, int32_t slopeValue)
718 {
719 // Make sure only the correct bits are set
720 openrct2_assert((slopeValue & 3) == slopeValue, "slopeValue doesn't match its mask");
721 auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::WallSetSlope, elementIndex, slopeValue);
722 GameActions::Execute(&modifyTile);
723 }
724
window_tile_inspector_wall_animation_frame_offset(int16_t elementIndex,int8_t animationFrameOffset)725 static void window_tile_inspector_wall_animation_frame_offset(int16_t elementIndex, int8_t animationFrameOffset)
726 {
727 auto modifyTile = TileModifyAction(
728 windowTileInspectorToolMap, TileModifyType::WallSetAnimationFrame, elementIndex, animationFrameOffset);
729 GameActions::Execute(&modifyTile);
730 }
731
window_tile_inspector_track_block_height_offset(int32_t elementIndex,int8_t heightOffset)732 static void window_tile_inspector_track_block_height_offset(int32_t elementIndex, int8_t heightOffset)
733 {
734 auto modifyTile = TileModifyAction(
735 windowTileInspectorToolMap, TileModifyType::TrackBaseHeightOffset, elementIndex, heightOffset);
736 GameActions::Execute(&modifyTile);
737 }
738
window_tile_inspector_track_block_set_lift(int32_t elementIndex,bool entireTrackBlock,bool chain)739 static void window_tile_inspector_track_block_set_lift(int32_t elementIndex, bool entireTrackBlock, bool chain)
740 {
741 auto modifyTile = TileModifyAction(
742 windowTileInspectorToolMap, entireTrackBlock ? TileModifyType::TrackSetChainBlock : TileModifyType::TrackSetChain,
743 elementIndex, chain);
744 GameActions::Execute(&modifyTile);
745 }
746
window_tile_inspector_track_set_block_brake(int32_t elementIndex,bool blockBrake)747 static void window_tile_inspector_track_set_block_brake(int32_t elementIndex, bool blockBrake)
748 {
749 auto modifyTile = TileModifyAction(
750 windowTileInspectorToolMap, TileModifyType::TrackSetBlockBrake, elementIndex, blockBrake);
751 GameActions::Execute(&modifyTile);
752 }
753
window_tile_inspector_track_set_indestructible(int32_t elementIndex,bool isIndestructible)754 static void window_tile_inspector_track_set_indestructible(int32_t elementIndex, bool isIndestructible)
755 {
756 auto modifyTile = TileModifyAction(
757 windowTileInspectorToolMap, TileModifyType::TrackSetIndestructible, elementIndex, isIndestructible);
758 GameActions::Execute(&modifyTile);
759 }
760
window_tile_inspector_quarter_tile_set(int32_t elementIndex,const int32_t quarterIndex)761 static void window_tile_inspector_quarter_tile_set(int32_t elementIndex, const int32_t quarterIndex)
762 {
763 // quarterIndex is widget index relative to WIDX_SCENERY_CHECK_QUARTER_N, so a value from 0-3
764 openrct2_assert(quarterIndex >= 0 && quarterIndex < 4, "quarterIndex out of range");
765 auto modifyTile = TileModifyAction(
766 windowTileInspectorToolMap, TileModifyType::ScenerySetQuarterLocation, elementIndex,
767 (quarterIndex - get_current_rotation()) & 3);
768 GameActions::Execute(&modifyTile);
769 }
770
window_tile_inspector_toggle_quadrant_collosion(int32_t elementIndex,const int32_t quadrantIndex)771 static void window_tile_inspector_toggle_quadrant_collosion(int32_t elementIndex, const int32_t quadrantIndex)
772 {
773 auto modifyTile = TileModifyAction(
774 windowTileInspectorToolMap, TileModifyType::ScenerySetQuarterCollision, elementIndex,
775 (quadrantIndex + 2 - get_current_rotation()) & 3);
776 GameActions::Execute(&modifyTile);
777 }
778
window_tile_inspector_banner_toggle_block(int32_t elementIndex,int32_t edgeIndex)779 static void window_tile_inspector_banner_toggle_block(int32_t elementIndex, int32_t edgeIndex)
780 {
781 openrct2_assert(edgeIndex >= 0 && edgeIndex < 4, "edgeIndex out of range");
782
783 // Make edgeIndex abstract
784 edgeIndex = (edgeIndex - get_current_rotation()) & 3;
785 auto modifyTile = TileModifyAction(
786 windowTileInspectorToolMap, TileModifyType::BannerToggleBlockingEdge, elementIndex, edgeIndex);
787 GameActions::Execute(&modifyTile);
788 }
789
window_tile_inspector_clamp_corrupt(int32_t elementIndex)790 static void window_tile_inspector_clamp_corrupt(int32_t elementIndex)
791 {
792 auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::CorruptClamp, elementIndex);
793 GameActions::Execute(&modifyTile);
794 }
795
window_tile_inspector_close(rct_window * w)796 static void window_tile_inspector_close(rct_window* w)
797 {
798 OpenRCT2::TileInspector::SetSelectedElement(nullptr);
799 }
800
window_tile_inspector_mouseup(rct_window * w,rct_widgetindex widgetIndex)801 static void window_tile_inspector_mouseup(rct_window* w, rct_widgetindex widgetIndex)
802 {
803 switch (widgetIndex)
804 {
805 case WIDX_CLOSE:
806 tool_cancel();
807 window_close(w);
808 return;
809 case WIDX_BUTTON_CORRUPT:
810 window_tile_inspector_insert_corrupt_element(windowTileInspectorSelectedIndex);
811 break;
812 case WIDX_BUTTON_REMOVE:
813 {
814 int32_t nextItemToSelect = windowTileInspectorSelectedIndex - 1;
815 window_tile_inspector_remove_element(windowTileInspectorSelectedIndex);
816 window_tile_inspector_select_element_from_list(w, nextItemToSelect);
817 break;
818 }
819 case WIDX_BUTTON_ROTATE:
820 window_tile_inspector_rotate_element(windowTileInspectorSelectedIndex);
821 break;
822 case WIDX_BUTTON_SORT:
823 window_tile_inspector_sort_elements();
824 break;
825 case WIDX_BUTTON_COPY:
826 window_tile_inspector_copy_element(w);
827 break;
828 case WIDX_BUTTON_PASTE:
829 window_tile_inspector_paste_element(w);
830 break;
831 case WIDX_BUTTON_MOVE_UP:
832 window_tile_inspector_swap_elements(windowTileInspectorSelectedIndex, windowTileInspectorSelectedIndex + 1);
833 break;
834 case WIDX_BUTTON_MOVE_DOWN:
835 window_tile_inspector_swap_elements(windowTileInspectorSelectedIndex - 1, windowTileInspectorSelectedIndex);
836 break;
837 }
838
839 // Only element-specific widgets from now on
840 if (w->tileInspectorPage == TileInspectorPage::Default || windowTileInspectorSelectedIndex == -1)
841 {
842 return;
843 }
844
845 TileElement* const tileElement = window_tile_inspector_get_selected_element(w);
846
847 // Update selection, can be nullptr.
848 OpenRCT2::TileInspector::SetSelectedElement(tileElement);
849
850 if (tileElement == nullptr)
851 return;
852
853 // Page widgets
854 switch (tileElement->GetType())
855 {
856 case TILE_ELEMENT_TYPE_SURFACE:
857 switch (widgetIndex)
858 {
859 case WIDX_SURFACE_BUTTON_REMOVE_FENCES:
860 window_tile_inspector_surface_show_park_fences(false);
861 break;
862 case WIDX_SURFACE_BUTTON_RESTORE_FENCES:
863 window_tile_inspector_surface_show_park_fences(true);
864 break;
865 case WIDX_SURFACE_CHECK_CORNER_N:
866 case WIDX_SURFACE_CHECK_CORNER_E:
867 case WIDX_SURFACE_CHECK_CORNER_S:
868 case WIDX_SURFACE_CHECK_CORNER_W:
869 window_tile_inspector_surface_toggle_corner(
870 ((widgetIndex - WIDX_SURFACE_CHECK_CORNER_N) + 2 - get_current_rotation()) & 3);
871 break;
872 case WIDX_SURFACE_CHECK_DIAGONAL:
873 window_tile_inspector_surface_toggle_diagonal();
874 break;
875 } // switch widgetindex
876 break;
877
878 case TILE_ELEMENT_TYPE_PATH:
879 switch (widgetIndex)
880 {
881 case WIDX_PATH_CHECK_SLOPED:
882 window_tile_inspector_path_set_sloped(windowTileInspectorSelectedIndex, !tileElement->AsPath()->IsSloped());
883 break;
884 case WIDX_PATH_CHECK_BROKEN:
885 window_tile_inspector_path_set_broken(windowTileInspectorSelectedIndex, !tileElement->AsPath()->IsBroken());
886 break;
887 case WIDX_PATH_CHECK_EDGE_E:
888 case WIDX_PATH_CHECK_EDGE_S:
889 case WIDX_PATH_CHECK_EDGE_W:
890 case WIDX_PATH_CHECK_EDGE_N:
891 {
892 // 0 = east/right, 1 = south/bottom, 2 = west/left, 3 = north/top
893 const int32_t eswn = (widgetIndex - WIDX_PATH_CHECK_EDGE_E) / 2;
894 // Transform to world orientation
895 const int32_t index = (eswn - get_current_rotation()) & 3;
896 window_tile_inspector_path_toggle_edge(
897 windowTileInspectorSelectedIndex,
898 index + 4); // The corners are stored in the 4 most significant bits, hence the + 4
899 break;
900 }
901 case WIDX_PATH_CHECK_EDGE_NE:
902 case WIDX_PATH_CHECK_EDGE_SE:
903 case WIDX_PATH_CHECK_EDGE_SW:
904 case WIDX_PATH_CHECK_EDGE_NW:
905 {
906 // 0 = NE, 1 = SE, 2 = SW, 3 = NW
907 const int32_t neseswnw = (widgetIndex - WIDX_PATH_CHECK_EDGE_NE) / 2;
908 // Transform to world orientation
909 const int32_t index = (neseswnw - get_current_rotation()) & 3;
910 window_tile_inspector_path_toggle_edge(windowTileInspectorSelectedIndex, index);
911 break;
912 }
913 } // switch widget index
914 break;
915
916 case TILE_ELEMENT_TYPE_TRACK:
917 switch (widgetIndex)
918 {
919 case WIDX_TRACK_CHECK_APPLY_TO_ALL:
920 windowTileInspectorApplyToAll ^= 1;
921 widget_invalidate(w, widgetIndex);
922 break;
923 case WIDX_TRACK_CHECK_CHAIN_LIFT:
924 {
925 bool entireTrackBlock = WidgetIsPressed(w, WIDX_TRACK_CHECK_APPLY_TO_ALL);
926 bool newLift = !tileElement->AsTrack()->HasChain();
927 window_tile_inspector_track_block_set_lift(windowTileInspectorSelectedIndex, entireTrackBlock, newLift);
928 break;
929 }
930 case WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED:
931 window_tile_inspector_track_set_block_brake(
932 windowTileInspectorSelectedIndex, !tileElement->AsTrack()->BlockBrakeClosed());
933 break;
934 case WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE:
935 window_tile_inspector_track_set_indestructible(
936 windowTileInspectorSelectedIndex, !tileElement->AsTrack()->IsIndestructible());
937 break;
938 } // switch widget index
939 break;
940
941 case TILE_ELEMENT_TYPE_SMALL_SCENERY:
942 switch (widgetIndex)
943 {
944 case WIDX_SCENERY_CHECK_QUARTER_N:
945 case WIDX_SCENERY_CHECK_QUARTER_E:
946 case WIDX_SCENERY_CHECK_QUARTER_S:
947 case WIDX_SCENERY_CHECK_QUARTER_W:
948 window_tile_inspector_quarter_tile_set(
949 windowTileInspectorSelectedIndex, widgetIndex - WIDX_SCENERY_CHECK_QUARTER_N);
950 break;
951 case WIDX_SCENERY_CHECK_COLLISION_N:
952 case WIDX_SCENERY_CHECK_COLLISION_E:
953 case WIDX_SCENERY_CHECK_COLLISION_S:
954 case WIDX_SCENERY_CHECK_COLLISION_W:
955 window_tile_inspector_toggle_quadrant_collosion(
956 windowTileInspectorSelectedIndex, widgetIndex - WIDX_SCENERY_CHECK_COLLISION_N);
957 break;
958 } // switch widget index
959 break;
960
961 case TILE_ELEMENT_TYPE_ENTRANCE:
962 switch (widgetIndex)
963 {
964 case WIDX_ENTRANCE_BUTTON_MAKE_USABLE:
965 window_tile_inspector_entrance_make_usable(windowTileInspectorSelectedIndex);
966 break;
967 } // switch widget index
968 break;
969
970 case TILE_ELEMENT_TYPE_BANNER:
971 switch (widgetIndex)
972 {
973 case WIDX_BANNER_CHECK_BLOCK_NE:
974 case WIDX_BANNER_CHECK_BLOCK_SE:
975 case WIDX_BANNER_CHECK_BLOCK_SW:
976 case WIDX_BANNER_CHECK_BLOCK_NW:
977 window_tile_inspector_banner_toggle_block(
978 windowTileInspectorSelectedIndex, widgetIndex - WIDX_BANNER_CHECK_BLOCK_NE);
979 break;
980 } // switch widget index
981 break;
982
983 case TILE_ELEMENT_TYPE_CORRUPT:
984 switch (widgetIndex)
985 {
986 case WIDX_CORRUPT_BUTTON_CLAMP:
987 window_tile_inspector_clamp_corrupt(windowTileInspectorSelectedIndex);
988 break;
989 } // switch widget index
990 break;
991 case TILE_ELEMENT_TYPE_LARGE_SCENERY:
992 case TILE_ELEMENT_TYPE_WALL:
993 default:
994 break;
995 }
996 }
997
window_tile_inspector_resize(rct_window * w)998 static void window_tile_inspector_resize(rct_window* w)
999 {
1000 if (w->width < w->min_width)
1001 {
1002 w->Invalidate();
1003 w->width = w->min_width;
1004 }
1005 if (w->height < w->min_height)
1006 {
1007 w->Invalidate();
1008 w->height = w->min_height;
1009 }
1010 }
1011
window_tile_inspector_mousedown(rct_window * w,rct_widgetindex widgetIndex,rct_widget * widget)1012 static void window_tile_inspector_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget)
1013 {
1014 switch (widgetIndex)
1015 {
1016 case WIDX_SPINNER_X_INCREASE:
1017 windowTileInspectorTile.x = std::min<int32_t>(windowTileInspectorTile.x + 1, MAXIMUM_MAP_SIZE_TECHNICAL - 1);
1018 windowTileInspectorToolMap.x = std::min<int32_t>(windowTileInspectorToolMap.x + 32, MAXIMUM_TILE_START_XY);
1019 window_tile_inspector_load_tile(w, nullptr);
1020 break;
1021 case WIDX_SPINNER_X_DECREASE:
1022 windowTileInspectorTile.x = std::max<int32_t>(windowTileInspectorTile.x - 1, 0);
1023 windowTileInspectorToolMap.x = std::max<int32_t>(windowTileInspectorToolMap.x - 32, 0);
1024 window_tile_inspector_load_tile(w, nullptr);
1025 break;
1026 case WIDX_SPINNER_Y_INCREASE:
1027 windowTileInspectorTile.y = std::min<int32_t>(windowTileInspectorTile.y + 1, MAXIMUM_MAP_SIZE_TECHNICAL - 1);
1028 windowTileInspectorToolMap.y = std::min<int32_t>(windowTileInspectorToolMap.y + 32, MAXIMUM_TILE_START_XY);
1029 window_tile_inspector_load_tile(w, nullptr);
1030 break;
1031 case WIDX_SPINNER_Y_DECREASE:
1032 windowTileInspectorTile.y = std::max<int32_t>(windowTileInspectorTile.y - 1, 0);
1033 windowTileInspectorToolMap.y = std::max<int32_t>(windowTileInspectorToolMap.y - 32, 0);
1034 window_tile_inspector_load_tile(w, nullptr);
1035 break;
1036 } // switch widget index
1037
1038 // Only element-specific widgets from now on
1039 if (w->tileInspectorPage == TileInspectorPage::Default || windowTileInspectorSelectedIndex == -1)
1040 {
1041 return;
1042 }
1043
1044 const TileElement* tileElement = window_tile_inspector_get_selected_element(w);
1045 if (tileElement == nullptr)
1046 return;
1047
1048 switch (tileElement->GetType())
1049 {
1050 case TILE_ELEMENT_TYPE_SURFACE:
1051 switch (widgetIndex)
1052 {
1053 case WIDX_SURFACE_SPINNER_HEIGHT_INCREASE:
1054 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, 1);
1055 break;
1056 case WIDX_SURFACE_SPINNER_HEIGHT_DECREASE:
1057 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, -1);
1058 break;
1059 } // switch widget index
1060 break;
1061
1062 case TILE_ELEMENT_TYPE_PATH:
1063 switch (widgetIndex)
1064 {
1065 case WIDX_PATH_SPINNER_HEIGHT_INCREASE:
1066 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, 1);
1067 break;
1068 case WIDX_PATH_SPINNER_HEIGHT_DECREASE:
1069 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, -1);
1070 break;
1071 } // switch widget index
1072 break;
1073
1074 case TILE_ELEMENT_TYPE_TRACK:
1075 switch (widgetIndex)
1076 {
1077 case WIDX_TRACK_SPINNER_HEIGHT_INCREASE:
1078 if (WidgetIsPressed(w, WIDX_TRACK_CHECK_APPLY_TO_ALL))
1079 {
1080 window_tile_inspector_track_block_height_offset(windowTileInspectorSelectedIndex, 1);
1081 }
1082 else
1083 {
1084 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, 1);
1085 }
1086 break;
1087 case WIDX_TRACK_SPINNER_HEIGHT_DECREASE:
1088 if (WidgetIsPressed(w, WIDX_TRACK_CHECK_APPLY_TO_ALL))
1089 {
1090 window_tile_inspector_track_block_height_offset(windowTileInspectorSelectedIndex, -1);
1091 }
1092 else
1093 {
1094 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, -1);
1095 }
1096 break;
1097 } // switch widget index
1098 break;
1099
1100 case TILE_ELEMENT_TYPE_SMALL_SCENERY:
1101 switch (widgetIndex)
1102 {
1103 case WIDX_SCENERY_SPINNER_HEIGHT_INCREASE:
1104 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, 1);
1105 break;
1106 case WIDX_SCENERY_SPINNER_HEIGHT_DECREASE:
1107 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, -1);
1108 break;
1109 } // switch widget index
1110 break;
1111
1112 case TILE_ELEMENT_TYPE_ENTRANCE:
1113 switch (widgetIndex)
1114 {
1115 case WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE:
1116 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, 1);
1117 break;
1118 case WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE:
1119 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, -1);
1120 break;
1121 case WIDX_ENTRANCE_BUTTON_MAKE_USABLE:
1122 window_tile_inspector_entrance_make_usable(windowTileInspectorSelectedIndex);
1123 break;
1124 } // switch widget index
1125 break;
1126
1127 case TILE_ELEMENT_TYPE_WALL:
1128 switch (widgetIndex)
1129 {
1130 case WIDX_WALL_SPINNER_HEIGHT_INCREASE:
1131 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, 1);
1132 break;
1133 case WIDX_WALL_SPINNER_HEIGHT_DECREASE:
1134 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, -1);
1135 break;
1136 case WIDX_WALL_DROPDOWN_SLOPE_BUTTON:
1137 {
1138 // Use dropdown instead of dropdown button
1139 widget--;
1140
1141 // Fill dropdown list
1142 gDropdownItemsFormat[0] = STR_DROPDOWN_MENU_LABEL;
1143 gDropdownItemsFormat[1] = STR_DROPDOWN_MENU_LABEL;
1144 gDropdownItemsFormat[2] = STR_DROPDOWN_MENU_LABEL;
1145 gDropdownItemsArgs[0] = STR_TILE_INSPECTOR_WALL_FLAT;
1146 gDropdownItemsArgs[1] = STR_TILE_INSPECTOR_WALL_SLOPED_LEFT;
1147 gDropdownItemsArgs[2] = STR_TILE_INSPECTOR_WALL_SLOPED_RIGHT;
1148 WindowDropdownShowTextCustomWidth(
1149 { w->windowPos.x + widget->left, w->windowPos.y + widget->top }, widget->height() + 1, w->colours[1], 0,
1150 Dropdown::Flag::StayOpen, 3, widget->width() - 3);
1151
1152 // Set current value as checked
1153 Dropdown::SetChecked(tileElement->AsWall()->GetSlope(), true);
1154 break;
1155 }
1156 case WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE:
1157 window_tile_inspector_wall_animation_frame_offset(windowTileInspectorSelectedIndex, 1);
1158 break;
1159 case WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE:
1160 window_tile_inspector_wall_animation_frame_offset(windowTileInspectorSelectedIndex, -1);
1161 break;
1162 } // switch widget index
1163 break;
1164
1165 case TILE_ELEMENT_TYPE_LARGE_SCENERY:
1166 switch (widgetIndex)
1167 {
1168 case WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE:
1169 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, 1);
1170 break;
1171 case WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE:
1172 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, -1);
1173 break;
1174 } // switch widget index
1175 break;
1176
1177 case TILE_ELEMENT_TYPE_BANNER:
1178 switch (widgetIndex)
1179 {
1180 case WIDX_BANNER_SPINNER_HEIGHT_INCREASE:
1181 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, 1);
1182 break;
1183 case WIDX_BANNER_SPINNER_HEIGHT_DECREASE:
1184 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, -1);
1185 break;
1186 } // switch widget index
1187 break;
1188
1189 case TILE_ELEMENT_TYPE_CORRUPT:
1190 switch (widgetIndex)
1191 {
1192 case WIDX_CORRUPT_SPINNER_HEIGHT_INCREASE:
1193 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, 1);
1194 break;
1195 case WIDX_CORRUPT_SPINNER_HEIGHT_DECREASE:
1196 window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, -1);
1197 break;
1198 } // switch widget index
1199 default:
1200 break;
1201 }
1202 }
1203
window_tile_inspector_update(rct_window * w)1204 static void window_tile_inspector_update(rct_window* w)
1205 {
1206 // Check if the mouse is hovering over the list
1207 if (!WidgetIsHighlighted(w, WIDX_LIST))
1208 {
1209 windowTileInspectorHighlightedIndex = -1;
1210 widget_invalidate(w, WIDX_LIST);
1211 }
1212
1213 if (gCurrentToolWidget.window_classification != WC_TILE_INSPECTOR)
1214 window_close(w);
1215 }
1216
window_tile_inspector_dropdown(rct_window * w,rct_widgetindex widgetIndex,int32_t dropdownIndex)1217 static void window_tile_inspector_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
1218 {
1219 if (dropdownIndex == -1)
1220 {
1221 return;
1222 }
1223
1224 // Get selected element
1225 TileElement* const tileElement = window_tile_inspector_get_selected_element(w);
1226
1227 if (w->tileInspectorPage == TileInspectorPage::Wall)
1228 {
1229 openrct2_assert(tileElement->GetType() == TILE_ELEMENT_TYPE_WALL, "Element is not a wall");
1230
1231 if (widgetIndex == WIDX_WALL_DROPDOWN_SLOPE_BUTTON)
1232 {
1233 window_tile_inspector_wall_set_slope(windowTileInspectorSelectedIndex, dropdownIndex);
1234 }
1235 }
1236 }
1237
window_tile_inspector_tool_update(rct_window * w,rct_widgetindex widgetIndex,const ScreenCoordsXY & screenCoords)1238 static void window_tile_inspector_tool_update(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
1239 {
1240 map_invalidate_selection_rect();
1241
1242 gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE;
1243
1244 CoordsXY mapCoords;
1245 TileElement* clickedElement = nullptr;
1246 bool mouseOnViewport = false;
1247 if (InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z))
1248 {
1249 auto info = get_map_coordinates_from_pos(screenCoords, ViewportInteractionFlags);
1250 clickedElement = info.Element;
1251 mapCoords = info.Loc;
1252 }
1253
1254 // Even if Ctrl was pressed, fall back to normal selection when there was nothing under the cursor
1255 if (clickedElement == nullptr)
1256 {
1257 auto mouseCoords = screen_pos_to_map_pos(screenCoords, nullptr);
1258 if (mouseCoords.has_value())
1259 {
1260 mouseOnViewport = true;
1261 mapCoords = mouseCoords.value();
1262 }
1263 }
1264
1265 if (mouseOnViewport)
1266 {
1267 gMapSelectPositionA = gMapSelectPositionB = mapCoords;
1268 }
1269 else if (windowTileInspectorTileSelected)
1270 {
1271 gMapSelectPositionA = gMapSelectPositionB = windowTileInspectorToolMap;
1272 }
1273 else
1274 {
1275 gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
1276 }
1277
1278 gMapSelectType = MAP_SELECT_TYPE_FULL;
1279 map_invalidate_selection_rect();
1280 }
1281
window_tile_inspector_update_selected_tile(rct_window * w,const ScreenCoordsXY & screenCoords)1282 static void window_tile_inspector_update_selected_tile(rct_window* w, const ScreenCoordsXY& screenCoords)
1283 {
1284 const bool ctrlIsHeldDown = InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z);
1285
1286 // Mouse hasn't moved
1287 if (screenCoords.x == windowTileInspectorToolMouseX && screenCoords.y == windowTileInspectorToolMouseY
1288 && windowTileInspectorToolCtrlDown == ctrlIsHeldDown)
1289 {
1290 return;
1291 }
1292
1293 windowTileInspectorToolMouseX = screenCoords.x;
1294 windowTileInspectorToolMouseY = screenCoords.y;
1295 windowTileInspectorToolCtrlDown = ctrlIsHeldDown;
1296
1297 CoordsXY mapCoords{};
1298 TileElement* clickedElement = nullptr;
1299 if (ctrlIsHeldDown)
1300 {
1301 auto info = get_map_coordinates_from_pos(screenCoords, ViewportInteractionFlags);
1302 clickedElement = info.Element;
1303 mapCoords = info.Loc;
1304 }
1305
1306 // Even if Ctrl was pressed, fall back to normal selection when there was nothing under the cursor
1307 if (clickedElement == nullptr)
1308 {
1309 auto mouseCoords = screen_pos_to_map_pos(screenCoords, nullptr);
1310
1311 if (!mouseCoords.has_value())
1312 {
1313 return;
1314 }
1315
1316 mapCoords = mouseCoords.value();
1317 // Tile is already selected
1318 if (windowTileInspectorTileSelected && mapCoords.x == windowTileInspectorToolMap.x
1319 && mapCoords.y == windowTileInspectorToolMap.y)
1320 {
1321 return;
1322 }
1323 }
1324
1325 windowTileInspectorTileSelected = true;
1326 windowTileInspectorToolMap = mapCoords;
1327 windowTileInspectorTile = TileCoordsXY(mapCoords);
1328
1329 OpenRCT2::TileInspector::SetSelectedElement(clickedElement);
1330
1331 window_tile_inspector_load_tile(w, clickedElement);
1332 }
1333
window_tile_inspector_tool_down(rct_window * w,rct_widgetindex widgetIndex,const ScreenCoordsXY & screenCoords)1334 static void window_tile_inspector_tool_down(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
1335 {
1336 window_tile_inspector_update_selected_tile(w, screenCoords);
1337 }
1338
window_tile_inspector_tool_drag(rct_window * w,rct_widgetindex widgetIndex,const ScreenCoordsXY & screenCoords)1339 static void window_tile_inspector_tool_drag(rct_window* w, rct_widgetindex widgetIndex, const ScreenCoordsXY& screenCoords)
1340 {
1341 window_tile_inspector_update_selected_tile(w, screenCoords);
1342 }
1343
window_tile_inspector_scrollgetsize(rct_window * w,int32_t scrollIndex,int32_t * width,int32_t * height)1344 static void window_tile_inspector_scrollgetsize(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height)
1345 {
1346 *width = WW - 30;
1347 *height = windowTileInspectorElementCount * SCROLLABLE_ROW_HEIGHT;
1348 }
1349
window_tile_inspector_set_page(rct_window * w,const TileInspectorPage page)1350 static void window_tile_inspector_set_page(rct_window* w, const TileInspectorPage page)
1351 {
1352 // Invalidate the window already, because the size may change
1353 w->Invalidate();
1354
1355 // subtract current page height, then add new page height
1356 if (w->tileInspectorPage != TileInspectorPage::Default)
1357 {
1358 auto index = EnumValue(w->tileInspectorPage) - 1;
1359 w->height -= PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3;
1360 w->min_height -= PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3;
1361 }
1362 if (page != TileInspectorPage::Default)
1363 {
1364 auto index = EnumValue(page) - 1;
1365 w->height += PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3;
1366 w->min_height += PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3;
1367 }
1368 w->tileInspectorPage = page;
1369 auto pageIndex = EnumValue(page);
1370 w->widgets = PageWidgets[pageIndex];
1371 w->enabled_widgets = PageEnabledWidgets[pageIndex];
1372 w->hold_down_widgets = PageHoldDownWidgets[pageIndex];
1373 w->disabled_widgets = PageDisabledWidgets[pageIndex];
1374 w->pressed_widgets = 0;
1375 }
1376
window_tile_inspector_scrollmousedown(rct_window * w,int32_t scrollIndex,const ScreenCoordsXY & screenCoords)1377 static void window_tile_inspector_scrollmousedown(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords)
1378 {
1379 // Because the list items are displayed in reverse order, subtract the calculated index from the amount of elements
1380 const int16_t index = windowTileInspectorElementCount - (screenCoords.y - 1) / SCROLLABLE_ROW_HEIGHT - 1;
1381 window_tile_inspector_select_element_from_list(w, index);
1382 }
1383
window_tile_inspector_scrollmouseover(rct_window * w,int32_t scrollIndex,const ScreenCoordsXY & screenCoords)1384 static void window_tile_inspector_scrollmouseover(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords)
1385 {
1386 int16_t index = windowTileInspectorElementCount - (screenCoords.y - 1) / SCROLLABLE_ROW_HEIGHT - 1;
1387 if (index < 0 || index >= windowTileInspectorElementCount)
1388 windowTileInspectorHighlightedIndex = -1;
1389 else
1390 windowTileInspectorHighlightedIndex = index;
1391
1392 widget_invalidate(w, WIDX_LIST);
1393 }
1394
window_tile_inspector_invalidate(rct_window * w)1395 static void window_tile_inspector_invalidate(rct_window* w)
1396 {
1397 // Set the correct page automatically
1398 TileInspectorPage page = TileInspectorPage::Default;
1399 if (windowTileInspectorSelectedIndex != -1)
1400 {
1401 const auto element = window_tile_inspector_get_selected_element(w);
1402 auto type = element->GetType();
1403 switch (type)
1404 {
1405 case TILE_ELEMENT_TYPE_SURFACE:
1406 page = TileInspectorPage::Surface;
1407 break;
1408 case TILE_ELEMENT_TYPE_PATH:
1409 page = TileInspectorPage::Path;
1410 break;
1411 case TILE_ELEMENT_TYPE_TRACK:
1412 page = TileInspectorPage::Track;
1413 break;
1414 case TILE_ELEMENT_TYPE_SMALL_SCENERY:
1415 page = TileInspectorPage::Scenery;
1416 break;
1417 case TILE_ELEMENT_TYPE_ENTRANCE:
1418 page = TileInspectorPage::Entrance;
1419 break;
1420 case TILE_ELEMENT_TYPE_WALL:
1421 page = TileInspectorPage::Wall;
1422 break;
1423 case TILE_ELEMENT_TYPE_LARGE_SCENERY:
1424 page = TileInspectorPage::LargeScenery;
1425 break;
1426 case TILE_ELEMENT_TYPE_BANNER:
1427 page = TileInspectorPage::Banner;
1428 break;
1429 case TILE_ELEMENT_TYPE_CORRUPT:
1430 default:
1431 page = TileInspectorPage::Corrupt;
1432 break;
1433 }
1434 }
1435
1436 if (w->tileInspectorPage != page)
1437 {
1438 window_tile_inspector_set_page(w, page);
1439 w->Invalidate();
1440 }
1441
1442 // X and Y spinners
1443 WidgetSetEnabled(
1444 w, WIDX_SPINNER_X_INCREASE,
1445 (windowTileInspectorTileSelected && ((windowTileInspectorToolMap.x / 32) < MAXIMUM_MAP_SIZE_TECHNICAL - 1)));
1446 WidgetSetEnabled(
1447 w, WIDX_SPINNER_X_DECREASE, (windowTileInspectorTileSelected && ((windowTileInspectorToolMap.x / 32) > 0)));
1448 WidgetSetEnabled(
1449 w, WIDX_SPINNER_Y_INCREASE,
1450 (windowTileInspectorTileSelected && ((windowTileInspectorToolMap.y / 32) < MAXIMUM_MAP_SIZE_TECHNICAL - 1)));
1451 WidgetSetEnabled(
1452 w, WIDX_SPINNER_Y_DECREASE, (windowTileInspectorTileSelected && ((windowTileInspectorToolMap.y / 32) > 0)));
1453
1454 // Sort buttons
1455 WidgetSetEnabled(w, WIDX_BUTTON_SORT, (windowTileInspectorTileSelected && windowTileInspectorElementCount > 1));
1456
1457 // Move Up button
1458 WidgetSetEnabled(
1459 w, WIDX_BUTTON_MOVE_UP,
1460 (windowTileInspectorSelectedIndex != -1 && windowTileInspectorSelectedIndex < windowTileInspectorElementCount - 1));
1461 widget_invalidate(w, WIDX_BUTTON_MOVE_UP);
1462
1463 // Move Down button
1464 WidgetSetEnabled(w, WIDX_BUTTON_MOVE_DOWN, (windowTileInspectorSelectedIndex > 0));
1465 widget_invalidate(w, WIDX_BUTTON_MOVE_DOWN);
1466
1467 // Copy button
1468 WidgetSetEnabled(w, WIDX_BUTTON_COPY, windowTileInspectorSelectedIndex >= 0);
1469 widget_invalidate(w, WIDX_BUTTON_COPY);
1470
1471 // Paste button
1472 WidgetSetEnabled(w, WIDX_BUTTON_PASTE, windowTileInspectorTileSelected && windowTileInspectorElementCopied);
1473 widget_invalidate(w, WIDX_BUTTON_PASTE);
1474
1475 w->widgets[WIDX_BACKGROUND].bottom = w->height - 1;
1476
1477 if (w->tileInspectorPage == TileInspectorPage::Default)
1478 {
1479 w->widgets[WIDX_GROUPBOX_DETAILS].type = WindowWidgetType::Empty;
1480 w->widgets[WIDX_GROUPBOX_PROPERTIES].type = WindowWidgetType::Empty;
1481 w->widgets[WIDX_LIST].bottom = w->height - PADDING_BOTTOM;
1482 }
1483 else
1484 {
1485 w->widgets[WIDX_GROUPBOX_DETAILS].type = WindowWidgetType::Groupbox;
1486 w->widgets[WIDX_GROUPBOX_PROPERTIES].type = WindowWidgetType::Groupbox;
1487 auto pageIndex = EnumValue(w->tileInspectorPage) - 1;
1488 w->widgets[WIDX_GROUPBOX_DETAILS].text = PageGroupBoxSettings[pageIndex].string_id;
1489 w->widgets[WIDX_GROUPBOX_DETAILS].top = w->height - PageGroupBoxSettings[pageIndex].details_top_offset;
1490 w->widgets[WIDX_GROUPBOX_DETAILS].bottom = w->height - PageGroupBoxSettings[pageIndex].details_bottom_offset;
1491 w->widgets[WIDX_GROUPBOX_PROPERTIES].top = w->height - PageGroupBoxSettings[pageIndex].properties_top_offset;
1492 w->widgets[WIDX_GROUPBOX_PROPERTIES].bottom = w->height - PageGroupBoxSettings[pageIndex].properties_bottom_offset;
1493 w->widgets[WIDX_LIST].bottom = w->widgets[WIDX_GROUPBOX_DETAILS].top - GROUPBOX_PADDING;
1494 }
1495
1496 // The default page doesn't need further invalidation
1497 if (w->tileInspectorPage == TileInspectorPage::Default)
1498 {
1499 return;
1500 }
1501
1502 // Using a switch, because I don't think giving each page their own callbacks is
1503 // needed here, as only the mouseup and invalidate functions are different.
1504 const int32_t propertiesAnchor = w->widgets[WIDX_GROUPBOX_PROPERTIES].top;
1505 const TileElement* const tileElement = window_tile_inspector_get_selected_element(w);
1506 if (tileElement == nullptr)
1507 return;
1508
1509 switch (tileElement->GetType())
1510 {
1511 case TILE_ELEMENT_TYPE_SURFACE:
1512 w->widgets[WIDX_SURFACE_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3;
1513 w->widgets[WIDX_SURFACE_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3;
1514 w->widgets[WIDX_SURFACE_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4;
1515 w->widgets[WIDX_SURFACE_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
1516 w->widgets[WIDX_SURFACE_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4;
1517 w->widgets[WIDX_SURFACE_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
1518 w->widgets[WIDX_SURFACE_BUTTON_REMOVE_FENCES].top = GBBT(propertiesAnchor, 1);
1519 w->widgets[WIDX_SURFACE_BUTTON_REMOVE_FENCES].bottom = GBBB(propertiesAnchor, 1);
1520 w->widgets[WIDX_SURFACE_BUTTON_RESTORE_FENCES].top = GBBT(propertiesAnchor, 1);
1521 w->widgets[WIDX_SURFACE_BUTTON_RESTORE_FENCES].bottom = GBBB(propertiesAnchor, 1);
1522 w->widgets[WIDX_SURFACE_CHECK_CORNER_N].top = GBBT(propertiesAnchor, 2) + 7 * 0;
1523 w->widgets[WIDX_SURFACE_CHECK_CORNER_N].bottom = w->widgets[WIDX_SURFACE_CHECK_CORNER_N].top + 13;
1524 w->widgets[WIDX_SURFACE_CHECK_CORNER_E].top = GBBT(propertiesAnchor, 2) + 7 * 1;
1525 w->widgets[WIDX_SURFACE_CHECK_CORNER_E].bottom = w->widgets[WIDX_SURFACE_CHECK_CORNER_E].top + 13;
1526 w->widgets[WIDX_SURFACE_CHECK_CORNER_S].top = GBBT(propertiesAnchor, 2) + 7 * 2;
1527 w->widgets[WIDX_SURFACE_CHECK_CORNER_S].bottom = w->widgets[WIDX_SURFACE_CHECK_CORNER_S].top + 13;
1528 w->widgets[WIDX_SURFACE_CHECK_CORNER_W].top = GBBT(propertiesAnchor, 2) + 7 * 1;
1529 w->widgets[WIDX_SURFACE_CHECK_CORNER_W].bottom = w->widgets[WIDX_SURFACE_CHECK_CORNER_W].top + 13;
1530 w->widgets[WIDX_SURFACE_CHECK_DIAGONAL].top = GBBT(propertiesAnchor, 3) + 7 * 1;
1531 w->widgets[WIDX_SURFACE_CHECK_DIAGONAL].bottom = w->widgets[WIDX_SURFACE_CHECK_DIAGONAL].top + 13;
1532 WidgetSetCheckboxValue(
1533 w, WIDX_SURFACE_CHECK_CORNER_N,
1534 tileElement->AsSurface()->GetSlope() & (1 << ((2 - get_current_rotation()) & 3)));
1535 WidgetSetCheckboxValue(
1536 w, WIDX_SURFACE_CHECK_CORNER_E,
1537 tileElement->AsSurface()->GetSlope() & (1 << ((3 - get_current_rotation()) & 3)));
1538 WidgetSetCheckboxValue(
1539 w, WIDX_SURFACE_CHECK_CORNER_S,
1540 tileElement->AsSurface()->GetSlope() & (1 << ((0 - get_current_rotation()) & 3)));
1541 WidgetSetCheckboxValue(
1542 w, WIDX_SURFACE_CHECK_CORNER_W,
1543 tileElement->AsSurface()->GetSlope() & (1 << ((1 - get_current_rotation()) & 3)));
1544 WidgetSetCheckboxValue(
1545 w, WIDX_SURFACE_CHECK_DIAGONAL, tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT);
1546 break;
1547 case TILE_ELEMENT_TYPE_PATH:
1548 w->widgets[WIDX_PATH_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3;
1549 w->widgets[WIDX_PATH_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3;
1550 w->widgets[WIDX_PATH_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4;
1551 w->widgets[WIDX_PATH_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
1552 w->widgets[WIDX_PATH_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4;
1553 w->widgets[WIDX_PATH_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
1554 w->widgets[WIDX_PATH_CHECK_BROKEN].top = GBBT(propertiesAnchor, 1);
1555 w->widgets[WIDX_PATH_CHECK_BROKEN].bottom = GBBB(propertiesAnchor, 1);
1556 w->widgets[WIDX_PATH_CHECK_SLOPED].top = GBBT(propertiesAnchor, 2);
1557 w->widgets[WIDX_PATH_CHECK_SLOPED].bottom = GBBB(propertiesAnchor, 2);
1558 w->widgets[WIDX_PATH_CHECK_EDGE_N].top = GBBT(propertiesAnchor, 3) + 7 * 0;
1559 w->widgets[WIDX_PATH_CHECK_EDGE_N].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_N].top + 13;
1560 w->widgets[WIDX_PATH_CHECK_EDGE_NE].top = GBBT(propertiesAnchor, 3) + 7 * 1;
1561 w->widgets[WIDX_PATH_CHECK_EDGE_NE].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_NE].top + 13;
1562 w->widgets[WIDX_PATH_CHECK_EDGE_E].top = GBBT(propertiesAnchor, 3) + 7 * 2;
1563 w->widgets[WIDX_PATH_CHECK_EDGE_E].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_E].top + 13;
1564 w->widgets[WIDX_PATH_CHECK_EDGE_SE].top = GBBT(propertiesAnchor, 3) + 7 * 3;
1565 w->widgets[WIDX_PATH_CHECK_EDGE_SE].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_SE].top + 13;
1566 w->widgets[WIDX_PATH_CHECK_EDGE_S].top = GBBT(propertiesAnchor, 3) + 7 * 4;
1567 w->widgets[WIDX_PATH_CHECK_EDGE_S].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_S].top + 13;
1568 w->widgets[WIDX_PATH_CHECK_EDGE_SW].top = GBBT(propertiesAnchor, 3) + 7 * 3;
1569 w->widgets[WIDX_PATH_CHECK_EDGE_SW].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_SW].top + 13;
1570 w->widgets[WIDX_PATH_CHECK_EDGE_W].top = GBBT(propertiesAnchor, 3) + 7 * 2;
1571 w->widgets[WIDX_PATH_CHECK_EDGE_W].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_W].top + 13;
1572 w->widgets[WIDX_PATH_CHECK_EDGE_NW].top = GBBT(propertiesAnchor, 3) + 7 * 1;
1573 w->widgets[WIDX_PATH_CHECK_EDGE_NW].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_NW].top + 13;
1574 WidgetSetCheckboxValue(w, WIDX_PATH_CHECK_SLOPED, tileElement->AsPath()->IsSloped());
1575 WidgetSetCheckboxValue(w, WIDX_PATH_CHECK_BROKEN, tileElement->AsPath()->IsBroken());
1576 WidgetSetCheckboxValue(
1577 w, WIDX_PATH_CHECK_EDGE_NE, tileElement->AsPath()->GetEdges() & (1 << ((0 - get_current_rotation()) & 3)));
1578 WidgetSetCheckboxValue(
1579 w, WIDX_PATH_CHECK_EDGE_SE, tileElement->AsPath()->GetEdges() & (1 << ((1 - get_current_rotation()) & 3)));
1580 WidgetSetCheckboxValue(
1581 w, WIDX_PATH_CHECK_EDGE_SW, tileElement->AsPath()->GetEdges() & (1 << ((2 - get_current_rotation()) & 3)));
1582 WidgetSetCheckboxValue(
1583 w, WIDX_PATH_CHECK_EDGE_NW, tileElement->AsPath()->GetEdges() & (1 << ((3 - get_current_rotation()) & 3)));
1584 WidgetSetCheckboxValue(
1585 w, WIDX_PATH_CHECK_EDGE_E, tileElement->AsPath()->GetCorners() & (1 << ((0 - get_current_rotation()) & 3)));
1586 WidgetSetCheckboxValue(
1587 w, WIDX_PATH_CHECK_EDGE_S, tileElement->AsPath()->GetCorners() & (1 << ((1 - get_current_rotation()) & 3)));
1588 WidgetSetCheckboxValue(
1589 w, WIDX_PATH_CHECK_EDGE_W, tileElement->AsPath()->GetCorners() & (1 << ((2 - get_current_rotation()) & 3)));
1590 WidgetSetCheckboxValue(
1591 w, WIDX_PATH_CHECK_EDGE_N, tileElement->AsPath()->GetCorners() & (1 << ((3 - get_current_rotation()) & 3)));
1592 break;
1593 case TILE_ELEMENT_TYPE_TRACK:
1594 w->widgets[WIDX_TRACK_CHECK_APPLY_TO_ALL].top = GBBT(propertiesAnchor, 0);
1595 w->widgets[WIDX_TRACK_CHECK_APPLY_TO_ALL].bottom = GBBB(propertiesAnchor, 0);
1596 w->widgets[WIDX_TRACK_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 1) + 3;
1597 w->widgets[WIDX_TRACK_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 1) - 3;
1598 w->widgets[WIDX_TRACK_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 1) + 4;
1599 w->widgets[WIDX_TRACK_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 1) - 4;
1600 w->widgets[WIDX_TRACK_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 1) + 4;
1601 w->widgets[WIDX_TRACK_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 1) - 4;
1602 w->widgets[WIDX_TRACK_CHECK_CHAIN_LIFT].top = GBBT(propertiesAnchor, 2);
1603 w->widgets[WIDX_TRACK_CHECK_CHAIN_LIFT].bottom = GBBB(propertiesAnchor, 2);
1604 w->widgets[WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED].top = GBBT(propertiesAnchor, 3);
1605 w->widgets[WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED].bottom = GBBB(propertiesAnchor, 3);
1606 w->widgets[WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE].top = GBBT(propertiesAnchor, 4);
1607 w->widgets[WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE].bottom = GBBB(propertiesAnchor, 4);
1608 WidgetSetCheckboxValue(w, WIDX_TRACK_CHECK_APPLY_TO_ALL, windowTileInspectorApplyToAll);
1609 WidgetSetCheckboxValue(w, WIDX_TRACK_CHECK_CHAIN_LIFT, tileElement->AsTrack()->HasChain());
1610 WidgetSetCheckboxValue(w, WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED, tileElement->AsTrack()->BlockBrakeClosed());
1611 WidgetSetCheckboxValue(w, WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE, tileElement->AsTrack()->IsIndestructible());
1612 break;
1613 case TILE_ELEMENT_TYPE_SMALL_SCENERY:
1614 {
1615 // Raise / Lower
1616 w->widgets[WIDX_SCENERY_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3;
1617 w->widgets[WIDX_SCENERY_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3;
1618 w->widgets[WIDX_SCENERY_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4;
1619 w->widgets[WIDX_SCENERY_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
1620 w->widgets[WIDX_SCENERY_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4;
1621 w->widgets[WIDX_SCENERY_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
1622
1623 // Quadrant checkboxes
1624 w->widgets[WIDX_SCENERY_CHECK_QUARTER_N].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 0;
1625 w->widgets[WIDX_SCENERY_CHECK_QUARTER_N].bottom = w->widgets[WIDX_SCENERY_CHECK_QUARTER_N].top + 13;
1626 w->widgets[WIDX_SCENERY_CHECK_QUARTER_E].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 1;
1627 w->widgets[WIDX_SCENERY_CHECK_QUARTER_E].bottom = w->widgets[WIDX_SCENERY_CHECK_QUARTER_E].top + 13;
1628 w->widgets[WIDX_SCENERY_CHECK_QUARTER_S].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 2;
1629 w->widgets[WIDX_SCENERY_CHECK_QUARTER_S].bottom = w->widgets[WIDX_SCENERY_CHECK_QUARTER_S].top + 13;
1630 w->widgets[WIDX_SCENERY_CHECK_QUARTER_W].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 1;
1631 w->widgets[WIDX_SCENERY_CHECK_QUARTER_W].bottom = w->widgets[WIDX_SCENERY_CHECK_QUARTER_W].top + 13;
1632 // This gets the relative rotation, by subtracting the camera's rotation, and wrapping it between 0-3 inclusive
1633 bool N = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((0 - get_current_rotation()) & 3);
1634 bool E = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((1 - get_current_rotation()) & 3);
1635 bool S = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((2 - get_current_rotation()) & 3);
1636 bool W = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((3 - get_current_rotation()) & 3);
1637 WidgetSetCheckboxValue(w, WIDX_SCENERY_CHECK_QUARTER_N, N);
1638 WidgetSetCheckboxValue(w, WIDX_SCENERY_CHECK_QUARTER_E, E);
1639 WidgetSetCheckboxValue(w, WIDX_SCENERY_CHECK_QUARTER_S, S);
1640 WidgetSetCheckboxValue(w, WIDX_SCENERY_CHECK_QUARTER_W, W);
1641
1642 // Collision checkboxes
1643 w->widgets[WIDX_SCENERY_CHECK_COLLISION_N].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 0;
1644 w->widgets[WIDX_SCENERY_CHECK_COLLISION_N].bottom = w->widgets[WIDX_SCENERY_CHECK_COLLISION_N].top + 13;
1645 w->widgets[WIDX_SCENERY_CHECK_COLLISION_E].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 1;
1646 w->widgets[WIDX_SCENERY_CHECK_COLLISION_E].bottom = w->widgets[WIDX_SCENERY_CHECK_COLLISION_E].top + 13;
1647 w->widgets[WIDX_SCENERY_CHECK_COLLISION_S].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 2;
1648 w->widgets[WIDX_SCENERY_CHECK_COLLISION_S].bottom = w->widgets[WIDX_SCENERY_CHECK_COLLISION_S].top + 13;
1649 w->widgets[WIDX_SCENERY_CHECK_COLLISION_W].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 1;
1650 w->widgets[WIDX_SCENERY_CHECK_COLLISION_W].bottom = w->widgets[WIDX_SCENERY_CHECK_COLLISION_W].top + 13;
1651 auto occupiedQuadrants = tileElement->GetOccupiedQuadrants();
1652 N = (occupiedQuadrants & (1 << ((2 - get_current_rotation()) & 3))) != 0;
1653 E = (occupiedQuadrants & (1 << ((3 - get_current_rotation()) & 3))) != 0;
1654 S = (occupiedQuadrants & (1 << ((0 - get_current_rotation()) & 3))) != 0;
1655 W = (occupiedQuadrants & (1 << ((1 - get_current_rotation()) & 3))) != 0;
1656 WidgetSetCheckboxValue(w, WIDX_SCENERY_CHECK_COLLISION_N, N);
1657 WidgetSetCheckboxValue(w, WIDX_SCENERY_CHECK_COLLISION_E, E);
1658 WidgetSetCheckboxValue(w, WIDX_SCENERY_CHECK_COLLISION_S, S);
1659 WidgetSetCheckboxValue(w, WIDX_SCENERY_CHECK_COLLISION_W, W);
1660 break;
1661 }
1662 case TILE_ELEMENT_TYPE_ENTRANCE:
1663 w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3;
1664 w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3;
1665 w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4;
1666 w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
1667 w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4;
1668 w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
1669 w->widgets[WIDX_ENTRANCE_BUTTON_MAKE_USABLE].top = GBBT(propertiesAnchor, 1);
1670 w->widgets[WIDX_ENTRANCE_BUTTON_MAKE_USABLE].bottom = GBBB(propertiesAnchor, 1);
1671 WidgetSetEnabled(
1672 w, WIDX_ENTRANCE_BUTTON_MAKE_USABLE,
1673 tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_PARK_ENTRANCE);
1674 break;
1675 case TILE_ELEMENT_TYPE_WALL:
1676 {
1677 bool canBeSloped = false;
1678 bool hasAnimation = false;
1679 const auto wallEntry = tileElement->AsWall()->GetEntry();
1680 if (wallEntry != nullptr)
1681 {
1682 canBeSloped = !(wallEntry->flags & WALL_SCENERY_CANT_BUILD_ON_SLOPE);
1683 hasAnimation = wallEntry->flags & WALL_SCENERY_IS_DOOR;
1684 }
1685
1686 w->widgets[WIDX_WALL_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3;
1687 w->widgets[WIDX_WALL_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3;
1688 w->widgets[WIDX_WALL_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4;
1689 w->widgets[WIDX_WALL_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
1690 w->widgets[WIDX_WALL_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4;
1691 w->widgets[WIDX_WALL_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
1692 w->widgets[WIDX_WALL_DROPDOWN_SLOPE].top = GBBT(propertiesAnchor, 1) + 3;
1693 w->widgets[WIDX_WALL_DROPDOWN_SLOPE].bottom = GBBB(propertiesAnchor, 1) - 3;
1694 w->widgets[WIDX_WALL_DROPDOWN_SLOPE].text = WallSlopeStringIds[tileElement->AsWall()->GetSlope()];
1695 w->widgets[WIDX_WALL_DROPDOWN_SLOPE_BUTTON].top = GBBT(propertiesAnchor, 1) + 4;
1696 w->widgets[WIDX_WALL_DROPDOWN_SLOPE_BUTTON].bottom = GBBB(propertiesAnchor, 1) - 4;
1697 w->widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].top = GBBT(propertiesAnchor, 2) + 3;
1698 w->widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].bottom = GBBB(propertiesAnchor, 2) - 3;
1699 w->widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE].top = GBBT(propertiesAnchor, 2) + 4;
1700 w->widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE].bottom = GBBB(propertiesAnchor, 2) - 4;
1701 w->widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE].top = GBBT(propertiesAnchor, 2) + 4;
1702 w->widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE].bottom = GBBB(propertiesAnchor, 2) - 4;
1703
1704 // Wall slope dropdown
1705 WidgetSetEnabled(w, WIDX_WALL_DROPDOWN_SLOPE, canBeSloped);
1706 widget_invalidate(w, WIDX_WALL_DROPDOWN_SLOPE);
1707 WidgetSetEnabled(w, WIDX_WALL_DROPDOWN_SLOPE_BUTTON, canBeSloped);
1708 widget_invalidate(w, WIDX_WALL_DROPDOWN_SLOPE_BUTTON);
1709 // Wall animation frame spinner
1710 WidgetSetEnabled(w, WIDX_WALL_SPINNER_ANIMATION_FRAME, hasAnimation);
1711 WidgetSetEnabled(w, WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE, hasAnimation);
1712 WidgetSetEnabled(w, WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE, hasAnimation);
1713 break;
1714 }
1715 case TILE_ELEMENT_TYPE_LARGE_SCENERY:
1716 w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3;
1717 w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3;
1718 w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4;
1719 w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
1720 w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4;
1721 w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
1722 break;
1723 case TILE_ELEMENT_TYPE_BANNER:
1724 w->widgets[WIDX_BANNER_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3;
1725 w->widgets[WIDX_BANNER_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3;
1726 w->widgets[WIDX_BANNER_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4;
1727 w->widgets[WIDX_BANNER_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
1728 w->widgets[WIDX_BANNER_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4;
1729 w->widgets[WIDX_BANNER_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
1730 w->widgets[WIDX_BANNER_CHECK_BLOCK_NE].top = GBBT(propertiesAnchor, 1);
1731 w->widgets[WIDX_BANNER_CHECK_BLOCK_NE].bottom = GBBB(propertiesAnchor, 1);
1732 w->widgets[WIDX_BANNER_CHECK_BLOCK_SE].top = GBBT(propertiesAnchor, 2);
1733 w->widgets[WIDX_BANNER_CHECK_BLOCK_SE].bottom = GBBB(propertiesAnchor, 2);
1734 w->widgets[WIDX_BANNER_CHECK_BLOCK_SW].top = GBBT(propertiesAnchor, 2);
1735 w->widgets[WIDX_BANNER_CHECK_BLOCK_SW].bottom = GBBB(propertiesAnchor, 2);
1736 w->widgets[WIDX_BANNER_CHECK_BLOCK_NW].top = GBBT(propertiesAnchor, 1);
1737 w->widgets[WIDX_BANNER_CHECK_BLOCK_NW].bottom = GBBB(propertiesAnchor, 1);
1738 WidgetSetCheckboxValue(
1739 w, WIDX_BANNER_CHECK_BLOCK_NE,
1740 !(tileElement->AsBanner()->GetAllowedEdges() & (1 << ((0 - get_current_rotation()) & 3))));
1741 WidgetSetCheckboxValue(
1742 w, WIDX_BANNER_CHECK_BLOCK_SE,
1743 !(tileElement->AsBanner()->GetAllowedEdges() & (1 << ((1 - get_current_rotation()) & 3))));
1744 WidgetSetCheckboxValue(
1745 w, WIDX_BANNER_CHECK_BLOCK_SW,
1746 !(tileElement->AsBanner()->GetAllowedEdges() & (1 << ((2 - get_current_rotation()) & 3))));
1747 WidgetSetCheckboxValue(
1748 w, WIDX_BANNER_CHECK_BLOCK_NW,
1749 !(tileElement->AsBanner()->GetAllowedEdges() & (1 << ((3 - get_current_rotation()) & 3))));
1750 break;
1751 case TILE_ELEMENT_TYPE_CORRUPT:
1752 w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3;
1753 w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3;
1754 w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4;
1755 w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
1756 w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4;
1757 w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
1758 w->widgets[WIDX_CORRUPT_BUTTON_CLAMP].top = GBBT(propertiesAnchor, 1);
1759 w->widgets[WIDX_CORRUPT_BUTTON_CLAMP].bottom = GBBB(propertiesAnchor, 1);
1760 break;
1761 default:
1762 break; // Nothing.
1763 }
1764 }
1765
window_tile_inspector_paint(rct_window * w,rct_drawpixelinfo * dpi)1766 static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi)
1767 {
1768 WindowDrawWidgets(w, dpi);
1769
1770 ScreenCoordsXY screenCoords(w->windowPos.x, w->windowPos.y);
1771
1772 // Draw coordinates
1773 gfx_draw_string(dpi, screenCoords + ScreenCoordsXY(5, 24), "X:", { w->colours[1] });
1774 gfx_draw_string(dpi, screenCoords + ScreenCoordsXY(74, 24), "Y:", { w->colours[1] });
1775 if (windowTileInspectorTileSelected)
1776 {
1777 auto tileCoords = TileCoordsXY{ windowTileInspectorToolMap };
1778 auto ft = Formatter();
1779 ft.Add<int32_t>(tileCoords.x);
1780 DrawTextBasic(
1781 dpi, screenCoords + ScreenCoordsXY{ 43, 24 }, STR_FORMAT_INTEGER, ft, { w->colours[1], TextAlignment::RIGHT });
1782 ft = Formatter();
1783 ft.Add<int32_t>(tileCoords.y);
1784 DrawTextBasic(
1785 dpi, screenCoords + ScreenCoordsXY{ 113, 24 }, STR_FORMAT_INTEGER, ft, { w->colours[1], TextAlignment::RIGHT });
1786 }
1787 else
1788 {
1789 gfx_draw_string(dpi, screenCoords + ScreenCoordsXY(43 - 7, 24), "-", { w->colours[1] });
1790 gfx_draw_string(dpi, screenCoords + ScreenCoordsXY(113 - 7, 24), "-", { w->colours[1] });
1791 }
1792
1793 if (windowTileInspectorSelectedIndex != -1)
1794 {
1795 // X and Y of first element in detail box
1796 screenCoords = w->windowPos
1797 + ScreenCoordsXY{ w->widgets[WIDX_GROUPBOX_DETAILS].left + 7, w->widgets[WIDX_GROUPBOX_DETAILS].top + 14 };
1798
1799 // Get map element
1800 TileElement* const tileElement = window_tile_inspector_get_selected_element(w);
1801 if (tileElement == nullptr)
1802 return;
1803
1804 switch (tileElement->GetType())
1805 {
1806 case TILE_ELEMENT_TYPE_SURFACE:
1807 {
1808 // Details
1809 // Terrain texture name
1810 rct_string_id terrainNameId = STR_EMPTY;
1811 auto surfaceStyle = tileElement->AsSurface()->GetSurfaceStyleObject();
1812 if (surfaceStyle != nullptr)
1813 {
1814 terrainNameId = surfaceStyle->NameStringId;
1815 }
1816 auto ft = Formatter();
1817 ft.Add<rct_string_id>(terrainNameId);
1818 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_SURFACE_TERAIN, ft, { w->colours[1] });
1819
1820 // Edge texture name
1821 rct_string_id terrainEdgeNameId = STR_EMPTY;
1822 auto edgeStyle = tileElement->AsSurface()->GetEdgeStyleObject();
1823 if (edgeStyle != nullptr)
1824 {
1825 terrainEdgeNameId = edgeStyle->NameStringId;
1826 }
1827 ft = Formatter();
1828 ft.Add<rct_string_id>(terrainEdgeNameId);
1829 DrawTextBasic(
1830 dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_SURFACE_EDGE, ft, { w->colours[1] });
1831
1832 // Land ownership
1833 rct_string_id landOwnership;
1834 if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_OWNED)
1835 landOwnership = STR_LAND_OWNED;
1836 else if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_AVAILABLE)
1837 landOwnership = STR_LAND_SALE;
1838 else if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED)
1839 landOwnership = STR_CONSTRUCTION_RIGHTS_OWNED;
1840 else if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE)
1841 landOwnership = STR_CONSTRUCTION_RIGHTS_SALE;
1842 else
1843 landOwnership = STR_TILE_INSPECTOR_LAND_NOT_OWNED_AND_NOT_AVAILABLE;
1844 ft = Formatter();
1845 ft.Add<rct_string_id>(landOwnership);
1846 DrawTextBasic(
1847 dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_SURFACE_OWNERSHIP, ft, { w->colours[1] });
1848
1849 // Water level
1850 ft = Formatter();
1851 ft.Add<uint32_t>(tileElement->AsSurface()->GetWaterHeight());
1852 DrawTextBasic(
1853 dpi, screenCoords + ScreenCoordsXY{ 0, 33 }, STR_TILE_INSPECTOR_SURFACE_WATER_LEVEL, ft, { w->colours[1] });
1854
1855 // Properties
1856 // Raise / lower label
1857 screenCoords = w->windowPos
1858 + ScreenCoordsXY{ w->widgets[WIDX_GROUPBOX_DETAILS].left + 7, w->widgets[WIDX_SURFACE_SPINNER_HEIGHT].top };
1859 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] });
1860
1861 // Current base height
1862 screenCoords.x = w->windowPos.x + w->widgets[WIDX_SURFACE_SPINNER_HEIGHT].left + 3;
1863 ft = Formatter();
1864 ft.Add<int32_t>(tileElement->base_height);
1865 DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] });
1866
1867 // Raised corners
1868 screenCoords = w->windowPos
1869 + ScreenCoordsXY{ w->widgets[WIDX_GROUPBOX_DETAILS].left + 7, w->widgets[WIDX_SURFACE_CHECK_CORNER_E].top };
1870 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_SURFACE_CORNERS, {}, { w->colours[1] });
1871 break;
1872 }
1873
1874 case TILE_ELEMENT_TYPE_PATH:
1875 {
1876 // Details
1877 // Path name
1878 auto ft = Formatter();
1879 ft.Add<rct_string_id>(tileElement->AsPath()->GetSurfaceDescriptor()->Name);
1880 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_PATH_NAME, ft, { w->colours[1] });
1881
1882 // Path addition
1883 if (tileElement->AsPath()->HasAddition())
1884 {
1885 const auto pathAdditionType = tileElement->AsPath()->GetAdditionEntryIndex();
1886 const auto* pathBitEntry = get_footpath_item_entry(pathAdditionType);
1887 rct_string_id additionNameId = pathBitEntry != nullptr
1888 ? pathBitEntry->name
1889 : static_cast<rct_string_id>(STR_UNKNOWN_OBJECT_TYPE);
1890 ft = Formatter();
1891 ft.Add<rct_string_id>(additionNameId);
1892 DrawTextBasic(
1893 dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_PATH_ADDITIONS, ft, { w->colours[1] });
1894 }
1895 else
1896 DrawTextBasic(
1897 dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_PATH_ADDITIONS_NONE, {},
1898 { w->colours[1] });
1899
1900 // Properties
1901 // Raise / lower label
1902 screenCoords = w->windowPos
1903 + ScreenCoordsXY{ w->widgets[WIDX_GROUPBOX_DETAILS].left + 7, w->widgets[WIDX_PATH_SPINNER_HEIGHT].top };
1904 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] });
1905
1906 // Current base height
1907 screenCoords.x = w->windowPos.x + w->widgets[WIDX_PATH_SPINNER_HEIGHT].left + 3;
1908 ft = Formatter();
1909 ft.Add<int32_t>(tileElement->base_height);
1910 DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] });
1911
1912 // Path connections
1913 screenCoords = w->windowPos
1914 + ScreenCoordsXY{ w->widgets[WIDX_GROUPBOX_DETAILS].left + 7, w->widgets[WIDX_PATH_CHECK_EDGE_W].top };
1915 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_PATH_CONNECTED_EDGES, {}, { w->colours[1] });
1916 break;
1917 }
1918
1919 case TILE_ELEMENT_TYPE_TRACK:
1920 {
1921 auto trackElement = tileElement->AsTrack();
1922 ride_id_t rideId = trackElement->GetRideIndex();
1923 auto ride = get_ride(rideId);
1924
1925 // Ride ID
1926 auto ft = Formatter();
1927 ft.Add<int16_t>(rideId);
1928 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_TRACK_RIDE_ID, ft, { w->colours[1] });
1929
1930 // Ride name
1931 if (ride != nullptr)
1932 {
1933 ft = Formatter();
1934 ride->FormatNameTo(ft);
1935 DrawTextBasic(
1936 dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_TRACK_RIDE_NAME, ft, { w->colours[1] });
1937 }
1938
1939 // Ride type. Individual pieces may be of a different ride type from the ride it belongs to.
1940 const auto& rtd = GetRideTypeDescriptor(trackElement->GetRideType());
1941 ft = Formatter();
1942 ft.Add<rct_string_id>(rtd.Naming.Name);
1943 DrawTextBasic(
1944 dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_TRACK_RIDE_TYPE, ft, { w->colours[1] });
1945
1946 // Track
1947 ft = Formatter();
1948 ft.Add<track_type_t>(trackElement->GetTrackType());
1949 DrawTextBasic(
1950 dpi, screenCoords + ScreenCoordsXY{ 0, 33 }, STR_TILE_INSPECTOR_TRACK_PIECE_ID, ft, { w->colours[1] });
1951
1952 ft = Formatter();
1953 ft.Add<track_type_t>(trackElement->GetSequenceIndex());
1954 DrawTextBasic(
1955 dpi, screenCoords + ScreenCoordsXY{ 0, 44 }, STR_TILE_INSPECTOR_TRACK_SEQUENCE, ft, { w->colours[1] });
1956 if (trackElement->IsStation())
1957 {
1958 int16_t stationIndex = trackElement->GetStationIndex();
1959 ft = Formatter();
1960 ft.Add<rct_string_id>(STR_COMMA16);
1961 ft.Add<int16_t>(stationIndex);
1962 DrawTextBasic(
1963 dpi, screenCoords + ScreenCoordsXY{ 0, 55 }, STR_TILE_INSPECTOR_STATION_INDEX, ft, { w->colours[1] });
1964 }
1965 else
1966 {
1967 const char* stationNone = "-";
1968 ft = Formatter();
1969 ft.Add<rct_string_id>(STR_STRING);
1970 ft.Add<char*>(stationNone);
1971 DrawTextBasic(
1972 dpi, screenCoords + ScreenCoordsXY{ 0, 55 }, STR_TILE_INSPECTOR_STATION_INDEX, ft, { w->colours[1] });
1973 }
1974
1975 ft = Formatter();
1976 ft.Add<rct_string_id>(ColourSchemeNames[trackElement->GetColourScheme()]);
1977 DrawTextBasic(
1978 dpi, screenCoords + ScreenCoordsXY{ 0, 66 }, STR_TILE_INSPECTOR_COLOUR_SCHEME, ft, { w->colours[1] });
1979
1980 // Properties
1981 // Raise / lower label
1982 screenCoords.y = w->windowPos.y + w->widgets[WIDX_TRACK_SPINNER_HEIGHT].top;
1983 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] });
1984
1985 // Current base height
1986 screenCoords.x = w->windowPos.x + w->widgets[WIDX_TRACK_SPINNER_HEIGHT].left + 3;
1987 ft = Formatter();
1988 ft.Add<int32_t>(tileElement->base_height);
1989 DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] });
1990 break;
1991 }
1992
1993 case TILE_ELEMENT_TYPE_SMALL_SCENERY:
1994 {
1995 // Details
1996 // Age
1997 auto ft = Formatter();
1998 ft.Add<int16_t>(tileElement->AsSmallScenery()->GetAge());
1999 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_SCENERY_AGE, ft, { w->colours[1] });
2000
2001 // Quadrant value
2002 const auto* sceneryEntry = tileElement->AsSmallScenery()->GetEntry();
2003 if (sceneryEntry != nullptr && !(sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE)))
2004 {
2005 int16_t quadrant = tileElement->AsSmallScenery()->GetSceneryQuadrant();
2006 static constexpr rct_string_id quadrant_string_idx[] = {
2007 STR_TILE_INSPECTOR_SCENERY_QUADRANT_SW,
2008 STR_TILE_INSPECTOR_SCENERY_QUADRANT_NW,
2009 STR_TILE_INSPECTOR_SCENERY_QUADRANT_NE,
2010 STR_TILE_INSPECTOR_SCENERY_QUADRANT_SE,
2011 };
2012 ft = Formatter();
2013 ft.Add<rct_string_id>(quadrant_string_idx[quadrant]);
2014 DrawTextBasic(
2015 dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_SCENERY_QUADRANT, ft,
2016 { w->colours[1] });
2017 }
2018
2019 // Scenery ID
2020 ft = Formatter();
2021 ft.Add<ObjectEntryIndex>(tileElement->AsSmallScenery()->GetEntryIndex());
2022 DrawTextBasic(
2023 dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_SCENERY_ENTRY_IDX, ft, { w->colours[1] });
2024
2025 // Properties
2026 // Raise / Lower
2027 screenCoords.y = w->windowPos.y + w->widgets[WIDX_SCENERY_SPINNER_HEIGHT].top;
2028 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] });
2029
2030 // Current base height
2031 screenCoords.x = w->windowPos.x + w->widgets[WIDX_SCENERY_SPINNER_HEIGHT].left + 3;
2032 ft = Formatter();
2033 ft.Add<int32_t>(tileElement->base_height);
2034 DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] });
2035
2036 // Quarter tile
2037 screenCoords = w->windowPos
2038 + ScreenCoordsXY{ w->widgets[WIDX_GROUPBOX_DETAILS].left + 7,
2039 w->widgets[WIDX_SCENERY_CHECK_QUARTER_E].top };
2040 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_SCENERY_QUADRANT_LABEL, {}, { w->colours[1] });
2041
2042 // Collision
2043 screenCoords.y = w->windowPos.y + w->widgets[WIDX_SCENERY_CHECK_COLLISION_E].top;
2044 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_COLLISSION, {}, { w->colours[1] });
2045 break;
2046 }
2047
2048 case TILE_ELEMENT_TYPE_ENTRANCE:
2049 {
2050 // Details
2051 // Entrance type
2052 auto ft = Formatter();
2053 ft.Add<rct_string_id>(EntranceTypeStringIds[tileElement->AsEntrance()->GetEntranceType()]);
2054 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_ENTRANCE_TYPE, ft, { w->colours[1] });
2055
2056 if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE)
2057 {
2058 // TODO: Make this work with Left/Right park entrance parts
2059 ft = Formatter();
2060 ft.Add<rct_string_id>(park_entrance_get_index({ windowTileInspectorToolMap, tileElement->GetBaseZ() }));
2061 DrawTextBasic(
2062 dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRANCE_ENTRANCE_ID, ft,
2063 { w->colours[1] });
2064 }
2065 else
2066 {
2067 ft = Formatter();
2068 ft.Add<int16_t>(tileElement->AsEntrance()->GetStationIndex());
2069 if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_RIDE_ENTRANCE)
2070 {
2071 // Ride entrance ID
2072 DrawTextBasic(
2073 dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRANCE_ENTRANCE_ID, ft,
2074 { w->colours[1] });
2075 }
2076 else
2077 {
2078 // Ride exit ID
2079 DrawTextBasic(
2080 dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRANCE_EXIT_ID, ft,
2081 { w->colours[1] });
2082 }
2083 }
2084
2085 if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE)
2086 {
2087 // Entrance part
2088 ft = Formatter();
2089 ft.Add<rct_string_id>(ParkEntrancePartStringIds[tileElement->AsEntrance()->GetSequenceIndex()]);
2090 DrawTextBasic(
2091 dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRANCE_PART, ft, { w->colours[1] });
2092 }
2093 else
2094 {
2095 // Ride ID
2096 ft = Formatter();
2097 ft.Add<ride_id_t>(tileElement->AsEntrance()->GetRideIndex());
2098 DrawTextBasic(
2099 dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRANCE_RIDE_ID, ft,
2100 { w->colours[1] });
2101 // Station index
2102 int16_t stationIndex = tileElement->AsEntrance()->GetStationIndex();
2103 ft = Formatter();
2104 ft.Add<rct_string_id>(STR_COMMA16);
2105 ft.Add<int16_t>(stationIndex);
2106 DrawTextBasic(
2107 dpi, screenCoords + ScreenCoordsXY{ 0, 33 }, STR_TILE_INSPECTOR_STATION_INDEX, ft, { w->colours[1] });
2108 }
2109
2110 // Properties
2111 // Raise / Lower
2112 screenCoords.y = w->windowPos.y + w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].top;
2113 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] });
2114
2115 // Current base height
2116 screenCoords.x = w->windowPos.x + w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].left + 3;
2117 ft = Formatter();
2118 ft.Add<int32_t>(tileElement->base_height);
2119 DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] });
2120 break;
2121 }
2122
2123 case TILE_ELEMENT_TYPE_WALL:
2124 {
2125 // Details
2126 // Type
2127 auto ft = Formatter();
2128 ft.Add<ObjectEntryIndex>(tileElement->AsWall()->GetEntryIndex());
2129 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_WALL_TYPE, ft, { w->colours[1] });
2130
2131 // Banner info
2132 auto banner = tileElement->AsWall()->GetBanner();
2133 if (banner != nullptr)
2134 {
2135 ft = Formatter();
2136 banner->FormatTextTo(ft);
2137 DrawTextBasic(
2138 dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, ft,
2139 { w->colours[1] });
2140 }
2141 else
2142 {
2143 DrawTextBasic(
2144 dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRY_BANNER_NONE, {},
2145 { w->colours[1] });
2146 }
2147
2148 // Properties
2149 // Raise / lower label
2150 screenCoords.y = w->windowPos.y + w->widgets[WIDX_WALL_SPINNER_HEIGHT].top;
2151 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] });
2152
2153 // Current base height
2154 screenCoords.x = w->windowPos.x + w->widgets[WIDX_WALL_SPINNER_HEIGHT].left + 3;
2155 ft = Formatter();
2156 ft.Add<int32_t>(tileElement->base_height);
2157 DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] });
2158
2159 // Slope label
2160 screenCoords = w->windowPos
2161 + ScreenCoordsXY{ w->widgets[WIDX_GROUPBOX_DETAILS].left + 7, w->widgets[WIDX_WALL_DROPDOWN_SLOPE].top };
2162 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_WALL_SLOPE, {}, { w->colours[1] });
2163
2164 // Animation frame label
2165 screenCoords.y = w->windowPos.y + w->widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].top;
2166 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_WALL_ANIMATION_FRAME, {}, { w->colours[1] });
2167
2168 // Current animation frame
2169 colour_t colour = w->colours[1];
2170 if (WidgetIsDisabled(w, WIDX_WALL_SPINNER_ANIMATION_FRAME))
2171 {
2172 colour = w->colours[0] | COLOUR_FLAG_INSET;
2173 }
2174 screenCoords.x = w->windowPos.x + w->widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].left + 3;
2175 ft = Formatter();
2176 ft.Add<int32_t>(tileElement->AsWall()->GetAnimationFrame());
2177 DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colour });
2178 break;
2179 }
2180
2181 case TILE_ELEMENT_TYPE_LARGE_SCENERY:
2182 {
2183 // Details
2184 // Type
2185 auto sceneryElement = tileElement->AsLargeScenery();
2186 ObjectEntryIndex largeSceneryType = sceneryElement->GetEntryIndex();
2187 auto ft = Formatter();
2188 ft.Add<ObjectEntryIndex>(largeSceneryType);
2189 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_LARGE_SCENERY_TYPE, ft, { w->colours[1] });
2190
2191 // Part ID
2192 ft = Formatter();
2193 ft.Add<int16_t>(sceneryElement->GetSequenceIndex());
2194 DrawTextBasic(
2195 dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_LARGE_SCENERY_PIECE_ID, ft,
2196 { w->colours[1] });
2197
2198 // Banner info
2199 auto* largeSceneryEntry = get_large_scenery_entry(largeSceneryType);
2200 if (largeSceneryEntry != nullptr && largeSceneryEntry->scrolling_mode != SCROLLING_MODE_NONE)
2201 {
2202 auto banner = sceneryElement->GetBanner();
2203 if (banner != nullptr)
2204 {
2205 ft = Formatter();
2206 banner->FormatTextTo(ft);
2207 DrawTextBasic(
2208 dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, ft,
2209 { w->colours[1] });
2210 }
2211 }
2212 else
2213 {
2214 DrawTextBasic(
2215 dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRY_BANNER_NONE, {},
2216 { w->colours[1] });
2217 }
2218
2219 // Properties
2220 // Raise / lower label
2221 screenCoords.y = w->windowPos.y + w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].top;
2222 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] });
2223
2224 // Current base height
2225 screenCoords.x = w->windowPos.x + w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].left + 3;
2226 ft = Formatter();
2227 ft.Add<int32_t>(tileElement->base_height);
2228 DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] });
2229 break;
2230 }
2231
2232 case TILE_ELEMENT_TYPE_BANNER:
2233 {
2234 // Details
2235 // Banner info
2236 auto banner = tileElement->AsBanner()->GetBanner();
2237 if (banner != nullptr)
2238 {
2239 Formatter ft;
2240 banner->FormatTextTo(ft);
2241 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, ft, { w->colours[1] });
2242 }
2243
2244 // Properties
2245 // Raise / lower label
2246 screenCoords.y = w->windowPos.y + w->widgets[WIDX_BANNER_SPINNER_HEIGHT].top;
2247 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] });
2248
2249 // Current base height
2250 screenCoords.x = w->windowPos.x + w->widgets[WIDX_BANNER_SPINNER_HEIGHT].left + 3;
2251 auto ft = Formatter();
2252 ft.Add<int32_t>(tileElement->base_height);
2253 DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] });
2254
2255 // Blocked paths
2256 screenCoords.y += 28;
2257 screenCoords.x = w->windowPos.x + w->widgets[WIDX_GROUPBOX_DETAILS].left + 7;
2258 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BANNER_BLOCKED_PATHS, {}, { w->colours[1] });
2259 break;
2260 }
2261
2262 case TILE_ELEMENT_TYPE_CORRUPT:
2263 {
2264 // Properties
2265 // Raise / lower label
2266 screenCoords.y = w->windowPos.y + w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT].top;
2267 DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] });
2268
2269 // Current base height
2270 screenCoords.x = w->windowPos.x + w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT].left + 3;
2271 auto ft = Formatter();
2272 ft.Add<int32_t>(tileElement->base_height);
2273 DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] });
2274 break;
2275 }
2276
2277 default:
2278 {
2279 break;
2280 }
2281 }
2282 }
2283 }
2284
window_tile_inspector_scrollpaint(rct_window * w,rct_drawpixelinfo * dpi,int32_t scrollIndex)2285 static void window_tile_inspector_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex)
2286 {
2287 const int32_t listWidth = w->widgets[WIDX_LIST].width();
2288 gfx_fill_rect(
2289 dpi, { { dpi->x, dpi->y }, { dpi->x + dpi->width - 1, dpi->y + dpi->height - 1 } },
2290 ColourMapA[w->colours[1]].mid_light);
2291
2292 ScreenCoordsXY screenCoords{};
2293 screenCoords.y = SCROLLABLE_ROW_HEIGHT * (windowTileInspectorElementCount - 1);
2294 int32_t i = 0;
2295 char buffer[256];
2296
2297 if (!windowTileInspectorTileSelected)
2298 return;
2299
2300 const TileElement* tileElement = map_get_first_element_at(windowTileInspectorToolMap);
2301
2302 do
2303 {
2304 if (tileElement == nullptr)
2305 break;
2306 const bool selectedRow = i == windowTileInspectorSelectedIndex;
2307 const bool hoveredRow = i == windowTileInspectorHighlightedIndex;
2308 int32_t type = tileElement->GetType();
2309 const char* typeName = "";
2310
2311 auto fillRectangle = ScreenRect{ { 0, screenCoords.y }, { listWidth, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 } };
2312 if (selectedRow)
2313 {
2314 gfx_fill_rect(dpi, fillRectangle, ColourMapA[w->colours[1]].mid_dark);
2315 }
2316 else if (hoveredRow)
2317 {
2318 gfx_fill_rect(dpi, fillRectangle, ColourMapA[w->colours[1]].mid_dark | 0x1000000);
2319 }
2320 else if (((windowTileInspectorElementCount - i) & 1) == 0)
2321 {
2322 // Zebra stripes
2323 gfx_fill_rect(dpi, fillRectangle, ColourMapA[w->colours[1]].light | 0x1000000);
2324 }
2325
2326 switch (type)
2327 {
2328 case TILE_ELEMENT_TYPE_SURFACE:
2329 typeName = language_get_string(STR_TILE_INSPECTOR_SURFACE);
2330 break;
2331 case TILE_ELEMENT_TYPE_PATH:
2332 typeName = tileElement->AsPath()->IsQueue() ? language_get_string(STR_QUEUE_LINE_MAP_TIP)
2333 : language_get_string(STR_FOOTPATH_MAP_TIP);
2334 break;
2335 case TILE_ELEMENT_TYPE_TRACK:
2336 typeName = language_get_string(STR_RIDE_COMPONENT_TRACK_CAPITALISED);
2337 break;
2338 case TILE_ELEMENT_TYPE_SMALL_SCENERY:
2339 {
2340 const auto* sceneryEntry = tileElement->AsSmallScenery()->GetEntry();
2341 snprintf(
2342 buffer, sizeof(buffer), "%s (%s)", language_get_string(STR_OBJECT_SELECTION_SMALL_SCENERY),
2343 sceneryEntry != nullptr ? language_get_string(sceneryEntry->name) : "");
2344 typeName = buffer;
2345 break;
2346 }
2347 case TILE_ELEMENT_TYPE_ENTRANCE:
2348 typeName = language_get_string(STR_RIDE_CONSTRUCTION_ENTRANCE);
2349 break;
2350 case TILE_ELEMENT_TYPE_WALL:
2351 {
2352 const auto* entry = tileElement->AsWall()->GetEntry();
2353 snprintf(
2354 buffer, sizeof(buffer), "%s (%s)", language_get_string(STR_TILE_INSPECTOR_WALL),
2355 entry != nullptr ? language_get_string(entry->name) : "");
2356 typeName = buffer;
2357 break;
2358 }
2359 case TILE_ELEMENT_TYPE_LARGE_SCENERY:
2360 typeName = language_get_string(STR_OBJECT_SELECTION_LARGE_SCENERY);
2361 break;
2362 case TILE_ELEMENT_TYPE_BANNER:
2363 snprintf(
2364 buffer, sizeof(buffer), "%s (%d)", language_get_string(STR_BANNER_WINDOW_TITLE),
2365 tileElement->AsBanner()->GetIndex());
2366 typeName = buffer;
2367 break;
2368 case TILE_ELEMENT_TYPE_CORRUPT:
2369 // fall-through
2370 default:
2371 snprintf(buffer, sizeof(buffer), "%s (%d)", language_get_string(STR_UNKNOWN_OBJECT_TYPE), type);
2372 typeName = buffer;
2373 }
2374
2375 const int32_t clearanceHeight = tileElement->clearance_height;
2376 const bool ghost = tileElement->IsGhost();
2377 const bool last = tileElement->IsLastForTile();
2378
2379 const rct_string_id stringFormat = (selectedRow || hoveredRow) ? STR_WHITE_STRING : STR_WINDOW_COLOUR_2_STRINGID;
2380
2381 // Element name
2382 auto ft = Formatter();
2383 ft.Add<rct_string_id>(STR_STRING);
2384 ft.Add<char*>(typeName);
2385 DrawTextEllipsised(dpi, screenCoords + ScreenCoordsXY{ TypeColumnXY.x, 0 }, TypeColumnSize.width, stringFormat, ft);
2386
2387 // Base height
2388 ft = Formatter();
2389 ft.Add<rct_string_id>(STR_FORMAT_INTEGER);
2390 ft.Add<int32_t>(tileElement->base_height);
2391 DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ BaseHeightColumnXY.x, 0 }, stringFormat, ft);
2392
2393 // Clearance height
2394 ft = Formatter();
2395 ft.Add<rct_string_id>(STR_FORMAT_INTEGER);
2396 ft.Add<int32_t>(clearanceHeight);
2397 DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ ClearanceHeightColumnXY.x, 0 }, stringFormat, ft);
2398
2399 // Direction
2400 ft = Formatter();
2401 ft.Add<rct_string_id>(STR_FORMAT_INTEGER);
2402 ft.Add<int32_t>(tileElement->GetDirection());
2403 DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ DirectionColumnXY.x, 0 }, stringFormat, ft);
2404
2405 // Checkmarks for ghost and last for tile
2406 ft = Formatter();
2407 ft.Add<rct_string_id>(STR_STRING);
2408 ft.Add<char*>(CheckBoxMarkString);
2409 if (ghost)
2410 {
2411 DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ GhostFlagColumnXY.x, 0 }, stringFormat, ft);
2412 }
2413 if (last)
2414 {
2415 DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ LastFlagColumnXY.x, 0 }, stringFormat, ft);
2416 }
2417
2418 screenCoords.y -= SCROLLABLE_ROW_HEIGHT;
2419 i++;
2420 } while (!(tileElement++)->IsLastForTile());
2421 }
2422