OpenTTD Source 20250328-master-gc3457cd4c0
viewport.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
63#include "stdafx.h"
64#include "core/backup_type.hpp"
65#include "landscape.h"
66#include "viewport_func.h"
67#include "station_base.h"
68#include "waypoint_base.h"
69#include "town.h"
70#include "signs_base.h"
71#include "signs_func.h"
72#include "vehicle_base.h"
73#include "vehicle_gui.h"
74#include "blitter/factory.hpp"
75#include "strings_func.h"
76#include "zoom_func.h"
77#include "vehicle_func.h"
78#include "company_func.h"
79#include "waypoint_func.h"
80#include "window_func.h"
81#include "tilehighlight_func.h"
82#include "window_gui.h"
84#include "viewport_kdtree.h"
85#include "town_kdtree.h"
87#include "bridge_map.h"
88#include "company_base.h"
89#include "command_func.h"
91#include "framerate_type.h"
92#include "viewport_cmd.h"
93
94#include <forward_list>
95#include <stack>
96
98
99#include "table/strings.h"
100#include "table/string_colours.h"
101
102#include "safeguards.h"
103
104Point _tile_fract_coords;
105
106
107ViewportSignKdtree _viewport_sign_kdtree{};
108static int _viewport_sign_maxwidth = 0;
109
110
111static const int MAX_TILE_EXTENT_LEFT = ZOOM_BASE * TILE_PIXELS;
112static const int MAX_TILE_EXTENT_RIGHT = ZOOM_BASE * TILE_PIXELS;
113static const int MAX_TILE_EXTENT_TOP = ZOOM_BASE * MAX_BUILDING_PIXELS;
114static const int MAX_TILE_EXTENT_BOTTOM = ZOOM_BASE * (TILE_PIXELS + 2 * TILE_HEIGHT);
115
117 std::string string;
118 uint16_t width;
119 Colours colour;
121 int32_t x;
122 int32_t y;
123};
124
126 SpriteID image;
127 PaletteID pal;
128 const SubSprite *sub;
129 int32_t x;
130 int32_t y;
131};
132
134 SpriteID image;
135 PaletteID pal;
136 const SubSprite *sub;
137 int32_t x;
138 int32_t y;
139 bool relative;
140 int next;
141};
142
144enum FoundationPart : uint8_t {
148 FOUNDATION_PART_END
149};
150
160
161typedef std::vector<TileSpriteToDraw> TileSpriteToDrawVector;
162typedef std::vector<StringSpriteToDraw> StringSpriteToDrawVector;
163typedef std::vector<ParentSpriteToDraw> ParentSpriteToDrawVector;
164typedef std::vector<ChildScreenSpriteToDraw> ChildScreenSpriteToDrawVector;
165
166constexpr int LAST_CHILD_NONE = -1;
167constexpr int LAST_CHILD_PARENT = -2;
168
171 DrawPixelInfo dpi;
172
173 StringSpriteToDrawVector string_sprites_to_draw;
174 TileSpriteToDrawVector tile_sprites_to_draw;
175 ParentSpriteToDrawVector parent_sprites_to_draw;
176 ParentSpriteToSortVector parent_sprites_to_sort;
177 ChildScreenSpriteToDrawVector child_screen_sprites_to_draw;
178
179 int last_child;
180
182
183 int foundation[FOUNDATION_PART_END];
185 int last_foundation_child[FOUNDATION_PART_END];
186 Point foundation_offset[FOUNDATION_PART_END];
187};
188
189static bool MarkViewportDirty(const Viewport *vp, int left, int top, int right, int bottom);
190
191static ViewportDrawer _vd;
192
194static TileInfo _cur_ti;
195bool _draw_bounding_boxes = false;
196bool _draw_dirty_blocks = false;
197uint _dirty_block_colour = 0;
198static VpSpriteSorter _vp_sprite_sorter = nullptr;
199
200static Point MapXYZToViewport(const Viewport *vp, int x, int y, int z)
201{
202 Point p = RemapCoords(x, y, z);
203 p.x -= vp->virtual_width / 2;
204 p.y -= vp->virtual_height / 2;
205 return p;
206}
207
208void DeleteWindowViewport(Window *w)
209{
210 delete w->viewport;
211 w->viewport = nullptr;
212}
213
224void InitializeWindowViewport(Window *w, int x, int y,
225 int width, int height, std::variant<TileIndex, VehicleID> focus, ZoomLevel zoom)
226{
227 assert(w->viewport == nullptr);
228
229 ViewportData *vp = new ViewportData();
230
231 vp->left = x + w->left;
232 vp->top = y + w->top;
233 vp->width = width;
234 vp->height = height;
235
237
238 vp->virtual_width = ScaleByZoom(width, zoom);
239 vp->virtual_height = ScaleByZoom(height, zoom);
240
241 Point pt;
242
243 if (std::holds_alternative<VehicleID>(focus)) {
244 const Vehicle *veh;
245
246 vp->follow_vehicle = std::get<VehicleID>(focus);
247 veh = Vehicle::Get(vp->follow_vehicle);
248 pt = MapXYZToViewport(vp, veh->x_pos, veh->y_pos, veh->z_pos);
249 } else {
250 TileIndex tile = std::get<TileIndex>(focus);
251 if (tile == INVALID_TILE) {
252 /* No tile? Use center of main viewport. */
253 const Window *mw = GetMainWindow();
254
255 /* center on same place as main window (zoom is maximum, no adjustment needed) */
256 pt.x = mw->viewport->scrollpos_x + mw->viewport->virtual_width / 2;
257 pt.x -= vp->virtual_width / 2;
258 pt.y = mw->viewport->scrollpos_y + mw->viewport->virtual_height / 2;
259 pt.y -= vp->virtual_height / 2;
260 } else {
261 x = TileX(tile) * TILE_SIZE;
262 y = TileY(tile) * TILE_SIZE;
263 pt = MapXYZToViewport(vp, x, y, GetSlopePixelZ(x, y));
264 }
265 vp->follow_vehicle = VehicleID::Invalid();
266 }
267
268 vp->scrollpos_x = pt.x;
269 vp->scrollpos_y = pt.y;
270 vp->dest_scrollpos_x = pt.x;
271 vp->dest_scrollpos_y = pt.y;
272
273 vp->overlay = nullptr;
274
275 w->viewport = vp;
276 vp->virtual_left = 0;
277 vp->virtual_top = 0;
278}
279
280static Point _vp_move_offs;
281
282static void DoSetViewportPosition(Window::IteratorToFront it, int left, int top, int width, int height)
283{
284 for (; !it.IsEnd(); ++it) {
285 const Window *w = *it;
286 if (left + width > w->left &&
287 w->left + w->width > left &&
288 top + height > w->top &&
289 w->top + w->height > top) {
290
291 if (left < w->left) {
292 DoSetViewportPosition(it, left, top, w->left - left, height);
293 DoSetViewportPosition(it, left + (w->left - left), top, width - (w->left - left), height);
294 return;
295 }
296
297 if (left + width > w->left + w->width) {
298 DoSetViewportPosition(it, left, top, (w->left + w->width - left), height);
299 DoSetViewportPosition(it, left + (w->left + w->width - left), top, width - (w->left + w->width - left), height);
300 return;
301 }
302
303 if (top < w->top) {
304 DoSetViewportPosition(it, left, top, width, (w->top - top));
305 DoSetViewportPosition(it, left, top + (w->top - top), width, height - (w->top - top));
306 return;
307 }
308
309 if (top + height > w->top + w->height) {
310 DoSetViewportPosition(it, left, top, width, (w->top + w->height - top));
311 DoSetViewportPosition(it, left, top + (w->top + w->height - top), width, height - (w->top + w->height - top));
312 return;
313 }
314
315 return;
316 }
317 }
318
319 {
320 int xo = _vp_move_offs.x;
321 int yo = _vp_move_offs.y;
322
323 if (abs(xo) >= width || abs(yo) >= height) {
324 /* fully_outside */
325 RedrawScreenRect(left, top, left + width, top + height);
326 return;
327 }
328
329 GfxScroll(left, top, width, height, xo, yo);
330
331 if (xo > 0) {
332 RedrawScreenRect(left, top, xo + left, top + height);
333 left += xo;
334 width -= xo;
335 } else if (xo < 0) {
336 RedrawScreenRect(left + width + xo, top, left + width, top + height);
337 width += xo;
338 }
339
340 if (yo > 0) {
341 RedrawScreenRect(left, top, width + left, top + yo);
342 } else if (yo < 0) {
343 RedrawScreenRect(left, top + height + yo, width + left, top + height);
344 }
345 }
346}
347
348static void SetViewportPosition(Window *w, int x, int y)
349{
350 Viewport *vp = w->viewport;
351 int old_left = vp->virtual_left;
352 int old_top = vp->virtual_top;
353 int i;
354 int left, top, width, height;
355
356 vp->virtual_left = x;
357 vp->virtual_top = y;
358
359 /* Viewport is bound to its left top corner, so it must be rounded down (UnScaleByZoomLower)
360 * else glitch described in FS#1412 will happen (offset by 1 pixel with zoom level > NORMAL)
361 */
362 old_left = UnScaleByZoomLower(old_left, vp->zoom);
363 old_top = UnScaleByZoomLower(old_top, vp->zoom);
364 x = UnScaleByZoomLower(x, vp->zoom);
365 y = UnScaleByZoomLower(y, vp->zoom);
366
367 old_left -= x;
368 old_top -= y;
369
370 if (old_top == 0 && old_left == 0) return;
371
372 _vp_move_offs.x = old_left;
373 _vp_move_offs.y = old_top;
374
375 left = vp->left;
376 top = vp->top;
377 width = vp->width;
378 height = vp->height;
379
380 if (left < 0) {
381 width += left;
382 left = 0;
383 }
384
385 i = left + width - _screen.width;
386 if (i >= 0) width -= i;
387
388 if (width > 0) {
389 if (top < 0) {
390 height += top;
391 top = 0;
392 }
393
394 i = top + height - _screen.height;
395 if (i >= 0) height -= i;
396
397 if (height > 0) {
399 ++it;
400 DoSetViewportPosition(it, left, top, width, height);
401 }
402 }
403}
404
413Viewport *IsPtInWindowViewport(const Window *w, int x, int y)
414{
415 Viewport *vp = w->viewport;
416
417 if (vp != nullptr &&
418 IsInsideMM(x, vp->left, vp->left + vp->width) &&
419 IsInsideMM(y, vp->top, vp->top + vp->height))
420 return vp;
421
422 return nullptr;
423}
424
437Point TranslateXYToTileCoord(const Viewport *vp, int x, int y, bool clamp_to_map)
438{
439 if (!IsInsideBS(x, vp->left, vp->width) || !IsInsideBS(y, vp->top, vp->height)) {
440 Point pt = { -1, -1 };
441 return pt;
442 }
443
444 return InverseRemapCoords2(
445 ScaleByZoom(x - vp->left, vp->zoom) + vp->virtual_left,
446 ScaleByZoom(y - vp->top, vp->zoom) + vp->virtual_top, clamp_to_map);
447}
448
449/* When used for zooming, check area below current coordinates (x,y)
450 * and return the tile of the zoomed out/in position (zoom_x, zoom_y)
451 * when you just want the tile, make x = zoom_x and y = zoom_y */
452static Point GetTileFromScreenXY(int x, int y, int zoom_x, int zoom_y)
453{
454 Window *w;
455 Viewport *vp;
456 Point pt;
457
458 if ( (w = FindWindowFromPt(x, y)) != nullptr &&
459 (vp = IsPtInWindowViewport(w, x, y)) != nullptr)
460 return TranslateXYToTileCoord(vp, zoom_x, zoom_y);
461
462 pt.y = pt.x = -1;
463 return pt;
464}
465
466Point GetTileBelowCursor()
467{
468 return GetTileFromScreenXY(_cursor.pos.x, _cursor.pos.y, _cursor.pos.x, _cursor.pos.y);
469}
470
471
472Point GetTileZoomCenterWindow(bool in, Window * w)
473{
474 int x, y;
475 Viewport *vp = w->viewport;
476
477 if (in) {
478 x = ((_cursor.pos.x - vp->left) >> 1) + (vp->width >> 2);
479 y = ((_cursor.pos.y - vp->top) >> 1) + (vp->height >> 2);
480 } else {
481 x = vp->width - (_cursor.pos.x - vp->left);
482 y = vp->height - (_cursor.pos.y - vp->top);
483 }
484 /* Get the tile below the cursor and center on the zoomed-out center */
485 return GetTileFromScreenXY(_cursor.pos.x, _cursor.pos.y, x + vp->left, y + vp->top);
486}
487
496void HandleZoomMessage(Window *w, const Viewport *vp, WidgetID widget_zoom_in, WidgetID widget_zoom_out)
497{
498 w->SetWidgetDisabledState(widget_zoom_in, vp->zoom <= _settings_client.gui.zoom_min);
499 w->SetWidgetDirty(widget_zoom_in);
500
501 w->SetWidgetDisabledState(widget_zoom_out, vp->zoom >= _settings_client.gui.zoom_max);
502 w->SetWidgetDirty(widget_zoom_out);
503}
504
517static void AddTileSpriteToDraw(SpriteID image, PaletteID pal, int32_t x, int32_t y, int z, const SubSprite *sub = nullptr, int extra_offs_x = 0, int extra_offs_y = 0)
518{
519 assert((image & SPRITE_MASK) < MAX_SPRITES);
520
521 TileSpriteToDraw &ts = _vd.tile_sprites_to_draw.emplace_back();
522 ts.image = image;
523 ts.pal = pal;
524 ts.sub = sub;
525 Point pt = RemapCoords(x, y, z);
526 ts.x = pt.x + extra_offs_x;
527 ts.y = pt.y + extra_offs_y;
528}
529
542static void AddChildSpriteToFoundation(SpriteID image, PaletteID pal, const SubSprite *sub, FoundationPart foundation_part, int extra_offs_x, int extra_offs_y)
543{
544 assert(IsInsideMM(foundation_part, 0, FOUNDATION_PART_END));
545 assert(_vd.foundation[foundation_part] != -1);
546 Point offs = _vd.foundation_offset[foundation_part];
547
548 /* Change the active ChildSprite list to the one of the foundation */
549 AutoRestoreBackup backup(_vd.last_child, _vd.last_foundation_child[foundation_part]);
550 AddChildSpriteScreen(image, pal, offs.x + extra_offs_x, offs.y + extra_offs_y, false, sub, false, false);
551}
552
566void DrawGroundSpriteAt(SpriteID image, PaletteID pal, int32_t x, int32_t y, int z, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
567{
568 /* Switch to first foundation part, if no foundation was drawn */
570
571 if (_vd.foundation[_vd.foundation_part] != -1) {
572 Point pt = RemapCoords(x, y, z);
573 AddChildSpriteToFoundation(image, pal, sub, _vd.foundation_part, pt.x + extra_offs_x * ZOOM_BASE, pt.y + extra_offs_y * ZOOM_BASE);
574 } else {
575 AddTileSpriteToDraw(image, pal, _cur_ti.x + x, _cur_ti.y + y, _cur_ti.z + z, sub, extra_offs_x * ZOOM_BASE, extra_offs_y * ZOOM_BASE);
576 }
577}
578
589void DrawGroundSprite(SpriteID image, PaletteID pal, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
590{
591 DrawGroundSpriteAt(image, pal, 0, 0, 0, sub, extra_offs_x, extra_offs_y);
592}
593
601void OffsetGroundSprite(int x, int y)
602{
603 /* Switch to next foundation part */
604 switch (_vd.foundation_part) {
607 break;
610 break;
611 default: NOT_REACHED();
612 }
613
614 /* _vd.last_child is LAST_CHILD_NONE if foundation sprite was clipped by the viewport bounds */
615 if (_vd.last_child != LAST_CHILD_NONE) _vd.foundation[_vd.foundation_part] = static_cast<uint>(_vd.parent_sprites_to_draw.size()) - 1;
616
617 _vd.foundation_offset[_vd.foundation_part].x = x * ZOOM_BASE;
618 _vd.foundation_offset[_vd.foundation_part].y = y * ZOOM_BASE;
619 _vd.last_foundation_child[_vd.foundation_part] = _vd.last_child;
620}
621
633static void AddCombinedSprite(SpriteID image, PaletteID pal, int x, int y, int z, const SubSprite *sub)
634{
635 Point pt = RemapCoords(x, y, z);
636 const Sprite *spr = GetSprite(image & SPRITE_MASK, SpriteType::Normal);
637
638 if (pt.x + spr->x_offs >= _vd.dpi.left + _vd.dpi.width ||
639 pt.x + spr->x_offs + spr->width <= _vd.dpi.left ||
640 pt.y + spr->y_offs >= _vd.dpi.top + _vd.dpi.height ||
641 pt.y + spr->y_offs + spr->height <= _vd.dpi.top)
642 return;
643
644 const ParentSpriteToDraw &pstd = _vd.parent_sprites_to_draw.back();
645 AddChildSpriteScreen(image, pal, pt.x - pstd.left, pt.y - pstd.top, false, sub, false);
646}
647
673void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int w, int h, int dz, int z, bool transparent, int bb_offset_x, int bb_offset_y, int bb_offset_z, const SubSprite *sub)
674{
675 int32_t left, right, top, bottom;
676
677 assert((image & SPRITE_MASK) < MAX_SPRITES);
678
679 /* make the sprites transparent with the right palette */
680 if (transparent) {
683 }
684
686 AddCombinedSprite(image, pal, x, y, z, sub);
687 return;
688 }
689
690 _vd.last_child = LAST_CHILD_NONE;
691
692 Point pt = RemapCoords(x, y, z);
693 int tmp_left, tmp_top, tmp_x = pt.x, tmp_y = pt.y;
694
695 /* Compute screen extents of sprite */
696 if (image == SPR_EMPTY_BOUNDING_BOX) {
697 left = tmp_left = RemapCoords(x + w , y + bb_offset_y, z + bb_offset_z).x;
698 right = RemapCoords(x + bb_offset_x, y + h , z + bb_offset_z).x + 1;
699 top = tmp_top = RemapCoords(x + bb_offset_x, y + bb_offset_y, z + dz ).y;
700 bottom = RemapCoords(x + w , y + h , z + bb_offset_z).y + 1;
701 } else {
702 const Sprite *spr = GetSprite(image & SPRITE_MASK, SpriteType::Normal);
703 left = tmp_left = (pt.x += spr->x_offs);
704 right = (pt.x + spr->width );
705 top = tmp_top = (pt.y += spr->y_offs);
706 bottom = (pt.y + spr->height);
707 }
708
709 if (_draw_bounding_boxes && (image != SPR_EMPTY_BOUNDING_BOX)) {
710 /* Compute maximal extents of sprite and its bounding box */
711 left = std::min(left , RemapCoords(x + w , y + bb_offset_y, z + bb_offset_z).x);
712 right = std::max(right , RemapCoords(x + bb_offset_x, y + h , z + bb_offset_z).x + 1);
713 top = std::min(top , RemapCoords(x + bb_offset_x, y + bb_offset_y, z + dz ).y);
714 bottom = std::max(bottom, RemapCoords(x + w , y + h , z + bb_offset_z).y + 1);
715 }
716
717 /* Do not add the sprite to the viewport, if it is outside */
718 if (left >= _vd.dpi.left + _vd.dpi.width ||
719 right <= _vd.dpi.left ||
720 top >= _vd.dpi.top + _vd.dpi.height ||
721 bottom <= _vd.dpi.top) {
722 return;
723 }
724
725 ParentSpriteToDraw &ps = _vd.parent_sprites_to_draw.emplace_back();
726 ps.x = tmp_x;
727 ps.y = tmp_y;
728
729 ps.left = tmp_left;
730 ps.top = tmp_top;
731
732 ps.image = image;
733 ps.pal = pal;
734 ps.sub = sub;
735 ps.xmin = x + bb_offset_x;
736 ps.xmax = x + std::max(bb_offset_x, w) - 1;
737
738 ps.ymin = y + bb_offset_y;
739 ps.ymax = y + std::max(bb_offset_y, h) - 1;
740
741 ps.zmin = z + bb_offset_z;
742 ps.zmax = z + std::max(bb_offset_z, dz) - 1;
743
745
746 _vd.last_child = LAST_CHILD_PARENT;
747
749}
750
774
784
794static bool IsInRangeInclusive(int begin, int end, int check)
795{
796 if (begin > end) std::swap(begin, end);
797 return begin <= check && check <= end;
798}
799
806bool IsInsideRotatedRectangle(int x, int y)
807{
808 int dist_a = (_thd.size.x + _thd.size.y); // Rotated coordinate system for selected rectangle.
809 int dist_b = (_thd.size.x - _thd.size.y); // We don't have to divide by 2. It's all relative!
810 int a = ((x - _thd.pos.x) + (y - _thd.pos.y)); // Rotated coordinate system for the point under scrutiny.
811 int b = ((x - _thd.pos.x) - (y - _thd.pos.y));
812
813 /* Check if a and b are between 0 and dist_a or dist_b respectively. */
814 return IsInRangeInclusive(dist_a, 0, a) && IsInRangeInclusive(dist_b, 0, b);
815}
816
829void AddChildSpriteScreen(SpriteID image, PaletteID pal, int x, int y, bool transparent, const SubSprite *sub, bool scale, bool relative)
830{
831 assert((image & SPRITE_MASK) < MAX_SPRITES);
832
833 /* If the ParentSprite was clipped by the viewport bounds, do not draw the ChildSprites either */
834 if (_vd.last_child == LAST_CHILD_NONE) return;
835
836 /* make the sprites transparent with the right palette */
837 if (transparent) {
840 }
841
842 int32_t child_id = static_cast<int32_t>(_vd.child_screen_sprites_to_draw.size());
843 if (_vd.last_child != LAST_CHILD_PARENT) {
844 _vd.child_screen_sprites_to_draw[_vd.last_child].next = child_id;
845 } else {
846 _vd.parent_sprites_to_draw.back().first_child = child_id;
847 }
848
849 ChildScreenSpriteToDraw &cs = _vd.child_screen_sprites_to_draw.emplace_back();
850 cs.image = image;
851 cs.pal = pal;
852 cs.sub = sub;
853 cs.x = scale ? x * ZOOM_BASE : x;
854 cs.y = scale ? y * ZOOM_BASE : y;
855 cs.relative = relative;
857
858 /* Append the sprite to the active ChildSprite list.
859 * If the active ParentSprite is a foundation, update last_foundation_child as well.
860 * Note: ChildSprites of foundations are NOT sequential in the vector, as selection sprites are added at last. */
861 if (_vd.last_foundation_child[0] == _vd.last_child) _vd.last_foundation_child[0] = child_id;
862 if (_vd.last_foundation_child[1] == _vd.last_child) _vd.last_foundation_child[1] = child_id;
863 _vd.last_child = child_id;
864}
865
875static std::string &AddStringToDraw(int x, int y, Colours colour, ViewportStringFlags flags, uint16_t width)
876{
877 assert(width != 0);
878 StringSpriteToDraw &ss = _vd.string_sprites_to_draw.emplace_back();
879 ss.colour = colour;
880 ss.flags = flags;
881 ss.x = x;
882 ss.y = y;
883 ss.width = width;
884
885 return ss.string;
886}
887
888
902static void DrawSelectionSprite(SpriteID image, PaletteID pal, const TileInfo *ti, int z_offset, FoundationPart foundation_part, int extra_offs_x = 0, int extra_offs_y = 0)
903{
904 if (_vd.foundation[foundation_part] == -1) {
905 /* draw on real ground */
906 AddTileSpriteToDraw(image, pal, ti->x, ti->y, ti->z + z_offset, nullptr, extra_offs_x, extra_offs_y);
907 } else {
908 /* draw on top of foundation */
909 AddChildSpriteToFoundation(image, pal, nullptr, foundation_part, extra_offs_x, extra_offs_y - z_offset * ZOOM_BASE);
910 }
911}
912
919static void DrawTileSelectionRect(const TileInfo *ti, PaletteID pal)
920{
921 if (!IsValidTile(ti->tile)) return;
922
923 SpriteID sel;
924 if (IsHalftileSlope(ti->tileh)) {
925 Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
926 SpriteID sel2 = SPR_HALFTILE_SELECTION_FLAT + halftile_corner;
928
929 Corner opposite_corner = OppositeCorner(halftile_corner);
930 if (IsSteepSlope(ti->tileh)) {
931 sel = SPR_HALFTILE_SELECTION_DOWN;
932 } else {
933 sel = ((ti->tileh & SlopeWithOneCornerRaised(opposite_corner)) != 0 ? SPR_HALFTILE_SELECTION_UP : SPR_HALFTILE_SELECTION_FLAT);
934 }
935 sel += opposite_corner;
936 } else {
937 sel = SPR_SELECT_TILE + SlopeToSpriteOffset(ti->tileh);
938 }
940}
941
942static bool IsPartOfAutoLine(int px, int py)
943{
944 px -= _thd.selstart.x;
945 py -= _thd.selstart.y;
946
947 if ((_thd.drawstyle & HT_DRAG_MASK) != HT_LINE) return false;
948
949 switch (_thd.drawstyle & HT_DIR_MASK) {
950 case HT_DIR_X: return py == 0; // x direction
951 case HT_DIR_Y: return px == 0; // y direction
952 case HT_DIR_HU: return px == -py || px == -py - 16; // horizontal upper
953 case HT_DIR_HL: return px == -py || px == -py + 16; // horizontal lower
954 case HT_DIR_VL: return px == py || px == py + 16; // vertical left
955 case HT_DIR_VR: return px == py || px == py - 16; // vertical right
956 default:
957 NOT_REACHED();
958 }
959}
960
961/* [direction][side] */
962static const HighLightStyle _autorail_type[6][2] = {
963 { HT_DIR_X, HT_DIR_X },
964 { HT_DIR_Y, HT_DIR_Y },
965 { HT_DIR_HU, HT_DIR_HL },
966 { HT_DIR_HL, HT_DIR_HU },
967 { HT_DIR_VL, HT_DIR_VR },
969};
970
971#include "table/autorail.h"
972
979static void DrawAutorailSelection(const TileInfo *ti, uint autorail_type)
980{
981 SpriteID image;
982 PaletteID pal;
983 int offset;
984
985 FoundationPart foundation_part = FOUNDATION_PART_NORMAL;
986 Slope autorail_tileh = RemoveHalftileSlope(ti->tileh);
987 if (IsHalftileSlope(ti->tileh)) {
988 static const uint _lower_rail[4] = { 5U, 2U, 4U, 3U };
989 Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
990 if (autorail_type != _lower_rail[halftile_corner]) {
991 foundation_part = FOUNDATION_PART_HALFTILE;
992 /* Here we draw the highlights of the "three-corners-raised"-slope. That looks ok to me. */
993 autorail_tileh = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
994 }
995 }
996
997 offset = _AutorailTilehSprite[autorail_tileh][autorail_type];
998 if (offset >= 0) {
999 image = SPR_AUTORAIL_BASE + offset;
1000 pal = PAL_NONE;
1001 } else {
1002 image = SPR_AUTORAIL_BASE - offset;
1004 }
1005
1006 DrawSelectionSprite(image, _thd.make_square_red ? PALETTE_SEL_TILE_RED : pal, ti, 7, foundation_part);
1007}
1008
1009enum TileHighlightType : uint8_t {
1010 THT_NONE,
1011 THT_WHITE,
1012 THT_BLUE,
1013 THT_RED,
1014};
1015
1019
1025static TileHighlightType GetTileHighlightType(TileIndex t)
1026{
1027 if (_viewport_highlight_station != nullptr) {
1028 if (IsTileType(t, MP_STATION) && GetStationIndex(t) == _viewport_highlight_station->index) return THT_WHITE;
1029 if (_viewport_highlight_station->TileIsInCatchment(t)) return THT_BLUE;
1030 }
1031 if (_viewport_highlight_waypoint != nullptr) {
1032 if (IsTileType(t, MP_STATION) && GetStationIndex(t) == _viewport_highlight_waypoint->index) return THT_BLUE;
1033 }
1034
1035 if (_viewport_highlight_town != nullptr) {
1036 if (IsTileType(t, MP_HOUSE)) {
1038 TileHighlightType type = THT_RED;
1040 if (st->owner != _current_company) continue;
1041 if (st->TileIsInCatchment(t)) return THT_BLUE;
1042 }
1043 return type;
1044 }
1045 } else if (IsTileType(t, MP_STATION)) {
1047 if (st->owner != _current_company) continue;
1048 if (GetStationIndex(t) == st->index) return THT_WHITE;
1049 }
1050 }
1051 }
1052
1053 return THT_NONE;
1054}
1055
1061static void DrawTileHighlightType(const TileInfo *ti, TileHighlightType tht)
1062{
1063 switch (tht) {
1064 default:
1065 case THT_NONE: break;
1066 case THT_WHITE: DrawTileSelectionRect(ti, PAL_NONE); break;
1067 case THT_BLUE: DrawTileSelectionRect(ti, PALETTE_SEL_TILE_BLUE); break;
1068 case THT_RED: DrawTileSelectionRect(ti, PALETTE_SEL_TILE_RED); break;
1069 }
1070}
1071
1077{
1078 /* Going through cases in order of computational time. */
1079
1080 if (_town_local_authority_kdtree.Count() == 0) return;
1081
1082 /* Tile belongs to town regardless of distance from town. */
1083 if (GetTileType(ti->tile) == MP_HOUSE) {
1084 if (!Town::GetByTile(ti->tile)->show_zone) return;
1085
1087 return;
1088 }
1089
1090 /* If the closest town in the highlighted list is far, we can stop searching. */
1091 TownID tid = _town_local_authority_kdtree.FindNearest(TileX(ti->tile), TileY(ti->tile));
1092 Town *closest_highlighted_town = Town::Get(tid);
1093
1094 if (DistanceManhattan(ti->tile, closest_highlighted_town->xy) >= _settings_game.economy.dist_local_authority) return;
1095
1096 /* Tile is inside of the local autrhority distance of a highlighted town,
1097 but it is possible that a non-highlighted town is even closer. */
1099
1100 if (closest_town->show_zone) {
1102 }
1103
1104}
1105
1110static void DrawTileSelection(const TileInfo *ti)
1111{
1112 /* Highlight tiles insede local authority of selected towns. */
1114
1115 /* Draw a red error square? */
1116 bool is_redsq = _thd.redsq == ti->tile;
1118
1119 TileHighlightType tht = GetTileHighlightType(ti->tile);
1120 DrawTileHighlightType(ti, tht);
1121
1122 /* No tile selection active? */
1123 if ((_thd.drawstyle & HT_DRAG_MASK) == HT_NONE) return;
1124
1125 if (_thd.diagonal) { // We're drawing a 45 degrees rotated (diagonal) rectangle
1126 if (IsInsideRotatedRectangle((int)ti->x, (int)ti->y)) goto draw_inner;
1127 return;
1128 }
1129
1130 /* Inside the inner area? */
1131 if (IsInsideBS(ti->x, _thd.pos.x, _thd.size.x) &&
1132 IsInsideBS(ti->y, _thd.pos.y, _thd.size.y)) {
1133draw_inner:
1134 if (_thd.drawstyle & HT_RECT) {
1135 if (!is_redsq) DrawTileSelectionRect(ti, _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE);
1136 } else if (_thd.drawstyle & HT_POINT) {
1137 /* Figure out the Z coordinate for the single dot. */
1138 int z = 0;
1139 FoundationPart foundation_part = FOUNDATION_PART_NORMAL;
1140 if (ti->tileh & SLOPE_N) {
1141 z += TILE_HEIGHT;
1143 }
1144 if (IsHalftileSlope(ti->tileh)) {
1145 Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
1146 if ((halftile_corner == CORNER_W) || (halftile_corner == CORNER_E)) z += TILE_HEIGHT;
1147 if (halftile_corner != CORNER_S) {
1148 foundation_part = FOUNDATION_PART_HALFTILE;
1149 if (IsSteepSlope(ti->tileh)) z -= TILE_HEIGHT;
1150 }
1151 }
1152 DrawSelectionSprite(SPR_DOT, PAL_NONE, ti, z, foundation_part);
1153 } else if (_thd.drawstyle & HT_RAIL) {
1154 /* autorail highlight piece under cursor */
1155 HighLightStyle type = _thd.drawstyle & HT_DIR_MASK;
1156 assert(type < HT_DIR_END);
1157 DrawAutorailSelection(ti, _autorail_type[type][0]);
1158 } else if (IsPartOfAutoLine(ti->x, ti->y)) {
1159 /* autorail highlighting long line */
1161 uint side;
1162
1163 if (dir == HT_DIR_X || dir == HT_DIR_Y) {
1164 side = 0;
1165 } else {
1166 TileIndex start = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
1167 side = Delta(Delta(TileX(start), TileX(ti->tile)), Delta(TileY(start), TileY(ti->tile)));
1168 }
1169
1170 DrawAutorailSelection(ti, _autorail_type[dir][side]);
1171 }
1172 return;
1173 }
1174
1175 /* Check if it's inside the outer area? */
1176 if (!is_redsq && (tht == THT_NONE || tht == THT_RED) && _thd.outersize.x > 0 &&
1177 IsInsideBS(ti->x, _thd.pos.x + _thd.offs.x, _thd.size.x + _thd.outersize.x) &&
1178 IsInsideBS(ti->y, _thd.pos.y + _thd.offs.y, _thd.size.y + _thd.outersize.y)) {
1179 /* Draw a blue rect. */
1181 return;
1182 }
1183}
1184
1191static int GetViewportY(Point tile)
1192{
1193 /* Each increment in X or Y direction moves down by half a tile, i.e. TILE_PIXELS / 2. */
1194 return (tile.y * (int)(TILE_PIXELS / 2) + tile.x * (int)(TILE_PIXELS / 2) - TilePixelHeightOutsideMap(tile.x, tile.y)) << ZOOM_BASE_SHIFT;
1195}
1196
1201{
1202 assert(_vd.dpi.top <= _vd.dpi.top + _vd.dpi.height);
1203 assert(_vd.dpi.left <= _vd.dpi.left + _vd.dpi.width);
1204
1205 Point upper_left = InverseRemapCoords(_vd.dpi.left, _vd.dpi.top);
1206 Point upper_right = InverseRemapCoords(_vd.dpi.left + _vd.dpi.width, _vd.dpi.top);
1207
1208 /* Transformations between tile coordinates and viewport rows/columns: See vp_column_row
1209 * column = y - x
1210 * row = x + y
1211 * x = (row - column) / 2
1212 * y = (row + column) / 2
1213 * Note: (row, columns) pairs are only valid, if they are both even or both odd.
1214 */
1215
1216 /* Columns overlap with neighbouring columns by a half tile.
1217 * - Left column is column of upper_left (rounded down) and one column to the left.
1218 * - Right column is column of upper_right (rounded up) and one column to the right.
1219 * Note: Integer-division does not round down for negative numbers, so ensure rounding with another increment/decrement.
1220 */
1221 int left_column = (upper_left.y - upper_left.x) / (int)TILE_SIZE - 2;
1222 int right_column = (upper_right.y - upper_right.x) / (int)TILE_SIZE + 2;
1223
1224 int potential_bridge_height = ZOOM_BASE * TILE_HEIGHT * _settings_game.construction.max_bridge_height;
1225
1226 /* Rows overlap with neighbouring rows by a half tile.
1227 * The first row that could possibly be visible is the row above upper_left (if it is at height 0).
1228 * Due to integer-division not rounding down for negative numbers, we need another decrement.
1229 */
1230 int row = (upper_left.x + upper_left.y) / (int)TILE_SIZE - 2;
1231 bool last_row = false;
1232 for (; !last_row; row++) {
1233 last_row = true;
1234 for (int column = left_column; column <= right_column; column++) {
1235 /* Valid row/column? */
1236 if ((row + column) % 2 != 0) continue;
1237
1238 Point tilecoord;
1239 tilecoord.x = (row - column) / 2;
1240 tilecoord.y = (row + column) / 2;
1241 assert(column == tilecoord.y - tilecoord.x);
1242 assert(row == tilecoord.y + tilecoord.x);
1243
1244 TileType tile_type;
1245 _cur_ti.x = tilecoord.x * TILE_SIZE;
1246 _cur_ti.y = tilecoord.y * TILE_SIZE;
1247
1248 if (IsInsideBS(tilecoord.x, 0, Map::SizeX()) && IsInsideBS(tilecoord.y, 0, Map::SizeY())) {
1249 /* This includes the south border at Map::MaxX / Map::MaxY. When terraforming we still draw tile selections there. */
1250 _cur_ti.tile = TileXY(tilecoord.x, tilecoord.y);
1251 tile_type = GetTileType(_cur_ti.tile);
1252 } else {
1253 _cur_ti.tile = INVALID_TILE;
1254 tile_type = MP_VOID;
1255 }
1256
1257 if (tile_type != MP_VOID) {
1258 /* We are inside the map => paint landscape. */
1259 std::tie(_cur_ti.tileh, _cur_ti.z) = GetTilePixelSlope(_cur_ti.tile);
1260 } else {
1261 /* We are outside the map => paint black. */
1262 std::tie(_cur_ti.tileh, _cur_ti.z) = GetTilePixelSlopeOutsideMap(tilecoord.x, tilecoord.y);
1263 }
1264
1265 int viewport_y = GetViewportY(tilecoord);
1266
1267 if (viewport_y + MAX_TILE_EXTENT_BOTTOM < _vd.dpi.top) {
1268 /* The tile in this column is not visible yet.
1269 * Tiles in other columns may be visible, but we need more rows in any case. */
1270 last_row = false;
1271 continue;
1272 }
1273
1274 int min_visible_height = viewport_y - (_vd.dpi.top + _vd.dpi.height);
1275 bool tile_visible = min_visible_height <= 0;
1276
1277 if (tile_type != MP_VOID) {
1278 /* Is tile with buildings visible? */
1279 if (min_visible_height < MAX_TILE_EXTENT_TOP) tile_visible = true;
1280
1281 if (IsBridgeAbove(_cur_ti.tile)) {
1282 /* Is the bridge visible? */
1283 TileIndex bridge_tile = GetNorthernBridgeEnd(_cur_ti.tile);
1284 int bridge_height = ZOOM_BASE * (GetBridgePixelHeight(bridge_tile) - TilePixelHeight(_cur_ti.tile));
1285 if (min_visible_height < bridge_height + MAX_TILE_EXTENT_TOP) tile_visible = true;
1286 }
1287
1288 /* Would a higher bridge on a more southern tile be visible?
1289 * If yes, we need to loop over more rows to possibly find one. */
1290 if (min_visible_height < potential_bridge_height + MAX_TILE_EXTENT_TOP) last_row = false;
1291 } else {
1292 /* Outside of map. If we are on the north border of the map, there may still be a bridge visible,
1293 * so we need to loop over more rows to possibly find one. */
1294 if ((tilecoord.x <= 0 || tilecoord.y <= 0) && min_visible_height < potential_bridge_height + MAX_TILE_EXTENT_TOP) last_row = false;
1295 }
1296
1297 if (tile_visible) {
1298 last_row = false;
1300 _vd.foundation[0] = -1;
1301 _vd.foundation[1] = -1;
1304
1305 _tile_type_procs[tile_type]->draw_tile_proc(&_cur_ti);
1306 if (_cur_ti.tile != INVALID_TILE) DrawTileSelection(&_cur_ti);
1307 }
1308 }
1309 }
1310}
1311
1320std::string *ViewportAddString(const DrawPixelInfo *dpi, const ViewportSign *sign, ViewportStringFlags flags, Colours colour)
1321{
1322 int left = dpi->left;
1323 int top = dpi->top;
1324 int right = left + dpi->width;
1325 int bottom = top + dpi->height;
1326
1327 bool small = flags.Test(ViewportStringFlag::Small);
1328 int sign_height = ScaleByZoom(WidgetDimensions::scaled.fullbevel.top + GetCharacterHeight(small ? FS_SMALL : FS_NORMAL) + WidgetDimensions::scaled.fullbevel.bottom, dpi->zoom);
1329 int sign_half_width = ScaleByZoom((small ? sign->width_small : sign->width_normal) / 2, dpi->zoom);
1330
1331 if (bottom < sign->top ||
1332 top > sign->top + sign_height ||
1333 right < sign->center - sign_half_width ||
1334 left > sign->center + sign_half_width) {
1335 return nullptr;
1336 }
1337
1338 return &AddStringToDraw(sign->center - sign_half_width, sign->top, colour, flags, small ? sign->width_small : sign->width_normal);
1339}
1340
1341static Rect ExpandRectWithViewportSignMargins(Rect r, ZoomLevel zoom)
1342{
1343 const int fh = std::max(GetCharacterHeight(FS_NORMAL), GetCharacterHeight(FS_SMALL));
1344 const int max_tw = _viewport_sign_maxwidth / 2 + 1;
1345 const int expand_y = ScaleByZoom(WidgetDimensions::scaled.fullbevel.top + fh + WidgetDimensions::scaled.fullbevel.bottom, zoom);
1346 const int expand_x = ScaleByZoom(WidgetDimensions::scaled.fullbevel.left + max_tw + WidgetDimensions::scaled.fullbevel.right, zoom);
1347
1348 r.left -= expand_x;
1349 r.right += expand_x;
1350 r.top -= expand_y;
1351 r.bottom += expand_y;
1352
1353 return r;
1354}
1355
1362static void ViewportAddTownStrings(DrawPixelInfo *dpi, const std::vector<const Town *> &towns, bool small)
1363{
1364 ViewportStringFlags flags{};
1366
1367 StringID stringid = !small && _settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_TOWN_NAME;
1368 for (const Town *t : towns) {
1369 std::string *str = ViewportAddString(dpi, &t->cache.sign, flags, INVALID_COLOUR);
1370 if (str == nullptr) continue;
1371
1372 *str = GetString(stringid, t->index, t->cache.population);
1373 }
1374}
1375
1382static void ViewportAddSignStrings(DrawPixelInfo *dpi, const std::vector<const Sign *> &signs, bool small)
1383{
1384 ViewportStringFlags flags{};
1385 if (small) flags.Set(ViewportStringFlag::Small);
1386
1387 /* Signs placed by a game script don't have a frame. */
1388 ViewportStringFlags deity_flags{flags};
1390
1391 for (const Sign *si : signs) {
1392 std::string *str = ViewportAddString(dpi, &si->sign, (si->owner == OWNER_DEITY) ? deity_flags : flags,
1393 (si->owner == OWNER_NONE) ? COLOUR_GREY : (si->owner == OWNER_DEITY ? INVALID_COLOUR : _company_colours[si->owner]));
1394 if (str == nullptr) continue;
1395
1396 *str = GetString(STR_SIGN_NAME, si->index);
1397 }
1398}
1399
1406static void ViewportAddStationStrings(DrawPixelInfo *dpi, const std::vector<const BaseStation *> &stations, bool small)
1407{
1408 /* Transparent station signs have colour text instead of a colour panel. */
1410 if (small) flags.Set(ViewportStringFlag::Small);
1411
1412 for (const BaseStation *st : stations) {
1413 std::string *str = ViewportAddString(dpi, &st->sign, flags, (st->owner == OWNER_NONE || !st->IsInUse()) ? COLOUR_GREY : _company_colours[st->owner]);
1414 if (str == nullptr) continue;
1415
1416 if (Station::IsExpected(st)) { /* Station */
1417 *str = GetString(small ? STR_STATION_NAME : STR_VIEWPORT_STATION, st->index, st->facilities);
1418 } else { /* Waypoint */
1419 *str = GetString(STR_WAYPOINT_NAME, st->index);
1420 }
1421 }
1422}
1423
1424static void ViewportAddKdtreeSigns(DrawPixelInfo *dpi)
1425{
1426 Rect search_rect{ dpi->left, dpi->top, dpi->left + dpi->width, dpi->top + dpi->height };
1427 search_rect = ExpandRectWithViewportSignMargins(search_rect, dpi->zoom);
1428
1429 bool show_stations = HasBit(_display_opt, DO_SHOW_STATION_NAMES) && _game_mode != GM_MENU;
1430 bool show_waypoints = HasBit(_display_opt, DO_SHOW_WAYPOINT_NAMES) && _game_mode != GM_MENU;
1431 bool show_towns = HasBit(_display_opt, DO_SHOW_TOWN_NAMES) && _game_mode != GM_MENU;
1433 bool show_competitors = HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS);
1434
1435 /* Collect all the items first and draw afterwards, to ensure layering */
1436 std::vector<const BaseStation *> stations;
1437 std::vector<const Town *> towns;
1438 std::vector<const Sign *> signs;
1439
1440 _viewport_sign_kdtree.FindContained(search_rect.left, search_rect.top, search_rect.right, search_rect.bottom, [&](const ViewportSignKdtreeItem & item) {
1441 switch (item.type) {
1442 case ViewportSignKdtreeItem::VKI_STATION: {
1443 if (!show_stations) break;
1444 const BaseStation *st = BaseStation::Get(std::get<StationID>(item.id));
1445
1446 /* If no facilities are present the station is a ghost station. */
1447 StationFacilities facilities = st->facilities;
1448 if (facilities == StationFacilities{}) facilities = STATION_FACILITY_GHOST;
1449
1450 if (!facilities.Any(_facility_display_opt)) break;
1451
1452 /* Don't draw if station is owned by another company and competitor station names are hidden. Stations owned by none are never ignored. */
1453 if (!show_competitors && _local_company != st->owner && st->owner != OWNER_NONE) break;
1454
1455 stations.push_back(st);
1456 break;
1457 }
1458
1459 case ViewportSignKdtreeItem::VKI_WAYPOINT: {
1460 if (!show_waypoints) break;
1461 const BaseStation *st = BaseStation::Get(std::get<StationID>(item.id));
1462
1463 /* Don't draw if station is owned by another company and competitor station names are hidden. Stations owned by none are never ignored. */
1464 if (!show_competitors && _local_company != st->owner && st->owner != OWNER_NONE) break;
1465
1466 stations.push_back(st);
1467 break;
1468 }
1469
1470 case ViewportSignKdtreeItem::VKI_TOWN:
1471 if (!show_towns) break;
1472 towns.push_back(Town::Get(std::get<TownID>(item.id)));
1473 break;
1474
1475 case ViewportSignKdtreeItem::VKI_SIGN: {
1476 if (!show_signs) break;
1477 const Sign *si = Sign::Get(std::get<SignID>(item.id));
1478
1479 /* Don't draw if sign is owned by another company and competitor signs should be hidden.
1480 * Note: It is intentional that also signs owned by OWNER_NONE are hidden. Bankrupt
1481 * companies can leave OWNER_NONE signs after them. */
1482 if (!show_competitors && _local_company != si->owner && si->owner != OWNER_DEITY) break;
1483
1484 signs.push_back(si);
1485 break;
1486 }
1487
1488 default:
1489 NOT_REACHED();
1490 }
1491 });
1492
1493 /* Small versions of signs are used zoom level 4X and higher. */
1494 bool small = dpi->zoom >= ZOOM_LVL_OUT_4X;
1495
1496 /* Layering order (bottom to top): Town names, signs, stations */
1497 ViewportAddTownStrings(dpi, towns, small);
1498
1499 /* Do not draw signs nor station names if they are set invisible */
1500 if (IsInvisibilitySet(TO_SIGNS)) return;
1501
1502 ViewportAddSignStrings(dpi, signs, small);
1503 ViewportAddStationStrings(dpi, stations, small);
1504}
1505
1506
1514void ViewportSign::UpdatePosition(int center, int top, std::string_view str, std::string_view str_small)
1515{
1516 if (this->width_normal != 0) this->MarkDirty();
1517
1518 this->top = top;
1519
1521 this->center = center;
1522
1523 /* zoomed out version */
1524 if (str_small.empty()) str_small = str;
1526
1527 this->MarkDirty();
1528}
1529
1537{
1538 Rect zoomlevels[ZOOM_LVL_END];
1539
1540 /* We don't know which size will be drawn, so mark the largest area dirty. */
1541 const uint half_width = std::max(this->width_normal, this->width_small) / 2 + 1;
1543
1544 for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) {
1545 zoomlevels[zoom].left = this->center - ScaleByZoom(half_width, zoom);
1546 zoomlevels[zoom].top = this->top - ScaleByZoom(1, zoom);
1547 zoomlevels[zoom].right = this->center + ScaleByZoom(half_width, zoom);
1548 zoomlevels[zoom].bottom = this->top + ScaleByZoom(height, zoom);
1549 }
1550
1551 for (const Window *w : Window::Iterate()) {
1552 Viewport *vp = w->viewport;
1553 if (vp != nullptr && vp->zoom <= maxzoom) {
1554 assert(vp->width != 0);
1555 Rect &zl = zoomlevels[vp->zoom];
1556 MarkViewportDirty(vp, zl.left, zl.top, zl.right, zl.bottom);
1557 }
1558 }
1559}
1560
1561static void ViewportDrawTileSprites(const TileSpriteToDrawVector *tstdv)
1562{
1563 for (const TileSpriteToDraw &ts : *tstdv) {
1564 DrawSpriteViewport(ts.image, ts.pal, ts.x, ts.y, ts.sub);
1565 }
1566}
1567
1570{
1571 return true;
1572}
1573
1575static void ViewportSortParentSprites(ParentSpriteToSortVector *psdv)
1576{
1577 if (psdv->size() < 2) return;
1578
1579 /* We rely on sprites being, for the most part, already ordered.
1580 * So we don't need to move many of them and can keep track of their
1581 * order efficiently by using stack. We always move sprites to the front
1582 * of the current position, i.e. to the top of the stack.
1583 * Also use special constants to indicate sorting state without
1584 * adding extra fields to ParentSpriteToDraw structure.
1585 */
1586 const uint32_t ORDER_COMPARED = UINT32_MAX; // Sprite was compared but we still need to compare the ones preceding it
1587 const uint32_t ORDER_RETURNED = UINT32_MAX - 1; // Makr sorted sprite in case there are other occurrences of it in the stack
1588 std::stack<ParentSpriteToDraw *> sprite_order;
1589 uint32_t next_order = 0;
1590
1591 std::forward_list<std::pair<int64_t, ParentSpriteToDraw *>> sprite_list; // We store sprites in a list sorted by xmin+ymin
1592
1593 /* Initialize sprite list and order. */
1594 for (auto p = psdv->rbegin(); p != psdv->rend(); p++) {
1595 sprite_list.emplace_front((*p)->xmin + (*p)->ymin, *p);
1596 sprite_order.push(*p);
1597 (*p)->order = next_order++;
1598 }
1599
1600 sprite_list.sort();
1601
1602 std::vector<ParentSpriteToDraw *> preceding; // Temporarily stores sprites that precede current and their position in the list
1603 auto preceding_prev = sprite_list.begin(); // Store iterator in case we need to delete a single preceding sprite
1604 auto out = psdv->begin(); // Iterator to output sorted sprites
1605
1606 while (!sprite_order.empty()) {
1607
1608 auto s = sprite_order.top();
1609 sprite_order.pop();
1610
1611 /* Sprite is already sorted, ignore it. */
1612 if (s->order == ORDER_RETURNED) continue;
1613
1614 /* Sprite was already compared, just need to output it. */
1615 if (s->order == ORDER_COMPARED) {
1616 *(out++) = s;
1617 s->order = ORDER_RETURNED;
1618 continue;
1619 }
1620
1621 preceding.clear();
1622
1623 /* We only need sprites with xmin <= s->xmax && ymin <= s->ymax && zmin <= s->zmax
1624 * So by iterating sprites with xmin + ymin <= s->xmax + s->ymax
1625 * we get all we need and some more that we filter out later.
1626 * We don't include zmin into the sum as there are usually more neighbours on x and y than z
1627 * so including it will actually increase the amount of false positives.
1628 * Also min coordinates can be > max so using max(xmin, xmax) + max(ymin, ymax)
1629 * to ensure that we iterate the current sprite as we need to remove it from the list.
1630 */
1631 auto ssum = std::max(s->xmax, s->xmin) + std::max(s->ymax, s->ymin);
1632 auto prev = sprite_list.before_begin();
1633 auto x = sprite_list.begin();
1634 while (x != sprite_list.end() && ((*x).first <= ssum)) {
1635 auto p = (*x).second;
1636 if (p == s) {
1637 /* We found the current sprite, remove it and move on. */
1638 x = sprite_list.erase_after(prev);
1639 continue;
1640 }
1641
1642 auto p_prev = prev;
1643 prev = x++;
1644
1645 if (s->xmax < p->xmin || s->ymax < p->ymin || s->zmax < p->zmin) continue;
1646 if (s->xmin <= p->xmax && // overlap in X?
1647 s->ymin <= p->ymax && // overlap in Y?
1648 s->zmin <= p->zmax) { // overlap in Z?
1649 if (s->xmin + s->xmax + s->ymin + s->ymax + s->zmin + s->zmax <=
1650 p->xmin + p->xmax + p->ymin + p->ymax + p->zmin + p->zmax) {
1651 continue;
1652 }
1653 }
1654 preceding.push_back(p);
1655 preceding_prev = p_prev;
1656 }
1657
1658 if (preceding.empty()) {
1659 /* No preceding sprites, add current one to the output */
1660 *(out++) = s;
1661 s->order = ORDER_RETURNED;
1662 continue;
1663 }
1664
1665 /* Optimization for the case when we only have 1 sprite to move. */
1666 if (preceding.size() == 1) {
1667 auto p = preceding[0];
1668 /* We can only output the preceding sprite if there can't be any other sprites preceding it. */
1669 if (p->xmax <= s->xmax && p->ymax <= s->ymax && p->zmax <= s->zmax) {
1670 p->order = ORDER_RETURNED;
1671 s->order = ORDER_RETURNED;
1672 sprite_list.erase_after(preceding_prev);
1673 *(out++) = p;
1674 *(out++) = s;
1675 continue;
1676 }
1677 }
1678
1679 /* Sort all preceding sprites by order and assign new orders in reverse (as original sorter did). */
1680 std::sort(preceding.begin(), preceding.end(), [](const ParentSpriteToDraw *a, const ParentSpriteToDraw *b) {
1681 return a->order > b->order;
1682 });
1683
1684 s->order = ORDER_COMPARED;
1685 sprite_order.push(s); // Still need to output so push it back for now
1686
1687 for (auto p: preceding) {
1688 p->order = next_order++;
1689 sprite_order.push(p);
1690 }
1691 }
1692}
1693
1694
1695static void ViewportDrawParentSprites(const ParentSpriteToSortVector *psd, const ChildScreenSpriteToDrawVector *csstdv)
1696{
1697 for (const ParentSpriteToDraw *ps : *psd) {
1698 if (ps->image != SPR_EMPTY_BOUNDING_BOX) DrawSpriteViewport(ps->image, ps->pal, ps->x, ps->y, ps->sub);
1699
1700 int child_idx = ps->first_child;
1701 while (child_idx >= 0) {
1702 const ChildScreenSpriteToDraw *cs = &(*csstdv)[child_idx];
1703 child_idx = cs->next;
1704 if (cs->relative) {
1705 DrawSpriteViewport(cs->image, cs->pal, ps->left + cs->x, ps->top + cs->y, cs->sub);
1706 } else {
1707 DrawSpriteViewport(cs->image, cs->pal, ps->x + cs->x, ps->y + cs->y, cs->sub);
1708 }
1709 }
1710 }
1711}
1712
1717static void ViewportDrawBoundingBoxes(const ParentSpriteToSortVector *psd)
1718{
1719 for (const ParentSpriteToDraw *ps : *psd) {
1720 Point pt1 = RemapCoords(ps->xmax + 1, ps->ymax + 1, ps->zmax + 1); // top front corner
1721 Point pt2 = RemapCoords(ps->xmin , ps->ymax + 1, ps->zmax + 1); // top left corner
1722 Point pt3 = RemapCoords(ps->xmax + 1, ps->ymin , ps->zmax + 1); // top right corner
1723 Point pt4 = RemapCoords(ps->xmax + 1, ps->ymax + 1, ps->zmin ); // bottom front corner
1724
1725 DrawBox( pt1.x, pt1.y,
1726 pt2.x - pt1.x, pt2.y - pt1.y,
1727 pt3.x - pt1.x, pt3.y - pt1.y,
1728 pt4.x - pt1.x, pt4.y - pt1.y);
1729 }
1730}
1731
1736{
1738 const DrawPixelInfo *dpi = _cur_dpi;
1739 void *dst;
1740 int right = UnScaleByZoom(dpi->width, dpi->zoom);
1741 int bottom = UnScaleByZoom(dpi->height, dpi->zoom);
1742
1743 int colour = _string_colourmap[_dirty_block_colour & 0xF];
1744
1745 dst = dpi->dst_ptr;
1746
1747 uint8_t bo = UnScaleByZoom(dpi->left + dpi->top, dpi->zoom) & 1;
1748 do {
1749 for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, (uint8_t)colour);
1750 dst = blitter->MoveTo(dst, 0, 1);
1751 } while (--bottom > 0);
1752}
1753
1754static void ViewportDrawStrings(ZoomLevel zoom, const StringSpriteToDrawVector *sstdv)
1755{
1756 for (const StringSpriteToDraw &ss : *sstdv) {
1757 bool small = ss.flags.Test(ViewportStringFlag::Small);
1758 int w = ss.width;
1759 int x = UnScaleByZoom(ss.x, zoom);
1760 int y = UnScaleByZoom(ss.y, zoom);
1761 int h = WidgetDimensions::scaled.fullbevel.top + GetCharacterHeight(small ? FS_SMALL : FS_NORMAL) + WidgetDimensions::scaled.fullbevel.bottom;
1762
1763 TextColour colour = TC_WHITE;
1764 if (ss.flags.Test(ViewportStringFlag::ColourRect)) {
1765 if (ss.colour != INVALID_COLOUR) DrawFrameRect(x, y, x + w - 1, y + h - 1, ss.colour, {});
1766 colour = TC_BLACK;
1767 } else if (ss.flags.Test(ViewportStringFlag::TransparentRect)) {
1768 DrawFrameRect(x, y, x + w - 1, y + h - 1, ss.colour, FrameFlag::Transparent);
1769 }
1770
1771 if (ss.flags.Test(ViewportStringFlag::TextColour)) {
1772 if (ss.colour != INVALID_COLOUR) colour = static_cast<TextColour>(GetColourGradient(ss.colour, SHADE_LIGHTER) | TC_IS_PALETTE_COLOUR);
1773 }
1774
1775 int left = x + WidgetDimensions::scaled.fullbevel.left;
1776 int right = x + w - 1 - WidgetDimensions::scaled.fullbevel.right;
1777 int top = y + WidgetDimensions::scaled.fullbevel.top;
1778
1779 int shadow_offset = 0;
1780 if (small && ss.flags.Test(ViewportStringFlag::Shadow)) {
1781 /* Shadow needs to be shifted 1 pixel. */
1782 shadow_offset = WidgetDimensions::scaled.fullbevel.top;
1783 DrawString(left + shadow_offset, right + shadow_offset, top, ss.string, TC_BLACK, SA_HOR_CENTER, false, FS_SMALL);
1784 }
1785
1786 DrawString(left, right, top - shadow_offset, ss.string, colour, SA_HOR_CENTER, false, small ? FS_SMALL : FS_NORMAL);
1787 }
1788}
1789
1790void ViewportDoDraw(const Viewport *vp, int left, int top, int right, int bottom)
1791{
1792 _vd.dpi.zoom = vp->zoom;
1793 int mask = ScaleByZoom(-1, vp->zoom);
1794
1796
1797 _vd.dpi.width = (right - left) & mask;
1798 _vd.dpi.height = (bottom - top) & mask;
1799 _vd.dpi.left = left & mask;
1800 _vd.dpi.top = top & mask;
1801 _vd.dpi.pitch = _cur_dpi->pitch;
1802 _vd.last_child = LAST_CHILD_NONE;
1803
1804 int x = UnScaleByZoom(_vd.dpi.left - (vp->virtual_left & mask), vp->zoom) + vp->left;
1805 int y = UnScaleByZoom(_vd.dpi.top - (vp->virtual_top & mask), vp->zoom) + vp->top;
1806
1807 _vd.dpi.dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_cur_dpi->dst_ptr, x - _cur_dpi->left, y - _cur_dpi->top);
1808 AutoRestoreBackup dpi_backup(_cur_dpi, &_vd.dpi);
1809
1811 ViewportAddVehicles(&_vd.dpi);
1812
1813 ViewportAddKdtreeSigns(&_vd.dpi);
1814
1815 DrawTextEffects(&_vd.dpi);
1816
1817 if (!_vd.tile_sprites_to_draw.empty()) ViewportDrawTileSprites(&_vd.tile_sprites_to_draw);
1818
1819 for (auto &psd : _vd.parent_sprites_to_draw) {
1820 _vd.parent_sprites_to_sort.push_back(&psd);
1821 }
1822
1823 _vp_sprite_sorter(&_vd.parent_sprites_to_sort);
1824 ViewportDrawParentSprites(&_vd.parent_sprites_to_sort, &_vd.child_screen_sprites_to_draw);
1825
1826 if (_draw_bounding_boxes) ViewportDrawBoundingBoxes(&_vd.parent_sprites_to_sort);
1827 if (_draw_dirty_blocks) ViewportDrawDirtyBlocks();
1828
1829 DrawPixelInfo dp = _vd.dpi;
1830 ZoomLevel zoom = _vd.dpi.zoom;
1831 dp.zoom = ZOOM_LVL_MIN;
1832 dp.width = UnScaleByZoom(dp.width, zoom);
1833 dp.height = UnScaleByZoom(dp.height, zoom);
1834 AutoRestoreBackup cur_dpi(_cur_dpi, &dp);
1835
1836 if (vp->overlay != nullptr && vp->overlay->GetCargoMask() != 0 && vp->overlay->GetCompanyMask().Any()) {
1837 /* translate to window coordinates */
1838 dp.left = x;
1839 dp.top = y;
1840 vp->overlay->Draw(&dp);
1841 }
1842
1843 if (!_vd.string_sprites_to_draw.empty()) {
1844 /* translate to world coordinates */
1845 dp.left = UnScaleByZoom(_vd.dpi.left, zoom);
1846 dp.top = UnScaleByZoom(_vd.dpi.top, zoom);
1847 ViewportDrawStrings(zoom, &_vd.string_sprites_to_draw);
1848 }
1849
1850 _vd.string_sprites_to_draw.clear();
1851 _vd.tile_sprites_to_draw.clear();
1852 _vd.parent_sprites_to_draw.clear();
1853 _vd.parent_sprites_to_sort.clear();
1854 _vd.child_screen_sprites_to_draw.clear();
1855}
1856
1857static inline void ViewportDraw(const Viewport *vp, int left, int top, int right, int bottom)
1858{
1859 if (right <= vp->left || bottom <= vp->top) return;
1860
1861 if (left >= vp->left + vp->width) return;
1862
1863 if (left < vp->left) left = vp->left;
1864 if (right > vp->left + vp->width) right = vp->left + vp->width;
1865
1866 if (top >= vp->top + vp->height) return;
1867
1868 if (top < vp->top) top = vp->top;
1869 if (bottom > vp->top + vp->height) bottom = vp->top + vp->height;
1870
1871 ViewportDoDraw(vp,
1872 ScaleByZoom(left - vp->left, vp->zoom) + vp->virtual_left,
1873 ScaleByZoom(top - vp->top, vp->zoom) + vp->virtual_top,
1874 ScaleByZoom(right - vp->left, vp->zoom) + vp->virtual_left,
1875 ScaleByZoom(bottom - vp->top, vp->zoom) + vp->virtual_top
1876 );
1877}
1878
1883{
1885
1886 DrawPixelInfo *dpi = _cur_dpi;
1887
1888 dpi->left += this->left;
1889 dpi->top += this->top;
1890
1891 ViewportDraw(this->viewport, dpi->left, dpi->top, dpi->left + dpi->width, dpi->top + dpi->height);
1892
1893 dpi->left -= this->left;
1894 dpi->top -= this->top;
1895}
1896
1907static inline void ClampViewportToMap(const Viewport *vp, int *scroll_x, int *scroll_y)
1908{
1909 /* Centre of the viewport is hot spot. */
1910 Point pt = {
1911 *scroll_x + vp->virtual_width / 2,
1912 *scroll_y + vp->virtual_height / 2
1913 };
1914
1915 /* Find nearest tile that is within borders of the map. */
1916 bool clamped;
1917 pt = InverseRemapCoords2(pt.x, pt.y, true, &clamped);
1918
1919 if (clamped) {
1920 /* Convert back to viewport coordinates and remove centering. */
1921 pt = RemapCoords2(pt.x, pt.y);
1922 *scroll_x = pt.x - vp->virtual_width / 2;
1923 *scroll_y = pt.y - vp->virtual_height / 2;
1924 }
1925}
1926
1939static void ClampSmoothScroll(uint32_t delta_ms, int64_t delta_hi, int64_t delta_lo, int &delta_hi_clamped, int &delta_lo_clamped)
1940{
1942 constexpr int PIXELS_PER_TILE = TILE_PIXELS * 2 * ZOOM_BASE;
1943
1944 assert(delta_hi != 0);
1945
1946 /* Move at most 75% of the distance every 30ms, for a smooth experience */
1947 int64_t delta_left = delta_hi * std::pow(0.75, delta_ms / 30.0);
1948 /* Move never more than 16 tiles per 30ms. */
1949 int max_scroll = Map::ScaleBySize1D(16 * PIXELS_PER_TILE * delta_ms / 30);
1950
1951 /* We never go over the max_scroll speed. */
1952 delta_hi_clamped = Clamp(delta_hi - delta_left, -max_scroll, max_scroll);
1953 /* The lower delta is in ratio of the higher delta, so we keep going straight at the destination. */
1954 delta_lo_clamped = delta_lo * delta_hi_clamped / delta_hi;
1955
1956 /* Ensure we always move (delta_hi can't be zero). */
1957 if (delta_hi_clamped == 0) {
1958 delta_hi_clamped = delta_hi > 0 ? 1 : -1;
1959 }
1960}
1961
1967void UpdateViewportPosition(Window *w, uint32_t delta_ms)
1968{
1969 const Viewport *vp = w->viewport;
1970
1971 if (w->viewport->follow_vehicle != VehicleID::Invalid()) {
1972 const Vehicle *veh = Vehicle::Get(w->viewport->follow_vehicle);
1973 Point pt = MapXYZToViewport(vp, veh->x_pos, veh->y_pos, veh->z_pos);
1974
1975 w->viewport->scrollpos_x = pt.x;
1976 w->viewport->scrollpos_y = pt.y;
1977 SetViewportPosition(w, pt.x, pt.y);
1978 } else {
1979 /* Ensure the destination location is within the map */
1981
1982 int delta_x = w->viewport->dest_scrollpos_x - w->viewport->scrollpos_x;
1983 int delta_y = w->viewport->dest_scrollpos_y - w->viewport->scrollpos_y;
1984
1985 int current_x = w->viewport->scrollpos_x;
1986 int current_y = w->viewport->scrollpos_y;
1987
1988 bool update_overlay = false;
1989 if (delta_x != 0 || delta_y != 0) {
1991 int delta_x_clamped;
1992 int delta_y_clamped;
1993
1994 if (abs(delta_x) > abs(delta_y)) {
1995 ClampSmoothScroll(delta_ms, delta_x, delta_y, delta_x_clamped, delta_y_clamped);
1996 } else {
1997 ClampSmoothScroll(delta_ms, delta_y, delta_x, delta_y_clamped, delta_x_clamped);
1998 }
1999
2000 w->viewport->scrollpos_x += delta_x_clamped;
2001 w->viewport->scrollpos_y += delta_y_clamped;
2002 } else {
2005 }
2006 update_overlay = (w->viewport->scrollpos_x == w->viewport->dest_scrollpos_x &&
2008 }
2009
2011
2012 /* When moving small amounts around the border we can get stuck, and
2013 * not actually move. In those cases, teleport to the destination. */
2014 if ((delta_x != 0 || delta_y != 0) && current_x == w->viewport->scrollpos_x && current_y == w->viewport->scrollpos_y) {
2017 }
2018
2019 SetViewportPosition(w, w->viewport->scrollpos_x, w->viewport->scrollpos_y);
2020 if (update_overlay) RebuildViewportOverlay(w);
2021 }
2022}
2023
2034static bool MarkViewportDirty(const Viewport *vp, int left, int top, int right, int bottom)
2035{
2036 /* Rounding wrt. zoom-out level */
2037 right += (1 << vp->zoom) - 1;
2038 bottom += (1 << vp->zoom) - 1;
2039
2040 right -= vp->virtual_left;
2041 if (right <= 0) return false;
2042
2043 bottom -= vp->virtual_top;
2044 if (bottom <= 0) return false;
2045
2046 left = std::max(0, left - vp->virtual_left);
2047
2048 if (left >= vp->virtual_width) return false;
2049
2050 top = std::max(0, top - vp->virtual_top);
2051
2052 if (top >= vp->virtual_height) return false;
2053
2055 UnScaleByZoomLower(left, vp->zoom) + vp->left,
2056 UnScaleByZoomLower(top, vp->zoom) + vp->top,
2057 UnScaleByZoom(right, vp->zoom) + vp->left + 1,
2058 UnScaleByZoom(bottom, vp->zoom) + vp->top + 1
2059 );
2060
2061 return true;
2062}
2063
2073bool MarkAllViewportsDirty(int left, int top, int right, int bottom)
2074{
2075 bool dirty = false;
2076
2077 for (const Window *w : Window::Iterate()) {
2078 Viewport *vp = w->viewport;
2079 if (vp != nullptr) {
2080 assert(vp->width != 0);
2081 if (MarkViewportDirty(vp, left, top, right, bottom)) dirty = true;
2082 }
2083 }
2084
2085 return dirty;
2086}
2087
2088void ConstrainAllViewportsZoom()
2089{
2090 for (Window *w : Window::Iterate()) {
2091 if (w->viewport == nullptr) continue;
2092
2093 ZoomLevel zoom = static_cast<ZoomLevel>(Clamp(w->viewport->zoom, _settings_client.gui.zoom_min, _settings_client.gui.zoom_max));
2094 if (zoom != w->viewport->zoom) {
2095 while (w->viewport->zoom < zoom) DoZoomInOutWindow(ZOOM_OUT, w);
2096 while (w->viewport->zoom > zoom) DoZoomInOutWindow(ZOOM_IN, w);
2097 }
2098 }
2099}
2100
2108void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
2109{
2110 Point pt = RemapCoords(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE, tile_height_override * TILE_HEIGHT);
2112 pt.x - MAX_TILE_EXTENT_LEFT,
2113 pt.y - MAX_TILE_EXTENT_TOP - ZOOM_BASE * TILE_HEIGHT * bridge_level_offset,
2114 pt.x + MAX_TILE_EXTENT_RIGHT,
2115 pt.y + MAX_TILE_EXTENT_BOTTOM);
2116}
2117
2126{
2127 int x_size = _thd.size.x;
2128 int y_size = _thd.size.y;
2129
2130 if (!_thd.diagonal) { // Selecting in a straight rectangle (or a single square)
2131 int x_start = _thd.pos.x;
2132 int y_start = _thd.pos.y;
2133
2134 if (_thd.outersize.x != 0) {
2135 x_size += _thd.outersize.x;
2136 x_start += _thd.offs.x;
2137 y_size += _thd.outersize.y;
2138 y_start += _thd.offs.y;
2139 }
2140
2141 x_size -= TILE_SIZE;
2142 y_size -= TILE_SIZE;
2143
2144 assert(x_size >= 0);
2145 assert(y_size >= 0);
2146
2147 int x_end = Clamp(x_start + x_size, 0, Map::SizeX() * TILE_SIZE - TILE_SIZE);
2148 int y_end = Clamp(y_start + y_size, 0, Map::SizeY() * TILE_SIZE - TILE_SIZE);
2149
2150 x_start = Clamp(x_start, 0, Map::SizeX() * TILE_SIZE - TILE_SIZE);
2151 y_start = Clamp(y_start, 0, Map::SizeY() * TILE_SIZE - TILE_SIZE);
2152
2153 /* make sure everything is multiple of TILE_SIZE */
2154 assert((x_end | y_end | x_start | y_start) % TILE_SIZE == 0);
2155
2156 /* How it works:
2157 * Suppose we have to mark dirty rectangle of 3x4 tiles:
2158 * x
2159 * xxx
2160 * xxxxx
2161 * xxxxx
2162 * xxx
2163 * x
2164 * This algorithm marks dirty columns of tiles, so it is done in 3+4-1 steps:
2165 * 1) x 2) x
2166 * xxx Oxx
2167 * Oxxxx xOxxx
2168 * xxxxx Oxxxx
2169 * xxx xxx
2170 * x x
2171 * And so forth...
2172 */
2173
2174 int top_x = x_end; // coordinates of top dirty tile
2175 int top_y = y_start;
2176 int bot_x = top_x; // coordinates of bottom dirty tile
2177 int bot_y = top_y;
2178
2179 do {
2180 /* topmost dirty point */
2181 TileIndex top_tile = TileVirtXY(top_x, top_y);
2182 Point top = RemapCoords(top_x, top_y, GetTileMaxPixelZ(top_tile));
2183
2184 /* bottommost point */
2185 TileIndex bottom_tile = TileVirtXY(bot_x, bot_y);
2186 Point bot = RemapCoords(bot_x + TILE_SIZE, bot_y + TILE_SIZE, GetTilePixelZ(bottom_tile)); // bottommost point
2187
2188 /* the 'x' coordinate of 'top' and 'bot' is the same (and always in the same distance from tile middle),
2189 * tile height/slope affects only the 'y' on-screen coordinate! */
2190
2191 int l = top.x - TILE_PIXELS * ZOOM_BASE; // 'x' coordinate of left side of the dirty rectangle
2192 int t = top.y; // 'y' coordinate of top side of the dirty rectangle
2193 int r = top.x + TILE_PIXELS * ZOOM_BASE; // 'x' coordinate of right side of the dirty rectangle
2194 int b = bot.y; // 'y' coordinate of bottom side of the dirty rectangle
2195
2196 static const int OVERLAY_WIDTH = 4 * ZOOM_BASE; // part of selection sprites is drawn outside the selected area (in particular: terraforming)
2197
2198 /* For halftile foundations on SLOPE_STEEP_S the sprite extents some more towards the top */
2199 MarkAllViewportsDirty(l - OVERLAY_WIDTH, t - OVERLAY_WIDTH - TILE_HEIGHT * ZOOM_BASE, r + OVERLAY_WIDTH, b + OVERLAY_WIDTH);
2200
2201 /* haven't we reached the topmost tile yet? */
2202 if (top_x != x_start) {
2203 top_x -= TILE_SIZE;
2204 } else {
2205 top_y += TILE_SIZE;
2206 }
2207
2208 /* the way the bottom tile changes is different when we reach the bottommost tile */
2209 if (bot_y != y_end) {
2210 bot_y += TILE_SIZE;
2211 } else {
2212 bot_x -= TILE_SIZE;
2213 }
2214 } while (bot_x >= top_x);
2215 } else { // Selecting in a 45 degrees rotated (diagonal) rectangle.
2216 /* a_size, b_size describe a rectangle with rotated coordinates */
2217 int a_size = x_size + y_size, b_size = x_size - y_size;
2218
2219 int interval_a = a_size < 0 ? -(int)TILE_SIZE : (int)TILE_SIZE;
2220 int interval_b = b_size < 0 ? -(int)TILE_SIZE : (int)TILE_SIZE;
2221
2222 for (int a = -interval_a; a != a_size + interval_a; a += interval_a) {
2223 for (int b = -interval_b; b != b_size + interval_b; b += interval_b) {
2224 uint x = (_thd.pos.x + (a + b) / 2) / TILE_SIZE;
2225 uint y = (_thd.pos.y + (a - b) / 2) / TILE_SIZE;
2226
2227 if (x < Map::MaxX() && y < Map::MaxY()) {
2229 }
2230 }
2231 }
2232 }
2233}
2234
2235
2236void SetSelectionRed(bool b)
2237{
2238 _thd.make_square_red = b;
2240}
2241
2250static bool CheckClickOnViewportSign(const Viewport *vp, int x, int y, const ViewportSign *sign)
2251{
2252 bool small = (vp->zoom >= ZOOM_LVL_OUT_4X);
2253 int sign_half_width = ScaleByZoom((small ? sign->width_small : sign->width_normal) / 2, vp->zoom);
2254 int sign_height = ScaleByZoom(WidgetDimensions::scaled.fullbevel.top + GetCharacterHeight(small ? FS_SMALL : FS_NORMAL) + WidgetDimensions::scaled.fullbevel.bottom, vp->zoom);
2255
2256 return y >= sign->top && y < sign->top + sign_height &&
2257 x >= sign->center - sign_half_width && x < sign->center + sign_half_width;
2258}
2259
2260
2268static bool CheckClickOnViewportSign(const Viewport *vp, int x, int y)
2269{
2270 if (_game_mode == GM_MENU) return false;
2271
2272 x = ScaleByZoom(x - vp->left, vp->zoom) + vp->virtual_left;
2273 y = ScaleByZoom(y - vp->top, vp->zoom) + vp->virtual_top;
2274
2275 Rect search_rect{ x - 1, y - 1, x + 1, y + 1 };
2276 search_rect = ExpandRectWithViewportSignMargins(search_rect, vp->zoom);
2277
2280 bool show_towns = HasBit(_display_opt, DO_SHOW_TOWN_NAMES);
2282 bool show_competitors = HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS);
2283
2284 /* Topmost of each type that was hit */
2285 BaseStation *st = nullptr, *last_st = nullptr;
2286 Town *t = nullptr, *last_t = nullptr;
2287 Sign *si = nullptr, *last_si = nullptr;
2288
2289 /* See ViewportAddKdtreeSigns() for details on the search logic */
2290 _viewport_sign_kdtree.FindContained(search_rect.left, search_rect.top, search_rect.right, search_rect.bottom, [&](const ViewportSignKdtreeItem & item) {
2291 switch (item.type) {
2292 case ViewportSignKdtreeItem::VKI_STATION:
2293 if (!show_stations) break;
2294 st = BaseStation::Get(std::get<StationID>(item.id));
2295 if (!show_competitors && _local_company != st->owner && st->owner != OWNER_NONE) break;
2296 if (CheckClickOnViewportSign(vp, x, y, &st->sign)) last_st = st;
2297 break;
2298
2299 case ViewportSignKdtreeItem::VKI_WAYPOINT:
2300 if (!show_waypoints) break;
2301 st = BaseStation::Get(std::get<StationID>(item.id));
2302 if (!show_competitors && _local_company != st->owner && st->owner != OWNER_NONE) break;
2303 if (CheckClickOnViewportSign(vp, x, y, &st->sign)) last_st = st;
2304 break;
2305
2306 case ViewportSignKdtreeItem::VKI_TOWN:
2307 if (!show_towns) break;
2308 t = Town::Get(std::get<TownID>(item.id));
2309 if (CheckClickOnViewportSign(vp, x, y, &t->cache.sign)) last_t = t;
2310 break;
2311
2312 case ViewportSignKdtreeItem::VKI_SIGN:
2313 if (!show_signs) break;
2314 si = Sign::Get(std::get<SignID>(item.id));
2315 if (!show_competitors && _local_company != si->owner && si->owner != OWNER_DEITY) break;
2316 if (CheckClickOnViewportSign(vp, x, y, &si->sign)) last_si = si;
2317 break;
2318
2319 default:
2320 NOT_REACHED();
2321 }
2322 });
2323
2324 /* Select which hit to handle based on priority */
2325 if (last_st != nullptr) {
2326 if (Station::IsExpected(last_st)) {
2327 ShowStationViewWindow(last_st->index);
2328 } else {
2330 }
2331 return true;
2332 } else if (last_t != nullptr) {
2333 ShowTownViewWindow(last_t->index);
2334 return true;
2335 } else if (last_si != nullptr) {
2336 HandleClickOnSign(last_si);
2337 return true;
2338 } else {
2339 return false;
2340 }
2341}
2342
2343
2344ViewportSignKdtreeItem ViewportSignKdtreeItem::MakeStation(StationID id)
2345{
2347 item.type = VKI_STATION;
2348 item.id = id;
2349
2350 const Station *st = Station::Get(id);
2351 assert(st->sign.kdtree_valid);
2352 item.center = st->sign.center;
2353 item.top = st->sign.top;
2354
2355 /* Assume the sign can be a candidate for drawing, so measure its width */
2356 _viewport_sign_maxwidth = std::max<int>({_viewport_sign_maxwidth, st->sign.width_normal, st->sign.width_small});
2357
2358 return item;
2359}
2360
2361ViewportSignKdtreeItem ViewportSignKdtreeItem::MakeWaypoint(StationID id)
2362{
2364 item.type = VKI_WAYPOINT;
2365 item.id = id;
2366
2367 const Waypoint *st = Waypoint::Get(id);
2368 assert(st->sign.kdtree_valid);
2369 item.center = st->sign.center;
2370 item.top = st->sign.top;
2371
2372 /* Assume the sign can be a candidate for drawing, so measure its width */
2373 _viewport_sign_maxwidth = std::max<int>({_viewport_sign_maxwidth, st->sign.width_normal, st->sign.width_small});
2374
2375 return item;
2376}
2377
2378ViewportSignKdtreeItem ViewportSignKdtreeItem::MakeTown(TownID id)
2379{
2381 item.type = VKI_TOWN;
2382 item.id = id;
2383
2384 const Town *town = Town::Get(id);
2385 assert(town->cache.sign.kdtree_valid);
2386 item.center = town->cache.sign.center;
2387 item.top = town->cache.sign.top;
2388
2389 /* Assume the sign can be a candidate for drawing, so measure its width */
2390 _viewport_sign_maxwidth = std::max<int>({_viewport_sign_maxwidth, town->cache.sign.width_normal, town->cache.sign.width_small});
2391
2392 return item;
2393}
2394
2395ViewportSignKdtreeItem ViewportSignKdtreeItem::MakeSign(SignID id)
2396{
2398 item.type = VKI_SIGN;
2399 item.id = id;
2400
2401 const Sign *sign = Sign::Get(id);
2402 assert(sign->sign.kdtree_valid);
2403 item.center = sign->sign.center;
2404 item.top = sign->sign.top;
2405
2406 /* Assume the sign can be a candidate for drawing, so measure its width */
2407 _viewport_sign_maxwidth = std::max<int>({_viewport_sign_maxwidth, sign->sign.width_normal, sign->sign.width_small});
2408
2409 return item;
2410}
2411
2412void RebuildViewportKdtree()
2413{
2414 /* Reset biggest size sign seen */
2415 _viewport_sign_maxwidth = 0;
2416
2417 std::vector<ViewportSignKdtreeItem> items;
2419
2420 for (const Station *st : Station::Iterate()) {
2421 if (st->sign.kdtree_valid) items.push_back(ViewportSignKdtreeItem::MakeStation(st->index));
2422 }
2423
2424 for (const Waypoint *wp : Waypoint::Iterate()) {
2425 if (wp->sign.kdtree_valid) items.push_back(ViewportSignKdtreeItem::MakeWaypoint(wp->index));
2426 }
2427
2428 for (const Town *town : Town::Iterate()) {
2429 if (town->cache.sign.kdtree_valid) items.push_back(ViewportSignKdtreeItem::MakeTown(town->index));
2430 }
2431
2432 for (const Sign *sign : Sign::Iterate()) {
2433 if (sign->sign.kdtree_valid) items.push_back(ViewportSignKdtreeItem::MakeSign(sign->index));
2434 }
2435
2436 _viewport_sign_kdtree.Build(items.begin(), items.end());
2437}
2438
2439
2440static bool CheckClickOnLandscape(const Viewport *vp, int x, int y)
2441{
2442 Point pt = TranslateXYToTileCoord(vp, x, y);
2443
2444 if (pt.x != -1) return ClickTile(TileVirtXY(pt.x, pt.y));
2445 return true;
2446}
2447
2448static void PlaceObject()
2449{
2450 Point pt;
2451 Window *w;
2452
2453 pt = GetTileBelowCursor();
2454 if (pt.x == -1) return;
2455
2456 if ((_thd.place_mode & HT_DRAG_MASK) == HT_POINT) {
2457 pt.x += TILE_SIZE / 2;
2458 pt.y += TILE_SIZE / 2;
2459 }
2460
2461 _tile_fract_coords.x = pt.x & TILE_UNIT_MASK;
2462 _tile_fract_coords.y = pt.y & TILE_UNIT_MASK;
2463
2464 w = _thd.GetCallbackWnd();
2465 if (w != nullptr) w->OnPlaceObject(pt, TileVirtXY(pt.x, pt.y));
2466}
2467
2468
2469bool HandleViewportClicked(const Viewport *vp, int x, int y)
2470{
2471 const Vehicle *v = CheckClickOnVehicle(vp, x, y);
2472
2473 if (_thd.place_mode & HT_VEHICLE) {
2474 if (v != nullptr && VehicleClicked(v)) return true;
2475 }
2476
2477 /* Vehicle placement mode already handled above. */
2478 if ((_thd.place_mode & HT_DRAG_MASK) != HT_NONE) {
2479 PlaceObject();
2480 return true;
2481 }
2482
2483 if (CheckClickOnViewportSign(vp, x, y)) return true;
2484 bool result = CheckClickOnLandscape(vp, x, y);
2485
2486 if (v != nullptr) {
2487 Debug(misc, 2, "Vehicle {} (index {}) at {}", v->unitnumber, v->index, fmt::ptr(v));
2489 v = v->First();
2490 if (_ctrl_pressed && v->owner == _local_company) {
2491 StartStopVehicle(v, true);
2492 } else {
2494 }
2495 }
2496 return true;
2497 }
2498 return result;
2499}
2500
2501void RebuildViewportOverlay(Window *w)
2502{
2503 if (w->viewport->overlay != nullptr &&
2504 w->viewport->overlay->GetCompanyMask().Any() &&
2505 w->viewport->overlay->GetCargoMask() != 0) {
2506 w->viewport->overlay->SetDirty();
2507 w->SetDirty();
2508 }
2509}
2510
2520bool ScrollWindowTo(int x, int y, int z, Window *w, bool instant)
2521{
2522 /* The slope cannot be acquired outside of the map, so make sure we are always within the map. */
2523 if (z == -1) {
2524 if ( x >= 0 && x <= (int)Map::SizeX() * (int)TILE_SIZE - 1
2525 && y >= 0 && y <= (int)Map::SizeY() * (int)TILE_SIZE - 1) {
2526 z = GetSlopePixelZ(x, y);
2527 } else {
2528 z = TileHeightOutsideMap(x / (int)TILE_SIZE, y / (int)TILE_SIZE);
2529 }
2530 }
2531
2532 Point pt = MapXYZToViewport(w->viewport, x, y, z);
2533 w->viewport->CancelFollow(*w);
2534
2535 if (w->viewport->dest_scrollpos_x == pt.x && w->viewport->dest_scrollpos_y == pt.y) return false;
2536
2537 if (instant) {
2538 w->viewport->scrollpos_x = pt.x;
2539 w->viewport->scrollpos_y = pt.y;
2540 RebuildViewportOverlay(w);
2541 }
2542
2543 w->viewport->dest_scrollpos_x = pt.x;
2544 w->viewport->dest_scrollpos_y = pt.y;
2545 return true;
2546}
2547
2555bool ScrollWindowToTile(TileIndex tile, Window *w, bool instant)
2556{
2557 return ScrollWindowTo(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE, -1, w, instant);
2558}
2559
2566bool ScrollMainWindowToTile(TileIndex tile, bool instant)
2567{
2568 return ScrollMainWindowTo(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2, -1, instant);
2569}
2570
2576{
2577 TileIndex old;
2578
2579 old = _thd.redsq;
2580 _thd.redsq = tile;
2581
2582 if (tile != old) {
2583 if (tile != INVALID_TILE) MarkTileDirtyByTile(tile);
2584 if (old != INVALID_TILE) MarkTileDirtyByTile(old);
2585 }
2586}
2587
2593void SetTileSelectSize(int w, int h)
2594{
2595 _thd.new_size.x = w * TILE_SIZE;
2596 _thd.new_size.y = h * TILE_SIZE;
2597 _thd.new_outersize.x = 0;
2598 _thd.new_outersize.y = 0;
2599}
2600
2601void SetTileSelectBigSize(int ox, int oy, int sx, int sy)
2602{
2603 _thd.offs.x = ox * TILE_SIZE;
2604 _thd.offs.y = oy * TILE_SIZE;
2605 _thd.new_outersize.x = sx * TILE_SIZE;
2606 _thd.new_outersize.y = sy * TILE_SIZE;
2607}
2608
2610static HighLightStyle GetAutorailHT(int x, int y)
2611{
2612 return HT_RAIL | _autorail_piece[x & TILE_UNIT_MASK][y & TILE_UNIT_MASK];
2613}
2614
2619{
2620 this->pos.x = 0;
2621 this->pos.y = 0;
2622 this->new_pos.x = 0;
2623 this->new_pos.y = 0;
2624}
2625
2634
2643
2644
2645
2654{
2655 int x1;
2656 int y1;
2657
2658 if (_thd.freeze) return;
2659
2660 HighLightStyle new_drawstyle = HT_NONE;
2661 bool new_diagonal = false;
2662
2663 if ((_thd.place_mode & HT_DRAG_MASK) == HT_SPECIAL) {
2664 x1 = _thd.selend.x;
2665 y1 = _thd.selend.y;
2666 if (x1 != -1) {
2667 int x2 = _thd.selstart.x & ~TILE_UNIT_MASK;
2668 int y2 = _thd.selstart.y & ~TILE_UNIT_MASK;
2669 x1 &= ~TILE_UNIT_MASK;
2670 y1 &= ~TILE_UNIT_MASK;
2671
2672 if (_thd.IsDraggingDiagonal()) {
2673 new_diagonal = true;
2674 } else {
2675 if (x1 >= x2) std::swap(x1, x2);
2676 if (y1 >= y2) std::swap(y1, y2);
2677 }
2678 _thd.new_pos.x = x1;
2679 _thd.new_pos.y = y1;
2680 _thd.new_size.x = x2 - x1;
2681 _thd.new_size.y = y2 - y1;
2682 if (!new_diagonal) {
2683 _thd.new_size.x += TILE_SIZE;
2684 _thd.new_size.y += TILE_SIZE;
2685 }
2686 new_drawstyle = _thd.next_drawstyle;
2687 }
2688 } else if ((_thd.place_mode & HT_DRAG_MASK) != HT_NONE) {
2689 Point pt = GetTileBelowCursor();
2690 x1 = pt.x;
2691 y1 = pt.y;
2692 if (x1 != -1) {
2693 switch (_thd.place_mode & HT_DRAG_MASK) {
2694 case HT_RECT:
2695 new_drawstyle = HT_RECT;
2696 break;
2697 case HT_POINT:
2698 new_drawstyle = HT_POINT;
2699 x1 += TILE_SIZE / 2;
2700 y1 += TILE_SIZE / 2;
2701 break;
2702 case HT_RAIL:
2703 /* Draw one highlighted tile in any direction */
2704 new_drawstyle = GetAutorailHT(pt.x, pt.y);
2705 break;
2706 case HT_LINE:
2707 switch (_thd.place_mode & HT_DIR_MASK) {
2708 case HT_DIR_X: new_drawstyle = HT_LINE | HT_DIR_X; break;
2709 case HT_DIR_Y: new_drawstyle = HT_LINE | HT_DIR_Y; break;
2710
2711 case HT_DIR_HU:
2712 case HT_DIR_HL:
2713 new_drawstyle = (pt.x & TILE_UNIT_MASK) + (pt.y & TILE_UNIT_MASK) <= TILE_SIZE ? HT_LINE | HT_DIR_HU : HT_LINE | HT_DIR_HL;
2714 break;
2715
2716 case HT_DIR_VL:
2717 case HT_DIR_VR:
2718 new_drawstyle = (pt.x & TILE_UNIT_MASK) > (pt.y & TILE_UNIT_MASK) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
2719 break;
2720
2721 default: NOT_REACHED();
2722 }
2723 _thd.selstart.x = x1 & ~TILE_UNIT_MASK;
2724 _thd.selstart.y = y1 & ~TILE_UNIT_MASK;
2725 break;
2726 default:
2727 NOT_REACHED();
2728 }
2729 _thd.new_pos.x = x1 & ~TILE_UNIT_MASK;
2730 _thd.new_pos.y = y1 & ~TILE_UNIT_MASK;
2731 }
2732 }
2733
2734 /* redraw selection */
2735 if (_thd.drawstyle != new_drawstyle ||
2736 _thd.pos.x != _thd.new_pos.x || _thd.pos.y != _thd.new_pos.y ||
2737 _thd.size.x != _thd.new_size.x || _thd.size.y != _thd.new_size.y ||
2738 _thd.outersize.x != _thd.new_outersize.x ||
2739 _thd.outersize.y != _thd.new_outersize.y ||
2740 _thd.diagonal != new_diagonal) {
2741 /* Clear the old tile selection? */
2743
2744 _thd.drawstyle = new_drawstyle;
2745 _thd.pos = _thd.new_pos;
2746 _thd.size = _thd.new_size;
2747 _thd.outersize = _thd.new_outersize;
2748 _thd.diagonal = new_diagonal;
2749 _thd.dirty = 0xff;
2750
2751 /* Draw the new tile selection? */
2752 if ((new_drawstyle & HT_DRAG_MASK) != HT_NONE) SetSelectionTilesDirty();
2753 }
2754}
2755
2760static inline void ShowMeasurementTooltips(EncodedString &&text)
2761{
2763 GuiShowTooltips(_thd.GetCallbackWnd(), std::move(text), TCC_EXIT_VIEWPORT);
2764}
2765
2766static void HideMeasurementTooltips()
2767{
2769}
2770
2773{
2774 _thd.select_method = method;
2775 _thd.select_proc = process;
2776 _thd.selend.x = TileX(tile) * TILE_SIZE;
2777 _thd.selstart.x = TileX(tile) * TILE_SIZE;
2778 _thd.selend.y = TileY(tile) * TILE_SIZE;
2779 _thd.selstart.y = TileY(tile) * TILE_SIZE;
2780
2781 /* Needed so several things (road, autoroad, bridges, ...) are placed correctly.
2782 * In effect, placement starts from the centre of a tile
2783 */
2784 if (method == VPM_X_OR_Y || method == VPM_FIX_X || method == VPM_FIX_Y) {
2785 _thd.selend.x += TILE_SIZE / 2;
2786 _thd.selend.y += TILE_SIZE / 2;
2787 _thd.selstart.x += TILE_SIZE / 2;
2788 _thd.selstart.y += TILE_SIZE / 2;
2789 }
2790
2792 if ((_thd.place_mode & HT_DRAG_MASK) == HT_RECT) {
2793 _thd.place_mode = HT_SPECIAL | others;
2794 _thd.next_drawstyle = HT_RECT | others;
2795 } else if (_thd.place_mode & (HT_RAIL | HT_LINE)) {
2796 _thd.place_mode = HT_SPECIAL | others;
2797 _thd.next_drawstyle = _thd.drawstyle | others;
2798 } else {
2799 _thd.place_mode = HT_SPECIAL | others;
2800 _thd.next_drawstyle = HT_POINT | others;
2801 }
2803}
2804
2807{
2809 _thd.select_proc = process;
2810 _thd.selstart.x = 0;
2811 _thd.selstart.y = 0;
2812 _thd.next_drawstyle = HT_RECT;
2813
2815}
2816
2817void VpSetPlaceSizingLimit(int limit)
2818{
2819 _thd.sizelimit = limit;
2820}
2821
2828{
2829 uint64_t distance = DistanceManhattan(from, to) + 1;
2830
2831 _thd.selend.x = TileX(to) * TILE_SIZE;
2832 _thd.selend.y = TileY(to) * TILE_SIZE;
2833 _thd.selstart.x = TileX(from) * TILE_SIZE;
2834 _thd.selstart.y = TileY(from) * TILE_SIZE;
2835 _thd.next_drawstyle = HT_RECT;
2836
2837 /* show measurement only if there is any length to speak of */
2838 if (distance > 1) {
2839 ShowMeasurementTooltips(GetEncodedString(STR_MEASURE_LENGTH, distance));
2840 } else {
2841 HideMeasurementTooltips();
2842 }
2843}
2844
2845static void VpStartPreSizing()
2846{
2847 _thd.selend.x = -1;
2849}
2850
2856{
2857 int fxpy = _tile_fract_coords.x + _tile_fract_coords.y;
2858 int sxpy = (_thd.selend.x & TILE_UNIT_MASK) + (_thd.selend.y & TILE_UNIT_MASK);
2859 int fxmy = _tile_fract_coords.x - _tile_fract_coords.y;
2860 int sxmy = (_thd.selend.x & TILE_UNIT_MASK) - (_thd.selend.y & TILE_UNIT_MASK);
2861
2862 switch (mode) {
2863 default: NOT_REACHED();
2864 case 0: // end piece is lower right
2865 if (fxpy >= 20 && sxpy <= 12) return HT_DIR_HL;
2866 if (fxmy < -3 && sxmy > 3) return HT_DIR_VR;
2867 return HT_DIR_Y;
2868
2869 case 1:
2870 if (fxmy > 3 && sxmy < -3) return HT_DIR_VL;
2871 if (fxpy <= 12 && sxpy >= 20) return HT_DIR_HU;
2872 return HT_DIR_Y;
2873
2874 case 2:
2875 if (fxmy > 3 && sxmy < -3) return HT_DIR_VL;
2876 if (fxpy >= 20 && sxpy <= 12) return HT_DIR_HL;
2877 return HT_DIR_X;
2878
2879 case 3:
2880 if (fxmy < -3 && sxmy > 3) return HT_DIR_VR;
2881 if (fxpy <= 12 && sxpy >= 20) return HT_DIR_HU;
2882 return HT_DIR_X;
2883 }
2884}
2885
2899static bool SwapDirection(HighLightStyle style, TileIndex start_tile, TileIndex end_tile)
2900{
2901 uint start_x = TileX(start_tile);
2902 uint start_y = TileY(start_tile);
2903 uint end_x = TileX(end_tile);
2904 uint end_y = TileY(end_tile);
2905
2906 switch (style & HT_DRAG_MASK) {
2907 case HT_RAIL:
2908 case HT_LINE: return (end_x > start_x || (end_x == start_x && end_y > start_y));
2909
2910 case HT_RECT:
2911 case HT_POINT: return (end_x != start_x && end_y < start_y);
2912 default: NOT_REACHED();
2913 }
2914
2915 return false;
2916}
2917
2933static int CalcHeightdiff(HighLightStyle style, uint distance, TileIndex start_tile, TileIndex end_tile)
2934{
2935 bool swap = SwapDirection(style, start_tile, end_tile);
2936 uint h0, h1; // Start height and end height.
2937
2938 if (start_tile == end_tile) return 0;
2939 if (swap) std::swap(start_tile, end_tile);
2940
2941 switch (style & HT_DRAG_MASK) {
2942 case HT_RECT:
2943 /* In the case of an area we can determine whether we were dragging south or
2944 * east by checking the X-coordinates of the tiles */
2945 if (TileX(end_tile) > TileX(start_tile)) {
2946 /* Dragging south does not need to change the start tile. */
2947 end_tile = TileAddByDir(end_tile, DIR_S);
2948 } else {
2949 /* Dragging east. */
2950 start_tile = TileAddByDir(start_tile, DIR_SW);
2951 end_tile = TileAddByDir(end_tile, DIR_SE);
2952 }
2953 [[fallthrough]];
2954
2955 case HT_POINT:
2956 h0 = TileHeight(start_tile);
2957 h1 = TileHeight(end_tile);
2958 break;
2959 default: { // All other types, this is mostly only line/autorail
2960 static const HighLightStyle flip_style_direction[] = {
2962 };
2963 static const std::pair<TileIndexDiffC, TileIndexDiffC> start_heightdiff_line_by_dir[] = {
2964 { {1, 0}, {1, 1} }, // HT_DIR_X
2965 { {0, 1}, {1, 1} }, // HT_DIR_Y
2966 { {1, 0}, {0, 0} }, // HT_DIR_HU
2967 { {1, 0}, {1, 1} }, // HT_DIR_HL
2968 { {1, 0}, {1, 1} }, // HT_DIR_VL
2969 { {0, 1}, {1, 1} }, // HT_DIR_VR
2970 };
2971 static const std::pair<TileIndexDiffC, TileIndexDiffC> end_heightdiff_line_by_dir[] = {
2972 { {0, 1}, {0, 0} }, // HT_DIR_X
2973 { {1, 0}, {0, 0} }, // HT_DIR_Y
2974 { {0, 1}, {0, 0} }, // HT_DIR_HU
2975 { {1, 1}, {0, 1} }, // HT_DIR_HL
2976 { {1, 0}, {0, 0} }, // HT_DIR_VL
2977 { {0, 0}, {0, 1} }, // HT_DIR_VR
2978 };
2979 static_assert(std::size(start_heightdiff_line_by_dir) == HT_DIR_END);
2980 static_assert(std::size(end_heightdiff_line_by_dir) == HT_DIR_END);
2981
2982 distance %= 2; // we're only interested if the distance is even or uneven
2983 style &= HT_DIR_MASK;
2984 assert(style < HT_DIR_END);
2985
2986 /* To handle autorail, we do some magic to be able to use a lookup table.
2987 * Firstly if we drag the other way around, we switch start&end, and if needed
2988 * also flip the drag-position. Eg if it was on the left, and the distance is even
2989 * that means the end, which is now the start is on the right */
2990 if (swap && distance == 0) style = flip_style_direction[style];
2991
2992 /* Lambda to help calculating the height at one side of the line. */
2993 auto get_height = [](auto &tile, auto &heightdiffs) {
2994 return std::max(
2995 TileHeight(TileAdd(tile, ToTileIndexDiff(heightdiffs.first))),
2996 TileHeight(TileAdd(tile, ToTileIndexDiff(heightdiffs.second))));
2997 };
2998
2999 /* Use lookup table for start-tile based on HighLightStyle direction */
3000 h0 = get_height(start_tile, start_heightdiff_line_by_dir[style]);
3001
3002 /* Use lookup table for end-tile based on HighLightStyle direction
3003 * flip around side (lower/upper, left/right) based on distance */
3004 if (distance == 0) style = flip_style_direction[style];
3005 h1 = get_height(end_tile, end_heightdiff_line_by_dir[style]);
3006 break;
3007 }
3008 }
3009
3010 if (swap) std::swap(h0, h1);
3011 return (int)(h1 - h0) * TILE_HEIGHT_STEP;
3012}
3013
3020static void CheckUnderflow(int &test, int &other, int mult)
3021{
3022 if (test >= 0) return;
3023
3024 other += mult * test;
3025 test = 0;
3026}
3027
3035static void CheckOverflow(int &test, int &other, int max, int mult)
3036{
3037 if (test <= max) return;
3038
3039 other += mult * (test - max);
3040 test = max;
3041}
3042
3044static void CalcRaildirsDrawstyle(int x, int y, int method)
3045{
3047
3048 int dx = _thd.selstart.x - (_thd.selend.x & ~TILE_UNIT_MASK);
3049 int dy = _thd.selstart.y - (_thd.selend.y & ~TILE_UNIT_MASK);
3050 uint w = abs(dx) + TILE_SIZE;
3051 uint h = abs(dy) + TILE_SIZE;
3052
3053 if (method & ~(VPM_RAILDIRS | VPM_SIGNALDIRS)) {
3054 /* We 'force' a selection direction; first four rail buttons. */
3055 method &= ~(VPM_RAILDIRS | VPM_SIGNALDIRS);
3056 int raw_dx = _thd.selstart.x - _thd.selend.x;
3057 int raw_dy = _thd.selstart.y - _thd.selend.y;
3058 switch (method) {
3059 case VPM_FIX_X:
3060 b = HT_LINE | HT_DIR_Y;
3061 x = _thd.selstart.x;
3062 break;
3063
3064 case VPM_FIX_Y:
3065 b = HT_LINE | HT_DIR_X;
3066 y = _thd.selstart.y;
3067 break;
3068
3069 case VPM_FIX_HORIZONTAL:
3070 if (dx == -dy) {
3071 /* We are on a straight horizontal line. Determine the 'rail'
3072 * to build based the sub tile location. */
3074 } else {
3075 /* We are not on a straight line. Determine the rail to build
3076 * based on whether we are above or below it. */
3077 b = dx + dy >= (int)TILE_SIZE ? HT_LINE | HT_DIR_HU : HT_LINE | HT_DIR_HL;
3078
3079 /* Calculate where a horizontal line through the start point and
3080 * a vertical line from the selected end point intersect and
3081 * use that point as the end point. */
3082 int offset = (raw_dx - raw_dy) / 2;
3083 x = _thd.selstart.x - (offset & ~TILE_UNIT_MASK);
3084 y = _thd.selstart.y + (offset & ~TILE_UNIT_MASK);
3085
3086 /* 'Build' the last half rail tile if needed */
3087 if ((offset & TILE_UNIT_MASK) > (TILE_SIZE / 2)) {
3088 if (dx + dy >= (int)TILE_SIZE) {
3089 x -= (int)TILE_SIZE;
3090 } else {
3091 y += (int)TILE_SIZE;
3092 }
3093 }
3094
3095 /* Make sure we do not overflow the map! */
3096 CheckUnderflow(x, y, 1);
3097 CheckUnderflow(y, x, 1);
3098 CheckOverflow(x, y, (Map::MaxX() - 1) * TILE_SIZE, 1);
3099 CheckOverflow(y, x, (Map::MaxY() - 1) * TILE_SIZE, 1);
3100 assert(x >= 0 && y >= 0 && x <= (int)(Map::MaxX() * TILE_SIZE) && y <= (int)(Map::MaxY() * TILE_SIZE));
3101 }
3102 break;
3103
3104 case VPM_FIX_VERTICAL:
3105 if (dx == dy) {
3106 /* We are on a straight vertical line. Determine the 'rail'
3107 * to build based the sub tile location. */
3109 } else {
3110 /* We are not on a straight line. Determine the rail to build
3111 * based on whether we are left or right from it. */
3112 b = dx < dy ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
3113
3114 /* Calculate where a vertical line through the start point and
3115 * a horizontal line from the selected end point intersect and
3116 * use that point as the end point. */
3117 int offset = (raw_dx + raw_dy + (int)TILE_SIZE) / 2;
3118 x = _thd.selstart.x - (offset & ~TILE_UNIT_MASK);
3119 y = _thd.selstart.y - (offset & ~TILE_UNIT_MASK);
3120
3121 /* 'Build' the last half rail tile if needed */
3122 if ((offset & TILE_UNIT_MASK) > (TILE_SIZE / 2)) {
3123 if (dx < dy) {
3124 y -= (int)TILE_SIZE;
3125 } else {
3126 x -= (int)TILE_SIZE;
3127 }
3128 }
3129
3130 /* Make sure we do not overflow the map! */
3131 CheckUnderflow(x, y, -1);
3132 CheckUnderflow(y, x, -1);
3133 CheckOverflow(x, y, (Map::MaxX() - 1) * TILE_SIZE, -1);
3134 CheckOverflow(y, x, (Map::MaxY() - 1) * TILE_SIZE, -1);
3135 assert(x >= 0 && y >= 0 && x <= (int)(Map::MaxX() * TILE_SIZE) && y <= (int)(Map::MaxY() * TILE_SIZE));
3136 }
3137 break;
3138
3139 default:
3140 NOT_REACHED();
3141 }
3142 } else if (TileVirtXY(_thd.selstart.x, _thd.selstart.y) == TileVirtXY(x, y)) { // check if we're only within one tile
3143 if (method & VPM_RAILDIRS) {
3144 b = GetAutorailHT(x, y);
3145 } else { // rect for autosignals on one tile
3146 b = HT_RECT;
3147 }
3148 } else if (h == TILE_SIZE) { // Is this in X direction?
3149 if (dx == (int)TILE_SIZE) { // 2x1 special handling
3150 b = (Check2x1AutoRail(3)) | HT_LINE;
3151 } else if (dx == -(int)TILE_SIZE) {
3152 b = (Check2x1AutoRail(2)) | HT_LINE;
3153 } else {
3154 b = HT_LINE | HT_DIR_X;
3155 }
3156 y = _thd.selstart.y;
3157 } else if (w == TILE_SIZE) { // Or Y direction?
3158 if (dy == (int)TILE_SIZE) { // 2x1 special handling
3159 b = (Check2x1AutoRail(1)) | HT_LINE;
3160 } else if (dy == -(int)TILE_SIZE) { // 2x1 other direction
3161 b = (Check2x1AutoRail(0)) | HT_LINE;
3162 } else {
3163 b = HT_LINE | HT_DIR_Y;
3164 }
3165 x = _thd.selstart.x;
3166 } else if (w > h * 2) { // still count as x dir?
3167 b = HT_LINE | HT_DIR_X;
3168 y = _thd.selstart.y;
3169 } else if (h > w * 2) { // still count as y dir?
3170 b = HT_LINE | HT_DIR_Y;
3171 x = _thd.selstart.x;
3172 } else { // complicated direction
3173 int d = w - h;
3174 _thd.selend.x = _thd.selend.x & ~TILE_UNIT_MASK;
3175 _thd.selend.y = _thd.selend.y & ~TILE_UNIT_MASK;
3176
3177 /* four cases. */
3178 if (x > _thd.selstart.x) {
3179 if (y > _thd.selstart.y) {
3180 /* south */
3181 if (d == 0) {
3183 } else if (d >= 0) {
3184 x = _thd.selstart.x + h;
3185 b = HT_LINE | HT_DIR_VL;
3186 } else {
3187 y = _thd.selstart.y + w;
3188 b = HT_LINE | HT_DIR_VR;
3189 }
3190 } else {
3191 /* west */
3192 if (d == 0) {
3194 } else if (d >= 0) {
3195 x = _thd.selstart.x + h;
3196 b = HT_LINE | HT_DIR_HL;
3197 } else {
3198 y = _thd.selstart.y - w;
3199 b = HT_LINE | HT_DIR_HU;
3200 }
3201 }
3202 } else {
3203 if (y > _thd.selstart.y) {
3204 /* east */
3205 if (d == 0) {
3207 } else if (d >= 0) {
3208 x = _thd.selstart.x - h;
3209 b = HT_LINE | HT_DIR_HU;
3210 } else {
3211 y = _thd.selstart.y + w;
3212 b = HT_LINE | HT_DIR_HL;
3213 }
3214 } else {
3215 /* north */
3216 if (d == 0) {
3218 } else if (d >= 0) {
3219 x = _thd.selstart.x - h;
3220 b = HT_LINE | HT_DIR_VR;
3221 } else {
3222 y = _thd.selstart.y - w;
3223 b = HT_LINE | HT_DIR_VL;
3224 }
3225 }
3226 }
3227 }
3228
3230 TileIndex t0 = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
3231 TileIndex t1 = TileVirtXY(x, y);
3232 uint distance = DistanceManhattan(t0, t1) + 1;
3233
3234 if (distance == 1) {
3235 HideMeasurementTooltips();
3236 } else {
3237 int heightdiff = CalcHeightdiff(b, distance, t0, t1);
3238 /* If we are showing a tooltip for horizontal or vertical drags,
3239 * 2 tiles have a length of 1. To bias towards the ceiling we add
3240 * one before division. It feels more natural to count 3 lengths as 2 */
3241 if ((b & HT_DIR_MASK) != HT_DIR_X && (b & HT_DIR_MASK) != HT_DIR_Y) {
3242 distance = CeilDiv(distance, 2);
3243 }
3244
3245 if (heightdiff == 0) {
3246 ShowMeasurementTooltips(GetEncodedString(STR_MEASURE_LENGTH, distance));
3247 } else {
3248 ShowMeasurementTooltips(GetEncodedString(STR_MEASURE_LENGTH_HEIGHTDIFF, distance, heightdiff));
3249 }
3250 }
3251 }
3252
3253 _thd.selend.x = x;
3254 _thd.selend.y = y;
3255 _thd.next_drawstyle = b;
3256}
3257
3266{
3267 int sx, sy;
3268 HighLightStyle style;
3269
3270 if (x == -1) {
3271 _thd.selend.x = -1;
3272 return;
3273 }
3274
3275 /* Special handling of drag in any (8-way) direction */
3276 if (method & (VPM_RAILDIRS | VPM_SIGNALDIRS)) {
3277 _thd.selend.x = x;
3278 _thd.selend.y = y;
3279 CalcRaildirsDrawstyle(x, y, method);
3280 return;
3281 }
3282
3283 /* Needed so level-land is placed correctly */
3284 if ((_thd.next_drawstyle & HT_DRAG_MASK) == HT_POINT) {
3285 x += TILE_SIZE / 2;
3286 y += TILE_SIZE / 2;
3287 }
3288
3289 sx = _thd.selstart.x;
3290 sy = _thd.selstart.y;
3291
3292 int limit = 0;
3293
3294 switch (method) {
3295 case VPM_X_OR_Y: // drag in X or Y direction
3296 if (abs(sy - y) < abs(sx - x)) {
3297 y = sy;
3298 style = HT_DIR_X;
3299 } else {
3300 x = sx;
3301 style = HT_DIR_Y;
3302 }
3303 goto calc_heightdiff_single_direction;
3304
3305 case VPM_X_LIMITED: // Drag in X direction (limited size).
3306 limit = (_thd.sizelimit - 1) * TILE_SIZE;
3307 [[fallthrough]];
3308
3309 case VPM_FIX_X: // drag in Y direction
3310 x = sx;
3311 style = HT_DIR_Y;
3312 goto calc_heightdiff_single_direction;
3313
3314 case VPM_Y_LIMITED: // Drag in Y direction (limited size).
3315 limit = (_thd.sizelimit - 1) * TILE_SIZE;
3316 [[fallthrough]];
3317
3318 case VPM_FIX_Y: // drag in X direction
3319 y = sy;
3320 style = HT_DIR_X;
3321
3322calc_heightdiff_single_direction:;
3323 if (limit > 0) {
3324 x = sx + Clamp(x - sx, -limit, limit);
3325 y = sy + Clamp(y - sy, -limit, limit);
3326 }
3328 TileIndex t0 = TileVirtXY(sx, sy);
3329 TileIndex t1 = TileVirtXY(x, y);
3330 uint distance = DistanceManhattan(t0, t1) + 1;
3331
3332 if (distance == 1) {
3333 HideMeasurementTooltips();
3334 } else {
3335 /* With current code passing a HT_LINE style to calculate the height
3336 * difference is enough. However if/when a point-tool is created
3337 * with this method, function should be called with new_style (below)
3338 * instead of HT_LINE | style case HT_POINT is handled specially
3339 * new_style := (_thd.next_drawstyle & HT_RECT) ? HT_LINE | style : _thd.next_drawstyle; */
3340 int heightdiff = CalcHeightdiff(HT_LINE | style, 0, t0, t1);
3341
3342 if (heightdiff == 0) {
3343 ShowMeasurementTooltips(GetEncodedString(STR_MEASURE_LENGTH, distance));
3344 } else {
3345 ShowMeasurementTooltips(GetEncodedString(STR_MEASURE_LENGTH_HEIGHTDIFF, distance, heightdiff));
3346 }
3347 }
3348 }
3349 break;
3350
3351 case VPM_X_AND_Y_LIMITED: // Drag an X by Y constrained rect area.
3352 limit = (_thd.sizelimit - 1) * TILE_SIZE;
3353 x = sx + Clamp(x - sx, -limit, limit);
3354 y = sy + Clamp(y - sy, -limit, limit);
3355 [[fallthrough]];
3356
3357 case VPM_X_AND_Y: // drag an X by Y area
3359 TileIndex t0 = TileVirtXY(sx, sy);
3360 TileIndex t1 = TileVirtXY(x, y);
3361 uint dx = Delta(TileX(t0), TileX(t1)) + 1;
3362 uint dy = Delta(TileY(t0), TileY(t1)) + 1;
3363
3364 /* If dragging an area (eg dynamite tool) and it is actually a single
3365 * row/column, change the type to 'line' to get proper calculation for height */
3366 style = (HighLightStyle)_thd.next_drawstyle;
3367 if (_thd.IsDraggingDiagonal()) {
3368 /* Determine the "area" of the diagonal dragged selection.
3369 * We assume the area is the number of tiles along the X
3370 * edge and the number of tiles along the Y edge. However,
3371 * multiplying these two numbers does not give the exact
3372 * number of tiles; basically we are counting the black
3373 * squares on a chess board and ignore the white ones to
3374 * make the tile counts at the edges match up. There is no
3375 * other way to make a proper count though.
3376 *
3377 * First convert to the rotated coordinate system. */
3378 int dist_x = TileX(t0) - TileX(t1);
3379 int dist_y = TileY(t0) - TileY(t1);
3380 int a_max = dist_x + dist_y;
3381 int b_max = dist_y - dist_x;
3382
3383 /* Now determine the size along the edge, but due to the
3384 * chess board principle this counts double. */
3385 a_max = abs(a_max + (a_max > 0 ? 2 : -2)) / 2;
3386 b_max = abs(b_max + (b_max > 0 ? 2 : -2)) / 2;
3387
3388 /* We get a 1x1 on normal 2x1 rectangles, due to it being
3389 * a seen as two sides. As the result for actual building
3390 * will be the same as non-diagonal dragging revert to that
3391 * behaviour to give it a more normally looking size. */
3392 if (a_max != 1 || b_max != 1) {
3393 dx = a_max;
3394 dy = b_max;
3395 }
3396 } else if (style & HT_RECT) {
3397 if (dx == 1) {
3398 style = HT_LINE | HT_DIR_Y;
3399 } else if (dy == 1) {
3400 style = HT_LINE | HT_DIR_X;
3401 }
3402 }
3403
3404 if (dx != 1 || dy != 1) {
3405 int heightdiff = CalcHeightdiff(style, 0, t0, t1);
3406
3407 dx -= (style & HT_POINT ? 1 : 0);
3408 dy -= (style & HT_POINT ? 1 : 0);
3409
3410 if (heightdiff == 0) {
3411 ShowMeasurementTooltips(GetEncodedString(STR_MEASURE_AREA, dx, dy));
3412 } else {
3413 ShowMeasurementTooltips(GetEncodedString(STR_MEASURE_AREA_HEIGHTDIFF, dx, dy, heightdiff));
3414 }
3415 }
3416 }
3417 break;
3418
3419 default: NOT_REACHED();
3420 }
3421
3422 _thd.selend.x = x;
3423 _thd.selend.y = y;
3424}
3425
3431{
3433
3434 /* stop drag mode if the window has been closed */
3435 Window *w = _thd.GetCallbackWnd();
3436 if (w == nullptr) {
3438 return ES_HANDLED;
3439 }
3440
3441 /* while dragging execute the drag procedure of the corresponding window (mostly VpSelectTilesWithMethod() ) */
3442 if (_left_button_down) {
3444 /* Only register a drag event when the mouse moved. */
3445 if (_thd.new_pos.x == _thd.selstart.x && _thd.new_pos.y == _thd.selstart.y) return ES_HANDLED;
3446 _thd.selstart.x = _thd.new_pos.x;
3447 _thd.selstart.y = _thd.new_pos.y;
3448 }
3449
3450 w->OnPlaceDrag(_thd.select_method, _thd.select_proc, GetTileBelowCursor());
3451 return ES_HANDLED;
3452 }
3453
3454 /* Mouse button released. */
3457
3458 /* Keep the selected tool, but reset it to the original mode. */
3460 if ((_thd.next_drawstyle & HT_DRAG_MASK) == HT_RECT) {
3461 _thd.place_mode = HT_RECT | others;
3462 } else if (_thd.select_method & VPM_SIGNALDIRS) {
3463 _thd.place_mode = HT_RECT | others;
3464 } else if (_thd.select_method & VPM_RAILDIRS) {
3465 _thd.place_mode = (_thd.select_method & ~VPM_RAILDIRS) ? _thd.next_drawstyle : (HT_RAIL | others);
3466 } else {
3467 _thd.place_mode = HT_POINT | others;
3468 }
3469 SetTileSelectSize(1, 1);
3470
3471 HideMeasurementTooltips();
3472 w->OnPlaceMouseUp(_thd.select_method, _thd.select_proc, _thd.selend, TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y));
3473
3474 return ES_HANDLED;
3475}
3476
3485{
3486 SetObjectToPlace(icon, pal, mode, w->window_class, w->window_number);
3487}
3488
3489#include "table/animcursors.h"
3490
3499void SetObjectToPlace(CursorID icon, PaletteID pal, HighLightStyle mode, WindowClass window_class, WindowNumber window_num)
3500{
3501 if (_thd.window_class != WC_INVALID) {
3502 /* Undo clicking on button and drag & drop */
3503 Window *w = _thd.GetCallbackWnd();
3504 /* Call the abort function, but set the window class to something
3505 * that will never be used to avoid infinite loops. Setting it to
3506 * the 'next' window class must not be done because recursion into
3507 * this function might in some cases reset the newly set object to
3508 * place or not properly reset the original selection. */
3509 _thd.window_class = WC_INVALID;
3510 if (w != nullptr) {
3511 w->OnPlaceObjectAbort();
3512 HideMeasurementTooltips();
3513 }
3514 }
3515
3516 /* Mark the old selection dirty, in case the selection shape or colour changes */
3518
3519 SetTileSelectSize(1, 1);
3520
3521 _thd.make_square_red = false;
3522
3523 if (mode == HT_DRAG) { // HT_DRAG is for dragdropping trains in the depot window
3524 mode = HT_NONE;
3526 } else {
3528 }
3529
3530 _thd.place_mode = mode;
3531 _thd.window_class = window_class;
3532 _thd.window_number = window_num;
3533
3534 if ((mode & HT_DRAG_MASK) == HT_SPECIAL) { // special tools, like tunnels or docks start with presizing mode
3535 VpStartPreSizing();
3536 }
3537
3538 if ((icon & ANIMCURSOR_FLAG) != 0) {
3540 } else {
3541 SetMouseCursor(icon, pal);
3542 }
3543
3544}
3545
3551
3552Point GetViewportStationMiddle(const Viewport *vp, const Station *st)
3553{
3554 int x = TileX(st->xy) * TILE_SIZE;
3555 int y = TileY(st->xy) * TILE_SIZE;
3556 int z = GetSlopePixelZ(Clamp(x, 0, Map::SizeX() * TILE_SIZE - 1), Clamp(y, 0, Map::SizeY() * TILE_SIZE - 1));
3557
3558 Point p = RemapCoords(x, y, z);
3559 p.x = UnScaleByZoom(p.x - vp->virtual_left, vp->zoom) + vp->left;
3560 p.y = UnScaleByZoom(p.y - vp->virtual_top, vp->zoom) + vp->top;
3561 return p;
3562}
3563
3569
3572#ifdef WITH_SSE
3573 { &ViewportSortParentSpritesSSE41Checker, &ViewportSortParentSpritesSSE41 },
3574#endif
3576};
3577
3580{
3581 for (const auto &sprite_sorter : _vp_sprite_sorters) {
3582 if (sprite_sorter.fct_checker()) {
3583 _vp_sprite_sorter = sprite_sorter.fct_sorter;
3584 break;
3585 }
3586 }
3587 assert(_vp_sprite_sorter != nullptr);
3588}
3589
3599{
3600 if (_current_company != OWNER_DEITY) return CMD_ERROR;
3601 switch (target) {
3602 case VST_EVERYONE:
3603 break;
3604 case VST_COMPANY:
3605 if (_local_company != (CompanyID)ref) return CommandCost();
3606 break;
3607 case VST_CLIENT:
3608 if (_network_own_client_id != (ClientID)ref) return CommandCost();
3609 break;
3610 default:
3611 return CMD_ERROR;
3612 }
3613
3614 if (flags.Test(DoCommandFlag::Execute)) {
3617 }
3618 return CommandCost();
3619}
3620
3621void MarkCatchmentTilesDirty()
3622{
3623 if (_viewport_highlight_town != nullptr) {
3625 return;
3626 }
3627 if (_viewport_highlight_station != nullptr) {
3631 } else {
3633 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
3634 MarkTileDirtyByTile(tile);
3635 }
3636 }
3637 }
3638 if (_viewport_highlight_waypoint != nullptr) {
3641 }
3643 }
3644}
3645
3646static void SetWindowDirtyForViewportCatchment()
3647{
3651}
3652
3653static void ClearViewportCatchment()
3654{
3655 MarkCatchmentTilesDirty();
3658 _viewport_highlight_town = nullptr;
3659}
3660
3667void SetViewportCatchmentStation(const Station *st, bool sel)
3668{
3669 SetWindowDirtyForViewportCatchment();
3670 if (sel && _viewport_highlight_station != st) {
3671 ClearViewportCatchment();
3673 MarkCatchmentTilesDirty();
3674 } else if (!sel && _viewport_highlight_station == st) {
3675 MarkCatchmentTilesDirty();
3677 }
3679}
3680
3688{
3689 SetWindowDirtyForViewportCatchment();
3690 if (sel && _viewport_highlight_waypoint != wp) {
3691 ClearViewportCatchment();
3693 MarkCatchmentTilesDirty();
3694 } else if (!sel && _viewport_highlight_waypoint == wp) {
3695 MarkCatchmentTilesDirty();
3697 }
3699}
3700
3707void SetViewportCatchmentTown(const Town *t, bool sel)
3708{
3709 SetWindowDirtyForViewportCatchment();
3710 if (sel && _viewport_highlight_town != t) {
3711 ClearViewportCatchment();
3714 } else if (!sel && _viewport_highlight_town == t) {
3715 _viewport_highlight_town = nullptr;
3717 }
3719}
3720
3725void ViewportData::CancelFollow(const Window &viewport_window)
3726{
3727 if (this->follow_vehicle == VehicleID::Invalid()) return;
3728
3729 if (viewport_window.window_class == WC_MAIN_WINDOW) {
3730 /* We're cancelling follow in the main viewport, so we need to check for a vehicle view window
3731 * to raise the location follow widget. */
3732 Window *vehicle_window = FindWindowById(WC_VEHICLE_VIEW, this->follow_vehicle);
3733 if (vehicle_window != nullptr) vehicle_window->RaiseWidgetWhenLowered(WID_VV_LOCATION);
3734 }
3735
3736 this->follow_vehicle = VehicleID::Invalid();
3737}
This file defines all the the animated cursors.
static const AnimCursor *const _animcursors[]
This is an array of pointers to all the animated cursor definitions we have above.
Definition animcursors.h:85
Highlight/sprite information for autorail.
Class for backupping variables and making sure they are restored later.
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
TileIndex GetNorthernBridgeEnd(TileIndex t)
Finds the northern end of a bridge starting at a middle tile.
Map accessor functions for bridges.
int GetBridgePixelHeight(TileIndex tile)
Get the height ('z') of a bridge in pixels.
Definition bridge_map.h:84
bool IsBridgeAbove(Tile t)
checks if a bridge is set above the ground of this tile
Definition bridge_map.h:45
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Set()
Set all bits.
Iterator to iterate over all tiles belonging to a bitmaptilearea.
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
Definition factory.hpp:136
How all blitters should look like.
Definition base.hpp:29
virtual void * MoveTo(void *video, int x, int y)=0
Move the destination pointer the requested amount x and y, keeping in mind any pitch and bpp of the r...
virtual void SetPixel(void *video, int x, int y, uint8_t colour)=0
Draw a pixel with a given colour on the video-buffer.
Common return value for all commands.
Container for an encoded string, created by GetEncodedString.
K-dimensional tree, specialised for 2-dimensional space.
Definition kdtree.hpp:35
void Build(It begin, It end)
Clear and rebuild the tree from a new sequence of elements,.
Definition kdtree.hpp:362
size_t Count() const
Get number of elements stored in tree.
Definition kdtree.hpp:430
void FindContained(CoordT x1, CoordT y1, CoordT x2, CoordT y2, const Outputter &outputter) const
Find all items contained within the given rectangle.
Definition kdtree.hpp:459
T FindNearest(CoordT x, CoordT y) const
Find the element closest to given coordinate, in Manhattan distance.
Definition kdtree.hpp:441
RAII class for measuring multi-step elements of performance.
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:29
RectPadding fullbevel
Always-scaled bevel thickness.
Definition window_gui.h:39
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Execute
execute the given command
Definition of stuff that is very close to a company, like the company struct itself.
ReferenceThroughBaseContainer< std::array< Colours, MAX_COMPANIES > > _company_colours
NOSAVE: can be determined from company structs.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
Functions related to companies.
static constexpr Owner OWNER_DEITY
The object is owned by a superuser / goal script.
static constexpr Owner OWNER_NONE
The tile has no ownership.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
@ DIR_SW
Southwest.
@ DIR_SE
Southeast.
@ DIR_S
South.
Factory to 'query' all available blitters.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:77
Types for recording game performance data.
@ PFE_DRAWWORLD
Time spent drawing world viewports in GUI.
void SetMouseCursor(CursorID sprite, PaletteID pal)
Assign a single non-animated sprite to the cursor.
Definition gfx.cpp:1657
bool _left_button_down
Is left mouse button pressed?
Definition gfx.cpp:41
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:852
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:658
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:38
void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub)
Draw a sprite in a viewport.
Definition gfx.cpp:961
void SetAnimatedMouseCursor(const AnimCursor *table)
Assign an animation to the cursor.
Definition gfx.cpp:1670
void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
Draws the projection of a parallelepiped.
Definition gfx.cpp:419
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
@ Normal
The most basic (normal) sprite.
@ FS_SMALL
Index of the small font in the font tables.
Definition gfx_type.h:246
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:245
uint32_t CursorID
The number of the cursor (sprite)
Definition gfx_type.h:19
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:378
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:296
@ TC_IS_PALETTE_COLOUR
Colour value is already a real palette colour index, not an index of a StringColour.
Definition gfx_type.h:319
bool MarkAllViewportsDirty(int left, int top, int right, int bottom)
Mark all viewports that display an area as dirty (in need of repaint).
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition window.cpp:945
void AddDirtyBlock(int left, int top, int right, int bottom)
Extend the internal _invalid_rect rectangle to contain the rectangle defined by the given parameters.
Definition gfx.cpp:1467
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1500
static bool MarkViewportDirty(const Viewport *vp, int left, int top, int right, int bottom)
Marks a viewport as dirty for repaint if it displays (a part of) the area the needs to be repainted.
void RedrawScreenRect(int left, int top, int right, int bottom)
Repaints a specific rectangle of the screen.
Definition gfx.cpp:1377
void MarkDirty(ZoomLevel maxzoom=ZOOM_LVL_MAX) const
Mark the sign dirty in all viewports.
void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
Mark a tile given by its index dirty for repaint.
static void SetSelectionTilesDirty()
Marks the selected tiles as dirty.
const TileTypeProcs *const _tile_type_procs[16]
Tile callback functions for each type of tile.
Definition landscape.cpp:65
Point InverseRemapCoords2(int x, int y, bool clamp_to_map, bool *clamped)
Map 2D viewport or smallmap coordinate to 3D world or tile coordinate.
int GetSlopePixelZ(int x, int y, bool ground_vehicle)
Return world Z coordinate of a given point of a tile.
Functions related to OTTD's landscape.
Point RemapCoords(int x, int y, int z)
Map 3D world or tile coordinate to equivalent 2D coordinate as used in the viewports and smallmap.
Definition landscape.h:79
Point RemapCoords2(int x, int y)
Map 3D world or tile coordinate to equivalent 2D coordinate as used in the viewports and smallmap.
Definition landscape.h:95
Point InverseRemapCoords(int x, int y)
Map 2D viewport or smallmap coordinate to 3D world or tile coordinate.
Definition landscape.h:109
Declaration of linkgraph overlay GUI.
bool DoZoomInOutWindow(ZoomStateChange how, Window *w)
Zooms a viewport in a window in or out.
Definition main_gui.cpp:93
uint DistanceManhattan(TileIndex t0, TileIndex t1)
Gets the Manhattan distance between the two given tiles.
Definition map.cpp:142
TileIndex TileAddByDir(TileIndex tile, Direction dir)
Adds a Direction to a tile.
Definition map_func.h:598
static debug_inline TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:372
TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc)
Return the offset between two tiles from a TileIndexDiffC struct.
Definition map_func.h:439
constexpr TileIndex TileAdd(TileIndex tile, TileIndexDiff offset)
Adds a given offset to a tile.
Definition map_func.h:456
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:424
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:414
static debug_inline TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:403
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition math_func.hpp:23
constexpr T Align(const T x, uint n)
Return the smallest multiple of n equal or greater than x.
Definition math_func.hpp:37
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
constexpr T Delta(const T a, const T b)
Returns the (absolute) difference between two (scalar) variables.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
void GuiShowTooltips(Window *parent, EncodedString &&text, TooltipCloseCondition close_tooltip)
Shows a tooltip.
Definition misc_gui.cpp:691
ClientID _network_own_client_id
Our client identifier.
Definition network.cpp:72
Network functions used by other parts of OpenTTD.
ClientID
'Unique' identifier to be given to clients
@ DO_SHOW_TOWN_NAMES
Display town names.
Definition openttd.h:46
@ DO_SHOW_COMPETITOR_SIGNS
Display signs, station names and waypoint names of opponent companies. Buoys and oilrig-stations are ...
Definition openttd.h:52
@ DO_SHOW_SIGNS
Display signs.
Definition openttd.h:48
@ DO_SHOW_WAYPOINT_NAMES
Display waypoint names.
Definition openttd.h:51
@ DO_SHOW_STATION_NAMES
Display station names.
Definition openttd.h:47
uint8_t GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:387
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:59
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:58
Base class for signs.
Functions related to signs.
void HandleClickOnSign(const Sign *si)
Handle clicking on a sign.
Slope SlopeWithThreeCornersRaised(Corner corner)
Returns the slope with all except one corner raised.
Definition slope_func.h:206
Corner OppositeCorner(Corner corner)
Returns the opposite corner.
Definition slope_func.h:184
static constexpr Corner GetHalftileSlopeCorner(Slope s)
Returns the leveled halftile of a halftile slope.
Definition slope_func.h:148
static constexpr Slope RemoveHalftileSlope(Slope s)
Removes a halftile slope from a slope.
Definition slope_func.h:60
uint SlopeToSpriteOffset(Slope s)
Returns the Sprite offset for a given Slope.
Definition slope_func.h:415
static constexpr bool IsSteepSlope(Slope s)
Checks if a slope is steep.
Definition slope_func.h:36
static constexpr bool IsHalftileSlope(Slope s)
Checks for non-continuous slope on halftile foundations.
Definition slope_func.h:47
Slope SlopeWithOneCornerRaised(Corner corner)
Returns the slope with a specific corner raised.
Definition slope_func.h:99
Corner
Enumeration of tile corners.
Definition slope_type.h:22
Slope
Enumeration for the slope-type.
Definition slope_type.h:48
@ SLOPE_N
the north corner of the tile is raised
Definition slope_type.h:53
@ SLOPE_STEEP_N
a steep slope falling to south (from north)
Definition slope_type.h:69
bool ScrollMainWindowTo(int x, int y, int z, bool instant)
Scrolls the main window to given coordinates.
static constexpr uint32_t MAX_SPRITES
Masks needed for sprite operations.
Definition sprites.h:1559
static const PaletteID PALETTE_TILE_RED_PULSATING
pulsating red tile drawn if you try to build a wrong tunnel or raise/lower land where it is not possi...
Definition sprites.h:1573
static const CursorID ANIMCURSOR_FLAG
Flag for saying a cursor sprite is an animated cursor.
Definition sprites.h:1511
static const PaletteID PALETTE_SEL_TILE_RED
makes a square red. is used when removing rails or other stuff
Definition sprites.h:1574
static constexpr uint32_t SPRITE_MASK
The mask to for the main sprite.
Definition sprites.h:1560
static constexpr uint8_t PALETTE_MODIFIER_TRANSPARENT
when a sprite is to be displayed transparently, this bit needs to be set.
Definition sprites.h:1551
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1609
static const CursorID SPR_CURSOR_MOUSE
Cursor sprite numbers.
Definition sprites.h:1394
static const PaletteID PALETTE_SEL_TILE_BLUE
This draws a blueish square (catchment areas for example)
Definition sprites.h:1575
static const PaletteID PALETTE_TO_TRANSPARENT
This sets the sprite to transparent.
Definition sprites.h:1606
Base classes/functions for stations.
void ShowStationViewWindow(StationID station)
Opens StationViewWindow for given station.
StationID GetStationIndex(Tile t)
Get StationID from a tile.
Definition station_map.h:28
Definition of base types and functions in a cross-platform compatible way.
The colour translation of GRF's strings.
static const uint8_t _string_colourmap[17]
Colour mapping for TextColour.
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:426
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
Base class for all station-ish types.
TileIndex xy
Base tile of the station.
bool IsInUse() const
Check whether the base station currently is in use; in use means that it is not scheduled for deletio...
TrackedViewportSign sign
NOSAVE: Dimensions of sign.
int next
next child to draw (-1 at the end)
Definition viewport.cpp:140
const SubSprite * sub
only draw a rectangular part of the sprite
Definition viewport.cpp:136
GUISettings gui
settings related to the GUI
uint8_t max_bridge_height
maximum height of bridges
Point pos
logical mouse position
Definition gfx_type.h:126
Data about how and where to blit pixels.
Definition gfx_type.h:158
uint8_t dist_local_authority
distance for town local authority, default 20
bool population_in_label
show the population of a town in its label?
bool measure_tooltip
show a permanent tooltip when dragging tools
ZoomLevel zoom_min
minimum zoom out level
bool smooth_scroll
smooth scroll viewports
ZoomLevel zoom_max
maximum zoom out level
EconomySettings economy
settings to change the economy
ConstructionSettings construction
construction of things in-game
static uint SizeY()
Get the size of the map along the Y.
Definition map_func.h:278
static debug_inline uint SizeX()
Get the size of the map along the X.
Definition map_func.h:269
static uint ScaleBySize1D(uint n)
Scales the given value by the maps circumference, where the given value is for a 256 by 256 map.
Definition map_func.h:340
static uint MaxY()
Gets the maximum Y coordinate within the map, including MP_VOID.
Definition map_func.h:305
static debug_inline uint MaxX()
Gets the maximum X coordinate within the map, including MP_VOID.
Definition map_func.h:296
TileIndex tile
The base tile of the area.
Parent sprite that should be drawn.
const SubSprite * sub
only draw a rectangular part of the sprite
int32_t zmin
minimal world Z coordinate of bounding box
SpriteID image
sprite to draw
int32_t xmax
maximal world X coordinate of bounding box
int32_t ymax
maximal world Y coordinate of bounding box
int32_t x
screen X coordinate of sprite
int32_t y
screen Y coordinate of sprite
int32_t first_child
the first child to draw.
int32_t zmax
maximal world Z coordinate of bounding box
int32_t xmin
minimal world X coordinate of bounding box
int32_t ymin
minimal world Y coordinate of bounding box
PaletteID pal
palette to use
int32_t left
minimal screen X coordinate of sprite (= x + sprite->x_offs), reference point for child sprites
int32_t top
minimal screen Y coordinate of sprite (= y + sprite->y_offs), reference point for child sprites
Coordinates of a point in 2D.
static Titem * Get(auto index)
Returns Titem with given index.
static size_t GetNumItems()
Returns number of valid items in the pool.
Tindex index
Index of this pool item.
Specification of a rectangle with absolute coordinates of all edges.
static bool IsExpected(const BaseStation *st)
Helper for checking whether the given station is of this type.
static Station * Get(auto index)
Gets station with given index.
static Waypoint * From(BaseStation *st)
Converts a BaseStation to SpecializedStation with type checking.
Data structure describing a sprite.
Definition spritecache.h:17
uint16_t width
Width of the sprite.
Definition spritecache.h:19
uint16_t height
Height of the sprite.
Definition spritecache.h:18
int16_t y_offs
Number of pixels to shift the sprite downwards.
Definition spritecache.h:21
int16_t x_offs
Number of pixels to shift the sprite to the right.
Definition spritecache.h:20
Station data structure.
BitmapTileArea catchment_tiles
NOSAVE: Set of individual tiles covered by catchment area.
Used to only draw a part of the sprite.
Definition gfx_type.h:267
Metadata about the current highlighting.
TileIndex redsq
The tile that has to get a red selection.
Point new_pos
New value for pos; used to determine whether to redraw the selection.
Window * GetCallbackWnd()
Get the window that started the current highlighting.
bool make_square_red
Whether to give a tile a red selection.
bool IsDraggingDiagonal()
Is the user dragging a 'diagonal rectangle'?
ViewportDragDropSelectionProcess select_proc
The procedure that has to be called when the selection is done.
Point selstart
The location where the dragging started.
Point new_outersize
New value for outersize; used to determine whether to redraw the selection.
HighLightStyle drawstyle
Lower bits 0-3 are reserved for detailed highlight information.
HighLightStyle place_mode
Method which is used to place the selection.
WindowClass window_class
The WindowClass of the window that is responsible for the selection mode.
uint8_t dirty
Whether the build station window needs to redraw due to the changed selection.
Point offs
Offset, in tile "units", for the blue coverage area from the selected area's northern tile.
Point size
Size, in tile "units", of the white/red selection area.
uint8_t sizelimit
Whether the selection is limited in length, and what the maximum length is.
Point pos
Location, in tile "units", of the northern tile of the selected area.
Point outersize
Size, in tile "units", of the blue coverage area excluding the side of the selected area.
bool diagonal
Whether the dragged area is a 45 degrees rotated rectangle.
void Reset()
Reset tile highlighting.
Point selend
The location where the drag currently ends.
Point new_size
New value for size; used to determine whether to redraw the selection.
bool freeze
Freeze highlight in place.
HighLightStyle next_drawstyle
Queued, but not yet drawn style.
ViewportPlaceMethod select_method
The method which governs how tiles are selected.
WindowNumber window_number
The WindowNumber of the window that is responsible for the selection mode.
Tile information, used while rendering the tile.
Definition tile_cmd.h:43
int z
Height.
Definition tile_cmd.h:48
int x
X position of the tile in unit coordinates.
Definition tile_cmd.h:44
Slope tileh
Slope of the tile.
Definition tile_cmd.h:46
TileIndex tile
Tile index.
Definition tile_cmd.h:47
int y
Y position of the tile in unit coordinates.
Definition tile_cmd.h:45
int32_t y
screen Y coordinate of sprite
Definition viewport.cpp:130
int32_t x
screen X coordinate of sprite
Definition viewport.cpp:129
const SubSprite * sub
only draw a rectangular part of the sprite
Definition viewport.cpp:128
DrawTileProc * draw_tile_proc
Called to render the tile and its contents to the screen.
Definition tile_cmd.h:160
TrackedViewportSign sign
Location of name sign, UpdateVirtCoord updates this.
Definition town.h:43
Town data structure.
Definition town.h:52
TileIndex xy
town center tile
Definition town.h:53
TownCache cache
Container for all cacheable data.
Definition town.h:55
bool show_zone
NOSAVE: mark town to show the local authority zone in the viewports.
Definition town.h:102
StationList stations_near
NOSAVE: List of nearby stations.
Definition town.h:89
bool kdtree_valid
Are the sign data valid for use with the _viewport_sign_kdtree?
Vehicle data structure.
int32_t z_pos
z coordinate.
Vehicle * First() const
Get the first vehicle of this vehicle chain.
int32_t y_pos
y coordinate.
int32_t x_pos
x coordinate.
Owner owner
Which company owns the vehicle?
UnitID unitnumber
unit number, for display purposes only
Data structure for a window viewport.
Definition window_gui.h:251
void CancelFollow(const Window &viewport_window)
Cancel viewport vehicle following, and raise follow location widget if needed.
int32_t dest_scrollpos_y
Current destination y coordinate to display (virtual screen coordinate of topleft corner of the viewp...
Definition window_gui.h:256
int32_t scrollpos_y
Currently shown y coordinate (virtual screen coordinate of topleft corner of the viewport).
Definition window_gui.h:254
int32_t dest_scrollpos_x
Current destination x coordinate to display (virtual screen coordinate of topleft corner of the viewp...
Definition window_gui.h:255
VehicleID follow_vehicle
VehicleID to follow if following a vehicle, VehicleID::Invalid() otherwise.
Definition window_gui.h:252
int32_t scrollpos_x
Currently shown x coordinate (virtual screen coordinate of topleft corner of the viewport).
Definition window_gui.h:253
Data structure storing rendering information.
Definition viewport.cpp:170
int foundation[FOUNDATION_PART_END]
Foundation sprites (index into parent_sprites_to_draw).
Definition viewport.cpp:183
int last_foundation_child[FOUNDATION_PART_END]
Tail of ChildSprite list of the foundations. (index into child_screen_sprites_to_draw)
Definition viewport.cpp:185
ParentSpriteToSortVector parent_sprites_to_sort
Parent sprite pointer array used for sorting.
Definition viewport.cpp:176
Point foundation_offset[FOUNDATION_PART_END]
Pixel offset for ground sprites on the foundations.
Definition viewport.cpp:186
SpriteCombineMode combine_sprites
Current mode of "sprite combining".
Definition viewport.cpp:181
FoundationPart foundation_part
Currently active foundation for ground sprite drawing.
Definition viewport.cpp:184
Helper class for getting the best sprite sorter.
VpSorterChecker fct_checker
The check function.
VpSpriteSorter fct_sorter
The sorting function.
Location information about a sign as seen on the viewport.
int32_t center
The center position of the sign.
uint16_t width_small
The width when zoomed out (small font)
uint16_t width_normal
The width when not zoomed out (normal font)
void UpdatePosition(int center, int top, std::string_view str, std::string_view str_small={})
Update the position of the viewport sign.
int32_t top
The top of the sign.
Data structure for viewport, display of a part of the world.
int top
Screen coordinate top edge of the viewport.
int width
Screen width of the viewport.
ZoomLevel zoom
The zoom level of the viewport.
int virtual_top
Virtual top coordinate.
int virtual_left
Virtual left coordinate.
int virtual_width
width << zoom
int left
Screen coordinate left edge of the viewport.
int height
Screen height of the viewport.
int virtual_height
height << zoom
Representation of a waypoint.
Number to differentiate different windows of the same class.
Iterator to iterate all valid Windows.
Definition window_gui.h:865
Data structure for an opened window.
Definition window_gui.h:274
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:556
void RaiseWidgetWhenLowered(WidgetID widget_index)
Marks a widget as raised and dirty (redraw), when it is marked as lowered.
Definition window_gui.h:479
virtual void OnPlaceObjectAbort()
The user cancelled a tile highlight mode that has been set.
Definition window_gui.h:812
WindowClass window_class
Window class.
Definition window_gui.h:302
ViewportData * viewport
Pointer to viewport data, if present.
Definition window_gui.h:319
void DrawViewport() const
Draw the viewport of this window.
virtual void OnPlaceObject(Point pt, TileIndex tile)
The user clicked some place on the map when a tile highlight mode has been set.
Definition window_gui.h:791
int left
x position of left edge of the window
Definition window_gui.h:310
int top
y position of top edge of the window
Definition window_gui.h:311
virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile)
The user has dragged over the map when the tile highlight mode has been set.
Definition window_gui.h:833
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:973
virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt)
The user is dragging over the map when the tile highlight mode has been set.
Definition window_gui.h:822
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:382
AllWindows< false > Iterate
Iterate all windows in whatever order is easiest.
Definition window_gui.h:923
int height
Height of the window (number of pixels down in y direction)
Definition window_gui.h:313
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:312
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:303
std::tuple< Slope, int > GetTilePixelSlopeOutsideMap(int x, int y)
Return the slope of a given tile, also for tiles outside the map (virtual "black" tiles).
Definition tile_map.cpp:78
int GetTilePixelZ(TileIndex tile)
Get bottom height of the tile.
Definition tile_map.h:302
int GetTileMaxPixelZ(TileIndex tile)
Get top height of the tile.
Definition tile_map.h:312
static debug_inline TileType GetTileType(Tile tile)
Get the tiletype of a given tile.
Definition tile_map.h:96
std::tuple< Slope, int > GetTilePixelSlope(TileIndex tile)
Return the slope of a given tile.
Definition tile_map.h:289
bool IsValidTile(Tile tile)
Checks if a tile is valid.
Definition tile_map.h:161
uint TilePixelHeight(Tile tile)
Returns the height of a tile in pixels.
Definition tile_map.h:72
uint TilePixelHeightOutsideMap(int x, int y)
Returns the height of a tile in pixels, also for tiles outside the map (virtual "black" tiles).
Definition tile_map.h:84
static debug_inline bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
static debug_inline uint TileHeight(Tile tile)
Returns the height of a tile.
Definition tile_map.h:29
uint TileHeightOutsideMap(int x, int y)
Returns the height of a tile, also for tiles outside the map (virtual "black" tiles).
Definition tile_map.h:42
static const uint TILE_PIXELS
Pixel distance between tile columns/rows in ZOOM_BASE.
Definition tile_type.h:17
static const uint MAX_BUILDING_PIXELS
Maximum height of a building in pixels in ZOOM_BASE. (Also applies to "bridge buildings" on the bridg...
Definition tile_type.h:20
static const uint TILE_HEIGHT
Height of a height level in world coordinate AND in pixels in ZOOM_BASE.
Definition tile_type.h:18
static const uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
static const uint TILE_UNIT_MASK
For masking in/out the inner-tile world coordinate units.
Definition tile_type.h:16
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
TileType
The different types of tiles.
Definition tile_type.h:47
@ MP_STATION
A tile of a station.
Definition tile_type.h:53
@ MP_HOUSE
A house by a town.
Definition tile_type.h:51
@ MP_VOID
Invisible tiles at the SW and SE border.
Definition tile_type.h:55
Functions related to tile highlights.
HighLightStyle
Highlighting draw styles.
@ HT_LINE
used for autorail highlighting (longer stretches), lower bits: direction
@ HT_DIR_HL
horizontal lower
@ HT_DIR_HU
horizontal upper
@ HT_DRAG
dragging items in the depot windows
@ HT_NONE
default
@ HT_DIR_X
X direction.
@ HT_DIAGONAL
Also allow 'diagonal rectangles'. Only usable in combination with HT_RECT or HT_POINT.
@ HT_POINT
point (lower land, raise land, level land, ...)
@ HT_RECT
rectangle (stations, depots, ...)
@ HT_DIR_MASK
masks the drag-direction
@ HT_RAIL
autorail (one piece), lower bits: direction
@ HT_DIR_END
end marker
@ HT_DRAG_MASK
Mask for the tile drag-type modes.
@ HT_DIR_VL
vertical left
@ HT_VEHICLE
vehicle is accepted as target as well (bitmask)
@ HT_DIR_VR
vertical right
@ HT_SPECIAL
special mode used for highlighting while dragging (and for tunnels/docks)
@ HT_DIR_Y
Y direction.
Base of the town class.
Town * ClosestTownFromTile(TileIndex tile, uint threshold)
Return the town closest (in distance or ownership) to a given tile, within a given threshold.
Declarations for accessing the k-d tree of towns.
TownID GetTownIndex(Tile t)
Get the index of which town this house/street is attached to.
Definition town_map.h:23
bool IsTransparencySet(TransparencyOption to)
Check if the transparency option bit is set and if we aren't in the game menu (there's never transpar...
uint8_t _display_opt
What do we want to draw/do?
bool IsInvisibilitySet(TransparencyOption to)
Check if the invisibility option bit is set and if we aren't in the game menu (there's never transpar...
@ TO_SIGNS
signs
void ViewportAddVehicles(DrawPixelInfo *dpi)
Add the vehicle sprites that should be drawn at a part of the screen.
Definition vehicle.cpp:1148
Vehicle * CheckClickOnVehicle(const Viewport *vp, int x, int y)
Find the vehicle close to the clicked coordinates.
Definition vehicle.cpp:1242
Base class for all vehicles.
Functions related to vehicles.
bool IsCompanyBuildableVehicleType(VehicleType type)
Is the given vehicle type buildable by a company?
void ShowVehicleViewWindow(const Vehicle *v)
Shows the vehicle view window of the given vehicle.
bool VehicleClicked(const Vehicle *v)
Dispatch a "vehicle selected" event if any window waits for it.
void StartStopVehicle(const Vehicle *v, bool texteffect)
Executes CMD_START_STOP_VEHICLE for given vehicle.
Functions related to the vehicle's GUIs.
Types related to the vehicle widgets.
@ WID_VV_LOCATION
Center the main view on this vehicle.
static void HighlightTownLocalAuthorityTiles(const TileInfo *ti)
Highlights tiles insede local authority of selected towns.
std::string * ViewportAddString(const DrawPixelInfo *dpi, const ViewportSign *sign, ViewportStringFlags flags, Colours colour)
Add a string to draw in the current viewport.
static void CheckOverflow(int &test, int &other, int max, int mult)
Check for overflowing the map.
bool ScrollWindowToTile(TileIndex tile, Window *w, bool instant)
Scrolls the viewport in a window to a given location.
FoundationPart
Enumeration of multi-part foundations.
Definition viewport.cpp:144
@ FOUNDATION_PART_NONE
Neither foundation nor groundsprite drawn yet.
Definition viewport.cpp:145
@ FOUNDATION_PART_HALFTILE
Second part (halftile foundation)
Definition viewport.cpp:147
@ FOUNDATION_PART_NORMAL
First part (normal foundation or no foundation)
Definition viewport.cpp:146
void SetTileSelectSize(int w, int h)
Highlight w by h tiles at the cursor.
CommandCost CmdScrollViewport(DoCommandFlags flags, TileIndex tile, ViewportScrollTarget target, uint32_t ref)
Scroll players main viewport.
static void ViewportAddStationStrings(DrawPixelInfo *dpi, const std::vector< const BaseStation * > &stations, bool small)
Add station strings to a viewport.
void OffsetGroundSprite(int x, int y)
Called when a foundation has been drawn for the current tile.
Definition viewport.cpp:601
void HandleZoomMessage(Window *w, const Viewport *vp, WidgetID widget_zoom_in, WidgetID widget_zoom_out)
Update the status of the zoom-buttons according to the zoom-level of the viewport.
Definition viewport.cpp:496
constexpr int LAST_CHILD_NONE
There is no last_child to fill.
Definition viewport.cpp:166
void ResetObjectToPlace()
Reset the cursor and mouse mode handling back to default (normal cursor, only clicking in windows).
bool ScrollMainWindowToTile(TileIndex tile, bool instant)
Scrolls the viewport of the main window to a given location.
static void ViewportAddSignStrings(DrawPixelInfo *dpi, const std::vector< const Sign * > &signs, bool small)
Add sign strings to a viewport.
void SetObjectToPlaceWnd(CursorID icon, PaletteID pal, HighLightStyle mode, Window *w)
Change the cursor and mouse click/drag handling to a mode for performing special operations like tile...
constexpr int LAST_CHILD_PARENT
Fill last_child of the most recent parent sprite.
Definition viewport.cpp:167
static const int MAX_TILE_EXTENT_TOP
Maximum top extent of tile relative to north corner (not considering bridges).
Definition viewport.cpp:113
static void ShowMeasurementTooltips(EncodedString &&text)
Displays the measurement tooltips when selecting multiple tiles.
static const int MAX_TILE_EXTENT_LEFT
Maximum left extent of tile relative to north corner.
Definition viewport.cpp:111
bool IsInsideRotatedRectangle(int x, int y)
Checks whether a point is inside the selected a diagonal rectangle given by _thd.size and _thd....
Definition viewport.cpp:806
static int CalcHeightdiff(HighLightStyle style, uint distance, TileIndex start_tile, TileIndex end_tile)
Calculates height difference between one tile and another.
static void DrawTileSelectionRect(const TileInfo *ti, PaletteID pal)
Draws a selection rectangle on a tile.
Definition viewport.cpp:919
void StartSpriteCombine()
Starts a block of sprites, which are "combined" into a single bounding box.
Definition viewport.cpp:769
void InitializeSpriteSorter()
Choose the "best" sprite sorter and set _vp_sprite_sorter.
static void AddCombinedSprite(SpriteID image, PaletteID pal, int x, int y, int z, const SubSprite *sub)
Adds a child sprite to a parent sprite.
Definition viewport.cpp:633
static int GetViewportY(Point tile)
Returns the y coordinate in the viewport coordinate system where the given tile is painted.
static void ClampViewportToMap(const Viewport *vp, int *scroll_x, int *scroll_y)
Ensure that a given viewport has a valid scroll position.
static void AddChildSpriteToFoundation(SpriteID image, PaletteID pal, const SubSprite *sub, FoundationPart foundation_part, int extra_offs_x, int extra_offs_y)
Adds a child sprite to the active foundation.
Definition viewport.cpp:542
static HighLightStyle GetAutorailHT(int x, int y)
returns the best autorail highlight type from map coordinates
static void ClampSmoothScroll(uint32_t delta_ms, int64_t delta_hi, int64_t delta_lo, int &delta_hi_clamped, int &delta_lo_clamped)
Clamp the smooth scroll to a maxmimum speed and distance based on time elapsed.
static void DrawTileSelection(const TileInfo *ti)
Checks if the specified tile is selected and if so draws selection using correct selectionstyle.
Point TranslateXYToTileCoord(const Viewport *vp, int x, int y, bool clamp_to_map)
Translate screen coordinate in a viewport to underlying tile coordinate.
Definition viewport.cpp:437
static TileHighlightType GetTileHighlightType(TileIndex t)
Get tile highlight type of coverage area for a given tile.
static bool IsInRangeInclusive(int begin, int end, int check)
Check if the parameter "check" is inside the interval between begin and end, including both begin and...
Definition viewport.cpp:794
static void DrawAutorailSelection(const TileInfo *ti, uint autorail_type)
Draws autorail highlights.
Definition viewport.cpp:979
SpriteCombineMode
Mode of "sprite combining".
Definition viewport.cpp:155
@ SPRITE_COMBINE_PENDING
Sprite combining will start with the next unclipped sprite.
Definition viewport.cpp:157
@ SPRITE_COMBINE_ACTIVE
Sprite combining is active. AddSortableSpriteToDraw outputs child sprites.
Definition viewport.cpp:158
@ SPRITE_COMBINE_NONE
Every AddSortableSpriteToDraw start its own bounding box.
Definition viewport.cpp:156
static void DrawTileHighlightType(const TileInfo *ti, TileHighlightType tht)
Draw tile highlight for coverage area highlight.
static void ViewportDrawBoundingBoxes(const ParentSpriteToSortVector *psd)
Draws the bounding boxes of all ParentSprites.
void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int w, int h, int dz, int z, bool transparent, int bb_offset_x, int bb_offset_y, int bb_offset_z, const SubSprite *sub)
Draw a (transparent) sprite at given coordinates with a given bounding box.
Definition viewport.cpp:673
static void ViewportAddTownStrings(DrawPixelInfo *dpi, const std::vector< const Town * > &towns, bool small)
Add town strings to a viewport.
void SetViewportCatchmentWaypoint(const Waypoint *wp, bool sel)
Select or deselect waypoint for coverage area highlight.
void SetRedErrorSquare(TileIndex tile)
Set a tile to display a red error square.
static void ViewportSortParentSprites(ParentSpriteToSortVector *psdv)
Sort parent sprites pointer array replicating the way original sorter did it.
void VpSetPresizeRange(TileIndex from, TileIndex to)
Highlights all tiles between a set of two tiles.
void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method)
Selects tiles while dragging.
EventState VpHandlePlaceSizingDrag()
Handle the mouse while dragging for placement/resizing.
Viewport * IsPtInWindowViewport(const Window *w, int x, int y)
Is a xy position inside the viewport of the window?
Definition viewport.cpp:413
void SetObjectToPlace(CursorID icon, PaletteID pal, HighLightStyle mode, WindowClass window_class, WindowNumber window_num)
Change the cursor and mouse click/drag handling to a mode for performing special operations like tile...
void EndSpriteCombine()
Terminates a block of sprites started by StartSpriteCombine.
Definition viewport.cpp:779
static const int MAX_TILE_EXTENT_RIGHT
Maximum right extent of tile relative to north corner.
Definition viewport.cpp:112
void AddChildSpriteScreen(SpriteID image, PaletteID pal, int x, int y, bool transparent, const SubSprite *sub, bool scale, bool relative)
Add a child sprite to a parent sprite.
Definition viewport.cpp:829
static void CheckUnderflow(int &test, int &other, int mult)
Check for underflowing the map.
static bool ViewportSortParentSpritesChecker()
This fallback sprite checker always exists.
void DrawGroundSpriteAt(SpriteID image, PaletteID pal, int32_t x, int32_t y, int z, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
Draws a ground sprite at a specific world-coordinate relative to the current tile.
Definition viewport.cpp:566
void VpStartDragging(ViewportDragDropSelectionProcess process)
Drag over the map while holding the left mouse down.
static ViewportSSCSS _vp_sprite_sorters[]
List of sorters ordered from best to worst.
void UpdateTileSelection()
Updates tile highlighting for all cases.
static void DrawSelectionSprite(SpriteID image, PaletteID pal, const TileInfo *ti, int z_offset, FoundationPart foundation_part, int extra_offs_x=0, int extra_offs_y=0)
Draws sprites between ground sprite and everything above.
Definition viewport.cpp:902
static const int MAX_TILE_EXTENT_BOTTOM
Maximum bottom extent of tile relative to north corner (worst case: SLOPE_STEEP_N).
Definition viewport.cpp:114
void SetViewportCatchmentStation(const Station *st, bool sel)
Select or deselect station for coverage area highlight.
void SetViewportCatchmentTown(const Town *t, bool sel)
Select or deselect town for coverage area highlight.
static bool SwapDirection(HighLightStyle style, TileIndex start_tile, TileIndex end_tile)
Check if the direction of start and end tile should be swapped based on the dragging-style.
void InitializeWindowViewport(Window *w, int x, int y, int width, int height, std::variant< TileIndex, VehicleID > focus, ZoomLevel zoom)
Initialize viewport of the window for use.
Definition viewport.cpp:224
void DrawGroundSprite(SpriteID image, PaletteID pal, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
Draws a ground sprite for the current tile.
Definition viewport.cpp:589
static void CalcRaildirsDrawstyle(int x, int y, int method)
while dragging
static HighLightStyle Check2x1AutoRail(int mode)
returns information about the 2x1 piece to be build.
static bool CheckClickOnViewportSign(const Viewport *vp, int x, int y, const ViewportSign *sign)
Test whether a sign is below the mouse.
static void ViewportDrawDirtyBlocks()
Draw/colour the blocks that have been redrawn.
void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process)
highlighting tiles while only going over them with the mouse
static void AddTileSpriteToDraw(SpriteID image, PaletteID pal, int32_t x, int32_t y, int z, const SubSprite *sub=nullptr, int extra_offs_x=0, int extra_offs_y=0)
Schedules a tile sprite for drawing.
Definition viewport.cpp:517
static void ViewportAddLandscape()
Add the landscape to the viewport, i.e.
static std::string & AddStringToDraw(int x, int y, Colours colour, ViewportStringFlags flags, uint16_t width)
Add a string to draw to a viewport.
Definition viewport.cpp:875
const Station * _viewport_highlight_station
Currently selected station for coverage area highlight.
const Town * _viewport_highlight_town
Currently selected town for coverage area highlight.
void UpdateViewportPosition(Window *w, uint32_t delta_ms)
Update the viewport position being displayed.
const Waypoint * _viewport_highlight_waypoint
Currently selected waypoint for coverage area highlight.
bool ScrollWindowTo(int x, int y, int z, Window *w, bool instant)
Scrolls the viewport in a window to a given location.
Command definitions related to viewports.
Functions related to (drawing on) viewports.
static const int TILE_HEIGHT_STEP
One Z unit tile height difference is displayed as 50m.
Types related to sprite sorting.
bool(* VpSorterChecker)()
Type for method for checking whether a viewport sprite sorter exists.
void(* VpSpriteSorter)(ParentSpriteToSortVector *psd)
Type for the actual viewport sprite sorter.
ViewportScrollTarget
Target of the viewport scrolling GS method.
@ VST_EVERYONE
All players.
@ VST_COMPANY
All players in specific company.
@ VST_CLIENT
Single player.
ViewportPlaceMethod
Viewport place method (type of highlighted area and placed objects)
@ VPM_FIX_Y
drag only in Y axis
@ VPM_Y_LIMITED
Drag only in Y axis with limited size.
@ VPM_X_AND_Y_LIMITED
area of land of limited size
@ VPM_FIX_VERTICAL
drag only in vertical direction
@ VPM_X_LIMITED
Drag only in X axis with limited size.
@ VPM_X_AND_Y
area of land in X and Y directions
@ VPM_FIX_HORIZONTAL
drag only in horizontal direction
@ VPM_FIX_X
drag only in X axis
@ VPM_SIGNALDIRS
similar to VMP_RAILDIRS, but with different cursor
@ VPM_X_OR_Y
drag in X or Y direction
@ VPM_RAILDIRS
all rail directions
@ ZOOM_IN
Zoom in (get more detailed view).
@ ZOOM_OUT
Zoom out (get helicopter view).
ViewportDragDropSelectionProcess
Drag and drop selection process, or, what to do with an area of land when you've selected it.
@ ColourRect
Draw a colour rect around the sign.
@ Small
Draw using the small font.
@ Shadow
Draw an extra text shadow. Should only be used with ViewportStringFlag::Small, as normal font already...
@ TransparentRect
Draw a transparent rect around the sign.
@ TextColour
Draw text in colour.
Base of waypoints.
Functions related to waypoints.
void ShowWaypointWindow(const Waypoint *wp)
Show the window for the given waypoint.
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
Definition widget.cpp:296
void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
Close a window by its class and window number (if it is open).
Definition window.cpp:1145
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1132
Window * FindWindowFromPt(int x, int y)
Do a search for a window at specific coordinates.
Definition window.cpp:1773
SpecialMouseMode _special_mouse_mode
Mode of the mouse.
Definition window.cpp:95
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1103
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3108
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.
@ Transparent
Makes the background transparent if set.
@ WSM_DRAGDROP
Drag&drop an object.
@ WSM_DRAGGING
Dragging mode (trees).
@ WSM_PRESIZE
Presizing mode (docks, tunnels).
@ WSM_NONE
No special mouse mode.
@ WSM_SIZING
Sizing mode.
int WidgetID
Widget ID.
Definition window_type.h:20
EventState
State of handling an event.
@ ES_HANDLED
The passed event is handled.
@ ES_NOT_HANDLED
The passed event is not handled.
WindowClass
Window classes.
Definition window_type.h:46
@ WC_INVALID
Invalid window.
@ WC_WAYPOINT_VIEW
Waypoint view; Window numbers:
@ WC_STATION_VIEW
Station view; Window numbers:
@ WC_MAIN_WINDOW
Main window; Window numbers:
Definition window_type.h:53
@ WC_TOWN_VIEW
Town view; Window numbers:
@ WC_TOOLTIPS
Tooltip window; Window numbers:
@ WC_VEHICLE_VIEW
Vehicle view; Window numbers:
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZOOM_LVL_MIN) When shifting right,...
Definition zoom_func.h:22
int UnScaleByZoomLower(int value, ZoomLevel zoom)
Scale by zoom level, usually shift right (when zoom > ZOOM_LVL_MIN)
Definition zoom_func.h:67
int UnScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift right (when zoom > ZOOM_LVL_MIN) When shifting right,...
Definition zoom_func.h:34
ZoomLevel
All zoom levels we know.
Definition zoom_type.h:16
@ ZOOM_LVL_BEGIN
Begin for iteration.
Definition zoom_type.h:18
@ ZOOM_LVL_OUT_4X
Zoomed 4 times out.
Definition zoom_type.h:23
@ ZOOM_LVL_END
End for iteration.
Definition zoom_type.h:25
@ ZOOM_LVL_MIN
Minimum zoom level.
Definition zoom_type.h:41