OpenTTD Source 20250205-master-gfd85ab1e2c
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
97#include "table/strings.h"
99
100#include "safeguards.h"
101
102Point _tile_fract_coords;
103
104
105ViewportSignKdtree _viewport_sign_kdtree{};
106static int _viewport_sign_maxwidth = 0;
107
108
109static const int MAX_TILE_EXTENT_LEFT = ZOOM_BASE * TILE_PIXELS;
110static const int MAX_TILE_EXTENT_RIGHT = ZOOM_BASE * TILE_PIXELS;
111static const int MAX_TILE_EXTENT_TOP = ZOOM_BASE * MAX_BUILDING_PIXELS;
112static const int MAX_TILE_EXTENT_BOTTOM = ZOOM_BASE * (TILE_PIXELS + 2 * TILE_HEIGHT);
113
115 std::string string;
116 uint16_t width;
117 Colours colour;
119 int32_t x;
120 int32_t y;
121};
122
124 SpriteID image;
125 PaletteID pal;
126 const SubSprite *sub;
127 int32_t x;
128 int32_t y;
129};
130
132 SpriteID image;
133 PaletteID pal;
134 const SubSprite *sub;
135 int32_t x;
136 int32_t y;
137 bool relative;
138 int next;
139};
140
142enum FoundationPart : uint8_t {
146 FOUNDATION_PART_END
147};
148
158
159typedef std::vector<TileSpriteToDraw> TileSpriteToDrawVector;
160typedef std::vector<StringSpriteToDraw> StringSpriteToDrawVector;
161typedef std::vector<ParentSpriteToDraw> ParentSpriteToDrawVector;
162typedef std::vector<ChildScreenSpriteToDraw> ChildScreenSpriteToDrawVector;
163
164constexpr int LAST_CHILD_NONE = -1;
165constexpr int LAST_CHILD_PARENT = -2;
166
169 DrawPixelInfo dpi;
170
171 StringSpriteToDrawVector string_sprites_to_draw;
172 TileSpriteToDrawVector tile_sprites_to_draw;
173 ParentSpriteToDrawVector parent_sprites_to_draw;
174 ParentSpriteToSortVector parent_sprites_to_sort;
175 ChildScreenSpriteToDrawVector child_screen_sprites_to_draw;
176
177 int last_child;
178
180
181 int foundation[FOUNDATION_PART_END];
183 int last_foundation_child[FOUNDATION_PART_END];
184 Point foundation_offset[FOUNDATION_PART_END];
185};
186
187static bool MarkViewportDirty(const Viewport *vp, int left, int top, int right, int bottom);
188
189static ViewportDrawer _vd;
190
192static TileInfo _cur_ti;
193bool _draw_bounding_boxes = false;
194bool _draw_dirty_blocks = false;
195uint _dirty_block_colour = 0;
196static VpSpriteSorter _vp_sprite_sorter = nullptr;
197
198static Point MapXYZToViewport(const Viewport *vp, int x, int y, int z)
199{
200 Point p = RemapCoords(x, y, z);
201 p.x -= vp->virtual_width / 2;
202 p.y -= vp->virtual_height / 2;
203 return p;
204}
205
206void DeleteWindowViewport(Window *w)
207{
208 delete w->viewport;
209 w->viewport = nullptr;
210}
211
222void InitializeWindowViewport(Window *w, int x, int y,
223 int width, int height, std::variant<TileIndex, VehicleID> focus, ZoomLevel zoom)
224{
225 assert(w->viewport == nullptr);
226
227 ViewportData *vp = new ViewportData();
228
229 vp->left = x + w->left;
230 vp->top = y + w->top;
231 vp->width = width;
232 vp->height = height;
233
235
236 vp->virtual_width = ScaleByZoom(width, zoom);
237 vp->virtual_height = ScaleByZoom(height, zoom);
238
239 Point pt;
240
241 if (std::holds_alternative<VehicleID>(focus)) {
242 const Vehicle *veh;
243
244 vp->follow_vehicle = std::get<VehicleID>(focus);
245 veh = Vehicle::Get(vp->follow_vehicle);
246 pt = MapXYZToViewport(vp, veh->x_pos, veh->y_pos, veh->z_pos);
247 } else {
248 TileIndex tile = std::get<TileIndex>(focus);
249 if (tile == INVALID_TILE) {
250 /* No tile? Use center of main viewport. */
251 const Window *mw = GetMainWindow();
252
253 /* center on same place as main window (zoom is maximum, no adjustment needed) */
254 pt.x = mw->viewport->scrollpos_x + mw->viewport->virtual_width / 2;
255 pt.x -= vp->virtual_width / 2;
256 pt.y = mw->viewport->scrollpos_y + mw->viewport->virtual_height / 2;
257 pt.y -= vp->virtual_height / 2;
258 } else {
259 x = TileX(tile) * TILE_SIZE;
260 y = TileY(tile) * TILE_SIZE;
261 pt = MapXYZToViewport(vp, x, y, GetSlopePixelZ(x, y));
262 }
264 }
265
266 vp->scrollpos_x = pt.x;
267 vp->scrollpos_y = pt.y;
268 vp->dest_scrollpos_x = pt.x;
269 vp->dest_scrollpos_y = pt.y;
270
271 vp->overlay = nullptr;
272
273 w->viewport = vp;
274 vp->virtual_left = 0;
275 vp->virtual_top = 0;
276}
277
278static Point _vp_move_offs;
279
280static void DoSetViewportPosition(Window::IteratorToFront it, int left, int top, int width, int height)
281{
282 for (; !it.IsEnd(); ++it) {
283 const Window *w = *it;
284 if (left + width > w->left &&
285 w->left + w->width > left &&
286 top + height > w->top &&
287 w->top + w->height > top) {
288
289 if (left < w->left) {
290 DoSetViewportPosition(it, left, top, w->left - left, height);
291 DoSetViewportPosition(it, left + (w->left - left), top, width - (w->left - left), height);
292 return;
293 }
294
295 if (left + width > w->left + w->width) {
296 DoSetViewportPosition(it, left, top, (w->left + w->width - left), height);
297 DoSetViewportPosition(it, left + (w->left + w->width - left), top, width - (w->left + w->width - left), height);
298 return;
299 }
300
301 if (top < w->top) {
302 DoSetViewportPosition(it, left, top, width, (w->top - top));
303 DoSetViewportPosition(it, left, top + (w->top - top), width, height - (w->top - top));
304 return;
305 }
306
307 if (top + height > w->top + w->height) {
308 DoSetViewportPosition(it, left, top, width, (w->top + w->height - top));
309 DoSetViewportPosition(it, left, top + (w->top + w->height - top), width, height - (w->top + w->height - top));
310 return;
311 }
312
313 return;
314 }
315 }
316
317 {
318 int xo = _vp_move_offs.x;
319 int yo = _vp_move_offs.y;
320
321 if (abs(xo) >= width || abs(yo) >= height) {
322 /* fully_outside */
323 RedrawScreenRect(left, top, left + width, top + height);
324 return;
325 }
326
327 GfxScroll(left, top, width, height, xo, yo);
328
329 if (xo > 0) {
330 RedrawScreenRect(left, top, xo + left, top + height);
331 left += xo;
332 width -= xo;
333 } else if (xo < 0) {
334 RedrawScreenRect(left + width + xo, top, left + width, top + height);
335 width += xo;
336 }
337
338 if (yo > 0) {
339 RedrawScreenRect(left, top, width + left, top + yo);
340 } else if (yo < 0) {
341 RedrawScreenRect(left, top + height + yo, width + left, top + height);
342 }
343 }
344}
345
346static void SetViewportPosition(Window *w, int x, int y)
347{
348 Viewport *vp = w->viewport;
349 int old_left = vp->virtual_left;
350 int old_top = vp->virtual_top;
351 int i;
352 int left, top, width, height;
353
354 vp->virtual_left = x;
355 vp->virtual_top = y;
356
357 /* Viewport is bound to its left top corner, so it must be rounded down (UnScaleByZoomLower)
358 * else glitch described in FS#1412 will happen (offset by 1 pixel with zoom level > NORMAL)
359 */
360 old_left = UnScaleByZoomLower(old_left, vp->zoom);
361 old_top = UnScaleByZoomLower(old_top, vp->zoom);
362 x = UnScaleByZoomLower(x, vp->zoom);
363 y = UnScaleByZoomLower(y, vp->zoom);
364
365 old_left -= x;
366 old_top -= y;
367
368 if (old_top == 0 && old_left == 0) return;
369
370 _vp_move_offs.x = old_left;
371 _vp_move_offs.y = old_top;
372
373 left = vp->left;
374 top = vp->top;
375 width = vp->width;
376 height = vp->height;
377
378 if (left < 0) {
379 width += left;
380 left = 0;
381 }
382
383 i = left + width - _screen.width;
384 if (i >= 0) width -= i;
385
386 if (width > 0) {
387 if (top < 0) {
388 height += top;
389 top = 0;
390 }
391
392 i = top + height - _screen.height;
393 if (i >= 0) height -= i;
394
395 if (height > 0) {
397 ++it;
398 DoSetViewportPosition(it, left, top, width, height);
399 }
400 }
401}
402
411Viewport *IsPtInWindowViewport(const Window *w, int x, int y)
412{
413 Viewport *vp = w->viewport;
414
415 if (vp != nullptr &&
416 IsInsideMM(x, vp->left, vp->left + vp->width) &&
417 IsInsideMM(y, vp->top, vp->top + vp->height))
418 return vp;
419
420 return nullptr;
421}
422
435Point TranslateXYToTileCoord(const Viewport *vp, int x, int y, bool clamp_to_map)
436{
437 if (!IsInsideBS(x, vp->left, vp->width) || !IsInsideBS(y, vp->top, vp->height)) {
438 Point pt = { -1, -1 };
439 return pt;
440 }
441
442 return InverseRemapCoords2(
443 ScaleByZoom(x - vp->left, vp->zoom) + vp->virtual_left,
444 ScaleByZoom(y - vp->top, vp->zoom) + vp->virtual_top, clamp_to_map);
445}
446
447/* When used for zooming, check area below current coordinates (x,y)
448 * and return the tile of the zoomed out/in position (zoom_x, zoom_y)
449 * when you just want the tile, make x = zoom_x and y = zoom_y */
450static Point GetTileFromScreenXY(int x, int y, int zoom_x, int zoom_y)
451{
452 Window *w;
453 Viewport *vp;
454 Point pt;
455
456 if ( (w = FindWindowFromPt(x, y)) != nullptr &&
457 (vp = IsPtInWindowViewport(w, x, y)) != nullptr)
458 return TranslateXYToTileCoord(vp, zoom_x, zoom_y);
459
460 pt.y = pt.x = -1;
461 return pt;
462}
463
464Point GetTileBelowCursor()
465{
466 return GetTileFromScreenXY(_cursor.pos.x, _cursor.pos.y, _cursor.pos.x, _cursor.pos.y);
467}
468
469
470Point GetTileZoomCenterWindow(bool in, Window * w)
471{
472 int x, y;
473 Viewport *vp = w->viewport;
474
475 if (in) {
476 x = ((_cursor.pos.x - vp->left) >> 1) + (vp->width >> 2);
477 y = ((_cursor.pos.y - vp->top) >> 1) + (vp->height >> 2);
478 } else {
479 x = vp->width - (_cursor.pos.x - vp->left);
480 y = vp->height - (_cursor.pos.y - vp->top);
481 }
482 /* Get the tile below the cursor and center on the zoomed-out center */
483 return GetTileFromScreenXY(_cursor.pos.x, _cursor.pos.y, x + vp->left, y + vp->top);
484}
485
494void HandleZoomMessage(Window *w, const Viewport *vp, WidgetID widget_zoom_in, WidgetID widget_zoom_out)
495{
496 w->SetWidgetDisabledState(widget_zoom_in, vp->zoom <= _settings_client.gui.zoom_min);
497 w->SetWidgetDirty(widget_zoom_in);
498
499 w->SetWidgetDisabledState(widget_zoom_out, vp->zoom >= _settings_client.gui.zoom_max);
500 w->SetWidgetDirty(widget_zoom_out);
501}
502
515static 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)
516{
517 assert((image & SPRITE_MASK) < MAX_SPRITES);
518
519 TileSpriteToDraw &ts = _vd.tile_sprites_to_draw.emplace_back();
520 ts.image = image;
521 ts.pal = pal;
522 ts.sub = sub;
523 Point pt = RemapCoords(x, y, z);
524 ts.x = pt.x + extra_offs_x;
525 ts.y = pt.y + extra_offs_y;
526}
527
540static void AddChildSpriteToFoundation(SpriteID image, PaletteID pal, const SubSprite *sub, FoundationPart foundation_part, int extra_offs_x, int extra_offs_y)
541{
542 assert(IsInsideMM(foundation_part, 0, FOUNDATION_PART_END));
543 assert(_vd.foundation[foundation_part] != -1);
544 Point offs = _vd.foundation_offset[foundation_part];
545
546 /* Change the active ChildSprite list to the one of the foundation */
547 AutoRestoreBackup backup(_vd.last_child, _vd.last_foundation_child[foundation_part]);
548 AddChildSpriteScreen(image, pal, offs.x + extra_offs_x, offs.y + extra_offs_y, false, sub, false, false);
549}
550
564void DrawGroundSpriteAt(SpriteID image, PaletteID pal, int32_t x, int32_t y, int z, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
565{
566 /* Switch to first foundation part, if no foundation was drawn */
568
569 if (_vd.foundation[_vd.foundation_part] != -1) {
570 Point pt = RemapCoords(x, y, z);
571 AddChildSpriteToFoundation(image, pal, sub, _vd.foundation_part, pt.x + extra_offs_x * ZOOM_BASE, pt.y + extra_offs_y * ZOOM_BASE);
572 } else {
573 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);
574 }
575}
576
587void DrawGroundSprite(SpriteID image, PaletteID pal, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
588{
589 DrawGroundSpriteAt(image, pal, 0, 0, 0, sub, extra_offs_x, extra_offs_y);
590}
591
599void OffsetGroundSprite(int x, int y)
600{
601 /* Switch to next foundation part */
602 switch (_vd.foundation_part) {
605 break;
608 break;
609 default: NOT_REACHED();
610 }
611
612 /* _vd.last_child is LAST_CHILD_NONE if foundation sprite was clipped by the viewport bounds */
613 if (_vd.last_child != LAST_CHILD_NONE) _vd.foundation[_vd.foundation_part] = static_cast<uint>(_vd.parent_sprites_to_draw.size()) - 1;
614
615 _vd.foundation_offset[_vd.foundation_part].x = x * ZOOM_BASE;
616 _vd.foundation_offset[_vd.foundation_part].y = y * ZOOM_BASE;
617 _vd.last_foundation_child[_vd.foundation_part] = _vd.last_child;
618}
619
631static void AddCombinedSprite(SpriteID image, PaletteID pal, int x, int y, int z, const SubSprite *sub)
632{
633 Point pt = RemapCoords(x, y, z);
634 const Sprite *spr = GetSprite(image & SPRITE_MASK, SpriteType::Normal);
635
636 if (pt.x + spr->x_offs >= _vd.dpi.left + _vd.dpi.width ||
637 pt.x + spr->x_offs + spr->width <= _vd.dpi.left ||
638 pt.y + spr->y_offs >= _vd.dpi.top + _vd.dpi.height ||
639 pt.y + spr->y_offs + spr->height <= _vd.dpi.top)
640 return;
641
642 const ParentSpriteToDraw &pstd = _vd.parent_sprites_to_draw.back();
643 AddChildSpriteScreen(image, pal, pt.x - pstd.left, pt.y - pstd.top, false, sub, false);
644}
645
671void 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)
672{
673 int32_t left, right, top, bottom;
674
675 assert((image & SPRITE_MASK) < MAX_SPRITES);
676
677 /* make the sprites transparent with the right palette */
678 if (transparent) {
681 }
682
684 AddCombinedSprite(image, pal, x, y, z, sub);
685 return;
686 }
687
688 _vd.last_child = LAST_CHILD_NONE;
689
690 Point pt = RemapCoords(x, y, z);
691 int tmp_left, tmp_top, tmp_x = pt.x, tmp_y = pt.y;
692
693 /* Compute screen extents of sprite */
694 if (image == SPR_EMPTY_BOUNDING_BOX) {
695 left = tmp_left = RemapCoords(x + w , y + bb_offset_y, z + bb_offset_z).x;
696 right = RemapCoords(x + bb_offset_x, y + h , z + bb_offset_z).x + 1;
697 top = tmp_top = RemapCoords(x + bb_offset_x, y + bb_offset_y, z + dz ).y;
698 bottom = RemapCoords(x + w , y + h , z + bb_offset_z).y + 1;
699 } else {
700 const Sprite *spr = GetSprite(image & SPRITE_MASK, SpriteType::Normal);
701 left = tmp_left = (pt.x += spr->x_offs);
702 right = (pt.x + spr->width );
703 top = tmp_top = (pt.y += spr->y_offs);
704 bottom = (pt.y + spr->height);
705 }
706
707 if (_draw_bounding_boxes && (image != SPR_EMPTY_BOUNDING_BOX)) {
708 /* Compute maximal extents of sprite and its bounding box */
709 left = std::min(left , RemapCoords(x + w , y + bb_offset_y, z + bb_offset_z).x);
710 right = std::max(right , RemapCoords(x + bb_offset_x, y + h , z + bb_offset_z).x + 1);
711 top = std::min(top , RemapCoords(x + bb_offset_x, y + bb_offset_y, z + dz ).y);
712 bottom = std::max(bottom, RemapCoords(x + w , y + h , z + bb_offset_z).y + 1);
713 }
714
715 /* Do not add the sprite to the viewport, if it is outside */
716 if (left >= _vd.dpi.left + _vd.dpi.width ||
717 right <= _vd.dpi.left ||
718 top >= _vd.dpi.top + _vd.dpi.height ||
719 bottom <= _vd.dpi.top) {
720 return;
721 }
722
723 ParentSpriteToDraw &ps = _vd.parent_sprites_to_draw.emplace_back();
724 ps.x = tmp_x;
725 ps.y = tmp_y;
726
727 ps.left = tmp_left;
728 ps.top = tmp_top;
729
730 ps.image = image;
731 ps.pal = pal;
732 ps.sub = sub;
733 ps.xmin = x + bb_offset_x;
734 ps.xmax = x + std::max(bb_offset_x, w) - 1;
735
736 ps.ymin = y + bb_offset_y;
737 ps.ymax = y + std::max(bb_offset_y, h) - 1;
738
739 ps.zmin = z + bb_offset_z;
740 ps.zmax = z + std::max(bb_offset_z, dz) - 1;
741
743
744 _vd.last_child = LAST_CHILD_PARENT;
745
747}
748
772
782
792static bool IsInRangeInclusive(int begin, int end, int check)
793{
794 if (begin > end) Swap(begin, end);
795 return begin <= check && check <= end;
796}
797
804bool IsInsideRotatedRectangle(int x, int y)
805{
806 int dist_a = (_thd.size.x + _thd.size.y); // Rotated coordinate system for selected rectangle.
807 int dist_b = (_thd.size.x - _thd.size.y); // We don't have to divide by 2. It's all relative!
808 int a = ((x - _thd.pos.x) + (y - _thd.pos.y)); // Rotated coordinate system for the point under scrutiny.
809 int b = ((x - _thd.pos.x) - (y - _thd.pos.y));
810
811 /* Check if a and b are between 0 and dist_a or dist_b respectively. */
812 return IsInRangeInclusive(dist_a, 0, a) && IsInRangeInclusive(dist_b, 0, b);
813}
814
827void AddChildSpriteScreen(SpriteID image, PaletteID pal, int x, int y, bool transparent, const SubSprite *sub, bool scale, bool relative)
828{
829 assert((image & SPRITE_MASK) < MAX_SPRITES);
830
831 /* If the ParentSprite was clipped by the viewport bounds, do not draw the ChildSprites either */
832 if (_vd.last_child == LAST_CHILD_NONE) return;
833
834 /* make the sprites transparent with the right palette */
835 if (transparent) {
838 }
839
840 int32_t child_id = static_cast<int32_t>(_vd.child_screen_sprites_to_draw.size());
841 if (_vd.last_child != LAST_CHILD_PARENT) {
842 _vd.child_screen_sprites_to_draw[_vd.last_child].next = child_id;
843 } else {
844 _vd.parent_sprites_to_draw.back().first_child = child_id;
845 }
846
847 ChildScreenSpriteToDraw &cs = _vd.child_screen_sprites_to_draw.emplace_back();
848 cs.image = image;
849 cs.pal = pal;
850 cs.sub = sub;
851 cs.x = scale ? x * ZOOM_BASE : x;
852 cs.y = scale ? y * ZOOM_BASE : y;
853 cs.relative = relative;
855
856 /* Append the sprite to the active ChildSprite list.
857 * If the active ParentSprite is a foundation, update last_foundation_child as well.
858 * Note: ChildSprites of foundations are NOT sequential in the vector, as selection sprites are added at last. */
859 if (_vd.last_foundation_child[0] == _vd.last_child) _vd.last_foundation_child[0] = child_id;
860 if (_vd.last_foundation_child[1] == _vd.last_child) _vd.last_foundation_child[1] = child_id;
861 _vd.last_child = child_id;
862}
863
873static std::string &AddStringToDraw(int x, int y, Colours colour, ViewportStringFlags flags, uint16_t width)
874{
875 assert(width != 0);
876 StringSpriteToDraw &ss = _vd.string_sprites_to_draw.emplace_back();
877 ss.colour = colour;
878 ss.flags = flags;
879 ss.x = x;
880 ss.y = y;
881 ss.width = width;
882
883 return ss.string;
884}
885
886
900static 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)
901{
902 /* FIXME: This is not totally valid for some autorail highlights that extend over the edges of the tile. */
903 if (_vd.foundation[foundation_part] == -1) {
904 /* draw on real ground */
905 AddTileSpriteToDraw(image, pal, ti->x, ti->y, ti->z + z_offset, nullptr, extra_offs_x, extra_offs_y);
906 } else {
907 /* draw on top of foundation */
908 AddChildSpriteToFoundation(image, pal, nullptr, foundation_part, extra_offs_x, extra_offs_y - z_offset * ZOOM_BASE);
909 }
910}
911
918static void DrawTileSelectionRect(const TileInfo *ti, PaletteID pal)
919{
920 if (!IsValidTile(ti->tile)) return;
921
922 SpriteID sel;
923 if (IsHalftileSlope(ti->tileh)) {
924 Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
925 SpriteID sel2 = SPR_HALFTILE_SELECTION_FLAT + halftile_corner;
927
928 Corner opposite_corner = OppositeCorner(halftile_corner);
929 if (IsSteepSlope(ti->tileh)) {
930 sel = SPR_HALFTILE_SELECTION_DOWN;
931 } else {
932 sel = ((ti->tileh & SlopeWithOneCornerRaised(opposite_corner)) != 0 ? SPR_HALFTILE_SELECTION_UP : SPR_HALFTILE_SELECTION_FLAT);
933 }
934 sel += opposite_corner;
935 } else {
936 sel = SPR_SELECT_TILE + SlopeToSpriteOffset(ti->tileh);
937 }
939}
940
941static bool IsPartOfAutoLine(int px, int py)
942{
943 px -= _thd.selstart.x;
944 py -= _thd.selstart.y;
945
946 if ((_thd.drawstyle & HT_DRAG_MASK) != HT_LINE) return false;
947
948 switch (_thd.drawstyle & HT_DIR_MASK) {
949 case HT_DIR_X: return py == 0; // x direction
950 case HT_DIR_Y: return px == 0; // y direction
951 case HT_DIR_HU: return px == -py || px == -py - 16; // horizontal upper
952 case HT_DIR_HL: return px == -py || px == -py + 16; // horizontal lower
953 case HT_DIR_VL: return px == py || px == py + 16; // vertical left
954 case HT_DIR_VR: return px == py || px == py - 16; // vertical right
955 default:
956 NOT_REACHED();
957 }
958}
959
960/* [direction][side] */
961static const HighLightStyle _autorail_type[6][2] = {
962 { HT_DIR_X, HT_DIR_X },
963 { HT_DIR_Y, HT_DIR_Y },
964 { HT_DIR_HU, HT_DIR_HL },
965 { HT_DIR_HL, HT_DIR_HU },
966 { HT_DIR_VL, HT_DIR_VR },
968};
969
970#include "table/autorail.h"
971
978static void DrawAutorailSelection(const TileInfo *ti, uint autorail_type)
979{
980 SpriteID image;
981 PaletteID pal;
982 int offset;
983
984 FoundationPart foundation_part = FOUNDATION_PART_NORMAL;
985 Slope autorail_tileh = RemoveHalftileSlope(ti->tileh);
986 if (IsHalftileSlope(ti->tileh)) {
987 static const uint _lower_rail[4] = { 5U, 2U, 4U, 3U };
988 Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
989 if (autorail_type != _lower_rail[halftile_corner]) {
990 foundation_part = FOUNDATION_PART_HALFTILE;
991 /* Here we draw the highlights of the "three-corners-raised"-slope. That looks ok to me. */
992 autorail_tileh = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
993 }
994 }
995
996 offset = _AutorailTilehSprite[autorail_tileh][autorail_type];
997 if (offset >= 0) {
998 image = SPR_AUTORAIL_BASE + offset;
999 pal = PAL_NONE;
1000 } else {
1001 image = SPR_AUTORAIL_BASE - offset;
1003 }
1004
1005 DrawSelectionSprite(image, _thd.make_square_red ? PALETTE_SEL_TILE_RED : pal, ti, 7, foundation_part);
1006}
1007
1008enum TileHighlightType : uint8_t {
1009 THT_NONE,
1010 THT_WHITE,
1011 THT_BLUE,
1012 THT_RED,
1013};
1014
1018
1024static TileHighlightType GetTileHighlightType(TileIndex t)
1025{
1026 if (_viewport_highlight_station != nullptr) {
1027 if (IsTileType(t, MP_STATION) && GetStationIndex(t) == _viewport_highlight_station->index) return THT_WHITE;
1028 if (_viewport_highlight_station->TileIsInCatchment(t)) return THT_BLUE;
1029 }
1030 if (_viewport_highlight_waypoint != nullptr) {
1031 if (IsTileType(t, MP_STATION) && GetStationIndex(t) == _viewport_highlight_waypoint->index) return THT_BLUE;
1032 }
1033
1034 if (_viewport_highlight_town != nullptr) {
1035 if (IsTileType(t, MP_HOUSE)) {
1037 TileHighlightType type = THT_RED;
1039 if (st->owner != _current_company) continue;
1040 if (st->TileIsInCatchment(t)) return THT_BLUE;
1041 }
1042 return type;
1043 }
1044 } else if (IsTileType(t, MP_STATION)) {
1046 if (st->owner != _current_company) continue;
1047 if (GetStationIndex(t) == st->index) return THT_WHITE;
1048 }
1049 }
1050 }
1051
1052 return THT_NONE;
1053}
1054
1060static void DrawTileHighlightType(const TileInfo *ti, TileHighlightType tht)
1061{
1062 switch (tht) {
1063 default:
1064 case THT_NONE: break;
1065 case THT_WHITE: DrawTileSelectionRect(ti, PAL_NONE); break;
1066 case THT_BLUE: DrawTileSelectionRect(ti, PALETTE_SEL_TILE_BLUE); break;
1067 case THT_RED: DrawTileSelectionRect(ti, PALETTE_SEL_TILE_RED); break;
1068 }
1069}
1070
1076{
1077 /* Going through cases in order of computational time. */
1078
1079 if (_town_local_authority_kdtree.Count() == 0) return;
1080
1081 /* Tile belongs to town regardless of distance from town. */
1082 if (GetTileType(ti->tile) == MP_HOUSE) {
1083 if (!Town::GetByTile(ti->tile)->show_zone) return;
1084
1086 return;
1087 }
1088
1089 /* If the closest town in the highlighted list is far, we can stop searching. */
1090 TownID tid = _town_local_authority_kdtree.FindNearest(TileX(ti->tile), TileY(ti->tile));
1091 Town *closest_highlighted_town = Town::Get(tid);
1092
1093 if (DistanceManhattan(ti->tile, closest_highlighted_town->xy) >= _settings_game.economy.dist_local_authority) return;
1094
1095 /* Tile is inside of the local autrhority distance of a highlighted town,
1096 but it is possible that a non-highlighted town is even closer. */
1098
1099 if (closest_town->show_zone) {
1101 }
1102
1103}
1104
1109static void DrawTileSelection(const TileInfo *ti)
1110{
1111 /* Highlight tiles insede local authority of selected towns. */
1113
1114 /* Draw a red error square? */
1115 bool is_redsq = _thd.redsq == ti->tile;
1117
1118 TileHighlightType tht = GetTileHighlightType(ti->tile);
1119 DrawTileHighlightType(ti, tht);
1120
1121 /* No tile selection active? */
1122 if ((_thd.drawstyle & HT_DRAG_MASK) == HT_NONE) return;
1123
1124 if (_thd.diagonal) { // We're drawing a 45 degrees rotated (diagonal) rectangle
1125 if (IsInsideRotatedRectangle((int)ti->x, (int)ti->y)) goto draw_inner;
1126 return;
1127 }
1128
1129 /* Inside the inner area? */
1130 if (IsInsideBS(ti->x, _thd.pos.x, _thd.size.x) &&
1131 IsInsideBS(ti->y, _thd.pos.y, _thd.size.y)) {
1132draw_inner:
1133 if (_thd.drawstyle & HT_RECT) {
1134 if (!is_redsq) DrawTileSelectionRect(ti, _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE);
1135 } else if (_thd.drawstyle & HT_POINT) {
1136 /* Figure out the Z coordinate for the single dot. */
1137 int z = 0;
1138 FoundationPart foundation_part = FOUNDATION_PART_NORMAL;
1139 if (ti->tileh & SLOPE_N) {
1140 z += TILE_HEIGHT;
1142 }
1143 if (IsHalftileSlope(ti->tileh)) {
1144 Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
1145 if ((halftile_corner == CORNER_W) || (halftile_corner == CORNER_E)) z += TILE_HEIGHT;
1146 if (halftile_corner != CORNER_S) {
1147 foundation_part = FOUNDATION_PART_HALFTILE;
1148 if (IsSteepSlope(ti->tileh)) z -= TILE_HEIGHT;
1149 }
1150 }
1151 DrawSelectionSprite(SPR_DOT, PAL_NONE, ti, z, foundation_part);
1152 } else if (_thd.drawstyle & HT_RAIL) {
1153 /* autorail highlight piece under cursor */
1154 HighLightStyle type = _thd.drawstyle & HT_DIR_MASK;
1155 assert(type < HT_DIR_END);
1156 DrawAutorailSelection(ti, _autorail_type[type][0]);
1157 } else if (IsPartOfAutoLine(ti->x, ti->y)) {
1158 /* autorail highlighting long line */
1160 uint side;
1161
1162 if (dir == HT_DIR_X || dir == HT_DIR_Y) {
1163 side = 0;
1164 } else {
1165 TileIndex start = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
1166 side = Delta(Delta(TileX(start), TileX(ti->tile)), Delta(TileY(start), TileY(ti->tile)));
1167 }
1168
1169 DrawAutorailSelection(ti, _autorail_type[dir][side]);
1170 }
1171 return;
1172 }
1173
1174 /* Check if it's inside the outer area? */
1175 if (!is_redsq && (tht == THT_NONE || tht == THT_RED) && _thd.outersize.x > 0 &&
1176 IsInsideBS(ti->x, _thd.pos.x + _thd.offs.x, _thd.size.x + _thd.outersize.x) &&
1177 IsInsideBS(ti->y, _thd.pos.y + _thd.offs.y, _thd.size.y + _thd.outersize.y)) {
1178 /* Draw a blue rect. */
1180 return;
1181 }
1182}
1183
1190static int GetViewportY(Point tile)
1191{
1192 /* Each increment in X or Y direction moves down by half a tile, i.e. TILE_PIXELS / 2. */
1193 return (tile.y * (int)(TILE_PIXELS / 2) + tile.x * (int)(TILE_PIXELS / 2) - TilePixelHeightOutsideMap(tile.x, tile.y)) << ZOOM_BASE_SHIFT;
1194}
1195
1200{
1201 assert(_vd.dpi.top <= _vd.dpi.top + _vd.dpi.height);
1202 assert(_vd.dpi.left <= _vd.dpi.left + _vd.dpi.width);
1203
1204 Point upper_left = InverseRemapCoords(_vd.dpi.left, _vd.dpi.top);
1205 Point upper_right = InverseRemapCoords(_vd.dpi.left + _vd.dpi.width, _vd.dpi.top);
1206
1207 /* Transformations between tile coordinates and viewport rows/columns: See vp_column_row
1208 * column = y - x
1209 * row = x + y
1210 * x = (row - column) / 2
1211 * y = (row + column) / 2
1212 * Note: (row, columns) pairs are only valid, if they are both even or both odd.
1213 */
1214
1215 /* Columns overlap with neighbouring columns by a half tile.
1216 * - Left column is column of upper_left (rounded down) and one column to the left.
1217 * - Right column is column of upper_right (rounded up) and one column to the right.
1218 * Note: Integer-division does not round down for negative numbers, so ensure rounding with another increment/decrement.
1219 */
1220 int left_column = (upper_left.y - upper_left.x) / (int)TILE_SIZE - 2;
1221 int right_column = (upper_right.y - upper_right.x) / (int)TILE_SIZE + 2;
1222
1223 int potential_bridge_height = ZOOM_BASE * TILE_HEIGHT * _settings_game.construction.max_bridge_height;
1224
1225 /* Rows overlap with neighbouring rows by a half tile.
1226 * The first row that could possibly be visible is the row above upper_left (if it is at height 0).
1227 * Due to integer-division not rounding down for negative numbers, we need another decrement.
1228 */
1229 int row = (upper_left.x + upper_left.y) / (int)TILE_SIZE - 2;
1230 bool last_row = false;
1231 for (; !last_row; row++) {
1232 last_row = true;
1233 for (int column = left_column; column <= right_column; column++) {
1234 /* Valid row/column? */
1235 if ((row + column) % 2 != 0) continue;
1236
1237 Point tilecoord;
1238 tilecoord.x = (row - column) / 2;
1239 tilecoord.y = (row + column) / 2;
1240 assert(column == tilecoord.y - tilecoord.x);
1241 assert(row == tilecoord.y + tilecoord.x);
1242
1243 TileType tile_type;
1244 _cur_ti.x = tilecoord.x * TILE_SIZE;
1245 _cur_ti.y = tilecoord.y * TILE_SIZE;
1246
1247 if (IsInsideBS(tilecoord.x, 0, Map::SizeX()) && IsInsideBS(tilecoord.y, 0, Map::SizeY())) {
1248 /* This includes the south border at Map::MaxX / Map::MaxY. When terraforming we still draw tile selections there. */
1249 _cur_ti.tile = TileXY(tilecoord.x, tilecoord.y);
1250 tile_type = GetTileType(_cur_ti.tile);
1251 } else {
1252 _cur_ti.tile = INVALID_TILE;
1253 tile_type = MP_VOID;
1254 }
1255
1256 if (tile_type != MP_VOID) {
1257 /* We are inside the map => paint landscape. */
1258 std::tie(_cur_ti.tileh, _cur_ti.z) = GetTilePixelSlope(_cur_ti.tile);
1259 } else {
1260 /* We are outside the map => paint black. */
1261 std::tie(_cur_ti.tileh, _cur_ti.z) = GetTilePixelSlopeOutsideMap(tilecoord.x, tilecoord.y);
1262 }
1263
1264 int viewport_y = GetViewportY(tilecoord);
1265
1266 if (viewport_y + MAX_TILE_EXTENT_BOTTOM < _vd.dpi.top) {
1267 /* The tile in this column is not visible yet.
1268 * Tiles in other columns may be visible, but we need more rows in any case. */
1269 last_row = false;
1270 continue;
1271 }
1272
1273 int min_visible_height = viewport_y - (_vd.dpi.top + _vd.dpi.height);
1274 bool tile_visible = min_visible_height <= 0;
1275
1276 if (tile_type != MP_VOID) {
1277 /* Is tile with buildings visible? */
1278 if (min_visible_height < MAX_TILE_EXTENT_TOP) tile_visible = true;
1279
1280 if (IsBridgeAbove(_cur_ti.tile)) {
1281 /* Is the bridge visible? */
1282 TileIndex bridge_tile = GetNorthernBridgeEnd(_cur_ti.tile);
1283 int bridge_height = ZOOM_BASE * (GetBridgePixelHeight(bridge_tile) - TilePixelHeight(_cur_ti.tile));
1284 if (min_visible_height < bridge_height + MAX_TILE_EXTENT_TOP) tile_visible = true;
1285 }
1286
1287 /* Would a higher bridge on a more southern tile be visible?
1288 * If yes, we need to loop over more rows to possibly find one. */
1289 if (min_visible_height < potential_bridge_height + MAX_TILE_EXTENT_TOP) last_row = false;
1290 } else {
1291 /* Outside of map. If we are on the north border of the map, there may still be a bridge visible,
1292 * so we need to loop over more rows to possibly find one. */
1293 if ((tilecoord.x <= 0 || tilecoord.y <= 0) && min_visible_height < potential_bridge_height + MAX_TILE_EXTENT_TOP) last_row = false;
1294 }
1295
1296 if (tile_visible) {
1297 last_row = false;
1299 _vd.foundation[0] = -1;
1300 _vd.foundation[1] = -1;
1303
1304 _tile_type_procs[tile_type]->draw_tile_proc(&_cur_ti);
1305 if (_cur_ti.tile != INVALID_TILE) DrawTileSelection(&_cur_ti);
1306 }
1307 }
1308 }
1309}
1310
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 SetDParam(0, t->index);
1373 SetDParam(1, t->cache.population);
1374 *str = GetString(stringid);
1375 }
1376}
1377
1384static void ViewportAddSignStrings(DrawPixelInfo *dpi, const std::vector<const Sign *> &signs, bool small)
1385{
1386 ViewportStringFlags flags{};
1387 if (small) flags.Set(ViewportStringFlag::Small);
1388
1389 /* Signs placed by a game script don't have a frame. */
1390 ViewportStringFlags deity_flags{flags};
1392
1393 for (const Sign *si : signs) {
1394 std::string *str = ViewportAddString(dpi, &si->sign, (si->owner == OWNER_DEITY) ? deity_flags : flags,
1395 (si->owner == OWNER_NONE) ? COLOUR_GREY : (si->owner == OWNER_DEITY ? INVALID_COLOUR : _company_colours[si->owner]));
1396 if (str == nullptr) continue;
1397
1398 SetDParam(0, si->index);
1399 *str = GetString(STR_SIGN_NAME);
1400 }
1401}
1402
1409static void ViewportAddStationStrings(DrawPixelInfo *dpi, const std::vector<const BaseStation *> &stations, bool small)
1410{
1411 /* Transparent station signs have colour text instead of a colour panel. */
1413 if (small) flags.Set(ViewportStringFlag::Small);
1414
1415 for (const BaseStation *st : stations) {
1416 std::string *str = ViewportAddString(dpi, &st->sign, flags, (st->owner == OWNER_NONE || !st->IsInUse()) ? COLOUR_GREY : _company_colours[st->owner]);
1417 if (str == nullptr) continue;
1418
1419 if (Station::IsExpected(st)) { /* Station */
1420 SetDParam(0, st->index);
1421 SetDParam(1, st->facilities);
1422 *str = GetString(small ? STR_STATION_NAME : STR_VIEWPORT_STATION);
1423 } else { /* Waypoint */
1424 SetDParam(0, st->index);
1425 *str = GetString(STR_WAYPOINT_NAME);
1426 }
1427 }
1428}
1429
1430static void ViewportAddKdtreeSigns(DrawPixelInfo *dpi)
1431{
1432 Rect search_rect{ dpi->left, dpi->top, dpi->left + dpi->width, dpi->top + dpi->height };
1433 search_rect = ExpandRectWithViewportSignMargins(search_rect, dpi->zoom);
1434
1435 bool show_stations = HasBit(_display_opt, DO_SHOW_STATION_NAMES) && _game_mode != GM_MENU;
1436 bool show_waypoints = HasBit(_display_opt, DO_SHOW_WAYPOINT_NAMES) && _game_mode != GM_MENU;
1437 bool show_towns = HasBit(_display_opt, DO_SHOW_TOWN_NAMES) && _game_mode != GM_MENU;
1439 bool show_competitors = HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS);
1440
1441 /* Collect all the items first and draw afterwards, to ensure layering */
1442 std::vector<const BaseStation *> stations;
1443 std::vector<const Town *> towns;
1444 std::vector<const Sign *> signs;
1445
1446 _viewport_sign_kdtree.FindContained(search_rect.left, search_rect.top, search_rect.right, search_rect.bottom, [&](const ViewportSignKdtreeItem & item) {
1447 switch (item.type) {
1448 case ViewportSignKdtreeItem::VKI_STATION: {
1449 if (!show_stations) break;
1450 const BaseStation *st = BaseStation::Get(item.id.station);
1451
1452 /* If no facilities are present the station is a ghost station. */
1453 StationFacility facilities = st->facilities;
1454 if (facilities == FACIL_NONE) facilities = FACIL_GHOST;
1455
1456 if ((_facility_display_opt & facilities) == 0) break;
1457
1458 /* Don't draw if station is owned by another company and competitor station names are hidden. Stations owned by none are never ignored. */
1459 if (!show_competitors && _local_company != st->owner && st->owner != OWNER_NONE) break;
1460
1461 stations.push_back(st);
1462 break;
1463 }
1464
1465 case ViewportSignKdtreeItem::VKI_WAYPOINT: {
1466 if (!show_waypoints) break;
1467 const BaseStation *st = BaseStation::Get(item.id.station);
1468
1469 /* Don't draw if station is owned by another company and competitor station names are hidden. Stations owned by none are never ignored. */
1470 if (!show_competitors && _local_company != st->owner && st->owner != OWNER_NONE) break;
1471
1472 stations.push_back(st);
1473 break;
1474 }
1475
1476 case ViewportSignKdtreeItem::VKI_TOWN:
1477 if (!show_towns) break;
1478 towns.push_back(Town::Get(item.id.town));
1479 break;
1480
1481 case ViewportSignKdtreeItem::VKI_SIGN: {
1482 if (!show_signs) break;
1483 const Sign *si = Sign::Get(item.id.sign);
1484
1485 /* Don't draw if sign is owned by another company and competitor signs should be hidden.
1486 * Note: It is intentional that also signs owned by OWNER_NONE are hidden. Bankrupt
1487 * companies can leave OWNER_NONE signs after them. */
1488 if (!show_competitors && _local_company != si->owner && si->owner != OWNER_DEITY) break;
1489
1490 signs.push_back(si);
1491 break;
1492 }
1493
1494 default:
1495 NOT_REACHED();
1496 }
1497 });
1498
1499 /* Small versions of signs are used zoom level 4X and higher. */
1500 bool small = dpi->zoom >= ZOOM_LVL_OUT_4X;
1501
1502 /* Layering order (bottom to top): Town names, signs, stations */
1503 ViewportAddTownStrings(dpi, towns, small);
1504
1505 /* Do not draw signs nor station names if they are set invisible */
1506 if (IsInvisibilitySet(TO_SIGNS)) return;
1507
1508 ViewportAddSignStrings(dpi, signs, small);
1509 ViewportAddStationStrings(dpi, stations, small);
1510}
1511
1512
1520void ViewportSign::UpdatePosition(int center, int top, StringID str, StringID str_small)
1521{
1522 if (this->width_normal != 0) this->MarkDirty();
1523
1524 this->top = top;
1525
1526 std::string name = GetString(str);
1528 this->center = center;
1529
1530 /* zoomed out version */
1531 if (str_small != STR_NULL) {
1532 name = GetString(str_small);
1533 }
1535
1536 this->MarkDirty();
1537}
1538
1546{
1547 Rect zoomlevels[ZOOM_LVL_END];
1548
1549 /* We don't know which size will be drawn, so mark the largest area dirty. */
1550 const uint half_width = std::max(this->width_normal, this->width_small) / 2 + 1;
1552
1553 for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) {
1554 /* FIXME: This doesn't switch to width_small when appropriate. */
1555 zoomlevels[zoom].left = this->center - ScaleByZoom(half_width, zoom);
1556 zoomlevels[zoom].top = this->top - ScaleByZoom(1, zoom);
1557 zoomlevels[zoom].right = this->center + ScaleByZoom(half_width, zoom);
1558 zoomlevels[zoom].bottom = this->top + ScaleByZoom(height, zoom);
1559 }
1560
1561 for (const Window *w : Window::Iterate()) {
1562 Viewport *vp = w->viewport;
1563 if (vp != nullptr && vp->zoom <= maxzoom) {
1564 assert(vp->width != 0);
1565 Rect &zl = zoomlevels[vp->zoom];
1566 MarkViewportDirty(vp, zl.left, zl.top, zl.right, zl.bottom);
1567 }
1568 }
1569}
1570
1571static void ViewportDrawTileSprites(const TileSpriteToDrawVector *tstdv)
1572{
1573 for (const TileSpriteToDraw &ts : *tstdv) {
1574 DrawSpriteViewport(ts.image, ts.pal, ts.x, ts.y, ts.sub);
1575 }
1576}
1577
1580{
1581 return true;
1582}
1583
1585static void ViewportSortParentSprites(ParentSpriteToSortVector *psdv)
1586{
1587 if (psdv->size() < 2) return;
1588
1589 /* We rely on sprites being, for the most part, already ordered.
1590 * So we don't need to move many of them and can keep track of their
1591 * order efficiently by using stack. We always move sprites to the front
1592 * of the current position, i.e. to the top of the stack.
1593 * Also use special constants to indicate sorting state without
1594 * adding extra fields to ParentSpriteToDraw structure.
1595 */
1596 const uint32_t ORDER_COMPARED = UINT32_MAX; // Sprite was compared but we still need to compare the ones preceding it
1597 const uint32_t ORDER_RETURNED = UINT32_MAX - 1; // Makr sorted sprite in case there are other occurrences of it in the stack
1598 std::stack<ParentSpriteToDraw *> sprite_order;
1599 uint32_t next_order = 0;
1600
1601 std::forward_list<std::pair<int64_t, ParentSpriteToDraw *>> sprite_list; // We store sprites in a list sorted by xmin+ymin
1602
1603 /* Initialize sprite list and order. */
1604 for (auto p = psdv->rbegin(); p != psdv->rend(); p++) {
1605 sprite_list.emplace_front((*p)->xmin + (*p)->ymin, *p);
1606 sprite_order.push(*p);
1607 (*p)->order = next_order++;
1608 }
1609
1610 sprite_list.sort();
1611
1612 std::vector<ParentSpriteToDraw *> preceding; // Temporarily stores sprites that precede current and their position in the list
1613 auto preceding_prev = sprite_list.begin(); // Store iterator in case we need to delete a single preciding sprite
1614 auto out = psdv->begin(); // Iterator to output sorted sprites
1615
1616 while (!sprite_order.empty()) {
1617
1618 auto s = sprite_order.top();
1619 sprite_order.pop();
1620
1621 /* Sprite is already sorted, ignore it. */
1622 if (s->order == ORDER_RETURNED) continue;
1623
1624 /* Sprite was already compared, just need to output it. */
1625 if (s->order == ORDER_COMPARED) {
1626 *(out++) = s;
1627 s->order = ORDER_RETURNED;
1628 continue;
1629 }
1630
1631 preceding.clear();
1632
1633 /* We only need sprites with xmin <= s->xmax && ymin <= s->ymax && zmin <= s->zmax
1634 * So by iterating sprites with xmin + ymin <= s->xmax + s->ymax
1635 * we get all we need and some more that we filter out later.
1636 * We don't include zmin into the sum as there are usually more neighbors on x and y than z
1637 * so including it will actually increase the amount of false positives.
1638 * Also min coordinates can be > max so using max(xmin, xmax) + max(ymin, ymax)
1639 * to ensure that we iterate the current sprite as we need to remove it from the list.
1640 */
1641 auto ssum = std::max(s->xmax, s->xmin) + std::max(s->ymax, s->ymin);
1642 auto prev = sprite_list.before_begin();
1643 auto x = sprite_list.begin();
1644 while (x != sprite_list.end() && ((*x).first <= ssum)) {
1645 auto p = (*x).second;
1646 if (p == s) {
1647 /* We found the current sprite, remove it and move on. */
1648 x = sprite_list.erase_after(prev);
1649 continue;
1650 }
1651
1652 auto p_prev = prev;
1653 prev = x++;
1654
1655 if (s->xmax < p->xmin || s->ymax < p->ymin || s->zmax < p->zmin) continue;
1656 if (s->xmin <= p->xmax && // overlap in X?
1657 s->ymin <= p->ymax && // overlap in Y?
1658 s->zmin <= p->zmax) { // overlap in Z?
1659 if (s->xmin + s->xmax + s->ymin + s->ymax + s->zmin + s->zmax <=
1660 p->xmin + p->xmax + p->ymin + p->ymax + p->zmin + p->zmax) {
1661 continue;
1662 }
1663 }
1664 preceding.push_back(p);
1665 preceding_prev = p_prev;
1666 }
1667
1668 if (preceding.empty()) {
1669 /* No preceding sprites, add current one to the output */
1670 *(out++) = s;
1671 s->order = ORDER_RETURNED;
1672 continue;
1673 }
1674
1675 /* Optimization for the case when we only have 1 sprite to move. */
1676 if (preceding.size() == 1) {
1677 auto p = preceding[0];
1678 /* We can only output the preceding sprite if there can't be any other sprites preceding it. */
1679 if (p->xmax <= s->xmax && p->ymax <= s->ymax && p->zmax <= s->zmax) {
1680 p->order = ORDER_RETURNED;
1681 s->order = ORDER_RETURNED;
1682 sprite_list.erase_after(preceding_prev);
1683 *(out++) = p;
1684 *(out++) = s;
1685 continue;
1686 }
1687 }
1688
1689 /* Sort all preceding sprites by order and assign new orders in reverse (as original sorter did). */
1690 std::sort(preceding.begin(), preceding.end(), [](const ParentSpriteToDraw *a, const ParentSpriteToDraw *b) {
1691 return a->order > b->order;
1692 });
1693
1694 s->order = ORDER_COMPARED;
1695 sprite_order.push(s); // Still need to output so push it back for now
1696
1697 for (auto p: preceding) {
1698 p->order = next_order++;
1699 sprite_order.push(p);
1700 }
1701 }
1702}
1703
1704
1705static void ViewportDrawParentSprites(const ParentSpriteToSortVector *psd, const ChildScreenSpriteToDrawVector *csstdv)
1706{
1707 for (const ParentSpriteToDraw *ps : *psd) {
1708 if (ps->image != SPR_EMPTY_BOUNDING_BOX) DrawSpriteViewport(ps->image, ps->pal, ps->x, ps->y, ps->sub);
1709
1710 int child_idx = ps->first_child;
1711 while (child_idx >= 0) {
1712 const ChildScreenSpriteToDraw *cs = csstdv->data() + child_idx;
1713 child_idx = cs->next;
1714 if (cs->relative) {
1715 DrawSpriteViewport(cs->image, cs->pal, ps->left + cs->x, ps->top + cs->y, cs->sub);
1716 } else {
1717 DrawSpriteViewport(cs->image, cs->pal, ps->x + cs->x, ps->y + cs->y, cs->sub);
1718 }
1719 }
1720 }
1721}
1722
1727static void ViewportDrawBoundingBoxes(const ParentSpriteToSortVector *psd)
1728{
1729 for (const ParentSpriteToDraw *ps : *psd) {
1730 Point pt1 = RemapCoords(ps->xmax + 1, ps->ymax + 1, ps->zmax + 1); // top front corner
1731 Point pt2 = RemapCoords(ps->xmin , ps->ymax + 1, ps->zmax + 1); // top left corner
1732 Point pt3 = RemapCoords(ps->xmax + 1, ps->ymin , ps->zmax + 1); // top right corner
1733 Point pt4 = RemapCoords(ps->xmax + 1, ps->ymax + 1, ps->zmin ); // bottom front corner
1734
1735 DrawBox( pt1.x, pt1.y,
1736 pt2.x - pt1.x, pt2.y - pt1.y,
1737 pt3.x - pt1.x, pt3.y - pt1.y,
1738 pt4.x - pt1.x, pt4.y - pt1.y);
1739 }
1740}
1741
1746{
1748 const DrawPixelInfo *dpi = _cur_dpi;
1749 void *dst;
1750 int right = UnScaleByZoom(dpi->width, dpi->zoom);
1751 int bottom = UnScaleByZoom(dpi->height, dpi->zoom);
1752
1753 int colour = _string_colourmap[_dirty_block_colour & 0xF];
1754
1755 dst = dpi->dst_ptr;
1756
1757 uint8_t bo = UnScaleByZoom(dpi->left + dpi->top, dpi->zoom) & 1;
1758 do {
1759 for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, (uint8_t)colour);
1760 dst = blitter->MoveTo(dst, 0, 1);
1761 } while (--bottom > 0);
1762}
1763
1764static void ViewportDrawStrings(ZoomLevel zoom, const StringSpriteToDrawVector *sstdv)
1765{
1766 for (const StringSpriteToDraw &ss : *sstdv) {
1767 bool small = ss.flags.Test(ViewportStringFlag::Small);
1768 int w = ss.width;
1769 int x = UnScaleByZoom(ss.x, zoom);
1770 int y = UnScaleByZoom(ss.y, zoom);
1771 int h = WidgetDimensions::scaled.fullbevel.top + GetCharacterHeight(small ? FS_SMALL : FS_NORMAL) + WidgetDimensions::scaled.fullbevel.bottom;
1772
1773 TextColour colour = TC_WHITE;
1774 if (ss.flags.Test(ViewportStringFlag::ColourRect)) {
1775 if (ss.colour != INVALID_COLOUR) DrawFrameRect(x, y, x + w - 1, y + h - 1, ss.colour, {});
1776 colour = TC_BLACK;
1777 } else if (ss.flags.Test(ViewportStringFlag::TransparentRect)) {
1778 DrawFrameRect(x, y, x + w - 1, y + h - 1, ss.colour, FrameFlag::Transparent);
1779 }
1780
1781 if (ss.flags.Test(ViewportStringFlag::TextColour)) {
1782 if (ss.colour != INVALID_COLOUR) colour = static_cast<TextColour>(GetColourGradient(ss.colour, SHADE_LIGHTER) | TC_IS_PALETTE_COLOUR);
1783 }
1784
1785 int left = x + WidgetDimensions::scaled.fullbevel.left;
1786 int right = x + w - 1 - WidgetDimensions::scaled.fullbevel.right;
1787 int top = y + WidgetDimensions::scaled.fullbevel.top;
1788
1789 int shadow_offset = 0;
1790 if (small && ss.flags.Test(ViewportStringFlag::Shadow)) {
1791 /* Shadow needs to be shifted 1 pixel. */
1792 shadow_offset = WidgetDimensions::scaled.fullbevel.top;
1793 DrawString(left + shadow_offset, right + shadow_offset, top, ss.string, TC_BLACK, SA_HOR_CENTER, false, FS_SMALL);
1794 }
1795
1796 DrawString(left, right, top - shadow_offset, ss.string, colour, SA_HOR_CENTER, false, small ? FS_SMALL : FS_NORMAL);
1797 }
1798}
1799
1800void ViewportDoDraw(const Viewport *vp, int left, int top, int right, int bottom)
1801{
1802 _vd.dpi.zoom = vp->zoom;
1803 int mask = ScaleByZoom(-1, vp->zoom);
1804
1806
1807 _vd.dpi.width = (right - left) & mask;
1808 _vd.dpi.height = (bottom - top) & mask;
1809 _vd.dpi.left = left & mask;
1810 _vd.dpi.top = top & mask;
1811 _vd.dpi.pitch = _cur_dpi->pitch;
1812 _vd.last_child = LAST_CHILD_NONE;
1813
1814 int x = UnScaleByZoom(_vd.dpi.left - (vp->virtual_left & mask), vp->zoom) + vp->left;
1815 int y = UnScaleByZoom(_vd.dpi.top - (vp->virtual_top & mask), vp->zoom) + vp->top;
1816
1817 _vd.dpi.dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_cur_dpi->dst_ptr, x - _cur_dpi->left, y - _cur_dpi->top);
1818 AutoRestoreBackup dpi_backup(_cur_dpi, &_vd.dpi);
1819
1821 ViewportAddVehicles(&_vd.dpi);
1822
1823 ViewportAddKdtreeSigns(&_vd.dpi);
1824
1825 DrawTextEffects(&_vd.dpi);
1826
1827 if (!_vd.tile_sprites_to_draw.empty()) ViewportDrawTileSprites(&_vd.tile_sprites_to_draw);
1828
1829 for (auto &psd : _vd.parent_sprites_to_draw) {
1830 _vd.parent_sprites_to_sort.push_back(&psd);
1831 }
1832
1833 _vp_sprite_sorter(&_vd.parent_sprites_to_sort);
1834 ViewportDrawParentSprites(&_vd.parent_sprites_to_sort, &_vd.child_screen_sprites_to_draw);
1835
1836 if (_draw_bounding_boxes) ViewportDrawBoundingBoxes(&_vd.parent_sprites_to_sort);
1837 if (_draw_dirty_blocks) ViewportDrawDirtyBlocks();
1838
1839 DrawPixelInfo dp = _vd.dpi;
1840 ZoomLevel zoom = _vd.dpi.zoom;
1841 dp.zoom = ZOOM_LVL_MIN;
1842 dp.width = UnScaleByZoom(dp.width, zoom);
1843 dp.height = UnScaleByZoom(dp.height, zoom);
1844 _cur_dpi = &dp;
1845
1846 if (vp->overlay != nullptr && vp->overlay->GetCargoMask() != 0 && vp->overlay->GetCompanyMask() != 0) {
1847 /* translate to window coordinates */
1848 dp.left = x;
1849 dp.top = y;
1850 vp->overlay->Draw(&dp);
1851 }
1852
1853 if (!_vd.string_sprites_to_draw.empty()) {
1854 /* translate to world coordinates */
1855 dp.left = UnScaleByZoom(_vd.dpi.left, zoom);
1856 dp.top = UnScaleByZoom(_vd.dpi.top, zoom);
1857 ViewportDrawStrings(zoom, &_vd.string_sprites_to_draw);
1858 }
1859
1860 _vd.string_sprites_to_draw.clear();
1861 _vd.tile_sprites_to_draw.clear();
1862 _vd.parent_sprites_to_draw.clear();
1863 _vd.parent_sprites_to_sort.clear();
1864 _vd.child_screen_sprites_to_draw.clear();
1865}
1866
1867static inline void ViewportDraw(const Viewport *vp, int left, int top, int right, int bottom)
1868{
1869 if (right <= vp->left || bottom <= vp->top) return;
1870
1871 if (left >= vp->left + vp->width) return;
1872
1873 if (left < vp->left) left = vp->left;
1874 if (right > vp->left + vp->width) right = vp->left + vp->width;
1875
1876 if (top >= vp->top + vp->height) return;
1877
1878 if (top < vp->top) top = vp->top;
1879 if (bottom > vp->top + vp->height) bottom = vp->top + vp->height;
1880
1881 ViewportDoDraw(vp,
1882 ScaleByZoom(left - vp->left, vp->zoom) + vp->virtual_left,
1883 ScaleByZoom(top - vp->top, vp->zoom) + vp->virtual_top,
1884 ScaleByZoom(right - vp->left, vp->zoom) + vp->virtual_left,
1885 ScaleByZoom(bottom - vp->top, vp->zoom) + vp->virtual_top
1886 );
1887}
1888
1893{
1895
1896 DrawPixelInfo *dpi = _cur_dpi;
1897
1898 dpi->left += this->left;
1899 dpi->top += this->top;
1900
1901 ViewportDraw(this->viewport, dpi->left, dpi->top, dpi->left + dpi->width, dpi->top + dpi->height);
1902
1903 dpi->left -= this->left;
1904 dpi->top -= this->top;
1905}
1906
1917static inline void ClampViewportToMap(const Viewport *vp, int *scroll_x, int *scroll_y)
1918{
1919 /* Centre of the viewport is hot spot. */
1920 Point pt = {
1921 *scroll_x + vp->virtual_width / 2,
1922 *scroll_y + vp->virtual_height / 2
1923 };
1924
1925 /* Find nearest tile that is within borders of the map. */
1926 bool clamped;
1927 pt = InverseRemapCoords2(pt.x, pt.y, true, &clamped);
1928
1929 if (clamped) {
1930 /* Convert back to viewport coordinates and remove centering. */
1931 pt = RemapCoords2(pt.x, pt.y);
1932 *scroll_x = pt.x - vp->virtual_width / 2;
1933 *scroll_y = pt.y - vp->virtual_height / 2;
1934 }
1935}
1936
1949static void ClampSmoothScroll(uint32_t delta_ms, int64_t delta_hi, int64_t delta_lo, int &delta_hi_clamped, int &delta_lo_clamped)
1950{
1952 constexpr int PIXELS_PER_TILE = TILE_PIXELS * 2 * ZOOM_BASE;
1953
1954 assert(delta_hi != 0);
1955
1956 /* Move at most 75% of the distance every 30ms, for a smooth experience */
1957 int64_t delta_left = delta_hi * std::pow(0.75, delta_ms / 30.0);
1958 /* Move never more than 16 tiles per 30ms. */
1959 int max_scroll = Map::ScaleBySize1D(16 * PIXELS_PER_TILE * delta_ms / 30);
1960
1961 /* We never go over the max_scroll speed. */
1962 delta_hi_clamped = Clamp(delta_hi - delta_left, -max_scroll, max_scroll);
1963 /* The lower delta is in ratio of the higher delta, so we keep going straight at the destination. */
1964 delta_lo_clamped = delta_lo * delta_hi_clamped / delta_hi;
1965
1966 /* Ensure we always move (delta_hi can't be zero). */
1967 if (delta_hi_clamped == 0) {
1968 delta_hi_clamped = delta_hi > 0 ? 1 : -1;
1969 }
1970}
1971
1976void UpdateViewportPosition(Window *w, uint32_t delta_ms)
1977{
1978 const Viewport *vp = w->viewport;
1979
1981 const Vehicle *veh = Vehicle::Get(w->viewport->follow_vehicle);
1982 Point pt = MapXYZToViewport(vp, veh->x_pos, veh->y_pos, veh->z_pos);
1983
1984 w->viewport->scrollpos_x = pt.x;
1985 w->viewport->scrollpos_y = pt.y;
1986 SetViewportPosition(w, pt.x, pt.y);
1987 } else {
1988 /* Ensure the destination location is within the map */
1990
1991 int delta_x = w->viewport->dest_scrollpos_x - w->viewport->scrollpos_x;
1992 int delta_y = w->viewport->dest_scrollpos_y - w->viewport->scrollpos_y;
1993
1994 int current_x = w->viewport->scrollpos_x;
1995 int current_y = w->viewport->scrollpos_y;
1996
1997 bool update_overlay = false;
1998 if (delta_x != 0 || delta_y != 0) {
2000 int delta_x_clamped;
2001 int delta_y_clamped;
2002
2003 if (abs(delta_x) > abs(delta_y)) {
2004 ClampSmoothScroll(delta_ms, delta_x, delta_y, delta_x_clamped, delta_y_clamped);
2005 } else {
2006 ClampSmoothScroll(delta_ms, delta_y, delta_x, delta_y_clamped, delta_x_clamped);
2007 }
2008
2009 w->viewport->scrollpos_x += delta_x_clamped;
2010 w->viewport->scrollpos_y += delta_y_clamped;
2011 } else {
2014 }
2015 update_overlay = (w->viewport->scrollpos_x == w->viewport->dest_scrollpos_x &&
2017 }
2018
2020
2021 /* When moving small amounts around the border we can get stuck, and
2022 * not actually move. In those cases, teleport to the destination. */
2023 if ((delta_x != 0 || delta_y != 0) && current_x == w->viewport->scrollpos_x && current_y == w->viewport->scrollpos_y) {
2026 }
2027
2028 SetViewportPosition(w, w->viewport->scrollpos_x, w->viewport->scrollpos_y);
2029 if (update_overlay) RebuildViewportOverlay(w);
2030 }
2031}
2032
2043static bool MarkViewportDirty(const Viewport *vp, int left, int top, int right, int bottom)
2044{
2045 /* Rounding wrt. zoom-out level */
2046 right += (1 << vp->zoom) - 1;
2047 bottom += (1 << vp->zoom) - 1;
2048
2049 right -= vp->virtual_left;
2050 if (right <= 0) return false;
2051
2052 bottom -= vp->virtual_top;
2053 if (bottom <= 0) return false;
2054
2055 left = std::max(0, left - vp->virtual_left);
2056
2057 if (left >= vp->virtual_width) return false;
2058
2059 top = std::max(0, top - vp->virtual_top);
2060
2061 if (top >= vp->virtual_height) return false;
2062
2064 UnScaleByZoomLower(left, vp->zoom) + vp->left,
2065 UnScaleByZoomLower(top, vp->zoom) + vp->top,
2066 UnScaleByZoom(right, vp->zoom) + vp->left + 1,
2067 UnScaleByZoom(bottom, vp->zoom) + vp->top + 1
2068 );
2069
2070 return true;
2071}
2072
2082bool MarkAllViewportsDirty(int left, int top, int right, int bottom)
2083{
2084 bool dirty = false;
2085
2086 for (const Window *w : Window::Iterate()) {
2087 Viewport *vp = w->viewport;
2088 if (vp != nullptr) {
2089 assert(vp->width != 0);
2090 if (MarkViewportDirty(vp, left, top, right, bottom)) dirty = true;
2091 }
2092 }
2093
2094 return dirty;
2095}
2096
2097void ConstrainAllViewportsZoom()
2098{
2099 for (Window *w : Window::Iterate()) {
2100 if (w->viewport == nullptr) continue;
2101
2102 ZoomLevel zoom = static_cast<ZoomLevel>(Clamp(w->viewport->zoom, _settings_client.gui.zoom_min, _settings_client.gui.zoom_max));
2103 if (zoom != w->viewport->zoom) {
2104 while (w->viewport->zoom < zoom) DoZoomInOutWindow(ZOOM_OUT, w);
2105 while (w->viewport->zoom > zoom) DoZoomInOutWindow(ZOOM_IN, w);
2106 }
2107 }
2108}
2109
2117void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
2118{
2119 Point pt = RemapCoords(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE, tile_height_override * TILE_HEIGHT);
2121 pt.x - MAX_TILE_EXTENT_LEFT,
2122 pt.y - MAX_TILE_EXTENT_TOP - ZOOM_BASE * TILE_HEIGHT * bridge_level_offset,
2123 pt.x + MAX_TILE_EXTENT_RIGHT,
2124 pt.y + MAX_TILE_EXTENT_BOTTOM);
2125}
2126
2135{
2136 int x_size = _thd.size.x;
2137 int y_size = _thd.size.y;
2138
2139 if (!_thd.diagonal) { // Selecting in a straight rectangle (or a single square)
2140 int x_start = _thd.pos.x;
2141 int y_start = _thd.pos.y;
2142
2143 if (_thd.outersize.x != 0) {
2144 x_size += _thd.outersize.x;
2145 x_start += _thd.offs.x;
2146 y_size += _thd.outersize.y;
2147 y_start += _thd.offs.y;
2148 }
2149
2150 x_size -= TILE_SIZE;
2151 y_size -= TILE_SIZE;
2152
2153 assert(x_size >= 0);
2154 assert(y_size >= 0);
2155
2156 int x_end = Clamp(x_start + x_size, 0, Map::SizeX() * TILE_SIZE - TILE_SIZE);
2157 int y_end = Clamp(y_start + y_size, 0, Map::SizeY() * TILE_SIZE - TILE_SIZE);
2158
2159 x_start = Clamp(x_start, 0, Map::SizeX() * TILE_SIZE - TILE_SIZE);
2160 y_start = Clamp(y_start, 0, Map::SizeY() * TILE_SIZE - TILE_SIZE);
2161
2162 /* make sure everything is multiple of TILE_SIZE */
2163 assert((x_end | y_end | x_start | y_start) % TILE_SIZE == 0);
2164
2165 /* How it works:
2166 * Suppose we have to mark dirty rectangle of 3x4 tiles:
2167 * x
2168 * xxx
2169 * xxxxx
2170 * xxxxx
2171 * xxx
2172 * x
2173 * This algorithm marks dirty columns of tiles, so it is done in 3+4-1 steps:
2174 * 1) x 2) x
2175 * xxx Oxx
2176 * Oxxxx xOxxx
2177 * xxxxx Oxxxx
2178 * xxx xxx
2179 * x x
2180 * And so forth...
2181 */
2182
2183 int top_x = x_end; // coordinates of top dirty tile
2184 int top_y = y_start;
2185 int bot_x = top_x; // coordinates of bottom dirty tile
2186 int bot_y = top_y;
2187
2188 do {
2189 /* topmost dirty point */
2190 TileIndex top_tile = TileVirtXY(top_x, top_y);
2191 Point top = RemapCoords(top_x, top_y, GetTileMaxPixelZ(top_tile));
2192
2193 /* bottommost point */
2194 TileIndex bottom_tile = TileVirtXY(bot_x, bot_y);
2195 Point bot = RemapCoords(bot_x + TILE_SIZE, bot_y + TILE_SIZE, GetTilePixelZ(bottom_tile)); // bottommost point
2196
2197 /* the 'x' coordinate of 'top' and 'bot' is the same (and always in the same distance from tile middle),
2198 * tile height/slope affects only the 'y' on-screen coordinate! */
2199
2200 int l = top.x - TILE_PIXELS * ZOOM_BASE; // 'x' coordinate of left side of the dirty rectangle
2201 int t = top.y; // 'y' coordinate of top side of the dirty rectangle
2202 int r = top.x + TILE_PIXELS * ZOOM_BASE; // 'x' coordinate of right side of the dirty rectangle
2203 int b = bot.y; // 'y' coordinate of bottom side of the dirty rectangle
2204
2205 static const int OVERLAY_WIDTH = 4 * ZOOM_BASE; // part of selection sprites is drawn outside the selected area (in particular: terraforming)
2206
2207 /* For halftile foundations on SLOPE_STEEP_S the sprite extents some more towards the top */
2208 MarkAllViewportsDirty(l - OVERLAY_WIDTH, t - OVERLAY_WIDTH - TILE_HEIGHT * ZOOM_BASE, r + OVERLAY_WIDTH, b + OVERLAY_WIDTH);
2209
2210 /* haven't we reached the topmost tile yet? */
2211 if (top_x != x_start) {
2212 top_x -= TILE_SIZE;
2213 } else {
2214 top_y += TILE_SIZE;
2215 }
2216
2217 /* the way the bottom tile changes is different when we reach the bottommost tile */
2218 if (bot_y != y_end) {
2219 bot_y += TILE_SIZE;
2220 } else {
2221 bot_x -= TILE_SIZE;
2222 }
2223 } while (bot_x >= top_x);
2224 } else { // Selecting in a 45 degrees rotated (diagonal) rectangle.
2225 /* a_size, b_size describe a rectangle with rotated coordinates */
2226 int a_size = x_size + y_size, b_size = x_size - y_size;
2227
2228 int interval_a = a_size < 0 ? -(int)TILE_SIZE : (int)TILE_SIZE;
2229 int interval_b = b_size < 0 ? -(int)TILE_SIZE : (int)TILE_SIZE;
2230
2231 for (int a = -interval_a; a != a_size + interval_a; a += interval_a) {
2232 for (int b = -interval_b; b != b_size + interval_b; b += interval_b) {
2233 uint x = (_thd.pos.x + (a + b) / 2) / TILE_SIZE;
2234 uint y = (_thd.pos.y + (a - b) / 2) / TILE_SIZE;
2235
2236 if (x < Map::MaxX() && y < Map::MaxY()) {
2238 }
2239 }
2240 }
2241 }
2242}
2243
2244
2245void SetSelectionRed(bool b)
2246{
2247 _thd.make_square_red = b;
2249}
2250
2259static bool CheckClickOnViewportSign(const Viewport *vp, int x, int y, const ViewportSign *sign)
2260{
2261 bool small = (vp->zoom >= ZOOM_LVL_OUT_4X);
2262 int sign_half_width = ScaleByZoom((small ? sign->width_small : sign->width_normal) / 2, vp->zoom);
2263 int sign_height = ScaleByZoom(WidgetDimensions::scaled.fullbevel.top + GetCharacterHeight(small ? FS_SMALL : FS_NORMAL) + WidgetDimensions::scaled.fullbevel.bottom, vp->zoom);
2264
2265 return y >= sign->top && y < sign->top + sign_height &&
2266 x >= sign->center - sign_half_width && x < sign->center + sign_half_width;
2267}
2268
2269
2277static bool CheckClickOnViewportSign(const Viewport *vp, int x, int y)
2278{
2279 if (_game_mode == GM_MENU) return false;
2280
2281 x = ScaleByZoom(x - vp->left, vp->zoom) + vp->virtual_left;
2282 y = ScaleByZoom(y - vp->top, vp->zoom) + vp->virtual_top;
2283
2284 Rect search_rect{ x - 1, y - 1, x + 1, y + 1 };
2285 search_rect = ExpandRectWithViewportSignMargins(search_rect, vp->zoom);
2286
2289 bool show_towns = HasBit(_display_opt, DO_SHOW_TOWN_NAMES);
2291 bool show_competitors = HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS);
2292
2293 /* Topmost of each type that was hit */
2294 BaseStation *st = nullptr, *last_st = nullptr;
2295 Town *t = nullptr, *last_t = nullptr;
2296 Sign *si = nullptr, *last_si = nullptr;
2297
2298 /* See ViewportAddKdtreeSigns() for details on the search logic */
2299 _viewport_sign_kdtree.FindContained(search_rect.left, search_rect.top, search_rect.right, search_rect.bottom, [&](const ViewportSignKdtreeItem & item) {
2300 switch (item.type) {
2301 case ViewportSignKdtreeItem::VKI_STATION:
2302 if (!show_stations) break;
2303 st = BaseStation::Get(item.id.station);
2304 if (!show_competitors && _local_company != st->owner && st->owner != OWNER_NONE) break;
2305 if (CheckClickOnViewportSign(vp, x, y, &st->sign)) last_st = st;
2306 break;
2307
2308 case ViewportSignKdtreeItem::VKI_WAYPOINT:
2309 if (!show_waypoints) break;
2310 st = BaseStation::Get(item.id.station);
2311 if (!show_competitors && _local_company != st->owner && st->owner != OWNER_NONE) break;
2312 if (CheckClickOnViewportSign(vp, x, y, &st->sign)) last_st = st;
2313 break;
2314
2315 case ViewportSignKdtreeItem::VKI_TOWN:
2316 if (!show_towns) break;
2317 t = Town::Get(item.id.town);
2318 if (CheckClickOnViewportSign(vp, x, y, &t->cache.sign)) last_t = t;
2319 break;
2320
2321 case ViewportSignKdtreeItem::VKI_SIGN:
2322 if (!show_signs) break;
2323 si = Sign::Get(item.id.sign);
2324 if (!show_competitors && _local_company != si->owner && si->owner != OWNER_DEITY) break;
2325 if (CheckClickOnViewportSign(vp, x, y, &si->sign)) last_si = si;
2326 break;
2327
2328 default:
2329 NOT_REACHED();
2330 }
2331 });
2332
2333 /* Select which hit to handle based on priority */
2334 if (last_st != nullptr) {
2335 if (Station::IsExpected(last_st)) {
2336 ShowStationViewWindow(last_st->index);
2337 } else {
2339 }
2340 return true;
2341 } else if (last_t != nullptr) {
2342 ShowTownViewWindow(last_t->index);
2343 return true;
2344 } else if (last_si != nullptr) {
2345 HandleClickOnSign(last_si);
2346 return true;
2347 } else {
2348 return false;
2349 }
2350}
2351
2352
2353ViewportSignKdtreeItem ViewportSignKdtreeItem::MakeStation(StationID id)
2354{
2356 item.type = VKI_STATION;
2357 item.id.station = id;
2358
2359 const Station *st = Station::Get(id);
2360 assert(st->sign.kdtree_valid);
2361 item.center = st->sign.center;
2362 item.top = st->sign.top;
2363
2364 /* Assume the sign can be a candidate for drawing, so measure its width */
2365 _viewport_sign_maxwidth = std::max<int>({_viewport_sign_maxwidth, st->sign.width_normal, st->sign.width_small});
2366
2367 return item;
2368}
2369
2370ViewportSignKdtreeItem ViewportSignKdtreeItem::MakeWaypoint(StationID id)
2371{
2373 item.type = VKI_WAYPOINT;
2374 item.id.station = id;
2375
2376 const Waypoint *st = Waypoint::Get(id);
2377 assert(st->sign.kdtree_valid);
2378 item.center = st->sign.center;
2379 item.top = st->sign.top;
2380
2381 /* Assume the sign can be a candidate for drawing, so measure its width */
2382 _viewport_sign_maxwidth = std::max<int>({_viewport_sign_maxwidth, st->sign.width_normal, st->sign.width_small});
2383
2384 return item;
2385}
2386
2387ViewportSignKdtreeItem ViewportSignKdtreeItem::MakeTown(TownID id)
2388{
2390 item.type = VKI_TOWN;
2391 item.id.town = id;
2392
2393 const Town *town = Town::Get(id);
2394 assert(town->cache.sign.kdtree_valid);
2395 item.center = town->cache.sign.center;
2396 item.top = town->cache.sign.top;
2397
2398 /* Assume the sign can be a candidate for drawing, so measure its width */
2399 _viewport_sign_maxwidth = std::max<int>({_viewport_sign_maxwidth, town->cache.sign.width_normal, town->cache.sign.width_small});
2400
2401 return item;
2402}
2403
2404ViewportSignKdtreeItem ViewportSignKdtreeItem::MakeSign(SignID id)
2405{
2407 item.type = VKI_SIGN;
2408 item.id.sign = id;
2409
2410 const Sign *sign = Sign::Get(id);
2411 assert(sign->sign.kdtree_valid);
2412 item.center = sign->sign.center;
2413 item.top = sign->sign.top;
2414
2415 /* Assume the sign can be a candidate for drawing, so measure its width */
2416 _viewport_sign_maxwidth = std::max<int>({_viewport_sign_maxwidth, sign->sign.width_normal, sign->sign.width_small});
2417
2418 return item;
2419}
2420
2421void RebuildViewportKdtree()
2422{
2423 /* Reset biggest size sign seen */
2424 _viewport_sign_maxwidth = 0;
2425
2426 std::vector<ViewportSignKdtreeItem> items;
2428
2429 for (const Station *st : Station::Iterate()) {
2430 if (st->sign.kdtree_valid) items.push_back(ViewportSignKdtreeItem::MakeStation(st->index));
2431 }
2432
2433 for (const Waypoint *wp : Waypoint::Iterate()) {
2434 if (wp->sign.kdtree_valid) items.push_back(ViewportSignKdtreeItem::MakeWaypoint(wp->index));
2435 }
2436
2437 for (const Town *town : Town::Iterate()) {
2438 if (town->cache.sign.kdtree_valid) items.push_back(ViewportSignKdtreeItem::MakeTown(town->index));
2439 }
2440
2441 for (const Sign *sign : Sign::Iterate()) {
2442 if (sign->sign.kdtree_valid) items.push_back(ViewportSignKdtreeItem::MakeSign(sign->index));
2443 }
2444
2445 _viewport_sign_kdtree.Build(items.begin(), items.end());
2446}
2447
2448
2449static bool CheckClickOnLandscape(const Viewport *vp, int x, int y)
2450{
2451 Point pt = TranslateXYToTileCoord(vp, x, y);
2452
2453 if (pt.x != -1) return ClickTile(TileVirtXY(pt.x, pt.y));
2454 return true;
2455}
2456
2457static void PlaceObject()
2458{
2459 Point pt;
2460 Window *w;
2461
2462 pt = GetTileBelowCursor();
2463 if (pt.x == -1) return;
2464
2465 if ((_thd.place_mode & HT_DRAG_MASK) == HT_POINT) {
2466 pt.x += TILE_SIZE / 2;
2467 pt.y += TILE_SIZE / 2;
2468 }
2469
2470 _tile_fract_coords.x = pt.x & TILE_UNIT_MASK;
2471 _tile_fract_coords.y = pt.y & TILE_UNIT_MASK;
2472
2473 w = _thd.GetCallbackWnd();
2474 if (w != nullptr) w->OnPlaceObject(pt, TileVirtXY(pt.x, pt.y));
2475}
2476
2477
2478bool HandleViewportClicked(const Viewport *vp, int x, int y)
2479{
2480 const Vehicle *v = CheckClickOnVehicle(vp, x, y);
2481
2482 if (_thd.place_mode & HT_VEHICLE) {
2483 if (v != nullptr && VehicleClicked(v)) return true;
2484 }
2485
2486 /* Vehicle placement mode already handled above. */
2487 if ((_thd.place_mode & HT_DRAG_MASK) != HT_NONE) {
2488 PlaceObject();
2489 return true;
2490 }
2491
2492 if (CheckClickOnViewportSign(vp, x, y)) return true;
2493 bool result = CheckClickOnLandscape(vp, x, y);
2494
2495 if (v != nullptr) {
2496 Debug(misc, 2, "Vehicle {} (index {}) at {}", v->unitnumber, v->index, fmt::ptr(v));
2498 v = v->First();
2499 if (_ctrl_pressed && v->owner == _local_company) {
2500 StartStopVehicle(v, true);
2501 } else {
2503 }
2504 }
2505 return true;
2506 }
2507 return result;
2508}
2509
2510void RebuildViewportOverlay(Window *w)
2511{
2512 if (w->viewport->overlay != nullptr &&
2513 w->viewport->overlay->GetCompanyMask() != 0 &&
2514 w->viewport->overlay->GetCargoMask() != 0) {
2515 w->viewport->overlay->SetDirty();
2516 w->SetDirty();
2517 }
2518}
2519
2529bool ScrollWindowTo(int x, int y, int z, Window *w, bool instant)
2530{
2531 /* The slope cannot be acquired outside of the map, so make sure we are always within the map. */
2532 if (z == -1) {
2533 if ( x >= 0 && x <= (int)Map::SizeX() * (int)TILE_SIZE - 1
2534 && y >= 0 && y <= (int)Map::SizeY() * (int)TILE_SIZE - 1) {
2535 z = GetSlopePixelZ(x, y);
2536 } else {
2537 z = TileHeightOutsideMap(x / (int)TILE_SIZE, y / (int)TILE_SIZE);
2538 }
2539 }
2540
2541 Point pt = MapXYZToViewport(w->viewport, x, y, z);
2543
2544 if (w->viewport->dest_scrollpos_x == pt.x && w->viewport->dest_scrollpos_y == pt.y) return false;
2545
2546 if (instant) {
2547 w->viewport->scrollpos_x = pt.x;
2548 w->viewport->scrollpos_y = pt.y;
2549 RebuildViewportOverlay(w);
2550 }
2551
2552 w->viewport->dest_scrollpos_x = pt.x;
2553 w->viewport->dest_scrollpos_y = pt.y;
2554 return true;
2555}
2556
2564bool ScrollWindowToTile(TileIndex tile, Window *w, bool instant)
2565{
2566 return ScrollWindowTo(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE, -1, w, instant);
2567}
2568
2575bool ScrollMainWindowToTile(TileIndex tile, bool instant)
2576{
2577 return ScrollMainWindowTo(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2, -1, instant);
2578}
2579
2585{
2586 TileIndex old;
2587
2588 old = _thd.redsq;
2589 _thd.redsq = tile;
2590
2591 if (tile != old) {
2592 if (tile != INVALID_TILE) MarkTileDirtyByTile(tile);
2593 if (old != INVALID_TILE) MarkTileDirtyByTile(old);
2594 }
2595}
2596
2602void SetTileSelectSize(int w, int h)
2603{
2604 _thd.new_size.x = w * TILE_SIZE;
2605 _thd.new_size.y = h * TILE_SIZE;
2606 _thd.new_outersize.x = 0;
2607 _thd.new_outersize.y = 0;
2608}
2609
2610void SetTileSelectBigSize(int ox, int oy, int sx, int sy)
2611{
2612 _thd.offs.x = ox * TILE_SIZE;
2613 _thd.offs.y = oy * TILE_SIZE;
2614 _thd.new_outersize.x = sx * TILE_SIZE;
2615 _thd.new_outersize.y = sy * TILE_SIZE;
2616}
2617
2619static HighLightStyle GetAutorailHT(int x, int y)
2620{
2621 return HT_RAIL | _autorail_piece[x & TILE_UNIT_MASK][y & TILE_UNIT_MASK];
2622}
2623
2628{
2629 this->pos.x = 0;
2630 this->pos.y = 0;
2631 this->new_pos.x = 0;
2632 this->new_pos.y = 0;
2633}
2634
2643
2652
2653
2654
2663{
2664 int x1;
2665 int y1;
2666
2667 if (_thd.freeze) return;
2668
2669 HighLightStyle new_drawstyle = HT_NONE;
2670 bool new_diagonal = false;
2671
2672 if ((_thd.place_mode & HT_DRAG_MASK) == HT_SPECIAL) {
2673 x1 = _thd.selend.x;
2674 y1 = _thd.selend.y;
2675 if (x1 != -1) {
2676 int x2 = _thd.selstart.x & ~TILE_UNIT_MASK;
2677 int y2 = _thd.selstart.y & ~TILE_UNIT_MASK;
2678 x1 &= ~TILE_UNIT_MASK;
2679 y1 &= ~TILE_UNIT_MASK;
2680
2681 if (_thd.IsDraggingDiagonal()) {
2682 new_diagonal = true;
2683 } else {
2684 if (x1 >= x2) Swap(x1, x2);
2685 if (y1 >= y2) Swap(y1, y2);
2686 }
2687 _thd.new_pos.x = x1;
2688 _thd.new_pos.y = y1;
2689 _thd.new_size.x = x2 - x1;
2690 _thd.new_size.y = y2 - y1;
2691 if (!new_diagonal) {
2692 _thd.new_size.x += TILE_SIZE;
2693 _thd.new_size.y += TILE_SIZE;
2694 }
2695 new_drawstyle = _thd.next_drawstyle;
2696 }
2697 } else if ((_thd.place_mode & HT_DRAG_MASK) != HT_NONE) {
2698 Point pt = GetTileBelowCursor();
2699 x1 = pt.x;
2700 y1 = pt.y;
2701 if (x1 != -1) {
2702 switch (_thd.place_mode & HT_DRAG_MASK) {
2703 case HT_RECT:
2704 new_drawstyle = HT_RECT;
2705 break;
2706 case HT_POINT:
2707 new_drawstyle = HT_POINT;
2708 x1 += TILE_SIZE / 2;
2709 y1 += TILE_SIZE / 2;
2710 break;
2711 case HT_RAIL:
2712 /* Draw one highlighted tile in any direction */
2713 new_drawstyle = GetAutorailHT(pt.x, pt.y);
2714 break;
2715 case HT_LINE:
2716 switch (_thd.place_mode & HT_DIR_MASK) {
2717 case HT_DIR_X: new_drawstyle = HT_LINE | HT_DIR_X; break;
2718 case HT_DIR_Y: new_drawstyle = HT_LINE | HT_DIR_Y; break;
2719
2720 case HT_DIR_HU:
2721 case HT_DIR_HL:
2722 new_drawstyle = (pt.x & TILE_UNIT_MASK) + (pt.y & TILE_UNIT_MASK) <= TILE_SIZE ? HT_LINE | HT_DIR_HU : HT_LINE | HT_DIR_HL;
2723 break;
2724
2725 case HT_DIR_VL:
2726 case HT_DIR_VR:
2727 new_drawstyle = (pt.x & TILE_UNIT_MASK) > (pt.y & TILE_UNIT_MASK) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
2728 break;
2729
2730 default: NOT_REACHED();
2731 }
2732 _thd.selstart.x = x1 & ~TILE_UNIT_MASK;
2733 _thd.selstart.y = y1 & ~TILE_UNIT_MASK;
2734 break;
2735 default:
2736 NOT_REACHED();
2737 }
2738 _thd.new_pos.x = x1 & ~TILE_UNIT_MASK;
2739 _thd.new_pos.y = y1 & ~TILE_UNIT_MASK;
2740 }
2741 }
2742
2743 /* redraw selection */
2744 if (_thd.drawstyle != new_drawstyle ||
2745 _thd.pos.x != _thd.new_pos.x || _thd.pos.y != _thd.new_pos.y ||
2746 _thd.size.x != _thd.new_size.x || _thd.size.y != _thd.new_size.y ||
2747 _thd.outersize.x != _thd.new_outersize.x ||
2748 _thd.outersize.y != _thd.new_outersize.y ||
2749 _thd.diagonal != new_diagonal) {
2750 /* Clear the old tile selection? */
2752
2753 _thd.drawstyle = new_drawstyle;
2754 _thd.pos = _thd.new_pos;
2755 _thd.size = _thd.new_size;
2756 _thd.outersize = _thd.new_outersize;
2757 _thd.diagonal = new_diagonal;
2758 _thd.dirty = 0xff;
2759
2760 /* Draw the new tile selection? */
2761 if ((new_drawstyle & HT_DRAG_MASK) != HT_NONE) SetSelectionTilesDirty();
2762 }
2763}
2764
2770static inline void ShowMeasurementTooltips(StringID str, uint paramcount)
2771{
2773 GuiShowTooltips(_thd.GetCallbackWnd(), str, TCC_EXIT_VIEWPORT, paramcount);
2774}
2775
2776static void HideMeasurementTooltips()
2777{
2779}
2780
2783{
2784 _thd.select_method = method;
2785 _thd.select_proc = process;
2786 _thd.selend.x = TileX(tile) * TILE_SIZE;
2787 _thd.selstart.x = TileX(tile) * TILE_SIZE;
2788 _thd.selend.y = TileY(tile) * TILE_SIZE;
2789 _thd.selstart.y = TileY(tile) * TILE_SIZE;
2790
2791 /* Needed so several things (road, autoroad, bridges, ...) are placed correctly.
2792 * In effect, placement starts from the centre of a tile
2793 */
2794 if (method == VPM_X_OR_Y || method == VPM_FIX_X || method == VPM_FIX_Y) {
2795 _thd.selend.x += TILE_SIZE / 2;
2796 _thd.selend.y += TILE_SIZE / 2;
2797 _thd.selstart.x += TILE_SIZE / 2;
2798 _thd.selstart.y += TILE_SIZE / 2;
2799 }
2800
2802 if ((_thd.place_mode & HT_DRAG_MASK) == HT_RECT) {
2803 _thd.place_mode = HT_SPECIAL | others;
2804 _thd.next_drawstyle = HT_RECT | others;
2805 } else if (_thd.place_mode & (HT_RAIL | HT_LINE)) {
2806 _thd.place_mode = HT_SPECIAL | others;
2807 _thd.next_drawstyle = _thd.drawstyle | others;
2808 } else {
2809 _thd.place_mode = HT_SPECIAL | others;
2810 _thd.next_drawstyle = HT_POINT | others;
2811 }
2813}
2814
2817{
2819 _thd.select_proc = process;
2820 _thd.selstart.x = 0;
2821 _thd.selstart.y = 0;
2822 _thd.next_drawstyle = HT_RECT;
2823
2825}
2826
2827void VpSetPlaceSizingLimit(int limit)
2828{
2829 _thd.sizelimit = limit;
2830}
2831
2838{
2839 uint64_t distance = DistanceManhattan(from, to) + 1;
2840
2841 _thd.selend.x = TileX(to) * TILE_SIZE;
2842 _thd.selend.y = TileY(to) * TILE_SIZE;
2843 _thd.selstart.x = TileX(from) * TILE_SIZE;
2844 _thd.selstart.y = TileY(from) * TILE_SIZE;
2845 _thd.next_drawstyle = HT_RECT;
2846
2847 /* show measurement only if there is any length to speak of */
2848 if (distance > 1) {
2849 SetDParam(0, distance);
2850 ShowMeasurementTooltips(STR_MEASURE_LENGTH, 1);
2851 } else {
2852 HideMeasurementTooltips();
2853 }
2854}
2855
2856static void VpStartPreSizing()
2857{
2858 _thd.selend.x = -1;
2860}
2861
2867{
2868 int fxpy = _tile_fract_coords.x + _tile_fract_coords.y;
2869 int sxpy = (_thd.selend.x & TILE_UNIT_MASK) + (_thd.selend.y & TILE_UNIT_MASK);
2870 int fxmy = _tile_fract_coords.x - _tile_fract_coords.y;
2871 int sxmy = (_thd.selend.x & TILE_UNIT_MASK) - (_thd.selend.y & TILE_UNIT_MASK);
2872
2873 switch (mode) {
2874 default: NOT_REACHED();
2875 case 0: // end piece is lower right
2876 if (fxpy >= 20 && sxpy <= 12) return HT_DIR_HL;
2877 if (fxmy < -3 && sxmy > 3) return HT_DIR_VR;
2878 return HT_DIR_Y;
2879
2880 case 1:
2881 if (fxmy > 3 && sxmy < -3) return HT_DIR_VL;
2882 if (fxpy <= 12 && sxpy >= 20) return HT_DIR_HU;
2883 return HT_DIR_Y;
2884
2885 case 2:
2886 if (fxmy > 3 && sxmy < -3) return HT_DIR_VL;
2887 if (fxpy >= 20 && sxpy <= 12) return HT_DIR_HL;
2888 return HT_DIR_X;
2889
2890 case 3:
2891 if (fxmy < -3 && sxmy > 3) return HT_DIR_VR;
2892 if (fxpy <= 12 && sxpy >= 20) return HT_DIR_HU;
2893 return HT_DIR_X;
2894 }
2895}
2896
2910static bool SwapDirection(HighLightStyle style, TileIndex start_tile, TileIndex end_tile)
2911{
2912 uint start_x = TileX(start_tile);
2913 uint start_y = TileY(start_tile);
2914 uint end_x = TileX(end_tile);
2915 uint end_y = TileY(end_tile);
2916
2917 switch (style & HT_DRAG_MASK) {
2918 case HT_RAIL:
2919 case HT_LINE: return (end_x > start_x || (end_x == start_x && end_y > start_y));
2920
2921 case HT_RECT:
2922 case HT_POINT: return (end_x != start_x && end_y < start_y);
2923 default: NOT_REACHED();
2924 }
2925
2926 return false;
2927}
2928
2944static int CalcHeightdiff(HighLightStyle style, uint distance, TileIndex start_tile, TileIndex end_tile)
2945{
2946 bool swap = SwapDirection(style, start_tile, end_tile);
2947 uint h0, h1; // Start height and end height.
2948
2949 if (start_tile == end_tile) return 0;
2950 if (swap) Swap(start_tile, end_tile);
2951
2952 switch (style & HT_DRAG_MASK) {
2953 case HT_RECT:
2954 /* In the case of an area we can determine whether we were dragging south or
2955 * east by checking the X-coordinates of the tiles */
2956 if (TileX(end_tile) > TileX(start_tile)) {
2957 /* Dragging south does not need to change the start tile. */
2958 end_tile = TileAddByDir(end_tile, DIR_S);
2959 } else {
2960 /* Dragging east. */
2961 start_tile = TileAddByDir(start_tile, DIR_SW);
2962 end_tile = TileAddByDir(end_tile, DIR_SE);
2963 }
2964 [[fallthrough]];
2965
2966 case HT_POINT:
2967 h0 = TileHeight(start_tile);
2968 h1 = TileHeight(end_tile);
2969 break;
2970 default: { // All other types, this is mostly only line/autorail
2971 static const HighLightStyle flip_style_direction[] = {
2973 };
2974 static const std::pair<TileIndexDiffC, TileIndexDiffC> start_heightdiff_line_by_dir[] = {
2975 { {1, 0}, {1, 1} }, // HT_DIR_X
2976 { {0, 1}, {1, 1} }, // HT_DIR_Y
2977 { {1, 0}, {0, 0} }, // HT_DIR_HU
2978 { {1, 0}, {1, 1} }, // HT_DIR_HL
2979 { {1, 0}, {1, 1} }, // HT_DIR_VL
2980 { {0, 1}, {1, 1} }, // HT_DIR_VR
2981 };
2982 static const std::pair<TileIndexDiffC, TileIndexDiffC> end_heightdiff_line_by_dir[] = {
2983 { {0, 1}, {0, 0} }, // HT_DIR_X
2984 { {1, 0}, {0, 0} }, // HT_DIR_Y
2985 { {0, 1}, {0, 0} }, // HT_DIR_HU
2986 { {1, 1}, {0, 1} }, // HT_DIR_HL
2987 { {1, 0}, {0, 0} }, // HT_DIR_VL
2988 { {0, 0}, {0, 1} }, // HT_DIR_VR
2989 };
2990 static_assert(std::size(start_heightdiff_line_by_dir) == HT_DIR_END);
2991 static_assert(std::size(end_heightdiff_line_by_dir) == HT_DIR_END);
2992
2993 distance %= 2; // we're only interested if the distance is even or uneven
2994 style &= HT_DIR_MASK;
2995 assert(style < HT_DIR_END);
2996
2997 /* To handle autorail, we do some magic to be able to use a lookup table.
2998 * Firstly if we drag the other way around, we switch start&end, and if needed
2999 * also flip the drag-position. Eg if it was on the left, and the distance is even
3000 * that means the end, which is now the start is on the right */
3001 if (swap && distance == 0) style = flip_style_direction[style];
3002
3003 /* Lambda to help calculating the height at one side of the line. */
3004 auto get_height = [](auto &tile, auto &heightdiffs) {
3005 return std::max(
3006 TileHeight(TileAdd(tile, ToTileIndexDiff(heightdiffs.first))),
3007 TileHeight(TileAdd(tile, ToTileIndexDiff(heightdiffs.second))));
3008 };
3009
3010 /* Use lookup table for start-tile based on HighLightStyle direction */
3011 h0 = get_height(start_tile, start_heightdiff_line_by_dir[style]);
3012
3013 /* Use lookup table for end-tile based on HighLightStyle direction
3014 * flip around side (lower/upper, left/right) based on distance */
3015 if (distance == 0) style = flip_style_direction[style];
3016 h1 = get_height(end_tile, end_heightdiff_line_by_dir[style]);
3017 break;
3018 }
3019 }
3020
3021 if (swap) Swap(h0, h1);
3022 return (int)(h1 - h0) * TILE_HEIGHT_STEP;
3023}
3024
3025static const StringID measure_strings_length[] = {STR_NULL, STR_MEASURE_LENGTH, STR_MEASURE_LENGTH_HEIGHTDIFF};
3026
3033static void CheckUnderflow(int &test, int &other, int mult)
3034{
3035 if (test >= 0) return;
3036
3037 other += mult * test;
3038 test = 0;
3039}
3040
3048static void CheckOverflow(int &test, int &other, int max, int mult)
3049{
3050 if (test <= max) return;
3051
3052 other += mult * (test - max);
3053 test = max;
3054}
3055
3057static void CalcRaildirsDrawstyle(int x, int y, int method)
3058{
3060
3061 int dx = _thd.selstart.x - (_thd.selend.x & ~TILE_UNIT_MASK);
3062 int dy = _thd.selstart.y - (_thd.selend.y & ~TILE_UNIT_MASK);
3063 uint w = abs(dx) + TILE_SIZE;
3064 uint h = abs(dy) + TILE_SIZE;
3065
3066 if (method & ~(VPM_RAILDIRS | VPM_SIGNALDIRS)) {
3067 /* We 'force' a selection direction; first four rail buttons. */
3068 method &= ~(VPM_RAILDIRS | VPM_SIGNALDIRS);
3069 int raw_dx = _thd.selstart.x - _thd.selend.x;
3070 int raw_dy = _thd.selstart.y - _thd.selend.y;
3071 switch (method) {
3072 case VPM_FIX_X:
3073 b = HT_LINE | HT_DIR_Y;
3074 x = _thd.selstart.x;
3075 break;
3076
3077 case VPM_FIX_Y:
3078 b = HT_LINE | HT_DIR_X;
3079 y = _thd.selstart.y;
3080 break;
3081
3082 case VPM_FIX_HORIZONTAL:
3083 if (dx == -dy) {
3084 /* We are on a straight horizontal line. Determine the 'rail'
3085 * to build based the sub tile location. */
3087 } else {
3088 /* We are not on a straight line. Determine the rail to build
3089 * based on whether we are above or below it. */
3090 b = dx + dy >= (int)TILE_SIZE ? HT_LINE | HT_DIR_HU : HT_LINE | HT_DIR_HL;
3091
3092 /* Calculate where a horizontal line through the start point and
3093 * a vertical line from the selected end point intersect and
3094 * use that point as the end point. */
3095 int offset = (raw_dx - raw_dy) / 2;
3096 x = _thd.selstart.x - (offset & ~TILE_UNIT_MASK);
3097 y = _thd.selstart.y + (offset & ~TILE_UNIT_MASK);
3098
3099 /* 'Build' the last half rail tile if needed */
3100 if ((offset & TILE_UNIT_MASK) > (TILE_SIZE / 2)) {
3101 if (dx + dy >= (int)TILE_SIZE) {
3102 x += (dx + dy < 0) ? (int)TILE_SIZE : -(int)TILE_SIZE;
3103 } else {
3104 y += (dx + dy < 0) ? (int)TILE_SIZE : -(int)TILE_SIZE;
3105 }
3106 }
3107
3108 /* Make sure we do not overflow the map! */
3109 CheckUnderflow(x, y, 1);
3110 CheckUnderflow(y, x, 1);
3111 CheckOverflow(x, y, (Map::MaxX() - 1) * TILE_SIZE, 1);
3112 CheckOverflow(y, x, (Map::MaxY() - 1) * TILE_SIZE, 1);
3113 assert(x >= 0 && y >= 0 && x <= (int)(Map::MaxX() * TILE_SIZE) && y <= (int)(Map::MaxY() * TILE_SIZE));
3114 }
3115 break;
3116
3117 case VPM_FIX_VERTICAL:
3118 if (dx == dy) {
3119 /* We are on a straight vertical line. Determine the 'rail'
3120 * to build based the sub tile location. */
3122 } else {
3123 /* We are not on a straight line. Determine the rail to build
3124 * based on whether we are left or right from it. */
3125 b = dx < dy ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
3126
3127 /* Calculate where a vertical line through the start point and
3128 * a horizontal line from the selected end point intersect and
3129 * use that point as the end point. */
3130 int offset = (raw_dx + raw_dy + (int)TILE_SIZE) / 2;
3131 x = _thd.selstart.x - (offset & ~TILE_UNIT_MASK);
3132 y = _thd.selstart.y - (offset & ~TILE_UNIT_MASK);
3133
3134 /* 'Build' the last half rail tile if needed */
3135 if ((offset & TILE_UNIT_MASK) > (TILE_SIZE / 2)) {
3136 if (dx - dy < 0) {
3137 y += (dx > dy) ? (int)TILE_SIZE : -(int)TILE_SIZE;
3138 } else {
3139 x += (dx < dy) ? (int)TILE_SIZE : -(int)TILE_SIZE;
3140 }
3141 }
3142
3143 /* Make sure we do not overflow the map! */
3144 CheckUnderflow(x, y, -1);
3145 CheckUnderflow(y, x, -1);
3146 CheckOverflow(x, y, (Map::MaxX() - 1) * TILE_SIZE, -1);
3147 CheckOverflow(y, x, (Map::MaxY() - 1) * TILE_SIZE, -1);
3148 assert(x >= 0 && y >= 0 && x <= (int)(Map::MaxX() * TILE_SIZE) && y <= (int)(Map::MaxY() * TILE_SIZE));
3149 }
3150 break;
3151
3152 default:
3153 NOT_REACHED();
3154 }
3155 } else if (TileVirtXY(_thd.selstart.x, _thd.selstart.y) == TileVirtXY(x, y)) { // check if we're only within one tile
3156 if (method & VPM_RAILDIRS) {
3157 b = GetAutorailHT(x, y);
3158 } else { // rect for autosignals on one tile
3159 b = HT_RECT;
3160 }
3161 } else if (h == TILE_SIZE) { // Is this in X direction?
3162 if (dx == (int)TILE_SIZE) { // 2x1 special handling
3163 b = (Check2x1AutoRail(3)) | HT_LINE;
3164 } else if (dx == -(int)TILE_SIZE) {
3165 b = (Check2x1AutoRail(2)) | HT_LINE;
3166 } else {
3167 b = HT_LINE | HT_DIR_X;
3168 }
3169 y = _thd.selstart.y;
3170 } else if (w == TILE_SIZE) { // Or Y direction?
3171 if (dy == (int)TILE_SIZE) { // 2x1 special handling
3172 b = (Check2x1AutoRail(1)) | HT_LINE;
3173 } else if (dy == -(int)TILE_SIZE) { // 2x1 other direction
3174 b = (Check2x1AutoRail(0)) | HT_LINE;
3175 } else {
3176 b = HT_LINE | HT_DIR_Y;
3177 }
3178 x = _thd.selstart.x;
3179 } else if (w > h * 2) { // still count as x dir?
3180 b = HT_LINE | HT_DIR_X;
3181 y = _thd.selstart.y;
3182 } else if (h > w * 2) { // still count as y dir?
3183 b = HT_LINE | HT_DIR_Y;
3184 x = _thd.selstart.x;
3185 } else { // complicated direction
3186 int d = w - h;
3187 _thd.selend.x = _thd.selend.x & ~TILE_UNIT_MASK;
3188 _thd.selend.y = _thd.selend.y & ~TILE_UNIT_MASK;
3189
3190 /* four cases. */
3191 if (x > _thd.selstart.x) {
3192 if (y > _thd.selstart.y) {
3193 /* south */
3194 if (d == 0) {
3196 } else if (d >= 0) {
3197 x = _thd.selstart.x + h;
3198 b = HT_LINE | HT_DIR_VL;
3199 } else {
3200 y = _thd.selstart.y + w;
3201 b = HT_LINE | HT_DIR_VR;
3202 }
3203 } else {
3204 /* west */
3205 if (d == 0) {
3207 } else if (d >= 0) {
3208 x = _thd.selstart.x + h;
3209 b = HT_LINE | HT_DIR_HL;
3210 } else {
3211 y = _thd.selstart.y - w;
3212 b = HT_LINE | HT_DIR_HU;
3213 }
3214 }
3215 } else {
3216 if (y > _thd.selstart.y) {
3217 /* east */
3218 if (d == 0) {
3220 } else if (d >= 0) {
3221 x = _thd.selstart.x - h;
3222 b = HT_LINE | HT_DIR_HU;
3223 } else {
3224 y = _thd.selstart.y + w;
3225 b = HT_LINE | HT_DIR_HL;
3226 }
3227 } else {
3228 /* north */
3229 if (d == 0) {
3231 } else if (d >= 0) {
3232 x = _thd.selstart.x - h;
3233 b = HT_LINE | HT_DIR_VR;
3234 } else {
3235 y = _thd.selstart.y - w;
3236 b = HT_LINE | HT_DIR_VL;
3237 }
3238 }
3239 }
3240 }
3241
3243 TileIndex t0 = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
3244 TileIndex t1 = TileVirtXY(x, y);
3245 uint distance = DistanceManhattan(t0, t1) + 1;
3246 uint8_t index = 0;
3247
3248 if (distance != 1) {
3249 int heightdiff = CalcHeightdiff(b, distance, t0, t1);
3250 /* If we are showing a tooltip for horizontal or vertical drags,
3251 * 2 tiles have a length of 1. To bias towards the ceiling we add
3252 * one before division. It feels more natural to count 3 lengths as 2 */
3253 if ((b & HT_DIR_MASK) != HT_DIR_X && (b & HT_DIR_MASK) != HT_DIR_Y) {
3254 distance = CeilDiv(distance, 2);
3255 }
3256
3257 SetDParam(index++, distance);
3258 if (heightdiff != 0) SetDParam(index++, heightdiff);
3259 }
3260
3261 ShowMeasurementTooltips(measure_strings_length[index], index);
3262 }
3263
3264 _thd.selend.x = x;
3265 _thd.selend.y = y;
3266 _thd.next_drawstyle = b;
3267}
3268
3277{
3278 int sx, sy;
3279 HighLightStyle style;
3280
3281 if (x == -1) {
3282 _thd.selend.x = -1;
3283 return;
3284 }
3285
3286 /* Special handling of drag in any (8-way) direction */
3287 if (method & (VPM_RAILDIRS | VPM_SIGNALDIRS)) {
3288 _thd.selend.x = x;
3289 _thd.selend.y = y;
3290 CalcRaildirsDrawstyle(x, y, method);
3291 return;
3292 }
3293
3294 /* Needed so level-land is placed correctly */
3295 if ((_thd.next_drawstyle & HT_DRAG_MASK) == HT_POINT) {
3296 x += TILE_SIZE / 2;
3297 y += TILE_SIZE / 2;
3298 }
3299
3300 sx = _thd.selstart.x;
3301 sy = _thd.selstart.y;
3302
3303 int limit = 0;
3304
3305 switch (method) {
3306 case VPM_X_OR_Y: // drag in X or Y direction
3307 if (abs(sy - y) < abs(sx - x)) {
3308 y = sy;
3309 style = HT_DIR_X;
3310 } else {
3311 x = sx;
3312 style = HT_DIR_Y;
3313 }
3314 goto calc_heightdiff_single_direction;
3315
3316 case VPM_X_LIMITED: // Drag in X direction (limited size).
3317 limit = (_thd.sizelimit - 1) * TILE_SIZE;
3318 [[fallthrough]];
3319
3320 case VPM_FIX_X: // drag in Y direction
3321 x = sx;
3322 style = HT_DIR_Y;
3323 goto calc_heightdiff_single_direction;
3324
3325 case VPM_Y_LIMITED: // Drag in Y direction (limited size).
3326 limit = (_thd.sizelimit - 1) * TILE_SIZE;
3327 [[fallthrough]];
3328
3329 case VPM_FIX_Y: // drag in X direction
3330 y = sy;
3331 style = HT_DIR_X;
3332
3333calc_heightdiff_single_direction:;
3334 if (limit > 0) {
3335 x = sx + Clamp(x - sx, -limit, limit);
3336 y = sy + Clamp(y - sy, -limit, limit);
3337 }
3339 TileIndex t0 = TileVirtXY(sx, sy);
3340 TileIndex t1 = TileVirtXY(x, y);
3341 uint distance = DistanceManhattan(t0, t1) + 1;
3342 uint8_t index = 0;
3343
3344 if (distance != 1) {
3345 /* With current code passing a HT_LINE style to calculate the height
3346 * difference is enough. However if/when a point-tool is created
3347 * with this method, function should be called with new_style (below)
3348 * instead of HT_LINE | style case HT_POINT is handled specially
3349 * new_style := (_thd.next_drawstyle & HT_RECT) ? HT_LINE | style : _thd.next_drawstyle; */
3350 int heightdiff = CalcHeightdiff(HT_LINE | style, 0, t0, t1);
3351
3352 SetDParam(index++, distance);
3353 if (heightdiff != 0) SetDParam(index++, heightdiff);
3354 }
3355
3356 ShowMeasurementTooltips(measure_strings_length[index], index);
3357 }
3358 break;
3359
3360 case VPM_X_AND_Y_LIMITED: // Drag an X by Y constrained rect area.
3361 limit = (_thd.sizelimit - 1) * TILE_SIZE;
3362 x = sx + Clamp(x - sx, -limit, limit);
3363 y = sy + Clamp(y - sy, -limit, limit);
3364 [[fallthrough]];
3365
3366 case VPM_X_AND_Y: // drag an X by Y area
3368 static const StringID measure_strings_area[] = {
3369 STR_NULL, STR_NULL, STR_MEASURE_AREA, STR_MEASURE_AREA_HEIGHTDIFF
3370 };
3371
3372 TileIndex t0 = TileVirtXY(sx, sy);
3373 TileIndex t1 = TileVirtXY(x, y);
3374 uint dx = Delta(TileX(t0), TileX(t1)) + 1;
3375 uint dy = Delta(TileY(t0), TileY(t1)) + 1;
3376 uint8_t index = 0;
3377
3378 /* If dragging an area (eg dynamite tool) and it is actually a single
3379 * row/column, change the type to 'line' to get proper calculation for height */
3380 style = (HighLightStyle)_thd.next_drawstyle;
3381 if (_thd.IsDraggingDiagonal()) {
3382 /* Determine the "area" of the diagonal dragged selection.
3383 * We assume the area is the number of tiles along the X
3384 * edge and the number of tiles along the Y edge. However,
3385 * multiplying these two numbers does not give the exact
3386 * number of tiles; basically we are counting the black
3387 * squares on a chess board and ignore the white ones to
3388 * make the tile counts at the edges match up. There is no
3389 * other way to make a proper count though.
3390 *
3391 * First convert to the rotated coordinate system. */
3392 int dist_x = TileX(t0) - TileX(t1);
3393 int dist_y = TileY(t0) - TileY(t1);
3394 int a_max = dist_x + dist_y;
3395 int b_max = dist_y - dist_x;
3396
3397 /* Now determine the size along the edge, but due to the
3398 * chess board principle this counts double. */
3399 a_max = abs(a_max + (a_max > 0 ? 2 : -2)) / 2;
3400 b_max = abs(b_max + (b_max > 0 ? 2 : -2)) / 2;
3401
3402 /* We get a 1x1 on normal 2x1 rectangles, due to it being
3403 * a seen as two sides. As the result for actual building
3404 * will be the same as non-diagonal dragging revert to that
3405 * behaviour to give it a more normally looking size. */
3406 if (a_max != 1 || b_max != 1) {
3407 dx = a_max;
3408 dy = b_max;
3409 }
3410 } else if (style & HT_RECT) {
3411 if (dx == 1) {
3412 style = HT_LINE | HT_DIR_Y;
3413 } else if (dy == 1) {
3414 style = HT_LINE | HT_DIR_X;
3415 }
3416 }
3417
3418 if (dx != 1 || dy != 1) {
3419 int heightdiff = CalcHeightdiff(style, 0, t0, t1);
3420
3421 SetDParam(index++, dx - (style & HT_POINT ? 1 : 0));
3422 SetDParam(index++, dy - (style & HT_POINT ? 1 : 0));
3423 if (heightdiff != 0) SetDParam(index++, heightdiff);
3424 }
3425
3426 ShowMeasurementTooltips(measure_strings_area[index], index);
3427 }
3428 break;
3429
3430 default: NOT_REACHED();
3431 }
3432
3433 _thd.selend.x = x;
3434 _thd.selend.y = y;
3435}
3436
3442{
3444
3445 /* stop drag mode if the window has been closed */
3446 Window *w = _thd.GetCallbackWnd();
3447 if (w == nullptr) {
3449 return ES_HANDLED;
3450 }
3451
3452 /* while dragging execute the drag procedure of the corresponding window (mostly VpSelectTilesWithMethod() ) */
3453 if (_left_button_down) {
3455 /* Only register a drag event when the mouse moved. */
3456 if (_thd.new_pos.x == _thd.selstart.x && _thd.new_pos.y == _thd.selstart.y) return ES_HANDLED;
3457 _thd.selstart.x = _thd.new_pos.x;
3458 _thd.selstart.y = _thd.new_pos.y;
3459 }
3460
3461 w->OnPlaceDrag(_thd.select_method, _thd.select_proc, GetTileBelowCursor());
3462 return ES_HANDLED;
3463 }
3464
3465 /* Mouse button released. */
3468
3469 /* Keep the selected tool, but reset it to the original mode. */
3471 if ((_thd.next_drawstyle & HT_DRAG_MASK) == HT_RECT) {
3472 _thd.place_mode = HT_RECT | others;
3473 } else if (_thd.select_method & VPM_SIGNALDIRS) {
3474 _thd.place_mode = HT_RECT | others;
3475 } else if (_thd.select_method & VPM_RAILDIRS) {
3476 _thd.place_mode = (_thd.select_method & ~VPM_RAILDIRS) ? _thd.next_drawstyle : (HT_RAIL | others);
3477 } else {
3478 _thd.place_mode = HT_POINT | others;
3479 }
3480 SetTileSelectSize(1, 1);
3481
3482 HideMeasurementTooltips();
3483 w->OnPlaceMouseUp(_thd.select_method, _thd.select_proc, _thd.selend, TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y));
3484
3485 return ES_HANDLED;
3486}
3487
3496{
3497 SetObjectToPlace(icon, pal, mode, w->window_class, w->window_number);
3498}
3499
3500#include "table/animcursors.h"
3501
3510void SetObjectToPlace(CursorID icon, PaletteID pal, HighLightStyle mode, WindowClass window_class, WindowNumber window_num)
3511{
3512 if (_thd.window_class != WC_INVALID) {
3513 /* Undo clicking on button and drag & drop */
3514 Window *w = _thd.GetCallbackWnd();
3515 /* Call the abort function, but set the window class to something
3516 * that will never be used to avoid infinite loops. Setting it to
3517 * the 'next' window class must not be done because recursion into
3518 * this function might in some cases reset the newly set object to
3519 * place or not properly reset the original selection. */
3520 _thd.window_class = WC_INVALID;
3521 if (w != nullptr) {
3522 w->OnPlaceObjectAbort();
3523 HideMeasurementTooltips();
3524 }
3525 }
3526
3527 /* Mark the old selection dirty, in case the selection shape or colour changes */
3529
3530 SetTileSelectSize(1, 1);
3531
3532 _thd.make_square_red = false;
3533
3534 if (mode == HT_DRAG) { // HT_DRAG is for dragdropping trains in the depot window
3535 mode = HT_NONE;
3537 } else {
3539 }
3540
3541 _thd.place_mode = mode;
3542 _thd.window_class = window_class;
3543 _thd.window_number = window_num;
3544
3545 if ((mode & HT_DRAG_MASK) == HT_SPECIAL) { // special tools, like tunnels or docks start with presizing mode
3546 VpStartPreSizing();
3547 }
3548
3549 if ((icon & ANIMCURSOR_FLAG) != 0) {
3551 } else {
3552 SetMouseCursor(icon, pal);
3553 }
3554
3555}
3556
3562
3563Point GetViewportStationMiddle(const Viewport *vp, const Station *st)
3564{
3565 int x = TileX(st->xy) * TILE_SIZE;
3566 int y = TileY(st->xy) * TILE_SIZE;
3567 int z = GetSlopePixelZ(Clamp(x, 0, Map::SizeX() * TILE_SIZE - 1), Clamp(y, 0, Map::SizeY() * TILE_SIZE - 1));
3568
3569 Point p = RemapCoords(x, y, z);
3570 p.x = UnScaleByZoom(p.x - vp->virtual_left, vp->zoom) + vp->left;
3571 p.y = UnScaleByZoom(p.y - vp->virtual_top, vp->zoom) + vp->top;
3572 return p;
3573}
3574
3580
3583#ifdef WITH_SSE
3584 { &ViewportSortParentSpritesSSE41Checker, &ViewportSortParentSpritesSSE41 },
3585#endif
3587};
3588
3591{
3592 for (const auto &sprite_sorter : _vp_sprite_sorters) {
3593 if (sprite_sorter.fct_checker()) {
3594 _vp_sprite_sorter = sprite_sorter.fct_sorter;
3595 break;
3596 }
3597 }
3598 assert(_vp_sprite_sorter != nullptr);
3599}
3600
3610{
3611 if (_current_company != OWNER_DEITY) return CMD_ERROR;
3612 switch (target) {
3613 case VST_EVERYONE:
3614 break;
3615 case VST_COMPANY:
3616 if (_local_company != (CompanyID)ref) return CommandCost();
3617 break;
3618 case VST_CLIENT:
3619 if (_network_own_client_id != (ClientID)ref) return CommandCost();
3620 break;
3621 default:
3622 return CMD_ERROR;
3623 }
3624
3625 if (flags & DC_EXEC) {
3628 }
3629 return CommandCost();
3630}
3631
3632void MarkCatchmentTilesDirty()
3633{
3634 if (_viewport_highlight_town != nullptr) {
3636 return;
3637 }
3638 if (_viewport_highlight_station != nullptr) {
3642 } else {
3644 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
3645 MarkTileDirtyByTile(tile);
3646 }
3647 }
3648 }
3649 if (_viewport_highlight_waypoint != nullptr) {
3652 }
3654 }
3655}
3656
3657static void SetWindowDirtyForViewportCatchment()
3658{
3662}
3663
3664static void ClearViewportCatchment()
3665{
3666 MarkCatchmentTilesDirty();
3669 _viewport_highlight_town = nullptr;
3670}
3671
3678void SetViewportCatchmentStation(const Station *st, bool sel)
3679{
3680 SetWindowDirtyForViewportCatchment();
3681 if (sel && _viewport_highlight_station != st) {
3682 ClearViewportCatchment();
3684 MarkCatchmentTilesDirty();
3685 } else if (!sel && _viewport_highlight_station == st) {
3686 MarkCatchmentTilesDirty();
3688 }
3690}
3691
3699{
3700 SetWindowDirtyForViewportCatchment();
3701 if (sel && _viewport_highlight_waypoint != wp) {
3702 ClearViewportCatchment();
3704 MarkCatchmentTilesDirty();
3705 } else if (!sel && _viewport_highlight_waypoint == wp) {
3706 MarkCatchmentTilesDirty();
3708 }
3710}
3711
3718void SetViewportCatchmentTown(const Town *t, bool sel)
3719{
3720 SetWindowDirtyForViewportCatchment();
3721 if (sel && _viewport_highlight_town != t) {
3722 ClearViewportCatchment();
3725 } else if (!sel && _viewport_highlight_town == t) {
3726 _viewport_highlight_town = nullptr;
3728 }
3730}
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
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:138
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.
constexpr EnumBitSet & Set(Tenum value)
Set the enum value.
constexpr bool Test(Tenum value) const
Test if the enum value is set.
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:28
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.
DoCommandFlag
List of flags for a command.
@ DC_EXEC
execute the given command
Definition of stuff that is very close to a company, like the company struct itself.
Colours _company_colours[MAX_COMPANIES]
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.
Owner
Enum for all companies/owners.
@ OWNER_DEITY
The object is owned by a superuser / goal script.
@ OWNER_NONE
The tile has no ownership.
#define Debug(category, level, format_string,...)
Ouptut 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:1686
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:851
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:657
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:960
void SetAnimatedMouseCursor(const AnimCursor *table)
Assign an animation to the cursor.
Definition gfx.cpp:1699
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:418
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:244
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:243
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:376
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:294
@ TC_IS_PALETTE_COLOUR
Colour value is already a real palette colour index, not an index of a StringColour.
Definition gfx_type.h:317
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:937
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:1486
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1529
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:1371
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:143
TileIndex TileAddByDir(TileIndex tile, Direction dir)
Adds a Direction to a tile.
Definition map_func.h:599
static debug_inline TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:373
TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc)
Return the offset between two tiles from a TileIndexDiffC struct.
Definition map_func.h:440
constexpr TileIndex TileAdd(TileIndex tile, TileIndexDiff offset)
Adds a given offset to a tile.
Definition map_func.h:457
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:425
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:415
static debug_inline TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:404
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
constexpr void Swap(T &a, T &b)
Type safe swap operation.
void GuiShowTooltips(Window *parent, StringID str, TooltipCloseCondition close_tooltip, uint paramcount)
Shows a tooltip.
Definition misc_gui.cpp:740
ClientID _network_own_client_id
Our client identifier.
Definition network.cpp:70
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:57
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:56
Base class for signs.
Functions related to signs.
void HandleClickOnSign(const Sign *si)
Handle clicking on a sign.
uint16_t SignID
The type of the IDs of signs.
Definition signs_type.h:14
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:1555
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:1569
static const CursorID ANIMCURSOR_FLAG
Flag for saying a cursor sprite is an animated cursor.
Definition sprites.h:1507
static const PaletteID PALETTE_SEL_TILE_RED
makes a square red. is used when removing rails or other stuff
Definition sprites.h:1570
static constexpr uint32_t SPRITE_MASK
The mask to for the main sprite.
Definition sprites.h:1556
static constexpr uint8_t PALETTE_MODIFIER_TRANSPARENT
when a sprite is to be displayed transparently, this bit needs to be set.
Definition sprites.h:1547
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1605
static const CursorID SPR_CURSOR_MOUSE
Cursor sprite numbers.
Definition sprites.h:1390
static const PaletteID PALETTE_SEL_TILE_BLUE
This draws a blueish square (catchment areas for example)
Definition sprites.h:1571
static const PaletteID PALETTE_TO_TRANSPARENT
This sets the sprite to transparent.
Definition sprites.h:1602
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.
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition strings.cpp:104
std::string GetString(StringID string)
Resolve the given StringID into a std::string with all the associated DParam lookups and formatting.
Definition strings.cpp:332
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:138
const SubSprite * sub
only draw a rectangular part of the sprite
Definition viewport.cpp:134
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:124
Data about how and where to blit pixels.
Definition gfx_type.h:156
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:279
static debug_inline uint SizeX()
Get the size of the map along the X.
Definition map_func.h:270
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:341
static uint MaxY()
Gets the maximum Y coordinate within the map, including MP_VOID.
Definition map_func.h:306
static debug_inline uint MaxX()
Gets the maximum X coordinate within the map, including MP_VOID.
Definition map_func.h:297
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.
Tindex index
Index of this pool item.
static size_t GetNumItems()
Returns number of valid items in the pool.
static Titem * Get(size_t index)
Returns Titem with given index.
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(size_t 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:265
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:128
int32_t x
screen X coordinate of sprite
Definition viewport.cpp:127
const SubSprite * sub
only draw a rectangular part of the sprite
Definition viewport.cpp:126
DrawTileProc * draw_tile_proc
Called to render the tile and its contents to the screen.
Definition tile_cmd.h:159
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
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, INVALID_VEHICLE 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:168
int foundation[FOUNDATION_PART_END]
Foundation sprites (index into parent_sprites_to_draw).
Definition viewport.cpp:181
int last_foundation_child[FOUNDATION_PART_END]
Tail of ChildSprite list of the foundations. (index into child_screen_sprites_to_draw)
Definition viewport.cpp:183
ParentSpriteToSortVector parent_sprites_to_sort
Parent sprite pointer array used for sorting.
Definition viewport.cpp:174
Point foundation_offset[FOUNDATION_PART_END]
Pixel offset for ground sprites on the foundations.
Definition viewport.cpp:184
SpriteCombineMode combine_sprites
Current mode of "sprite combining".
Definition viewport.cpp:179
FoundationPart foundation_part
Currently active foundation for ground sprite drawing.
Definition viewport.cpp:182
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, StringID str, StringID str_small=STR_NULL)
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:861
Data structure for an opened window.
Definition window_gui.h:272
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:548
virtual void OnPlaceObjectAbort()
The user cancelled a tile highlight mode that has been set.
Definition window_gui.h:808
WindowClass window_class
Window class.
Definition window_gui.h:300
ViewportData * viewport
Pointer to viewport data, if present.
Definition window_gui.h:317
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:787
int left
x position of left edge of the window
Definition window_gui.h:308
int top
y position of top edge of the window
Definition window_gui.h:309
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:829
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:970
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:818
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:380
AllWindows< false > Iterate
Iterate all windows in whatever order is easiest.
Definition window_gui.h:920
int height
Height of the window (number of pixels down in y direction)
Definition window_gui.h:311
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:310
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:301
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:1154
Vehicle * CheckClickOnVehicle(const Viewport *vp, int x, int y)
Find the vehicle close to the clicked coordinates.
Definition vehicle.cpp:1248
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.
static const VehicleID INVALID_VEHICLE
Constant representing a non-existing 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.
CommandCost CmdScrollViewport(DoCommandFlag flags, TileIndex tile, ViewportScrollTarget target, uint32_t ref)
Scroll players main viewport.
FoundationPart
Enumeration of multi-part foundations.
Definition viewport.cpp:142
@ FOUNDATION_PART_NONE
Neither foundation nor groundsprite drawn yet.
Definition viewport.cpp:143
@ FOUNDATION_PART_HALFTILE
Second part (halftile foundation)
Definition viewport.cpp:145
@ FOUNDATION_PART_NORMAL
First part (normal foundation or no foundation)
Definition viewport.cpp:144
void SetTileSelectSize(int w, int h)
Highlight w by h tiles at the cursor.
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:599
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:494
constexpr int LAST_CHILD_NONE
There is no last_child to fill.
Definition viewport.cpp:164
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:165
static const int MAX_TILE_EXTENT_TOP
Maximum top extent of tile relative to north corner (not considering bridges).
Definition viewport.cpp:111
static const int MAX_TILE_EXTENT_LEFT
Maximum left extent of tile relative to north corner.
Definition viewport.cpp:109
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:804
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:918
void StartSpriteCombine()
Starts a block of sprites, which are "combined" into a single bounding box.
Definition viewport.cpp:767
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:631
static void ShowMeasurementTooltips(StringID str, uint paramcount)
Displays the measurement tooltips when selecting multiple tiles.
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:540
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:435
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:792
static void DrawAutorailSelection(const TileInfo *ti, uint autorail_type)
Draws autorail highlights.
Definition viewport.cpp:978
SpriteCombineMode
Mode of "sprite combining".
Definition viewport.cpp:153
@ SPRITE_COMBINE_PENDING
Sprite combining will start with the next unclipped sprite.
Definition viewport.cpp:155
@ SPRITE_COMBINE_ACTIVE
Sprite combining is active. AddSortableSpriteToDraw outputs child sprites.
Definition viewport.cpp:156
@ SPRITE_COMBINE_NONE
Every AddSortableSpriteToDraw start its own bounding box.
Definition viewport.cpp:154
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:671
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:411
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:777
static const int MAX_TILE_EXTENT_RIGHT
Maximum right extent of tile relative to north corner.
Definition viewport.cpp:110
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:827
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:564
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:900
static const int MAX_TILE_EXTENT_BOTTOM
Maximum bottom extent of tile relative to north corner (worst case: SLOPE_STEEP_N).
Definition viewport.cpp:112
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:222
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:587
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:515
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:873
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:283
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:1137
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1124
Window * FindWindowFromPt(int x, int y)
Do a search for a window at specific coordinates.
Definition window.cpp:1765
SpecialMouseMode _special_mouse_mode
Mode of the mouse.
Definition window.cpp:93
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1095
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3099
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:
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