OpenTTD Source  20241124-master-g9399a92a4f
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"
86 #include "viewport_sprite_sorter.h"
87 #include "bridge_map.h"
88 #include "company_base.h"
89 #include "command_func.h"
90 #include "network/network_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"
98 #include "table/string_colours.h"
99 
100 #include "safeguards.h"
101 
102 Point _tile_fract_coords;
103 
104 
105 ViewportSignKdtree _viewport_sign_kdtree{};
106 static int _viewport_sign_maxwidth = 0;
107 
108 
109 static const int MAX_TILE_EXTENT_LEFT = ZOOM_BASE * TILE_PIXELS;
110 static const int MAX_TILE_EXTENT_RIGHT = ZOOM_BASE * TILE_PIXELS;
111 static const int MAX_TILE_EXTENT_TOP = ZOOM_BASE * MAX_BUILDING_PIXELS;
112 static const int MAX_TILE_EXTENT_BOTTOM = ZOOM_BASE * (TILE_PIXELS + 2 * TILE_HEIGHT);
113 
115  std::string string;
116  StringID string_id;
117  Colours colour;
118  int32_t x;
119  int32_t y;
120  uint16_t width;
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 
146  FOUNDATION_PART_END
147 };
148 
157 };
158 
159 typedef std::vector<TileSpriteToDraw> TileSpriteToDrawVector;
160 typedef std::vector<StringSpriteToDraw> StringSpriteToDrawVector;
161 typedef std::vector<ParentSpriteToDraw> ParentSpriteToDrawVector;
162 typedef std::vector<ChildScreenSpriteToDraw> ChildScreenSpriteToDrawVector;
163 
164 constexpr int LAST_CHILD_NONE = -1;
165 constexpr 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 
187 static bool MarkViewportDirty(const Viewport *vp, int left, int top, int right, int bottom);
188 
189 static ViewportDrawer _vd;
190 
191 TileHighlightData _thd;
192 static TileInfo _cur_ti;
193 bool _draw_bounding_boxes = false;
194 bool _draw_dirty_blocks = false;
195 uint _dirty_block_colour = 0;
196 static VpSpriteSorter _vp_sprite_sorter = nullptr;
197 
198 static 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 
206 void DeleteWindowViewport(Window *w)
207 {
208  delete w->viewport;
209  w->viewport = nullptr;
210 }
211 
222 void 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 
278 static Point _vp_move_offs;
279 
280 static 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 
346 static 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 
411 Viewport *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 
435 Point 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 */
450 static 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 
464 Point GetTileBelowCursor()
465 {
466  return GetTileFromScreenXY(_cursor.pos.x, _cursor.pos.y, _cursor.pos.x, _cursor.pos.y);
467 }
468 
469 
470 Point 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 
494 void 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 
515 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)
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 
540 static 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 
564 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)
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 
587 void 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 
599 void 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 
631 static 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 
671 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)
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 
768 {
769  assert(_vd.combine_sprites == SPRITE_COMBINE_NONE);
771 }
772 
778 {
779  assert(_vd.combine_sprites != SPRITE_COMBINE_NONE);
781 }
782 
792 static bool IsInRangeInclusive(int begin, int end, int check)
793 {
794  if (begin > end) Swap(begin, end);
795  return begin <= check && check <= end;
796 }
797 
804 bool 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 
827 void 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;
854  cs.next = LAST_CHILD_NONE;
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 
864 static void AddStringToDraw(int x, int y, StringID string, Colours colour, uint16_t width)
865 {
866  assert(width != 0);
867  StringSpriteToDraw &ss = _vd.string_sprites_to_draw.emplace_back();
868  ss.string = GetString(string);
869  ss.string_id = string;
870  ss.x = x;
871  ss.y = y;
872  ss.width = width;
873  ss.colour = colour;
874 }
875 
876 
890 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)
891 {
892  /* FIXME: This is not totally valid for some autorail highlights that extend over the edges of the tile. */
893  if (_vd.foundation[foundation_part] == -1) {
894  /* draw on real ground */
895  AddTileSpriteToDraw(image, pal, ti->x, ti->y, ti->z + z_offset, nullptr, extra_offs_x, extra_offs_y);
896  } else {
897  /* draw on top of foundation */
898  AddChildSpriteToFoundation(image, pal, nullptr, foundation_part, extra_offs_x, extra_offs_y - z_offset * ZOOM_BASE);
899  }
900 }
901 
908 static void DrawTileSelectionRect(const TileInfo *ti, PaletteID pal)
909 {
910  if (!IsValidTile(ti->tile)) return;
911 
912  SpriteID sel;
913  if (IsHalftileSlope(ti->tileh)) {
914  Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
915  SpriteID sel2 = SPR_HALFTILE_SELECTION_FLAT + halftile_corner;
917 
918  Corner opposite_corner = OppositeCorner(halftile_corner);
919  if (IsSteepSlope(ti->tileh)) {
920  sel = SPR_HALFTILE_SELECTION_DOWN;
921  } else {
922  sel = ((ti->tileh & SlopeWithOneCornerRaised(opposite_corner)) != 0 ? SPR_HALFTILE_SELECTION_UP : SPR_HALFTILE_SELECTION_FLAT);
923  }
924  sel += opposite_corner;
925  } else {
926  sel = SPR_SELECT_TILE + SlopeToSpriteOffset(ti->tileh);
927  }
929 }
930 
931 static bool IsPartOfAutoLine(int px, int py)
932 {
933  px -= _thd.selstart.x;
934  py -= _thd.selstart.y;
935 
936  if ((_thd.drawstyle & HT_DRAG_MASK) != HT_LINE) return false;
937 
938  switch (_thd.drawstyle & HT_DIR_MASK) {
939  case HT_DIR_X: return py == 0; // x direction
940  case HT_DIR_Y: return px == 0; // y direction
941  case HT_DIR_HU: return px == -py || px == -py - 16; // horizontal upper
942  case HT_DIR_HL: return px == -py || px == -py + 16; // horizontal lower
943  case HT_DIR_VL: return px == py || px == py + 16; // vertical left
944  case HT_DIR_VR: return px == py || px == py - 16; // vertical right
945  default:
946  NOT_REACHED();
947  }
948 }
949 
950 /* [direction][side] */
951 static const HighLightStyle _autorail_type[6][2] = {
952  { HT_DIR_X, HT_DIR_X },
953  { HT_DIR_Y, HT_DIR_Y },
954  { HT_DIR_HU, HT_DIR_HL },
955  { HT_DIR_HL, HT_DIR_HU },
956  { HT_DIR_VL, HT_DIR_VR },
957  { HT_DIR_VR, HT_DIR_VL }
958 };
959 
960 #include "table/autorail.h"
961 
968 static void DrawAutorailSelection(const TileInfo *ti, uint autorail_type)
969 {
970  SpriteID image;
971  PaletteID pal;
972  int offset;
973 
974  FoundationPart foundation_part = FOUNDATION_PART_NORMAL;
975  Slope autorail_tileh = RemoveHalftileSlope(ti->tileh);
976  if (IsHalftileSlope(ti->tileh)) {
977  static const uint _lower_rail[4] = { 5U, 2U, 4U, 3U };
978  Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
979  if (autorail_type != _lower_rail[halftile_corner]) {
980  foundation_part = FOUNDATION_PART_HALFTILE;
981  /* Here we draw the highlights of the "three-corners-raised"-slope. That looks ok to me. */
982  autorail_tileh = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
983  }
984  }
985 
986  offset = _AutorailTilehSprite[autorail_tileh][autorail_type];
987  if (offset >= 0) {
988  image = SPR_AUTORAIL_BASE + offset;
989  pal = PAL_NONE;
990  } else {
991  image = SPR_AUTORAIL_BASE - offset;
992  pal = PALETTE_SEL_TILE_RED;
993  }
994 
995  DrawSelectionSprite(image, _thd.make_square_red ? PALETTE_SEL_TILE_RED : pal, ti, 7, foundation_part);
996 }
997 
998 enum TileHighlightType {
999  THT_NONE,
1000  THT_WHITE,
1001  THT_BLUE,
1002  THT_RED,
1003 };
1004 
1008 
1014 static TileHighlightType GetTileHighlightType(TileIndex t)
1015 {
1016  if (_viewport_highlight_station != nullptr) {
1017  if (IsTileType(t, MP_STATION) && GetStationIndex(t) == _viewport_highlight_station->index) return THT_WHITE;
1018  if (_viewport_highlight_station->TileIsInCatchment(t)) return THT_BLUE;
1019  }
1020  if (_viewport_highlight_waypoint != nullptr) {
1021  if (IsTileType(t, MP_STATION) && GetStationIndex(t) == _viewport_highlight_waypoint->index) return THT_BLUE;
1022  }
1023 
1024  if (_viewport_highlight_town != nullptr) {
1025  if (IsTileType(t, MP_HOUSE)) {
1027  TileHighlightType type = THT_RED;
1028  for (const Station *st : _viewport_highlight_town->stations_near) {
1029  if (st->owner != _current_company) continue;
1030  if (st->TileIsInCatchment(t)) return THT_BLUE;
1031  }
1032  return type;
1033  }
1034  } else if (IsTileType(t, MP_STATION)) {
1035  for (const Station *st : _viewport_highlight_town->stations_near) {
1036  if (st->owner != _current_company) continue;
1037  if (GetStationIndex(t) == st->index) return THT_WHITE;
1038  }
1039  }
1040  }
1041 
1042  return THT_NONE;
1043 }
1044 
1050 static void DrawTileHighlightType(const TileInfo *ti, TileHighlightType tht)
1051 {
1052  switch (tht) {
1053  default:
1054  case THT_NONE: break;
1055  case THT_WHITE: DrawTileSelectionRect(ti, PAL_NONE); break;
1056  case THT_BLUE: DrawTileSelectionRect(ti, PALETTE_SEL_TILE_BLUE); break;
1057  case THT_RED: DrawTileSelectionRect(ti, PALETTE_SEL_TILE_RED); break;
1058  }
1059 }
1060 
1066 {
1067  /* Going through cases in order of computational time. */
1068 
1069  if (_town_local_authority_kdtree.Count() == 0) return;
1070 
1071  /* Tile belongs to town regardless of distance from town. */
1072  if (GetTileType(ti->tile) == MP_HOUSE) {
1073  if (!Town::GetByTile(ti->tile)->show_zone) return;
1074 
1076  return;
1077  }
1078 
1079  /* If the closest town in the highlighted list is far, we can stop searching. */
1080  TownID tid = _town_local_authority_kdtree.FindNearest(TileX(ti->tile), TileY(ti->tile));
1081  Town *closest_highlighted_town = Town::Get(tid);
1082 
1083  if (DistanceManhattan(ti->tile, closest_highlighted_town->xy) >= _settings_game.economy.dist_local_authority) return;
1084 
1085  /* Tile is inside of the local autrhority distance of a highlighted town,
1086  but it is possible that a non-highlighted town is even closer. */
1088 
1089  if (closest_town->show_zone) {
1091  }
1092 
1093 }
1094 
1099 static void DrawTileSelection(const TileInfo *ti)
1100 {
1101  /* Highlight tiles insede local authority of selected towns. */
1103 
1104  /* Draw a red error square? */
1105  bool is_redsq = _thd.redsq == ti->tile;
1107 
1108  TileHighlightType tht = GetTileHighlightType(ti->tile);
1109  DrawTileHighlightType(ti, tht);
1110 
1111  /* No tile selection active? */
1112  if ((_thd.drawstyle & HT_DRAG_MASK) == HT_NONE) return;
1113 
1114  if (_thd.diagonal) { // We're drawing a 45 degrees rotated (diagonal) rectangle
1115  if (IsInsideRotatedRectangle((int)ti->x, (int)ti->y)) goto draw_inner;
1116  return;
1117  }
1118 
1119  /* Inside the inner area? */
1120  if (IsInsideBS(ti->x, _thd.pos.x, _thd.size.x) &&
1121  IsInsideBS(ti->y, _thd.pos.y, _thd.size.y)) {
1122 draw_inner:
1123  if (_thd.drawstyle & HT_RECT) {
1124  if (!is_redsq) DrawTileSelectionRect(ti, _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE);
1125  } else if (_thd.drawstyle & HT_POINT) {
1126  /* Figure out the Z coordinate for the single dot. */
1127  int z = 0;
1128  FoundationPart foundation_part = FOUNDATION_PART_NORMAL;
1129  if (ti->tileh & SLOPE_N) {
1130  z += TILE_HEIGHT;
1132  }
1133  if (IsHalftileSlope(ti->tileh)) {
1134  Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
1135  if ((halftile_corner == CORNER_W) || (halftile_corner == CORNER_E)) z += TILE_HEIGHT;
1136  if (halftile_corner != CORNER_S) {
1137  foundation_part = FOUNDATION_PART_HALFTILE;
1138  if (IsSteepSlope(ti->tileh)) z -= TILE_HEIGHT;
1139  }
1140  }
1141  DrawSelectionSprite(SPR_DOT, PAL_NONE, ti, z, foundation_part);
1142  } else if (_thd.drawstyle & HT_RAIL) {
1143  /* autorail highlight piece under cursor */
1144  HighLightStyle type = _thd.drawstyle & HT_DIR_MASK;
1145  assert(type < HT_DIR_END);
1146  DrawAutorailSelection(ti, _autorail_type[type][0]);
1147  } else if (IsPartOfAutoLine(ti->x, ti->y)) {
1148  /* autorail highlighting long line */
1149  HighLightStyle dir = _thd.drawstyle & HT_DIR_MASK;
1150  uint side;
1151 
1152  if (dir == HT_DIR_X || dir == HT_DIR_Y) {
1153  side = 0;
1154  } else {
1155  TileIndex start = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
1156  side = Delta(Delta(TileX(start), TileX(ti->tile)), Delta(TileY(start), TileY(ti->tile)));
1157  }
1158 
1159  DrawAutorailSelection(ti, _autorail_type[dir][side]);
1160  }
1161  return;
1162  }
1163 
1164  /* Check if it's inside the outer area? */
1165  if (!is_redsq && (tht == THT_NONE || tht == THT_RED) && _thd.outersize.x > 0 &&
1166  IsInsideBS(ti->x, _thd.pos.x + _thd.offs.x, _thd.size.x + _thd.outersize.x) &&
1167  IsInsideBS(ti->y, _thd.pos.y + _thd.offs.y, _thd.size.y + _thd.outersize.y)) {
1168  /* Draw a blue rect. */
1170  return;
1171  }
1172 }
1173 
1180 static int GetViewportY(Point tile)
1181 {
1182  /* Each increment in X or Y direction moves down by half a tile, i.e. TILE_PIXELS / 2. */
1183  return (tile.y * (int)(TILE_PIXELS / 2) + tile.x * (int)(TILE_PIXELS / 2) - TilePixelHeightOutsideMap(tile.x, tile.y)) << ZOOM_BASE_SHIFT;
1184 }
1185 
1190 {
1191  assert(_vd.dpi.top <= _vd.dpi.top + _vd.dpi.height);
1192  assert(_vd.dpi.left <= _vd.dpi.left + _vd.dpi.width);
1193 
1194  Point upper_left = InverseRemapCoords(_vd.dpi.left, _vd.dpi.top);
1195  Point upper_right = InverseRemapCoords(_vd.dpi.left + _vd.dpi.width, _vd.dpi.top);
1196 
1197  /* Transformations between tile coordinates and viewport rows/columns: See vp_column_row
1198  * column = y - x
1199  * row = x + y
1200  * x = (row - column) / 2
1201  * y = (row + column) / 2
1202  * Note: (row, columns) pairs are only valid, if they are both even or both odd.
1203  */
1204 
1205  /* Columns overlap with neighbouring columns by a half tile.
1206  * - Left column is column of upper_left (rounded down) and one column to the left.
1207  * - Right column is column of upper_right (rounded up) and one column to the right.
1208  * Note: Integer-division does not round down for negative numbers, so ensure rounding with another increment/decrement.
1209  */
1210  int left_column = (upper_left.y - upper_left.x) / (int)TILE_SIZE - 2;
1211  int right_column = (upper_right.y - upper_right.x) / (int)TILE_SIZE + 2;
1212 
1213  int potential_bridge_height = ZOOM_BASE * TILE_HEIGHT * _settings_game.construction.max_bridge_height;
1214 
1215  /* Rows overlap with neighbouring rows by a half tile.
1216  * The first row that could possibly be visible is the row above upper_left (if it is at height 0).
1217  * Due to integer-division not rounding down for negative numbers, we need another decrement.
1218  */
1219  int row = (upper_left.x + upper_left.y) / (int)TILE_SIZE - 2;
1220  bool last_row = false;
1221  for (; !last_row; row++) {
1222  last_row = true;
1223  for (int column = left_column; column <= right_column; column++) {
1224  /* Valid row/column? */
1225  if ((row + column) % 2 != 0) continue;
1226 
1227  Point tilecoord;
1228  tilecoord.x = (row - column) / 2;
1229  tilecoord.y = (row + column) / 2;
1230  assert(column == tilecoord.y - tilecoord.x);
1231  assert(row == tilecoord.y + tilecoord.x);
1232 
1233  TileType tile_type;
1234  _cur_ti.x = tilecoord.x * TILE_SIZE;
1235  _cur_ti.y = tilecoord.y * TILE_SIZE;
1236 
1237  if (IsInsideBS(tilecoord.x, 0, Map::SizeX()) && IsInsideBS(tilecoord.y, 0, Map::SizeY())) {
1238  /* This includes the south border at Map::MaxX / Map::MaxY. When terraforming we still draw tile selections there. */
1239  _cur_ti.tile = TileXY(tilecoord.x, tilecoord.y);
1240  tile_type = GetTileType(_cur_ti.tile);
1241  } else {
1242  _cur_ti.tile = INVALID_TILE;
1243  tile_type = MP_VOID;
1244  }
1245 
1246  if (tile_type != MP_VOID) {
1247  /* We are inside the map => paint landscape. */
1248  std::tie(_cur_ti.tileh, _cur_ti.z) = GetTilePixelSlope(_cur_ti.tile);
1249  } else {
1250  /* We are outside the map => paint black. */
1251  std::tie(_cur_ti.tileh, _cur_ti.z) = GetTilePixelSlopeOutsideMap(tilecoord.x, tilecoord.y);
1252  }
1253 
1254  int viewport_y = GetViewportY(tilecoord);
1255 
1256  if (viewport_y + MAX_TILE_EXTENT_BOTTOM < _vd.dpi.top) {
1257  /* The tile in this column is not visible yet.
1258  * Tiles in other columns may be visible, but we need more rows in any case. */
1259  last_row = false;
1260  continue;
1261  }
1262 
1263  int min_visible_height = viewport_y - (_vd.dpi.top + _vd.dpi.height);
1264  bool tile_visible = min_visible_height <= 0;
1265 
1266  if (tile_type != MP_VOID) {
1267  /* Is tile with buildings visible? */
1268  if (min_visible_height < MAX_TILE_EXTENT_TOP) tile_visible = true;
1269 
1270  if (IsBridgeAbove(_cur_ti.tile)) {
1271  /* Is the bridge visible? */
1272  TileIndex bridge_tile = GetNorthernBridgeEnd(_cur_ti.tile);
1273  int bridge_height = ZOOM_BASE * (GetBridgePixelHeight(bridge_tile) - TilePixelHeight(_cur_ti.tile));
1274  if (min_visible_height < bridge_height + MAX_TILE_EXTENT_TOP) tile_visible = true;
1275  }
1276 
1277  /* Would a higher bridge on a more southern tile be visible?
1278  * If yes, we need to loop over more rows to possibly find one. */
1279  if (min_visible_height < potential_bridge_height + MAX_TILE_EXTENT_TOP) last_row = false;
1280  } else {
1281  /* Outside of map. If we are on the north border of the map, there may still be a bridge visible,
1282  * so we need to loop over more rows to possibly find one. */
1283  if ((tilecoord.x <= 0 || tilecoord.y <= 0) && min_visible_height < potential_bridge_height + MAX_TILE_EXTENT_TOP) last_row = false;
1284  }
1285 
1286  if (tile_visible) {
1287  last_row = false;
1289  _vd.foundation[0] = -1;
1290  _vd.foundation[1] = -1;
1293 
1294  _tile_type_procs[tile_type]->draw_tile_proc(&_cur_ti);
1295  if (_cur_ti.tile != INVALID_TILE) DrawTileSelection(&_cur_ti);
1296  }
1297  }
1298  }
1299 }
1300 
1311 void ViewportAddString(const DrawPixelInfo *dpi, ZoomLevel small_from, const ViewportSign *sign, StringID string_normal, StringID string_small, StringID string_small_shadow, Colours colour)
1312 {
1313  bool small = dpi->zoom >= small_from;
1314 
1315  int left = dpi->left;
1316  int top = dpi->top;
1317  int right = left + dpi->width;
1318  int bottom = top + dpi->height;
1319 
1320  int sign_height = ScaleByZoom(WidgetDimensions::scaled.fullbevel.top + GetCharacterHeight(small ? FS_SMALL : FS_NORMAL) + WidgetDimensions::scaled.fullbevel.bottom, dpi->zoom);
1321  int sign_half_width = ScaleByZoom((small ? sign->width_small : sign->width_normal) / 2, dpi->zoom);
1322 
1323  if (bottom < sign->top ||
1324  top > sign->top + sign_height ||
1325  right < sign->center - sign_half_width ||
1326  left > sign->center + sign_half_width) {
1327  return;
1328  }
1329 
1330  if (!small) {
1331  AddStringToDraw(sign->center - sign_half_width, sign->top, string_normal, colour, sign->width_normal);
1332  } else {
1333  int shadow_offset = 0;
1334  if (string_small_shadow != STR_NULL) {
1335  shadow_offset = 4;
1336  AddStringToDraw(sign->center - sign_half_width + shadow_offset, sign->top, string_small_shadow, INVALID_COLOUR, sign->width_small | 0x8000);
1337  }
1338  AddStringToDraw(sign->center - sign_half_width, sign->top - shadow_offset, string_small, colour, sign->width_small | 0x8000);
1339  }
1340 }
1341 
1342 static Rect ExpandRectWithViewportSignMargins(Rect r, ZoomLevel zoom)
1343 {
1344  const int fh = std::max(GetCharacterHeight(FS_NORMAL), GetCharacterHeight(FS_SMALL));
1345  const int max_tw = _viewport_sign_maxwidth / 2 + 1;
1346  const int expand_y = ScaleByZoom(WidgetDimensions::scaled.fullbevel.top + fh + WidgetDimensions::scaled.fullbevel.bottom, zoom);
1347  const int expand_x = ScaleByZoom(WidgetDimensions::scaled.fullbevel.left + max_tw + WidgetDimensions::scaled.fullbevel.right, zoom);
1348 
1349  r.left -= expand_x;
1350  r.right += expand_x;
1351  r.top -= expand_y;
1352  r.bottom += expand_y;
1353 
1354  return r;
1355 }
1356 
1357 static void ViewportAddKdtreeSigns(DrawPixelInfo *dpi)
1358 {
1359  Rect search_rect{ dpi->left, dpi->top, dpi->left + dpi->width, dpi->top + dpi->height };
1360  search_rect = ExpandRectWithViewportSignMargins(search_rect, dpi->zoom);
1361 
1362  bool show_stations = HasBit(_display_opt, DO_SHOW_STATION_NAMES) && _game_mode != GM_MENU;
1363  bool show_waypoints = HasBit(_display_opt, DO_SHOW_WAYPOINT_NAMES) && _game_mode != GM_MENU;
1364  bool show_towns = HasBit(_display_opt, DO_SHOW_TOWN_NAMES) && _game_mode != GM_MENU;
1365  bool show_signs = HasBit(_display_opt, DO_SHOW_SIGNS) && !IsInvisibilitySet(TO_SIGNS);
1366  bool show_competitors = HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS);
1367 
1368  /* Collect all the items first and draw afterwards, to ensure layering */
1369  std::vector<const BaseStation *> stations;
1370  std::vector<const Town *> towns;
1371  std::vector<const Sign *> signs;
1372 
1373  _viewport_sign_kdtree.FindContained(search_rect.left, search_rect.top, search_rect.right, search_rect.bottom, [&](const ViewportSignKdtreeItem & item) {
1374  switch (item.type) {
1375  case ViewportSignKdtreeItem::VKI_STATION: {
1376  if (!show_stations) break;
1377  const BaseStation *st = BaseStation::Get(item.id.station);
1378 
1379  /* Don't draw if station is owned by another company and competitor station names are hidden. Stations owned by none are never ignored. */
1380  if (!show_competitors && _local_company != st->owner && st->owner != OWNER_NONE) break;
1381 
1382  stations.push_back(st);
1383  break;
1384  }
1385 
1386  case ViewportSignKdtreeItem::VKI_WAYPOINT: {
1387  if (!show_waypoints) break;
1388  const BaseStation *st = BaseStation::Get(item.id.station);
1389 
1390  /* Don't draw if station is owned by another company and competitor station names are hidden. Stations owned by none are never ignored. */
1391  if (!show_competitors && _local_company != st->owner && st->owner != OWNER_NONE) break;
1392 
1393  stations.push_back(st);
1394  break;
1395  }
1396 
1397  case ViewportSignKdtreeItem::VKI_TOWN:
1398  if (!show_towns) break;
1399  towns.push_back(Town::Get(item.id.town));
1400  break;
1401 
1402  case ViewportSignKdtreeItem::VKI_SIGN: {
1403  if (!show_signs) break;
1404  const Sign *si = Sign::Get(item.id.sign);
1405 
1406  /* Don't draw if sign is owned by another company and competitor signs should be hidden.
1407  * Note: It is intentional that also signs owned by OWNER_NONE are hidden. Bankrupt
1408  * companies can leave OWNER_NONE signs after them. */
1409  if (!show_competitors && _local_company != si->owner && si->owner != OWNER_DEITY) break;
1410 
1411  signs.push_back(si);
1412  break;
1413  }
1414 
1415  default:
1416  NOT_REACHED();
1417  }
1418  });
1419 
1420  /* Layering order (bottom to top): Town names, signs, stations */
1421 
1422  for (const auto *t : towns) {
1423  SetDParam(0, t->index);
1424  SetDParam(1, t->cache.population);
1425  ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &t->cache.sign,
1426  _settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_VIEWPORT_TOWN,
1427  STR_VIEWPORT_TOWN_TINY_WHITE, STR_VIEWPORT_TOWN_TINY_BLACK);
1428  }
1429 
1430  /* Do not draw signs nor station names if they are set invisible */
1431  if (IsInvisibilitySet(TO_SIGNS)) return;
1432 
1433  for (const auto *si : signs) {
1434  SetDParam(0, si->index);
1435  ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &si->sign,
1436  STR_WHITE_SIGN,
1437  (IsTransparencySet(TO_SIGNS) || si->owner == OWNER_DEITY) ? STR_VIEWPORT_SIGN_SMALL_WHITE : STR_VIEWPORT_SIGN_SMALL_BLACK, STR_NULL,
1438  (si->owner == OWNER_NONE) ? COLOUR_GREY : (si->owner == OWNER_DEITY ? INVALID_COLOUR : _company_colours[si->owner]));
1439  }
1440 
1441  for (const auto *st : stations) {
1442  SetDParam(0, st->index);
1443  SetDParam(1, st->facilities);
1444  if (Station::IsExpected(st)) {
1445  /* Station */
1446  ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &st->sign,
1447  STR_VIEWPORT_STATION, STR_VIEWPORT_STATION_TINY, STR_NULL,
1448  (st->owner == OWNER_NONE || !st->IsInUse()) ? COLOUR_GREY : _company_colours[st->owner]);
1449  } else {
1450  /* Waypoint */
1451  ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &st->sign,
1452  STR_VIEWPORT_WAYPOINT, STR_VIEWPORT_WAYPOINT_TINY, STR_NULL,
1453  (st->owner == OWNER_NONE || !st->IsInUse()) ? COLOUR_GREY : _company_colours[st->owner]);
1454  }
1455  }
1456 }
1457 
1458 
1466 void ViewportSign::UpdatePosition(int center, int top, StringID str, StringID str_small)
1467 {
1468  if (this->width_normal != 0) this->MarkDirty();
1469 
1470  this->top = top;
1471 
1472  std::string name = GetString(str);
1474  this->center = center;
1475 
1476  /* zoomed out version */
1477  if (str_small != STR_NULL) {
1478  name = GetString(str_small);
1479  }
1481 
1482  this->MarkDirty();
1483 }
1484 
1492 {
1493  Rect zoomlevels[ZOOM_LVL_END];
1494 
1495  /* We don't know which size will be drawn, so mark the largest area dirty. */
1496  const uint half_width = std::max(this->width_normal, this->width_small) / 2 + 1;
1498 
1499  for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) {
1500  /* FIXME: This doesn't switch to width_small when appropriate. */
1501  zoomlevels[zoom].left = this->center - ScaleByZoom(half_width, zoom);
1502  zoomlevels[zoom].top = this->top - ScaleByZoom(1, zoom);
1503  zoomlevels[zoom].right = this->center + ScaleByZoom(half_width, zoom);
1504  zoomlevels[zoom].bottom = this->top + ScaleByZoom(height, zoom);
1505  }
1506 
1507  for (const Window *w : Window::Iterate()) {
1508  Viewport *vp = w->viewport;
1509  if (vp != nullptr && vp->zoom <= maxzoom) {
1510  assert(vp->width != 0);
1511  Rect &zl = zoomlevels[vp->zoom];
1512  MarkViewportDirty(vp, zl.left, zl.top, zl.right, zl.bottom);
1513  }
1514  }
1515 }
1516 
1517 static void ViewportDrawTileSprites(const TileSpriteToDrawVector *tstdv)
1518 {
1519  for (const TileSpriteToDraw &ts : *tstdv) {
1520  DrawSpriteViewport(ts.image, ts.pal, ts.x, ts.y, ts.sub);
1521  }
1522 }
1523 
1526 {
1527  return true;
1528 }
1529 
1531 static void ViewportSortParentSprites(ParentSpriteToSortVector *psdv)
1532 {
1533  if (psdv->size() < 2) return;
1534 
1535  /* We rely on sprites being, for the most part, already ordered.
1536  * So we don't need to move many of them and can keep track of their
1537  * order efficiently by using stack. We always move sprites to the front
1538  * of the current position, i.e. to the top of the stack.
1539  * Also use special constants to indicate sorting state without
1540  * adding extra fields to ParentSpriteToDraw structure.
1541  */
1542  const uint32_t ORDER_COMPARED = UINT32_MAX; // Sprite was compared but we still need to compare the ones preceding it
1543  const uint32_t ORDER_RETURNED = UINT32_MAX - 1; // Makr sorted sprite in case there are other occurrences of it in the stack
1544  std::stack<ParentSpriteToDraw *> sprite_order;
1545  uint32_t next_order = 0;
1546 
1547  std::forward_list<std::pair<int64_t, ParentSpriteToDraw *>> sprite_list; // We store sprites in a list sorted by xmin+ymin
1548 
1549  /* Initialize sprite list and order. */
1550  for (auto p = psdv->rbegin(); p != psdv->rend(); p++) {
1551  sprite_list.emplace_front((*p)->xmin + (*p)->ymin, *p);
1552  sprite_order.push(*p);
1553  (*p)->order = next_order++;
1554  }
1555 
1556  sprite_list.sort();
1557 
1558  std::vector<ParentSpriteToDraw *> preceding; // Temporarily stores sprites that precede current and their position in the list
1559  auto preceding_prev = sprite_list.begin(); // Store iterator in case we need to delete a single preciding sprite
1560  auto out = psdv->begin(); // Iterator to output sorted sprites
1561 
1562  while (!sprite_order.empty()) {
1563 
1564  auto s = sprite_order.top();
1565  sprite_order.pop();
1566 
1567  /* Sprite is already sorted, ignore it. */
1568  if (s->order == ORDER_RETURNED) continue;
1569 
1570  /* Sprite was already compared, just need to output it. */
1571  if (s->order == ORDER_COMPARED) {
1572  *(out++) = s;
1573  s->order = ORDER_RETURNED;
1574  continue;
1575  }
1576 
1577  preceding.clear();
1578 
1579  /* We only need sprites with xmin <= s->xmax && ymin <= s->ymax && zmin <= s->zmax
1580  * So by iterating sprites with xmin + ymin <= s->xmax + s->ymax
1581  * we get all we need and some more that we filter out later.
1582  * We don't include zmin into the sum as there are usually more neighbors on x and y than z
1583  * so including it will actually increase the amount of false positives.
1584  * Also min coordinates can be > max so using max(xmin, xmax) + max(ymin, ymax)
1585  * to ensure that we iterate the current sprite as we need to remove it from the list.
1586  */
1587  auto ssum = std::max(s->xmax, s->xmin) + std::max(s->ymax, s->ymin);
1588  auto prev = sprite_list.before_begin();
1589  auto x = sprite_list.begin();
1590  while (x != sprite_list.end() && ((*x).first <= ssum)) {
1591  auto p = (*x).second;
1592  if (p == s) {
1593  /* We found the current sprite, remove it and move on. */
1594  x = sprite_list.erase_after(prev);
1595  continue;
1596  }
1597 
1598  auto p_prev = prev;
1599  prev = x++;
1600 
1601  if (s->xmax < p->xmin || s->ymax < p->ymin || s->zmax < p->zmin) continue;
1602  if (s->xmin <= p->xmax && // overlap in X?
1603  s->ymin <= p->ymax && // overlap in Y?
1604  s->zmin <= p->zmax) { // overlap in Z?
1605  if (s->xmin + s->xmax + s->ymin + s->ymax + s->zmin + s->zmax <=
1606  p->xmin + p->xmax + p->ymin + p->ymax + p->zmin + p->zmax) {
1607  continue;
1608  }
1609  }
1610  preceding.push_back(p);
1611  preceding_prev = p_prev;
1612  }
1613 
1614  if (preceding.empty()) {
1615  /* No preceding sprites, add current one to the output */
1616  *(out++) = s;
1617  s->order = ORDER_RETURNED;
1618  continue;
1619  }
1620 
1621  /* Optimization for the case when we only have 1 sprite to move. */
1622  if (preceding.size() == 1) {
1623  auto p = preceding[0];
1624  /* We can only output the preceding sprite if there can't be any other sprites preceding it. */
1625  if (p->xmax <= s->xmax && p->ymax <= s->ymax && p->zmax <= s->zmax) {
1626  p->order = ORDER_RETURNED;
1627  s->order = ORDER_RETURNED;
1628  sprite_list.erase_after(preceding_prev);
1629  *(out++) = p;
1630  *(out++) = s;
1631  continue;
1632  }
1633  }
1634 
1635  /* Sort all preceding sprites by order and assign new orders in reverse (as original sorter did). */
1636  std::sort(preceding.begin(), preceding.end(), [](const ParentSpriteToDraw *a, const ParentSpriteToDraw *b) {
1637  return a->order > b->order;
1638  });
1639 
1640  s->order = ORDER_COMPARED;
1641  sprite_order.push(s); // Still need to output so push it back for now
1642 
1643  for (auto p: preceding) {
1644  p->order = next_order++;
1645  sprite_order.push(p);
1646  }
1647  }
1648 }
1649 
1650 
1651 static void ViewportDrawParentSprites(const ParentSpriteToSortVector *psd, const ChildScreenSpriteToDrawVector *csstdv)
1652 {
1653  for (const ParentSpriteToDraw *ps : *psd) {
1654  if (ps->image != SPR_EMPTY_BOUNDING_BOX) DrawSpriteViewport(ps->image, ps->pal, ps->x, ps->y, ps->sub);
1655 
1656  int child_idx = ps->first_child;
1657  while (child_idx >= 0) {
1658  const ChildScreenSpriteToDraw *cs = csstdv->data() + child_idx;
1659  child_idx = cs->next;
1660  if (cs->relative) {
1661  DrawSpriteViewport(cs->image, cs->pal, ps->left + cs->x, ps->top + cs->y, cs->sub);
1662  } else {
1663  DrawSpriteViewport(cs->image, cs->pal, ps->x + cs->x, ps->y + cs->y, cs->sub);
1664  }
1665  }
1666  }
1667 }
1668 
1673 static void ViewportDrawBoundingBoxes(const ParentSpriteToSortVector *psd)
1674 {
1675  for (const ParentSpriteToDraw *ps : *psd) {
1676  Point pt1 = RemapCoords(ps->xmax + 1, ps->ymax + 1, ps->zmax + 1); // top front corner
1677  Point pt2 = RemapCoords(ps->xmin , ps->ymax + 1, ps->zmax + 1); // top left corner
1678  Point pt3 = RemapCoords(ps->xmax + 1, ps->ymin , ps->zmax + 1); // top right corner
1679  Point pt4 = RemapCoords(ps->xmax + 1, ps->ymax + 1, ps->zmin ); // bottom front corner
1680 
1681  DrawBox( pt1.x, pt1.y,
1682  pt2.x - pt1.x, pt2.y - pt1.y,
1683  pt3.x - pt1.x, pt3.y - pt1.y,
1684  pt4.x - pt1.x, pt4.y - pt1.y);
1685  }
1686 }
1687 
1692 {
1694  const DrawPixelInfo *dpi = _cur_dpi;
1695  void *dst;
1696  int right = UnScaleByZoom(dpi->width, dpi->zoom);
1697  int bottom = UnScaleByZoom(dpi->height, dpi->zoom);
1698 
1699  int colour = _string_colourmap[_dirty_block_colour & 0xF];
1700 
1701  dst = dpi->dst_ptr;
1702 
1703  uint8_t bo = UnScaleByZoom(dpi->left + dpi->top, dpi->zoom) & 1;
1704  do {
1705  for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, (uint8_t)colour);
1706  dst = blitter->MoveTo(dst, 0, 1);
1707  } while (--bottom > 0);
1708 }
1709 
1710 static void ViewportDrawStrings(ZoomLevel zoom, const StringSpriteToDrawVector *sstdv)
1711 {
1712  for (const StringSpriteToDraw &ss : *sstdv) {
1713  TextColour colour = TC_BLACK;
1714  bool small = HasBit(ss.width, 15);
1715  int w = GB(ss.width, 0, 15);
1716  int x = UnScaleByZoom(ss.x, zoom);
1717  int y = UnScaleByZoom(ss.y, zoom);
1719 
1720  if (ss.colour != INVALID_COLOUR) {
1721  if (IsTransparencySet(TO_SIGNS) && ss.string_id != STR_WHITE_SIGN) {
1722  /* Don't draw the rectangle.
1723  * Real colours need the TC_IS_PALETTE_COLOUR flag.
1724  * Otherwise colours from _string_colourmap are assumed. */
1725  colour = (TextColour)GetColourGradient(ss.colour, SHADE_LIGHTER) | TC_IS_PALETTE_COLOUR;
1726  } else {
1727  /* Draw the rectangle if 'transparent station signs' is off,
1728  * or if we are drawing a general text sign (STR_WHITE_SIGN). */
1729  DrawFrameRect(
1730  x, y, x + w - 1, y + h - 1, ss.colour,
1732  );
1733  }
1734  }
1735 
1736  DrawString(x + WidgetDimensions::scaled.fullbevel.left, x + w - 1 - WidgetDimensions::scaled.fullbevel.right, y + WidgetDimensions::scaled.fullbevel.top, ss.string, colour, SA_HOR_CENTER, false, small ? FS_SMALL : FS_NORMAL);
1737  }
1738 }
1739 
1740 void ViewportDoDraw(const Viewport *vp, int left, int top, int right, int bottom)
1741 {
1742  _vd.dpi.zoom = vp->zoom;
1743  int mask = ScaleByZoom(-1, vp->zoom);
1744 
1746 
1747  _vd.dpi.width = (right - left) & mask;
1748  _vd.dpi.height = (bottom - top) & mask;
1749  _vd.dpi.left = left & mask;
1750  _vd.dpi.top = top & mask;
1751  _vd.dpi.pitch = _cur_dpi->pitch;
1752  _vd.last_child = LAST_CHILD_NONE;
1753 
1754  int x = UnScaleByZoom(_vd.dpi.left - (vp->virtual_left & mask), vp->zoom) + vp->left;
1755  int y = UnScaleByZoom(_vd.dpi.top - (vp->virtual_top & mask), vp->zoom) + vp->top;
1756 
1757  _vd.dpi.dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_cur_dpi->dst_ptr, x - _cur_dpi->left, y - _cur_dpi->top);
1758  AutoRestoreBackup dpi_backup(_cur_dpi, &_vd.dpi);
1759 
1761  ViewportAddVehicles(&_vd.dpi);
1762 
1763  ViewportAddKdtreeSigns(&_vd.dpi);
1764 
1765  DrawTextEffects(&_vd.dpi);
1766 
1767  if (!_vd.tile_sprites_to_draw.empty()) ViewportDrawTileSprites(&_vd.tile_sprites_to_draw);
1768 
1769  for (auto &psd : _vd.parent_sprites_to_draw) {
1770  _vd.parent_sprites_to_sort.push_back(&psd);
1771  }
1772 
1773  _vp_sprite_sorter(&_vd.parent_sprites_to_sort);
1774  ViewportDrawParentSprites(&_vd.parent_sprites_to_sort, &_vd.child_screen_sprites_to_draw);
1775 
1776  if (_draw_bounding_boxes) ViewportDrawBoundingBoxes(&_vd.parent_sprites_to_sort);
1777  if (_draw_dirty_blocks) ViewportDrawDirtyBlocks();
1778 
1779  DrawPixelInfo dp = _vd.dpi;
1780  ZoomLevel zoom = _vd.dpi.zoom;
1781  dp.zoom = ZOOM_LVL_MIN;
1782  dp.width = UnScaleByZoom(dp.width, zoom);
1783  dp.height = UnScaleByZoom(dp.height, zoom);
1784  _cur_dpi = &dp;
1785 
1786  if (vp->overlay != nullptr && vp->overlay->GetCargoMask() != 0 && vp->overlay->GetCompanyMask() != 0) {
1787  /* translate to window coordinates */
1788  dp.left = x;
1789  dp.top = y;
1790  vp->overlay->Draw(&dp);
1791  }
1792 
1793  if (!_vd.string_sprites_to_draw.empty()) {
1794  /* translate to world coordinates */
1795  dp.left = UnScaleByZoom(_vd.dpi.left, zoom);
1796  dp.top = UnScaleByZoom(_vd.dpi.top, zoom);
1797  ViewportDrawStrings(zoom, &_vd.string_sprites_to_draw);
1798  }
1799 
1800  _vd.string_sprites_to_draw.clear();
1801  _vd.tile_sprites_to_draw.clear();
1802  _vd.parent_sprites_to_draw.clear();
1803  _vd.parent_sprites_to_sort.clear();
1804  _vd.child_screen_sprites_to_draw.clear();
1805 }
1806 
1807 static inline void ViewportDraw(const Viewport *vp, int left, int top, int right, int bottom)
1808 {
1809  if (right <= vp->left || bottom <= vp->top) return;
1810 
1811  if (left >= vp->left + vp->width) return;
1812 
1813  if (left < vp->left) left = vp->left;
1814  if (right > vp->left + vp->width) right = vp->left + vp->width;
1815 
1816  if (top >= vp->top + vp->height) return;
1817 
1818  if (top < vp->top) top = vp->top;
1819  if (bottom > vp->top + vp->height) bottom = vp->top + vp->height;
1820 
1821  ViewportDoDraw(vp,
1822  ScaleByZoom(left - vp->left, vp->zoom) + vp->virtual_left,
1823  ScaleByZoom(top - vp->top, vp->zoom) + vp->virtual_top,
1824  ScaleByZoom(right - vp->left, vp->zoom) + vp->virtual_left,
1825  ScaleByZoom(bottom - vp->top, vp->zoom) + vp->virtual_top
1826  );
1827 }
1828 
1833 {
1835 
1836  DrawPixelInfo *dpi = _cur_dpi;
1837 
1838  dpi->left += this->left;
1839  dpi->top += this->top;
1840 
1841  ViewportDraw(this->viewport, dpi->left, dpi->top, dpi->left + dpi->width, dpi->top + dpi->height);
1842 
1843  dpi->left -= this->left;
1844  dpi->top -= this->top;
1845 }
1846 
1857 static inline void ClampViewportToMap(const Viewport *vp, int *scroll_x, int *scroll_y)
1858 {
1859  /* Centre of the viewport is hot spot. */
1860  Point pt = {
1861  *scroll_x + vp->virtual_width / 2,
1862  *scroll_y + vp->virtual_height / 2
1863  };
1864 
1865  /* Find nearest tile that is within borders of the map. */
1866  bool clamped;
1867  pt = InverseRemapCoords2(pt.x, pt.y, true, &clamped);
1868 
1869  if (clamped) {
1870  /* Convert back to viewport coordinates and remove centering. */
1871  pt = RemapCoords2(pt.x, pt.y);
1872  *scroll_x = pt.x - vp->virtual_width / 2;
1873  *scroll_y = pt.y - vp->virtual_height / 2;
1874  }
1875 }
1876 
1889 static void ClampSmoothScroll(uint32_t delta_ms, int64_t delta_hi, int64_t delta_lo, int &delta_hi_clamped, int &delta_lo_clamped)
1890 {
1892  constexpr int PIXELS_PER_TILE = TILE_PIXELS * 2 * ZOOM_BASE;
1893 
1894  assert(delta_hi != 0);
1895 
1896  /* Move at most 75% of the distance every 30ms, for a smooth experience */
1897  int64_t delta_left = delta_hi * std::pow(0.75, delta_ms / 30.0);
1898  /* Move never more than 16 tiles per 30ms. */
1899  int max_scroll = Map::ScaleBySize1D(16 * PIXELS_PER_TILE * delta_ms / 30);
1900 
1901  /* We never go over the max_scroll speed. */
1902  delta_hi_clamped = Clamp(delta_hi - delta_left, -max_scroll, max_scroll);
1903  /* The lower delta is in ratio of the higher delta, so we keep going straight at the destination. */
1904  delta_lo_clamped = delta_lo * delta_hi_clamped / delta_hi;
1905 
1906  /* Ensure we always move (delta_hi can't be zero). */
1907  if (delta_hi_clamped == 0) {
1908  delta_hi_clamped = delta_hi > 0 ? 1 : -1;
1909  }
1910 }
1911 
1916 void UpdateViewportPosition(Window *w, uint32_t delta_ms)
1917 {
1918  const Viewport *vp = w->viewport;
1919 
1921  const Vehicle *veh = Vehicle::Get(w->viewport->follow_vehicle);
1922  Point pt = MapXYZToViewport(vp, veh->x_pos, veh->y_pos, veh->z_pos);
1923 
1924  w->viewport->scrollpos_x = pt.x;
1925  w->viewport->scrollpos_y = pt.y;
1926  SetViewportPosition(w, pt.x, pt.y);
1927  } else {
1928  /* Ensure the destination location is within the map */
1930 
1931  int delta_x = w->viewport->dest_scrollpos_x - w->viewport->scrollpos_x;
1932  int delta_y = w->viewport->dest_scrollpos_y - w->viewport->scrollpos_y;
1933 
1934  int current_x = w->viewport->scrollpos_x;
1935  int current_y = w->viewport->scrollpos_y;
1936 
1937  bool update_overlay = false;
1938  if (delta_x != 0 || delta_y != 0) {
1940  int delta_x_clamped;
1941  int delta_y_clamped;
1942 
1943  if (abs(delta_x) > abs(delta_y)) {
1944  ClampSmoothScroll(delta_ms, delta_x, delta_y, delta_x_clamped, delta_y_clamped);
1945  } else {
1946  ClampSmoothScroll(delta_ms, delta_y, delta_x, delta_y_clamped, delta_x_clamped);
1947  }
1948 
1949  w->viewport->scrollpos_x += delta_x_clamped;
1950  w->viewport->scrollpos_y += delta_y_clamped;
1951  } else {
1954  }
1955  update_overlay = (w->viewport->scrollpos_x == w->viewport->dest_scrollpos_x &&
1957  }
1958 
1960 
1961  /* When moving small amounts around the border we can get stuck, and
1962  * not actually move. In those cases, teleport to the destination. */
1963  if ((delta_x != 0 || delta_y != 0) && current_x == w->viewport->scrollpos_x && current_y == w->viewport->scrollpos_y) {
1966  }
1967 
1968  SetViewportPosition(w, w->viewport->scrollpos_x, w->viewport->scrollpos_y);
1969  if (update_overlay) RebuildViewportOverlay(w);
1970  }
1971 }
1972 
1983 static bool MarkViewportDirty(const Viewport *vp, int left, int top, int right, int bottom)
1984 {
1985  /* Rounding wrt. zoom-out level */
1986  right += (1 << vp->zoom) - 1;
1987  bottom += (1 << vp->zoom) - 1;
1988 
1989  right -= vp->virtual_left;
1990  if (right <= 0) return false;
1991 
1992  bottom -= vp->virtual_top;
1993  if (bottom <= 0) return false;
1994 
1995  left = std::max(0, left - vp->virtual_left);
1996 
1997  if (left >= vp->virtual_width) return false;
1998 
1999  top = std::max(0, top - vp->virtual_top);
2000 
2001  if (top >= vp->virtual_height) return false;
2002 
2003  AddDirtyBlock(
2004  UnScaleByZoomLower(left, vp->zoom) + vp->left,
2005  UnScaleByZoomLower(top, vp->zoom) + vp->top,
2006  UnScaleByZoom(right, vp->zoom) + vp->left + 1,
2007  UnScaleByZoom(bottom, vp->zoom) + vp->top + 1
2008  );
2009 
2010  return true;
2011 }
2012 
2022 bool MarkAllViewportsDirty(int left, int top, int right, int bottom)
2023 {
2024  bool dirty = false;
2025 
2026  for (const Window *w : Window::Iterate()) {
2027  Viewport *vp = w->viewport;
2028  if (vp != nullptr) {
2029  assert(vp->width != 0);
2030  if (MarkViewportDirty(vp, left, top, right, bottom)) dirty = true;
2031  }
2032  }
2033 
2034  return dirty;
2035 }
2036 
2037 void ConstrainAllViewportsZoom()
2038 {
2039  for (Window *w : Window::Iterate()) {
2040  if (w->viewport == nullptr) continue;
2041 
2042  ZoomLevel zoom = static_cast<ZoomLevel>(Clamp(w->viewport->zoom, _settings_client.gui.zoom_min, _settings_client.gui.zoom_max));
2043  if (zoom != w->viewport->zoom) {
2044  while (w->viewport->zoom < zoom) DoZoomInOutWindow(ZOOM_OUT, w);
2045  while (w->viewport->zoom > zoom) DoZoomInOutWindow(ZOOM_IN, w);
2046  }
2047  }
2048 }
2049 
2057 void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
2058 {
2059  Point pt = RemapCoords(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE, tile_height_override * TILE_HEIGHT);
2061  pt.x - MAX_TILE_EXTENT_LEFT,
2062  pt.y - MAX_TILE_EXTENT_TOP - ZOOM_BASE * TILE_HEIGHT * bridge_level_offset,
2063  pt.x + MAX_TILE_EXTENT_RIGHT,
2064  pt.y + MAX_TILE_EXTENT_BOTTOM);
2065 }
2066 
2075 {
2076  int x_size = _thd.size.x;
2077  int y_size = _thd.size.y;
2078 
2079  if (!_thd.diagonal) { // Selecting in a straight rectangle (or a single square)
2080  int x_start = _thd.pos.x;
2081  int y_start = _thd.pos.y;
2082 
2083  if (_thd.outersize.x != 0) {
2084  x_size += _thd.outersize.x;
2085  x_start += _thd.offs.x;
2086  y_size += _thd.outersize.y;
2087  y_start += _thd.offs.y;
2088  }
2089 
2090  x_size -= TILE_SIZE;
2091  y_size -= TILE_SIZE;
2092 
2093  assert(x_size >= 0);
2094  assert(y_size >= 0);
2095 
2096  int x_end = Clamp(x_start + x_size, 0, Map::SizeX() * TILE_SIZE - TILE_SIZE);
2097  int y_end = Clamp(y_start + y_size, 0, Map::SizeY() * TILE_SIZE - TILE_SIZE);
2098 
2099  x_start = Clamp(x_start, 0, Map::SizeX() * TILE_SIZE - TILE_SIZE);
2100  y_start = Clamp(y_start, 0, Map::SizeY() * TILE_SIZE - TILE_SIZE);
2101 
2102  /* make sure everything is multiple of TILE_SIZE */
2103  assert((x_end | y_end | x_start | y_start) % TILE_SIZE == 0);
2104 
2105  /* How it works:
2106  * Suppose we have to mark dirty rectangle of 3x4 tiles:
2107  * x
2108  * xxx
2109  * xxxxx
2110  * xxxxx
2111  * xxx
2112  * x
2113  * This algorithm marks dirty columns of tiles, so it is done in 3+4-1 steps:
2114  * 1) x 2) x
2115  * xxx Oxx
2116  * Oxxxx xOxxx
2117  * xxxxx Oxxxx
2118  * xxx xxx
2119  * x x
2120  * And so forth...
2121  */
2122 
2123  int top_x = x_end; // coordinates of top dirty tile
2124  int top_y = y_start;
2125  int bot_x = top_x; // coordinates of bottom dirty tile
2126  int bot_y = top_y;
2127 
2128  do {
2129  /* topmost dirty point */
2130  TileIndex top_tile = TileVirtXY(top_x, top_y);
2131  Point top = RemapCoords(top_x, top_y, GetTileMaxPixelZ(top_tile));
2132 
2133  /* bottommost point */
2134  TileIndex bottom_tile = TileVirtXY(bot_x, bot_y);
2135  Point bot = RemapCoords(bot_x + TILE_SIZE, bot_y + TILE_SIZE, GetTilePixelZ(bottom_tile)); // bottommost point
2136 
2137  /* the 'x' coordinate of 'top' and 'bot' is the same (and always in the same distance from tile middle),
2138  * tile height/slope affects only the 'y' on-screen coordinate! */
2139 
2140  int l = top.x - TILE_PIXELS * ZOOM_BASE; // 'x' coordinate of left side of the dirty rectangle
2141  int t = top.y; // 'y' coordinate of top side of the dirty rectangle
2142  int r = top.x + TILE_PIXELS * ZOOM_BASE; // 'x' coordinate of right side of the dirty rectangle
2143  int b = bot.y; // 'y' coordinate of bottom side of the dirty rectangle
2144 
2145  static const int OVERLAY_WIDTH = 4 * ZOOM_BASE; // part of selection sprites is drawn outside the selected area (in particular: terraforming)
2146 
2147  /* For halftile foundations on SLOPE_STEEP_S the sprite extents some more towards the top */
2148  MarkAllViewportsDirty(l - OVERLAY_WIDTH, t - OVERLAY_WIDTH - TILE_HEIGHT * ZOOM_BASE, r + OVERLAY_WIDTH, b + OVERLAY_WIDTH);
2149 
2150  /* haven't we reached the topmost tile yet? */
2151  if (top_x != x_start) {
2152  top_x -= TILE_SIZE;
2153  } else {
2154  top_y += TILE_SIZE;
2155  }
2156 
2157  /* the way the bottom tile changes is different when we reach the bottommost tile */
2158  if (bot_y != y_end) {
2159  bot_y += TILE_SIZE;
2160  } else {
2161  bot_x -= TILE_SIZE;
2162  }
2163  } while (bot_x >= top_x);
2164  } else { // Selecting in a 45 degrees rotated (diagonal) rectangle.
2165  /* a_size, b_size describe a rectangle with rotated coordinates */
2166  int a_size = x_size + y_size, b_size = x_size - y_size;
2167 
2168  int interval_a = a_size < 0 ? -(int)TILE_SIZE : (int)TILE_SIZE;
2169  int interval_b = b_size < 0 ? -(int)TILE_SIZE : (int)TILE_SIZE;
2170 
2171  for (int a = -interval_a; a != a_size + interval_a; a += interval_a) {
2172  for (int b = -interval_b; b != b_size + interval_b; b += interval_b) {
2173  uint x = (_thd.pos.x + (a + b) / 2) / TILE_SIZE;
2174  uint y = (_thd.pos.y + (a - b) / 2) / TILE_SIZE;
2175 
2176  if (x < Map::MaxX() && y < Map::MaxY()) {
2177  MarkTileDirtyByTile(TileXY(x, y));
2178  }
2179  }
2180  }
2181  }
2182 }
2183 
2184 
2185 void SetSelectionRed(bool b)
2186 {
2187  _thd.make_square_red = b;
2189 }
2190 
2199 static bool CheckClickOnViewportSign(const Viewport *vp, int x, int y, const ViewportSign *sign)
2200 {
2201  bool small = (vp->zoom >= ZOOM_LVL_OUT_4X);
2202  int sign_half_width = ScaleByZoom((small ? sign->width_small : sign->width_normal) / 2, vp->zoom);
2203  int sign_height = ScaleByZoom(WidgetDimensions::scaled.fullbevel.top + GetCharacterHeight(small ? FS_SMALL : FS_NORMAL) + WidgetDimensions::scaled.fullbevel.bottom, vp->zoom);
2204 
2205  return y >= sign->top && y < sign->top + sign_height &&
2206  x >= sign->center - sign_half_width && x < sign->center + sign_half_width;
2207 }
2208 
2209 
2217 static bool CheckClickOnViewportSign(const Viewport *vp, int x, int y)
2218 {
2219  if (_game_mode == GM_MENU) return false;
2220 
2221  x = ScaleByZoom(x - vp->left, vp->zoom) + vp->virtual_left;
2222  y = ScaleByZoom(y - vp->top, vp->zoom) + vp->virtual_top;
2223 
2224  Rect search_rect{ x - 1, y - 1, x + 1, y + 1 };
2225  search_rect = ExpandRectWithViewportSignMargins(search_rect, vp->zoom);
2226 
2229  bool show_towns = HasBit(_display_opt, DO_SHOW_TOWN_NAMES);
2230  bool show_signs = HasBit(_display_opt, DO_SHOW_SIGNS) && !IsInvisibilitySet(TO_SIGNS);
2231  bool show_competitors = HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS);
2232 
2233  /* Topmost of each type that was hit */
2234  BaseStation *st = nullptr, *last_st = nullptr;
2235  Town *t = nullptr, *last_t = nullptr;
2236  Sign *si = nullptr, *last_si = nullptr;
2237 
2238  /* See ViewportAddKdtreeSigns() for details on the search logic */
2239  _viewport_sign_kdtree.FindContained(search_rect.left, search_rect.top, search_rect.right, search_rect.bottom, [&](const ViewportSignKdtreeItem & item) {
2240  switch (item.type) {
2241  case ViewportSignKdtreeItem::VKI_STATION:
2242  if (!show_stations) break;
2243  st = BaseStation::Get(item.id.station);
2244  if (!show_competitors && _local_company != st->owner && st->owner != OWNER_NONE) break;
2245  if (CheckClickOnViewportSign(vp, x, y, &st->sign)) last_st = st;
2246  break;
2247 
2248  case ViewportSignKdtreeItem::VKI_WAYPOINT:
2249  if (!show_waypoints) break;
2250  st = BaseStation::Get(item.id.station);
2251  if (!show_competitors && _local_company != st->owner && st->owner != OWNER_NONE) break;
2252  if (CheckClickOnViewportSign(vp, x, y, &st->sign)) last_st = st;
2253  break;
2254 
2255  case ViewportSignKdtreeItem::VKI_TOWN:
2256  if (!show_towns) break;
2257  t = Town::Get(item.id.town);
2258  if (CheckClickOnViewportSign(vp, x, y, &t->cache.sign)) last_t = t;
2259  break;
2260 
2261  case ViewportSignKdtreeItem::VKI_SIGN:
2262  if (!show_signs) break;
2263  si = Sign::Get(item.id.sign);
2264  if (!show_competitors && _local_company != si->owner && si->owner != OWNER_DEITY) break;
2265  if (CheckClickOnViewportSign(vp, x, y, &si->sign)) last_si = si;
2266  break;
2267 
2268  default:
2269  NOT_REACHED();
2270  }
2271  });
2272 
2273  /* Select which hit to handle based on priority */
2274  if (last_st != nullptr) {
2275  if (Station::IsExpected(last_st)) {
2276  ShowStationViewWindow(last_st->index);
2277  } else {
2279  }
2280  return true;
2281  } else if (last_t != nullptr) {
2282  ShowTownViewWindow(last_t->index);
2283  return true;
2284  } else if (last_si != nullptr) {
2285  HandleClickOnSign(last_si);
2286  return true;
2287  } else {
2288  return false;
2289  }
2290 }
2291 
2292 
2293 ViewportSignKdtreeItem ViewportSignKdtreeItem::MakeStation(StationID id)
2294 {
2296  item.type = VKI_STATION;
2297  item.id.station = id;
2298 
2299  const Station *st = Station::Get(id);
2300  assert(st->sign.kdtree_valid);
2301  item.center = st->sign.center;
2302  item.top = st->sign.top;
2303 
2304  /* Assume the sign can be a candidate for drawing, so measure its width */
2305  _viewport_sign_maxwidth = std::max<int>({_viewport_sign_maxwidth, st->sign.width_normal, st->sign.width_small});
2306 
2307  return item;
2308 }
2309 
2310 ViewportSignKdtreeItem ViewportSignKdtreeItem::MakeWaypoint(StationID id)
2311 {
2313  item.type = VKI_WAYPOINT;
2314  item.id.station = id;
2315 
2316  const Waypoint *st = Waypoint::Get(id);
2317  assert(st->sign.kdtree_valid);
2318  item.center = st->sign.center;
2319  item.top = st->sign.top;
2320 
2321  /* Assume the sign can be a candidate for drawing, so measure its width */
2322  _viewport_sign_maxwidth = std::max<int>({_viewport_sign_maxwidth, st->sign.width_normal, st->sign.width_small});
2323 
2324  return item;
2325 }
2326 
2327 ViewportSignKdtreeItem ViewportSignKdtreeItem::MakeTown(TownID id)
2328 {
2330  item.type = VKI_TOWN;
2331  item.id.town = id;
2332 
2333  const Town *town = Town::Get(id);
2334  assert(town->cache.sign.kdtree_valid);
2335  item.center = town->cache.sign.center;
2336  item.top = town->cache.sign.top;
2337 
2338  /* Assume the sign can be a candidate for drawing, so measure its width */
2339  _viewport_sign_maxwidth = std::max<int>({_viewport_sign_maxwidth, town->cache.sign.width_normal, town->cache.sign.width_small});
2340 
2341  return item;
2342 }
2343 
2344 ViewportSignKdtreeItem ViewportSignKdtreeItem::MakeSign(SignID id)
2345 {
2347  item.type = VKI_SIGN;
2348  item.id.sign = id;
2349 
2350  const Sign *sign = Sign::Get(id);
2351  assert(sign->sign.kdtree_valid);
2352  item.center = sign->sign.center;
2353  item.top = sign->sign.top;
2354 
2355  /* Assume the sign can be a candidate for drawing, so measure its width */
2356  _viewport_sign_maxwidth = std::max<int>({_viewport_sign_maxwidth, sign->sign.width_normal, sign->sign.width_small});
2357 
2358  return item;
2359 }
2360 
2361 void RebuildViewportKdtree()
2362 {
2363  /* Reset biggest size sign seen */
2364  _viewport_sign_maxwidth = 0;
2365 
2366  std::vector<ViewportSignKdtreeItem> items;
2368 
2369  for (const Station *st : Station::Iterate()) {
2370  if (st->sign.kdtree_valid) items.push_back(ViewportSignKdtreeItem::MakeStation(st->index));
2371  }
2372 
2373  for (const Waypoint *wp : Waypoint::Iterate()) {
2374  if (wp->sign.kdtree_valid) items.push_back(ViewportSignKdtreeItem::MakeWaypoint(wp->index));
2375  }
2376 
2377  for (const Town *town : Town::Iterate()) {
2378  if (town->cache.sign.kdtree_valid) items.push_back(ViewportSignKdtreeItem::MakeTown(town->index));
2379  }
2380 
2381  for (const Sign *sign : Sign::Iterate()) {
2382  if (sign->sign.kdtree_valid) items.push_back(ViewportSignKdtreeItem::MakeSign(sign->index));
2383  }
2384 
2385  _viewport_sign_kdtree.Build(items.begin(), items.end());
2386 }
2387 
2388 
2389 static bool CheckClickOnLandscape(const Viewport *vp, int x, int y)
2390 {
2391  Point pt = TranslateXYToTileCoord(vp, x, y);
2392 
2393  if (pt.x != -1) return ClickTile(TileVirtXY(pt.x, pt.y));
2394  return true;
2395 }
2396 
2397 static void PlaceObject()
2398 {
2399  Point pt;
2400  Window *w;
2401 
2402  pt = GetTileBelowCursor();
2403  if (pt.x == -1) return;
2404 
2405  if ((_thd.place_mode & HT_DRAG_MASK) == HT_POINT) {
2406  pt.x += TILE_SIZE / 2;
2407  pt.y += TILE_SIZE / 2;
2408  }
2409 
2410  _tile_fract_coords.x = pt.x & TILE_UNIT_MASK;
2411  _tile_fract_coords.y = pt.y & TILE_UNIT_MASK;
2412 
2413  w = _thd.GetCallbackWnd();
2414  if (w != nullptr) w->OnPlaceObject(pt, TileVirtXY(pt.x, pt.y));
2415 }
2416 
2417 
2418 bool HandleViewportClicked(const Viewport *vp, int x, int y)
2419 {
2420  const Vehicle *v = CheckClickOnVehicle(vp, x, y);
2421 
2422  if (_thd.place_mode & HT_VEHICLE) {
2423  if (v != nullptr && VehicleClicked(v)) return true;
2424  }
2425 
2426  /* Vehicle placement mode already handled above. */
2427  if ((_thd.place_mode & HT_DRAG_MASK) != HT_NONE) {
2428  PlaceObject();
2429  return true;
2430  }
2431 
2432  if (CheckClickOnViewportSign(vp, x, y)) return true;
2433  bool result = CheckClickOnLandscape(vp, x, y);
2434 
2435  if (v != nullptr) {
2436  Debug(misc, 2, "Vehicle {} (index {}) at {}", v->unitnumber, v->index, fmt::ptr(v));
2438  v = v->First();
2439  if (_ctrl_pressed && v->owner == _local_company) {
2440  StartStopVehicle(v, true);
2441  } else {
2443  }
2444  }
2445  return true;
2446  }
2447  return result;
2448 }
2449 
2450 void RebuildViewportOverlay(Window *w)
2451 {
2452  if (w->viewport->overlay != nullptr &&
2453  w->viewport->overlay->GetCompanyMask() != 0 &&
2454  w->viewport->overlay->GetCargoMask() != 0) {
2455  w->viewport->overlay->SetDirty();
2456  w->SetDirty();
2457  }
2458 }
2459 
2469 bool ScrollWindowTo(int x, int y, int z, Window *w, bool instant)
2470 {
2471  /* The slope cannot be acquired outside of the map, so make sure we are always within the map. */
2472  if (z == -1) {
2473  if ( x >= 0 && x <= (int)Map::SizeX() * (int)TILE_SIZE - 1
2474  && y >= 0 && y <= (int)Map::SizeY() * (int)TILE_SIZE - 1) {
2475  z = GetSlopePixelZ(x, y);
2476  } else {
2477  z = TileHeightOutsideMap(x / (int)TILE_SIZE, y / (int)TILE_SIZE);
2478  }
2479  }
2480 
2481  Point pt = MapXYZToViewport(w->viewport, x, y, z);
2483 
2484  if (w->viewport->dest_scrollpos_x == pt.x && w->viewport->dest_scrollpos_y == pt.y) return false;
2485 
2486  if (instant) {
2487  w->viewport->scrollpos_x = pt.x;
2488  w->viewport->scrollpos_y = pt.y;
2489  RebuildViewportOverlay(w);
2490  }
2491 
2492  w->viewport->dest_scrollpos_x = pt.x;
2493  w->viewport->dest_scrollpos_y = pt.y;
2494  return true;
2495 }
2496 
2504 bool ScrollWindowToTile(TileIndex tile, Window *w, bool instant)
2505 {
2506  return ScrollWindowTo(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE, -1, w, instant);
2507 }
2508 
2515 bool ScrollMainWindowToTile(TileIndex tile, bool instant)
2516 {
2517  return ScrollMainWindowTo(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2, -1, instant);
2518 }
2519 
2525 {
2526  TileIndex old;
2527 
2528  old = _thd.redsq;
2529  _thd.redsq = tile;
2530 
2531  if (tile != old) {
2532  if (tile != INVALID_TILE) MarkTileDirtyByTile(tile);
2533  if (old != INVALID_TILE) MarkTileDirtyByTile(old);
2534  }
2535 }
2536 
2542 void SetTileSelectSize(int w, int h)
2543 {
2544  _thd.new_size.x = w * TILE_SIZE;
2545  _thd.new_size.y = h * TILE_SIZE;
2546  _thd.new_outersize.x = 0;
2547  _thd.new_outersize.y = 0;
2548 }
2549 
2550 void SetTileSelectBigSize(int ox, int oy, int sx, int sy)
2551 {
2552  _thd.offs.x = ox * TILE_SIZE;
2553  _thd.offs.y = oy * TILE_SIZE;
2554  _thd.new_outersize.x = sx * TILE_SIZE;
2555  _thd.new_outersize.y = sy * TILE_SIZE;
2556 }
2557 
2559 static HighLightStyle GetAutorailHT(int x, int y)
2560 {
2561  return HT_RAIL | _autorail_piece[x & TILE_UNIT_MASK][y & TILE_UNIT_MASK];
2562 }
2563 
2568 {
2569  this->pos.x = 0;
2570  this->pos.y = 0;
2571  this->new_pos.x = 0;
2572  this->new_pos.y = 0;
2573 }
2574 
2580 {
2581  return (this->place_mode & HT_DIAGONAL) != 0 && _ctrl_pressed && _left_button_down;
2582 }
2583 
2589 {
2590  return FindWindowById(this->window_class, this->window_number);
2591 }
2592 
2593 
2594 
2603 {
2604  int x1;
2605  int y1;
2606 
2607  if (_thd.freeze) return;
2608 
2609  HighLightStyle new_drawstyle = HT_NONE;
2610  bool new_diagonal = false;
2611 
2612  if ((_thd.place_mode & HT_DRAG_MASK) == HT_SPECIAL) {
2613  x1 = _thd.selend.x;
2614  y1 = _thd.selend.y;
2615  if (x1 != -1) {
2616  int x2 = _thd.selstart.x & ~TILE_UNIT_MASK;
2617  int y2 = _thd.selstart.y & ~TILE_UNIT_MASK;
2618  x1 &= ~TILE_UNIT_MASK;
2619  y1 &= ~TILE_UNIT_MASK;
2620 
2621  if (_thd.IsDraggingDiagonal()) {
2622  new_diagonal = true;
2623  } else {
2624  if (x1 >= x2) Swap(x1, x2);
2625  if (y1 >= y2) Swap(y1, y2);
2626  }
2627  _thd.new_pos.x = x1;
2628  _thd.new_pos.y = y1;
2629  _thd.new_size.x = x2 - x1;
2630  _thd.new_size.y = y2 - y1;
2631  if (!new_diagonal) {
2632  _thd.new_size.x += TILE_SIZE;
2633  _thd.new_size.y += TILE_SIZE;
2634  }
2635  new_drawstyle = _thd.next_drawstyle;
2636  }
2637  } else if ((_thd.place_mode & HT_DRAG_MASK) != HT_NONE) {
2638  Point pt = GetTileBelowCursor();
2639  x1 = pt.x;
2640  y1 = pt.y;
2641  if (x1 != -1) {
2642  switch (_thd.place_mode & HT_DRAG_MASK) {
2643  case HT_RECT:
2644  new_drawstyle = HT_RECT;
2645  break;
2646  case HT_POINT:
2647  new_drawstyle = HT_POINT;
2648  x1 += TILE_SIZE / 2;
2649  y1 += TILE_SIZE / 2;
2650  break;
2651  case HT_RAIL:
2652  /* Draw one highlighted tile in any direction */
2653  new_drawstyle = GetAutorailHT(pt.x, pt.y);
2654  break;
2655  case HT_LINE:
2656  switch (_thd.place_mode & HT_DIR_MASK) {
2657  case HT_DIR_X: new_drawstyle = HT_LINE | HT_DIR_X; break;
2658  case HT_DIR_Y: new_drawstyle = HT_LINE | HT_DIR_Y; break;
2659 
2660  case HT_DIR_HU:
2661  case HT_DIR_HL:
2662  new_drawstyle = (pt.x & TILE_UNIT_MASK) + (pt.y & TILE_UNIT_MASK) <= TILE_SIZE ? HT_LINE | HT_DIR_HU : HT_LINE | HT_DIR_HL;
2663  break;
2664 
2665  case HT_DIR_VL:
2666  case HT_DIR_VR:
2667  new_drawstyle = (pt.x & TILE_UNIT_MASK) > (pt.y & TILE_UNIT_MASK) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
2668  break;
2669 
2670  default: NOT_REACHED();
2671  }
2672  _thd.selstart.x = x1 & ~TILE_UNIT_MASK;
2673  _thd.selstart.y = y1 & ~TILE_UNIT_MASK;
2674  break;
2675  default:
2676  NOT_REACHED();
2677  }
2678  _thd.new_pos.x = x1 & ~TILE_UNIT_MASK;
2679  _thd.new_pos.y = y1 & ~TILE_UNIT_MASK;
2680  }
2681  }
2682 
2683  /* redraw selection */
2684  if (_thd.drawstyle != new_drawstyle ||
2685  _thd.pos.x != _thd.new_pos.x || _thd.pos.y != _thd.new_pos.y ||
2686  _thd.size.x != _thd.new_size.x || _thd.size.y != _thd.new_size.y ||
2687  _thd.outersize.x != _thd.new_outersize.x ||
2688  _thd.outersize.y != _thd.new_outersize.y ||
2689  _thd.diagonal != new_diagonal) {
2690  /* Clear the old tile selection? */
2692 
2693  _thd.drawstyle = new_drawstyle;
2694  _thd.pos = _thd.new_pos;
2695  _thd.size = _thd.new_size;
2696  _thd.outersize = _thd.new_outersize;
2697  _thd.diagonal = new_diagonal;
2698  _thd.dirty = 0xff;
2699 
2700  /* Draw the new tile selection? */
2701  if ((new_drawstyle & HT_DRAG_MASK) != HT_NONE) SetSelectionTilesDirty();
2702  }
2703 }
2704 
2710 static inline void ShowMeasurementTooltips(StringID str, uint paramcount)
2711 {
2712  if (!_settings_client.gui.measure_tooltip) return;
2713  GuiShowTooltips(_thd.GetCallbackWnd(), str, TCC_EXIT_VIEWPORT, paramcount);
2714 }
2715 
2716 static void HideMeasurementTooltips()
2717 {
2719 }
2720 
2723 {
2724  _thd.select_method = method;
2725  _thd.select_proc = process;
2726  _thd.selend.x = TileX(tile) * TILE_SIZE;
2727  _thd.selstart.x = TileX(tile) * TILE_SIZE;
2728  _thd.selend.y = TileY(tile) * TILE_SIZE;
2729  _thd.selstart.y = TileY(tile) * TILE_SIZE;
2730 
2731  /* Needed so several things (road, autoroad, bridges, ...) are placed correctly.
2732  * In effect, placement starts from the centre of a tile
2733  */
2734  if (method == VPM_X_OR_Y || method == VPM_FIX_X || method == VPM_FIX_Y) {
2735  _thd.selend.x += TILE_SIZE / 2;
2736  _thd.selend.y += TILE_SIZE / 2;
2737  _thd.selstart.x += TILE_SIZE / 2;
2738  _thd.selstart.y += TILE_SIZE / 2;
2739  }
2740 
2741  HighLightStyle others = _thd.place_mode & ~(HT_DRAG_MASK | HT_DIR_MASK);
2742  if ((_thd.place_mode & HT_DRAG_MASK) == HT_RECT) {
2743  _thd.place_mode = HT_SPECIAL | others;
2744  _thd.next_drawstyle = HT_RECT | others;
2745  } else if (_thd.place_mode & (HT_RAIL | HT_LINE)) {
2746  _thd.place_mode = HT_SPECIAL | others;
2747  _thd.next_drawstyle = _thd.drawstyle | others;
2748  } else {
2749  _thd.place_mode = HT_SPECIAL | others;
2750  _thd.next_drawstyle = HT_POINT | others;
2751  }
2753 }
2754 
2757 {
2758  _thd.select_method = VPM_X_AND_Y;
2759  _thd.select_proc = process;
2760  _thd.selstart.x = 0;
2761  _thd.selstart.y = 0;
2762  _thd.next_drawstyle = HT_RECT;
2763 
2765 }
2766 
2767 void VpSetPlaceSizingLimit(int limit)
2768 {
2769  _thd.sizelimit = limit;
2770 }
2771 
2778 {
2779  uint64_t distance = DistanceManhattan(from, to) + 1;
2780 
2781  _thd.selend.x = TileX(to) * TILE_SIZE;
2782  _thd.selend.y = TileY(to) * TILE_SIZE;
2783  _thd.selstart.x = TileX(from) * TILE_SIZE;
2784  _thd.selstart.y = TileY(from) * TILE_SIZE;
2785  _thd.next_drawstyle = HT_RECT;
2786 
2787  /* show measurement only if there is any length to speak of */
2788  if (distance > 1) {
2789  SetDParam(0, distance);
2790  ShowMeasurementTooltips(STR_MEASURE_LENGTH, 1);
2791  } else {
2792  HideMeasurementTooltips();
2793  }
2794 }
2795 
2796 static void VpStartPreSizing()
2797 {
2798  _thd.selend.x = -1;
2800 }
2801 
2807 {
2808  int fxpy = _tile_fract_coords.x + _tile_fract_coords.y;
2809  int sxpy = (_thd.selend.x & TILE_UNIT_MASK) + (_thd.selend.y & TILE_UNIT_MASK);
2810  int fxmy = _tile_fract_coords.x - _tile_fract_coords.y;
2811  int sxmy = (_thd.selend.x & TILE_UNIT_MASK) - (_thd.selend.y & TILE_UNIT_MASK);
2812 
2813  switch (mode) {
2814  default: NOT_REACHED();
2815  case 0: // end piece is lower right
2816  if (fxpy >= 20 && sxpy <= 12) return HT_DIR_HL;
2817  if (fxmy < -3 && sxmy > 3) return HT_DIR_VR;
2818  return HT_DIR_Y;
2819 
2820  case 1:
2821  if (fxmy > 3 && sxmy < -3) return HT_DIR_VL;
2822  if (fxpy <= 12 && sxpy >= 20) return HT_DIR_HU;
2823  return HT_DIR_Y;
2824 
2825  case 2:
2826  if (fxmy > 3 && sxmy < -3) return HT_DIR_VL;
2827  if (fxpy >= 20 && sxpy <= 12) return HT_DIR_HL;
2828  return HT_DIR_X;
2829 
2830  case 3:
2831  if (fxmy < -3 && sxmy > 3) return HT_DIR_VR;
2832  if (fxpy <= 12 && sxpy >= 20) return HT_DIR_HU;
2833  return HT_DIR_X;
2834  }
2835 }
2836 
2850 static bool SwapDirection(HighLightStyle style, TileIndex start_tile, TileIndex end_tile)
2851 {
2852  uint start_x = TileX(start_tile);
2853  uint start_y = TileY(start_tile);
2854  uint end_x = TileX(end_tile);
2855  uint end_y = TileY(end_tile);
2856 
2857  switch (style & HT_DRAG_MASK) {
2858  case HT_RAIL:
2859  case HT_LINE: return (end_x > start_x || (end_x == start_x && end_y > start_y));
2860 
2861  case HT_RECT:
2862  case HT_POINT: return (end_x != start_x && end_y < start_y);
2863  default: NOT_REACHED();
2864  }
2865 
2866  return false;
2867 }
2868 
2884 static int CalcHeightdiff(HighLightStyle style, uint distance, TileIndex start_tile, TileIndex end_tile)
2885 {
2886  bool swap = SwapDirection(style, start_tile, end_tile);
2887  uint h0, h1; // Start height and end height.
2888 
2889  if (start_tile == end_tile) return 0;
2890  if (swap) Swap(start_tile, end_tile);
2891 
2892  switch (style & HT_DRAG_MASK) {
2893  case HT_RECT:
2894  /* In the case of an area we can determine whether we were dragging south or
2895  * east by checking the X-coordinates of the tiles */
2896  if (TileX(end_tile) > TileX(start_tile)) {
2897  /* Dragging south does not need to change the start tile. */
2898  end_tile = TileAddByDir(end_tile, DIR_S);
2899  } else {
2900  /* Dragging east. */
2901  start_tile = TileAddByDir(start_tile, DIR_SW);
2902  end_tile = TileAddByDir(end_tile, DIR_SE);
2903  }
2904  [[fallthrough]];
2905 
2906  case HT_POINT:
2907  h0 = TileHeight(start_tile);
2908  h1 = TileHeight(end_tile);
2909  break;
2910  default: { // All other types, this is mostly only line/autorail
2911  static const HighLightStyle flip_style_direction[] = {
2913  };
2914  static const std::pair<TileIndexDiffC, TileIndexDiffC> start_heightdiff_line_by_dir[] = {
2915  { {1, 0}, {1, 1} }, // HT_DIR_X
2916  { {0, 1}, {1, 1} }, // HT_DIR_Y
2917  { {1, 0}, {0, 0} }, // HT_DIR_HU
2918  { {1, 0}, {1, 1} }, // HT_DIR_HL
2919  { {1, 0}, {1, 1} }, // HT_DIR_VL
2920  { {0, 1}, {1, 1} }, // HT_DIR_VR
2921  };
2922  static const std::pair<TileIndexDiffC, TileIndexDiffC> end_heightdiff_line_by_dir[] = {
2923  { {0, 1}, {0, 0} }, // HT_DIR_X
2924  { {1, 0}, {0, 0} }, // HT_DIR_Y
2925  { {0, 1}, {0, 0} }, // HT_DIR_HU
2926  { {1, 1}, {0, 1} }, // HT_DIR_HL
2927  { {1, 0}, {0, 0} }, // HT_DIR_VL
2928  { {0, 0}, {0, 1} }, // HT_DIR_VR
2929  };
2930  static_assert(std::size(start_heightdiff_line_by_dir) == HT_DIR_END);
2931  static_assert(std::size(end_heightdiff_line_by_dir) == HT_DIR_END);
2932 
2933  distance %= 2; // we're only interested if the distance is even or uneven
2934  style &= HT_DIR_MASK;
2935  assert(style < HT_DIR_END);
2936 
2937  /* To handle autorail, we do some magic to be able to use a lookup table.
2938  * Firstly if we drag the other way around, we switch start&end, and if needed
2939  * also flip the drag-position. Eg if it was on the left, and the distance is even
2940  * that means the end, which is now the start is on the right */
2941  if (swap && distance == 0) style = flip_style_direction[style];
2942 
2943  /* Lambda to help calculating the height at one side of the line. */
2944  auto get_height = [](auto &tile, auto &heightdiffs) {
2945  return std::max(
2946  TileHeight(TileAdd(tile, ToTileIndexDiff(heightdiffs.first))),
2947  TileHeight(TileAdd(tile, ToTileIndexDiff(heightdiffs.second))));
2948  };
2949 
2950  /* Use lookup table for start-tile based on HighLightStyle direction */
2951  h0 = get_height(start_tile, start_heightdiff_line_by_dir[style]);
2952 
2953  /* Use lookup table for end-tile based on HighLightStyle direction
2954  * flip around side (lower/upper, left/right) based on distance */
2955  if (distance == 0) style = flip_style_direction[style];
2956  h1 = get_height(end_tile, end_heightdiff_line_by_dir[style]);
2957  break;
2958  }
2959  }
2960 
2961  if (swap) Swap(h0, h1);
2962  return (int)(h1 - h0) * TILE_HEIGHT_STEP;
2963 }
2964 
2965 static const StringID measure_strings_length[] = {STR_NULL, STR_MEASURE_LENGTH, STR_MEASURE_LENGTH_HEIGHTDIFF};
2966 
2973 static void CheckUnderflow(int &test, int &other, int mult)
2974 {
2975  if (test >= 0) return;
2976 
2977  other += mult * test;
2978  test = 0;
2979 }
2980 
2988 static void CheckOverflow(int &test, int &other, int max, int mult)
2989 {
2990  if (test <= max) return;
2991 
2992  other += mult * (test - max);
2993  test = max;
2994 }
2995 
2997 static void CalcRaildirsDrawstyle(int x, int y, int method)
2998 {
2999  HighLightStyle b;
3000 
3001  int dx = _thd.selstart.x - (_thd.selend.x & ~TILE_UNIT_MASK);
3002  int dy = _thd.selstart.y - (_thd.selend.y & ~TILE_UNIT_MASK);
3003  uint w = abs(dx) + TILE_SIZE;
3004  uint h = abs(dy) + TILE_SIZE;
3005 
3006  if (method & ~(VPM_RAILDIRS | VPM_SIGNALDIRS)) {
3007  /* We 'force' a selection direction; first four rail buttons. */
3008  method &= ~(VPM_RAILDIRS | VPM_SIGNALDIRS);
3009  int raw_dx = _thd.selstart.x - _thd.selend.x;
3010  int raw_dy = _thd.selstart.y - _thd.selend.y;
3011  switch (method) {
3012  case VPM_FIX_X:
3013  b = HT_LINE | HT_DIR_Y;
3014  x = _thd.selstart.x;
3015  break;
3016 
3017  case VPM_FIX_Y:
3018  b = HT_LINE | HT_DIR_X;
3019  y = _thd.selstart.y;
3020  break;
3021 
3022  case VPM_FIX_HORIZONTAL:
3023  if (dx == -dy) {
3024  /* We are on a straight horizontal line. Determine the 'rail'
3025  * to build based the sub tile location. */
3027  } else {
3028  /* We are not on a straight line. Determine the rail to build
3029  * based on whether we are above or below it. */
3030  b = dx + dy >= (int)TILE_SIZE ? HT_LINE | HT_DIR_HU : HT_LINE | HT_DIR_HL;
3031 
3032  /* Calculate where a horizontal line through the start point and
3033  * a vertical line from the selected end point intersect and
3034  * use that point as the end point. */
3035  int offset = (raw_dx - raw_dy) / 2;
3036  x = _thd.selstart.x - (offset & ~TILE_UNIT_MASK);
3037  y = _thd.selstart.y + (offset & ~TILE_UNIT_MASK);
3038 
3039  /* 'Build' the last half rail tile if needed */
3040  if ((offset & TILE_UNIT_MASK) > (TILE_SIZE / 2)) {
3041  if (dx + dy >= (int)TILE_SIZE) {
3042  x += (dx + dy < 0) ? (int)TILE_SIZE : -(int)TILE_SIZE;
3043  } else {
3044  y += (dx + dy < 0) ? (int)TILE_SIZE : -(int)TILE_SIZE;
3045  }
3046  }
3047 
3048  /* Make sure we do not overflow the map! */
3049  CheckUnderflow(x, y, 1);
3050  CheckUnderflow(y, x, 1);
3051  CheckOverflow(x, y, (Map::MaxX() - 1) * TILE_SIZE, 1);
3052  CheckOverflow(y, x, (Map::MaxY() - 1) * TILE_SIZE, 1);
3053  assert(x >= 0 && y >= 0 && x <= (int)(Map::MaxX() * TILE_SIZE) && y <= (int)(Map::MaxY() * TILE_SIZE));
3054  }
3055  break;
3056 
3057  case VPM_FIX_VERTICAL:
3058  if (dx == dy) {
3059  /* We are on a straight vertical line. Determine the 'rail'
3060  * to build based the sub tile location. */
3061  b = (x & TILE_UNIT_MASK) > (y & TILE_UNIT_MASK) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
3062  } else {
3063  /* We are not on a straight line. Determine the rail to build
3064  * based on whether we are left or right from it. */
3065  b = dx < dy ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
3066 
3067  /* Calculate where a vertical line through the start point and
3068  * a horizontal line from the selected end point intersect and
3069  * use that point as the end point. */
3070  int offset = (raw_dx + raw_dy + (int)TILE_SIZE) / 2;
3071  x = _thd.selstart.x - (offset & ~TILE_UNIT_MASK);
3072  y = _thd.selstart.y - (offset & ~TILE_UNIT_MASK);
3073 
3074  /* 'Build' the last half rail tile if needed */
3075  if ((offset & TILE_UNIT_MASK) > (TILE_SIZE / 2)) {
3076  if (dx - dy < 0) {
3077  y += (dx > dy) ? (int)TILE_SIZE : -(int)TILE_SIZE;
3078  } else {
3079  x += (dx < dy) ? (int)TILE_SIZE : -(int)TILE_SIZE;
3080  }
3081  }
3082 
3083  /* Make sure we do not overflow the map! */
3084  CheckUnderflow(x, y, -1);
3085  CheckUnderflow(y, x, -1);
3086  CheckOverflow(x, y, (Map::MaxX() - 1) * TILE_SIZE, -1);
3087  CheckOverflow(y, x, (Map::MaxY() - 1) * TILE_SIZE, -1);
3088  assert(x >= 0 && y >= 0 && x <= (int)(Map::MaxX() * TILE_SIZE) && y <= (int)(Map::MaxY() * TILE_SIZE));
3089  }
3090  break;
3091 
3092  default:
3093  NOT_REACHED();
3094  }
3095  } else if (TileVirtXY(_thd.selstart.x, _thd.selstart.y) == TileVirtXY(x, y)) { // check if we're only within one tile
3096  if (method & VPM_RAILDIRS) {
3097  b = GetAutorailHT(x, y);
3098  } else { // rect for autosignals on one tile
3099  b = HT_RECT;
3100  }
3101  } else if (h == TILE_SIZE) { // Is this in X direction?
3102  if (dx == (int)TILE_SIZE) { // 2x1 special handling
3103  b = (Check2x1AutoRail(3)) | HT_LINE;
3104  } else if (dx == -(int)TILE_SIZE) {
3105  b = (Check2x1AutoRail(2)) | HT_LINE;
3106  } else {
3107  b = HT_LINE | HT_DIR_X;
3108  }
3109  y = _thd.selstart.y;
3110  } else if (w == TILE_SIZE) { // Or Y direction?
3111  if (dy == (int)TILE_SIZE) { // 2x1 special handling
3112  b = (Check2x1AutoRail(1)) | HT_LINE;
3113  } else if (dy == -(int)TILE_SIZE) { // 2x1 other direction
3114  b = (Check2x1AutoRail(0)) | HT_LINE;
3115  } else {
3116  b = HT_LINE | HT_DIR_Y;
3117  }
3118  x = _thd.selstart.x;
3119  } else if (w > h * 2) { // still count as x dir?
3120  b = HT_LINE | HT_DIR_X;
3121  y = _thd.selstart.y;
3122  } else if (h > w * 2) { // still count as y dir?
3123  b = HT_LINE | HT_DIR_Y;
3124  x = _thd.selstart.x;
3125  } else { // complicated direction
3126  int d = w - h;
3127  _thd.selend.x = _thd.selend.x & ~TILE_UNIT_MASK;
3128  _thd.selend.y = _thd.selend.y & ~TILE_UNIT_MASK;
3129 
3130  /* four cases. */
3131  if (x > _thd.selstart.x) {
3132  if (y > _thd.selstart.y) {
3133  /* south */
3134  if (d == 0) {
3135  b = (x & TILE_UNIT_MASK) > (y & TILE_UNIT_MASK) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
3136  } else if (d >= 0) {
3137  x = _thd.selstart.x + h;
3138  b = HT_LINE | HT_DIR_VL;
3139  } else {
3140  y = _thd.selstart.y + w;
3141  b = HT_LINE | HT_DIR_VR;
3142  }
3143  } else {
3144  /* west */
3145  if (d == 0) {
3147  } else if (d >= 0) {
3148  x = _thd.selstart.x + h;
3149  b = HT_LINE | HT_DIR_HL;
3150  } else {
3151  y = _thd.selstart.y - w;
3152  b = HT_LINE | HT_DIR_HU;
3153  }
3154  }
3155  } else {
3156  if (y > _thd.selstart.y) {
3157  /* east */
3158  if (d == 0) {
3160  } else if (d >= 0) {
3161  x = _thd.selstart.x - h;
3162  b = HT_LINE | HT_DIR_HU;
3163  } else {
3164  y = _thd.selstart.y + w;
3165  b = HT_LINE | HT_DIR_HL;
3166  }
3167  } else {
3168  /* north */
3169  if (d == 0) {
3170  b = (x & TILE_UNIT_MASK) > (y & TILE_UNIT_MASK) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
3171  } else if (d >= 0) {
3172  x = _thd.selstart.x - h;
3173  b = HT_LINE | HT_DIR_VR;
3174  } else {
3175  y = _thd.selstart.y - w;
3176  b = HT_LINE | HT_DIR_VL;
3177  }
3178  }
3179  }
3180  }
3181 
3183  TileIndex t0 = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
3184  TileIndex t1 = TileVirtXY(x, y);
3185  uint distance = DistanceManhattan(t0, t1) + 1;
3186  uint8_t index = 0;
3187 
3188  if (distance != 1) {
3189  int heightdiff = CalcHeightdiff(b, distance, t0, t1);
3190  /* If we are showing a tooltip for horizontal or vertical drags,
3191  * 2 tiles have a length of 1. To bias towards the ceiling we add
3192  * one before division. It feels more natural to count 3 lengths as 2 */
3193  if ((b & HT_DIR_MASK) != HT_DIR_X && (b & HT_DIR_MASK) != HT_DIR_Y) {
3194  distance = CeilDiv(distance, 2);
3195  }
3196 
3197  SetDParam(index++, distance);
3198  if (heightdiff != 0) SetDParam(index++, heightdiff);
3199  }
3200 
3201  ShowMeasurementTooltips(measure_strings_length[index], index);
3202  }
3203 
3204  _thd.selend.x = x;
3205  _thd.selend.y = y;
3206  _thd.next_drawstyle = b;
3207 }
3208 
3217 {
3218  int sx, sy;
3219  HighLightStyle style;
3220 
3221  if (x == -1) {
3222  _thd.selend.x = -1;
3223  return;
3224  }
3225 
3226  /* Special handling of drag in any (8-way) direction */
3227  if (method & (VPM_RAILDIRS | VPM_SIGNALDIRS)) {
3228  _thd.selend.x = x;
3229  _thd.selend.y = y;
3230  CalcRaildirsDrawstyle(x, y, method);
3231  return;
3232  }
3233 
3234  /* Needed so level-land is placed correctly */
3235  if ((_thd.next_drawstyle & HT_DRAG_MASK) == HT_POINT) {
3236  x += TILE_SIZE / 2;
3237  y += TILE_SIZE / 2;
3238  }
3239 
3240  sx = _thd.selstart.x;
3241  sy = _thd.selstart.y;
3242 
3243  int limit = 0;
3244 
3245  switch (method) {
3246  case VPM_X_OR_Y: // drag in X or Y direction
3247  if (abs(sy - y) < abs(sx - x)) {
3248  y = sy;
3249  style = HT_DIR_X;
3250  } else {
3251  x = sx;
3252  style = HT_DIR_Y;
3253  }
3254  goto calc_heightdiff_single_direction;
3255 
3256  case VPM_X_LIMITED: // Drag in X direction (limited size).
3257  limit = (_thd.sizelimit - 1) * TILE_SIZE;
3258  [[fallthrough]];
3259 
3260  case VPM_FIX_X: // drag in Y direction
3261  x = sx;
3262  style = HT_DIR_Y;
3263  goto calc_heightdiff_single_direction;
3264 
3265  case VPM_Y_LIMITED: // Drag in Y direction (limited size).
3266  limit = (_thd.sizelimit - 1) * TILE_SIZE;
3267  [[fallthrough]];
3268 
3269  case VPM_FIX_Y: // drag in X direction
3270  y = sy;
3271  style = HT_DIR_X;
3272 
3273 calc_heightdiff_single_direction:;
3274  if (limit > 0) {
3275  x = sx + Clamp(x - sx, -limit, limit);
3276  y = sy + Clamp(y - sy, -limit, limit);
3277  }
3279  TileIndex t0 = TileVirtXY(sx, sy);
3280  TileIndex t1 = TileVirtXY(x, y);
3281  uint distance = DistanceManhattan(t0, t1) + 1;
3282  uint8_t index = 0;
3283 
3284  if (distance != 1) {
3285  /* With current code passing a HT_LINE style to calculate the height
3286  * difference is enough. However if/when a point-tool is created
3287  * with this method, function should be called with new_style (below)
3288  * instead of HT_LINE | style case HT_POINT is handled specially
3289  * new_style := (_thd.next_drawstyle & HT_RECT) ? HT_LINE | style : _thd.next_drawstyle; */
3290  int heightdiff = CalcHeightdiff(HT_LINE | style, 0, t0, t1);
3291 
3292  SetDParam(index++, distance);
3293  if (heightdiff != 0) SetDParam(index++, heightdiff);
3294  }
3295 
3296  ShowMeasurementTooltips(measure_strings_length[index], index);
3297  }
3298  break;
3299 
3300  case VPM_X_AND_Y_LIMITED: // Drag an X by Y constrained rect area.
3301  limit = (_thd.sizelimit - 1) * TILE_SIZE;
3302  x = sx + Clamp(x - sx, -limit, limit);
3303  y = sy + Clamp(y - sy, -limit, limit);
3304  [[fallthrough]];
3305 
3306  case VPM_X_AND_Y: // drag an X by Y area
3308  static const StringID measure_strings_area[] = {
3309  STR_NULL, STR_NULL, STR_MEASURE_AREA, STR_MEASURE_AREA_HEIGHTDIFF
3310  };
3311 
3312  TileIndex t0 = TileVirtXY(sx, sy);
3313  TileIndex t1 = TileVirtXY(x, y);
3314  uint dx = Delta(TileX(t0), TileX(t1)) + 1;
3315  uint dy = Delta(TileY(t0), TileY(t1)) + 1;
3316  uint8_t index = 0;
3317 
3318  /* If dragging an area (eg dynamite tool) and it is actually a single
3319  * row/column, change the type to 'line' to get proper calculation for height */
3320  style = (HighLightStyle)_thd.next_drawstyle;
3321  if (_thd.IsDraggingDiagonal()) {
3322  /* Determine the "area" of the diagonal dragged selection.
3323  * We assume the area is the number of tiles along the X
3324  * edge and the number of tiles along the Y edge. However,
3325  * multiplying these two numbers does not give the exact
3326  * number of tiles; basically we are counting the black
3327  * squares on a chess board and ignore the white ones to
3328  * make the tile counts at the edges match up. There is no
3329  * other way to make a proper count though.
3330  *
3331  * First convert to the rotated coordinate system. */
3332  int dist_x = TileX(t0) - TileX(t1);
3333  int dist_y = TileY(t0) - TileY(t1);
3334  int a_max = dist_x + dist_y;
3335  int b_max = dist_y - dist_x;
3336 
3337  /* Now determine the size along the edge, but due to the
3338  * chess board principle this counts double. */
3339  a_max = abs(a_max + (a_max > 0 ? 2 : -2)) / 2;
3340  b_max = abs(b_max + (b_max > 0 ? 2 : -2)) / 2;
3341 
3342  /* We get a 1x1 on normal 2x1 rectangles, due to it being
3343  * a seen as two sides. As the result for actual building
3344  * will be the same as non-diagonal dragging revert to that
3345  * behaviour to give it a more normally looking size. */
3346  if (a_max != 1 || b_max != 1) {
3347  dx = a_max;
3348  dy = b_max;
3349  }
3350  } else if (style & HT_RECT) {
3351  if (dx == 1) {
3352  style = HT_LINE | HT_DIR_Y;
3353  } else if (dy == 1) {
3354  style = HT_LINE | HT_DIR_X;
3355  }
3356  }
3357 
3358  if (dx != 1 || dy != 1) {
3359  int heightdiff = CalcHeightdiff(style, 0, t0, t1);
3360 
3361  SetDParam(index++, dx - (style & HT_POINT ? 1 : 0));
3362  SetDParam(index++, dy - (style & HT_POINT ? 1 : 0));
3363  if (heightdiff != 0) SetDParam(index++, heightdiff);
3364  }
3365 
3366  ShowMeasurementTooltips(measure_strings_area[index], index);
3367  }
3368  break;
3369 
3370  default: NOT_REACHED();
3371  }
3372 
3373  _thd.selend.x = x;
3374  _thd.selend.y = y;
3375 }
3376 
3382 {
3384 
3385  /* stop drag mode if the window has been closed */
3386  Window *w = _thd.GetCallbackWnd();
3387  if (w == nullptr) {
3389  return ES_HANDLED;
3390  }
3391 
3392  /* while dragging execute the drag procedure of the corresponding window (mostly VpSelectTilesWithMethod() ) */
3393  if (_left_button_down) {
3395  /* Only register a drag event when the mouse moved. */
3396  if (_thd.new_pos.x == _thd.selstart.x && _thd.new_pos.y == _thd.selstart.y) return ES_HANDLED;
3397  _thd.selstart.x = _thd.new_pos.x;
3398  _thd.selstart.y = _thd.new_pos.y;
3399  }
3400 
3401  w->OnPlaceDrag(_thd.select_method, _thd.select_proc, GetTileBelowCursor());
3402  return ES_HANDLED;
3403  }
3404 
3405  /* Mouse button released. */
3408 
3409  /* Keep the selected tool, but reset it to the original mode. */
3410  HighLightStyle others = _thd.place_mode & ~(HT_DRAG_MASK | HT_DIR_MASK);
3411  if ((_thd.next_drawstyle & HT_DRAG_MASK) == HT_RECT) {
3412  _thd.place_mode = HT_RECT | others;
3413  } else if (_thd.select_method & VPM_SIGNALDIRS) {
3414  _thd.place_mode = HT_RECT | others;
3415  } else if (_thd.select_method & VPM_RAILDIRS) {
3416  _thd.place_mode = (_thd.select_method & ~VPM_RAILDIRS) ? _thd.next_drawstyle : (HT_RAIL | others);
3417  } else {
3418  _thd.place_mode = HT_POINT | others;
3419  }
3420  SetTileSelectSize(1, 1);
3421 
3422  HideMeasurementTooltips();
3423  w->OnPlaceMouseUp(_thd.select_method, _thd.select_proc, _thd.selend, TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y));
3424 
3425  return ES_HANDLED;
3426 }
3427 
3436 {
3437  SetObjectToPlace(icon, pal, mode, w->window_class, w->window_number);
3438 }
3439 
3440 #include "table/animcursors.h"
3441 
3450 void SetObjectToPlace(CursorID icon, PaletteID pal, HighLightStyle mode, WindowClass window_class, WindowNumber window_num)
3451 {
3452  if (_thd.window_class != WC_INVALID) {
3453  /* Undo clicking on button and drag & drop */
3454  Window *w = _thd.GetCallbackWnd();
3455  /* Call the abort function, but set the window class to something
3456  * that will never be used to avoid infinite loops. Setting it to
3457  * the 'next' window class must not be done because recursion into
3458  * this function might in some cases reset the newly set object to
3459  * place or not properly reset the original selection. */
3460  _thd.window_class = WC_INVALID;
3461  if (w != nullptr) {
3462  w->OnPlaceObjectAbort();
3463  HideMeasurementTooltips();
3464  }
3465  }
3466 
3467  /* Mark the old selection dirty, in case the selection shape or colour changes */
3469 
3470  SetTileSelectSize(1, 1);
3471 
3472  _thd.make_square_red = false;
3473 
3474  if (mode == HT_DRAG) { // HT_DRAG is for dragdropping trains in the depot window
3475  mode = HT_NONE;
3477  } else {
3479  }
3480 
3481  _thd.place_mode = mode;
3482  _thd.window_class = window_class;
3483  _thd.window_number = window_num;
3484 
3485  if ((mode & HT_DRAG_MASK) == HT_SPECIAL) { // special tools, like tunnels or docks start with presizing mode
3486  VpStartPreSizing();
3487  }
3488 
3489  if ((icon & ANIMCURSOR_FLAG) != 0) {
3491  } else {
3492  SetMouseCursor(icon, pal);
3493  }
3494 
3495 }
3496 
3499 {
3501 }
3502 
3503 Point GetViewportStationMiddle(const Viewport *vp, const Station *st)
3504 {
3505  int x = TileX(st->xy) * TILE_SIZE;
3506  int y = TileY(st->xy) * TILE_SIZE;
3507  int z = GetSlopePixelZ(Clamp(x, 0, Map::SizeX() * TILE_SIZE - 1), Clamp(y, 0, Map::SizeY() * TILE_SIZE - 1));
3508 
3509  Point p = RemapCoords(x, y, z);
3510  p.x = UnScaleByZoom(p.x - vp->virtual_left, vp->zoom) + vp->left;
3511  p.y = UnScaleByZoom(p.y - vp->virtual_top, vp->zoom) + vp->top;
3512  return p;
3513 }
3514 
3519 };
3520 
3523 #ifdef WITH_SSE
3524  { &ViewportSortParentSpritesSSE41Checker, &ViewportSortParentSpritesSSE41 },
3525 #endif
3527 };
3528 
3531 {
3532  for (const auto &sprite_sorter : _vp_sprite_sorters) {
3533  if (sprite_sorter.fct_checker()) {
3534  _vp_sprite_sorter = sprite_sorter.fct_sorter;
3535  break;
3536  }
3537  }
3538  assert(_vp_sprite_sorter != nullptr);
3539 }
3540 
3550 {
3551  if (_current_company != OWNER_DEITY) return CMD_ERROR;
3552  switch (target) {
3553  case VST_EVERYONE:
3554  break;
3555  case VST_COMPANY:
3556  if (_local_company != (CompanyID)ref) return CommandCost();
3557  break;
3558  case VST_CLIENT:
3559  if (_network_own_client_id != (ClientID)ref) return CommandCost();
3560  break;
3561  default:
3562  return CMD_ERROR;
3563  }
3564 
3565  if (flags & DC_EXEC) {
3567  ScrollMainWindowToTile(tile);
3568  }
3569  return CommandCost();
3570 }
3571 
3572 void MarkCatchmentTilesDirty()
3573 {
3574  if (_viewport_highlight_town != nullptr) {
3576  return;
3577  }
3578  if (_viewport_highlight_station != nullptr) {
3581  _viewport_highlight_station = nullptr;
3582  } else {
3584  for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
3585  MarkTileDirtyByTile(tile);
3586  }
3587  }
3588  }
3589  if (_viewport_highlight_waypoint != nullptr) {
3591  _viewport_highlight_waypoint = nullptr;
3592  }
3594  }
3595 }
3596 
3597 static void SetWindowDirtyForViewportCatchment()
3598 {
3602 }
3603 
3604 static void ClearViewportCatchment()
3605 {
3606  MarkCatchmentTilesDirty();
3607  _viewport_highlight_station = nullptr;
3608  _viewport_highlight_waypoint = nullptr;
3609  _viewport_highlight_town = nullptr;
3610 }
3611 
3618 void SetViewportCatchmentStation(const Station *st, bool sel)
3619 {
3620  SetWindowDirtyForViewportCatchment();
3621  if (sel && _viewport_highlight_station != st) {
3622  ClearViewportCatchment();
3624  MarkCatchmentTilesDirty();
3625  } else if (!sel && _viewport_highlight_station == st) {
3626  MarkCatchmentTilesDirty();
3627  _viewport_highlight_station = nullptr;
3628  }
3630 }
3631 
3638 void SetViewportCatchmentWaypoint(const Waypoint *wp, bool sel)
3639 {
3640  SetWindowDirtyForViewportCatchment();
3641  if (sel && _viewport_highlight_waypoint != wp) {
3642  ClearViewportCatchment();
3644  MarkCatchmentTilesDirty();
3645  } else if (!sel && _viewport_highlight_waypoint == wp) {
3646  MarkCatchmentTilesDirty();
3647  _viewport_highlight_waypoint = nullptr;
3648  }
3650 }
3651 
3658 void SetViewportCatchmentTown(const Town *t, bool sel)
3659 {
3660  SetWindowDirtyForViewportCatchment();
3661  if (sel && _viewport_highlight_town != t) {
3662  ClearViewportCatchment();
3665  } else if (!sel && _viewport_highlight_town == t) {
3666  _viewport_highlight_town = nullptr;
3668  }
3670 }
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.
constexpr debug_inline 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.
constexpr static debug_inline uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
TileIndex GetNorthernBridgeEnd(TileIndex t)
Finds the northern end of a bridge starting at a middle tile.
Definition: bridge_map.cpp:39
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.
Definition: bitmap_type.h:106
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 SetPixel(void *video, int x, int y, uint8_t colour)=0
Draw a pixel with a given colour on the video-buffer.
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...
Common return value for all commands.
Definition: command_type.h:23
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:68
RectPadding fullbevel
Always-scaled bevel thickness.
Definition: window_gui.h:41
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
Definition: command_func.h:28
DoCommandFlag
List of flags for a command.
Definition: command_type.h:374
@ DC_EXEC
execute the given command
Definition: command_type.h:376
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.
Definition: company_cmd.cpp:54
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
Definition: company_cmd.cpp:52
CompanyID _current_company
Company currently doing an action.
Definition: company_cmd.cpp:53
Functions related to companies.
Owner
Enum for all companies/owners.
Definition: company_type.h:18
@ OWNER_DEITY
The object is owned by a superuser / goal script.
Definition: company_type.h:27
@ OWNER_NONE
The tile has no ownership.
Definition: company_type.h:25
#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:18
@ Normal
The most basic (normal) sprite.
@ SA_HOR_CENTER
Horizontally center the text.
Definition: gfx_type.h:344
uint32_t CursorID
The number of the cursor (sprite)
Definition: gfx_type.h:20
@ FS_SMALL
Index of the small font in the font tables.
Definition: gfx_type.h:210
@ FS_NORMAL
Index of the normal font in the font tables.
Definition: gfx_type.h:209
uint32_t PaletteID
The number of the palette.
Definition: gfx_type.h:19
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition: gfx_type.h:260
@ TC_IS_PALETTE_COLOUR
Colour value is already a real palette colour index, not an index of a StringColour.
Definition: gfx_type.h:283
bool MarkAllViewportsDirty(int left, int top, int right, int bottom)
Mark all viewports that display an area as dirty (in need of repaint).
Definition: viewport.cpp:2022
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition: window.cpp:940
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.
Definition: viewport.cpp:1983
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.
Definition: viewport.cpp:1491
void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
Mark a tile given by its index dirty for repaint.
Definition: viewport.cpp:2057
static void SetSelectionTilesDirty()
Marks the selected tiles as dirty.
Definition: viewport.cpp:2074
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.
Definition: landscape.cpp:111
int GetSlopePixelZ(int x, int y, bool ground_vehicle)
Return world Z coordinate of a given point of a tile.
Definition: landscape.cpp:303
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:146
TileIndex TileAddByDir(TileIndex tile, Direction dir)
Adds a Direction to a tile.
Definition: map_func.h:596
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:454
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.
Definition: math_func.hpp:252
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.
Definition: math_func.hpp:320
constexpr T Delta(const T a, const T b)
Returns the (absolute) difference between two (scalar) variables.
Definition: math_func.hpp:234
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition: math_func.hpp:79
constexpr bool IsInsideMM(const T x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
Definition: math_func.hpp:268
constexpr void Swap(T &a, T &b)
Type safe swap operation.
Definition: math_func.hpp:283
void GuiShowTooltips(Window *parent, StringID str, TooltipCloseCondition close_tooltip, uint paramcount)
Shows a tooltip.
Definition: misc_gui.cpp:760
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
Definition: network_type.h:49
@ 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:314
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.
Definition: signs_gui.cpp:556
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
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
Corner
Enumeration of tile corners.
Definition: slope_type.h:22
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:319
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Definition: strings_type.h:16
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.
Owner owner
The owner of this 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:125
Data about how and where to blit pixels.
Definition: gfx_type.h:157
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.
Definition: tilearea_type.h:19
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.
Definition: pool_type.hpp:238
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:339
static size_t GetNumItems()
Returns number of valid items in the pool.
Definition: pool_type.hpp:369
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
Definition: pool_type.hpp:388
Specification of a rectangle with absolute coordinates of all edges.
static Pool::IterateWrapper< Station > Iterate(size_t from=0)
Returns an iterable ensemble of all valid stations of type T.
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.
Definition: station_base.h:439
BitmapTileArea catchment_tiles
NOSAVE: Set of individual tiles covered by catchment area.
Definition: station_base.h:459
Used to only draw a part of the sprite.
Definition: gfx_type.h:231
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.
Definition: viewport.cpp:2588
bool make_square_red
Whether to give a tile a red selection.
bool IsDraggingDiagonal()
Is the user dragging a 'diagonal rectangle'?
Definition: viewport.cpp:2579
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.
Definition: viewport.cpp:2567
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:45
Town data structure.
Definition: town.h:54
TileIndex xy
town center tile
Definition: town.h:55
TownCache cache
Container for all cacheable data.
Definition: town.h:57
bool show_zone
NOSAVE: mark town to show the local authority zone in the viewports.
Definition: town.h:104
StationList stations_near
NOSAVE: List of nearby stations.
Definition: town.h:91
bool kdtree_valid
Are the sign data valid for use with the _viewport_sign_kdtree?
Definition: viewport_type.h:52
Vehicle data structure.
Definition: vehicle_base.h:244
int32_t z_pos
z coordinate.
Definition: vehicle_base.h:306
Vehicle * First() const
Get the first vehicle of this vehicle chain.
Definition: vehicle_base.h:645
int32_t y_pos
y coordinate.
Definition: vehicle_base.h:305
int32_t x_pos
x coordinate.
Definition: vehicle_base.h:304
Owner owner
Which company owns the vehicle?
Definition: vehicle_base.h:309
UnitID unitnumber
unit number, for display purposes only
Definition: vehicle_base.h:326
Data structure for a window viewport.
Definition: window_gui.h:252
int32_t dest_scrollpos_y
Current destination y coordinate to display (virtual screen coordinate of topleft corner of the viewp...
Definition: window_gui.h:257
int32_t scrollpos_y
Currently shown y coordinate (virtual screen coordinate of topleft corner of the viewport).
Definition: window_gui.h:255
int32_t dest_scrollpos_x
Current destination x coordinate to display (virtual screen coordinate of topleft corner of the viewp...
Definition: window_gui.h:256
VehicleID follow_vehicle
VehicleID to follow if following a vehicle, INVALID_VEHICLE otherwise.
Definition: window_gui.h:253
int32_t scrollpos_x
Currently shown x coordinate (virtual screen coordinate of topleft corner of the viewport).
Definition: window_gui.h:254
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.
Definition: viewport.cpp:3516
VpSorterChecker fct_checker
The check function.
Definition: viewport.cpp:3517
VpSpriteSorter fct_sorter
The sorting function.
Definition: viewport.cpp:3518
Location information about a sign as seen on the viewport.
Definition: viewport_type.h:38
int32_t center
The center position of the sign.
Definition: viewport_type.h:39
uint16_t width_small
The width when zoomed out (small font)
Definition: viewport_type.h:42
uint16_t width_normal
The width when not zoomed out (normal font)
Definition: viewport_type.h:41
void UpdatePosition(int center, int top, StringID str, StringID str_small=STR_NULL)
Update the position of the viewport sign.
Definition: viewport.cpp:1466
int32_t top
The top of the sign.
Definition: viewport_type.h:40
Data structure for viewport, display of a part of the world.
Definition: viewport_type.h:22
int top
Screen coordinate top edge of the viewport.
Definition: viewport_type.h:24
int width
Screen width of the viewport.
Definition: viewport_type.h:25
ZoomLevel zoom
The zoom level of the viewport.
Definition: viewport_type.h:33
int virtual_top
Virtual top coordinate.
Definition: viewport_type.h:29
int virtual_left
Virtual left coordinate.
Definition: viewport_type.h:28
int virtual_width
width << zoom
Definition: viewport_type.h:30
int left
Screen coordinate left edge of the viewport.
Definition: viewport_type.h:23
int height
Screen height of the viewport.
Definition: viewport_type.h:26
int virtual_height
height << zoom
Definition: viewport_type.h:31
Representation of a waypoint.
Definition: waypoint_base.h:23
Iterable ensemble of all valid Windows.
Definition: window_gui.h:913
Iterator to iterate all valid Windows.
Definition: window_gui.h:868
Data structure for an opened window.
Definition: window_gui.h:273
virtual void OnPlaceObject([[maybe_unused]] Point pt, [[maybe_unused]] TileIndex tile)
The user clicked some place on the map when a tile highlight mode has been set.
Definition: window_gui.h:794
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition: window.cpp:551
virtual void OnPlaceObjectAbort()
The user cancelled a tile highlight mode that has been set.
Definition: window_gui.h:815
WindowClass window_class
Window class.
Definition: window_gui.h:301
ViewportData * viewport
Pointer to viewport data, if present.
Definition: window_gui.h:318
virtual void OnPlaceDrag([[maybe_unused]] ViewportPlaceMethod select_method, [[maybe_unused]] ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt)
The user is dragging over the map when the tile highlight mode has been set.
Definition: window_gui.h:825
virtual void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, [[maybe_unused]] ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, [[maybe_unused]] TileIndex start_tile, [[maybe_unused]] TileIndex end_tile)
The user has dragged over the map when the tile highlight mode has been set.
Definition: window_gui.h:836
void DrawViewport() const
Draw the viewport of this window.
Definition: viewport.cpp:1832
int left
x position of left edge of the window
Definition: window_gui.h:309
int top
y position of top edge of the window
Definition: window_gui.h:310
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition: window_gui.h:387
int height
Height of the window (number of pixels down in y direction)
Definition: window_gui.h:312
int width
width of the window (number of pixels to the right in x direction)
Definition: window_gui.h:311
WindowNumber window_number
Window number within the window class.
Definition: window_gui.h:302
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
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
std::tuple< Slope, int > GetTilePixelSlope(TileIndex tile)
Return the slope of a given tile.
Definition: tile_map.h:289
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 brid...
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.
Definition: town_cmd.cpp:3870
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
@ TO_SIGNS
signs
Definition: transparency.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...
Definition: transparency.h:48
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...
Definition: transparency.h:59
Vehicle * CheckClickOnVehicle(const Viewport *vp, int x, int y)
Find the vehicle close to the clicked coordinates.
Definition: vehicle.cpp:1248
void ViewportAddVehicles(DrawPixelInfo *dpi)
Add the vehicle sprites that should be drawn at a part of the screen.
Definition: vehicle.cpp:1154
Base class for all vehicles.
Functions related to vehicles.
bool IsCompanyBuildableVehicleType(VehicleType type)
Is the given vehicle type buildable by a company?
Definition: vehicle_func.h:91
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.
Definition: vehicle_type.h:54
static void HighlightTownLocalAuthorityTiles(const TileInfo *ti)
Highlights tiles insede local authority of selected towns.
Definition: viewport.cpp:1065
static void CheckOverflow(int &test, int &other, int max, int mult)
Check for overflowing the map.
Definition: viewport.cpp:2988
bool ScrollWindowToTile(TileIndex tile, Window *w, bool instant)
Scrolls the viewport in a window to a given location.
Definition: viewport.cpp:2504
CommandCost CmdScrollViewport(DoCommandFlag flags, TileIndex tile, ViewportScrollTarget target, uint32_t ref)
Scroll players main viewport.
Definition: viewport.cpp:3549
void SetTileSelectSize(int w, int h)
Highlight w by h tiles at the cursor.
Definition: viewport.cpp:2542
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).
Definition: viewport.cpp:3498
bool ScrollMainWindowToTile(TileIndex tile, bool instant)
Scrolls the viewport of the main window to a given location.
Definition: viewport.cpp:2515
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
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...
Definition: viewport.cpp:3435
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.
Definition: viewport.cpp:2884
static void DrawTileSelectionRect(const TileInfo *ti, PaletteID pal)
Draws a selection rectangle on a tile.
Definition: viewport.cpp:908
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.
Definition: viewport.cpp:3530
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
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.
Definition: viewport.cpp:2710
static int GetViewportY(Point tile)
Returns the y coordinate in the viewport coordinate system where the given tile is painted.
Definition: viewport.cpp:1180
static void ClampViewportToMap(const Viewport *vp, int *scroll_x, int *scroll_y)
Ensure that a given viewport has a valid scroll position.
Definition: viewport.cpp:1857
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
Definition: viewport.cpp:2559
Viewport * IsPtInWindowViewport(const Window *w, int x, int y)
Is a xy position inside the viewport of the window?
Definition: viewport.cpp:411
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.
Definition: viewport.cpp:1889
static void DrawTileSelection(const TileInfo *ti)
Checks if the specified tile is selected and if so draws selection using correct selectionstyle.
Definition: viewport.cpp:1099
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.
Definition: viewport.cpp:1014
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:968
static void DrawTileHighlightType(const TileInfo *ti, TileHighlightType tht)
Draw tile highlight for coverage area highlight.
Definition: viewport.cpp:1050
static void ViewportDrawBoundingBoxes(const ParentSpriteToSortVector *psd)
Draws the bounding boxes of all ParentSprites.
Definition: viewport.cpp:1673
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
void SetViewportCatchmentWaypoint(const Waypoint *wp, bool sel)
Select or deselect waypoint for coverage area highlight.
Definition: viewport.cpp:3638
void SetRedErrorSquare(TileIndex tile)
Set a tile to display a red error square.
Definition: viewport.cpp:2524
static void ViewportSortParentSprites(ParentSpriteToSortVector *psdv)
Sort parent sprites pointer array replicating the way original sorter did it.
Definition: viewport.cpp:1531
void VpSetPresizeRange(TileIndex from, TileIndex to)
Highlights all tiles between a set of two tiles.
Definition: viewport.cpp:2777
void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method)
Selects tiles while dragging.
Definition: viewport.cpp:3216
EventState VpHandlePlaceSizingDrag()
Handle the mouse while dragging for placement/resizing.
Definition: viewport.cpp:3381
void ViewportAddString(const DrawPixelInfo *dpi, ZoomLevel small_from, const ViewportSign *sign, StringID string_normal, StringID string_small, StringID string_small_shadow, Colours colour)
Add a string to draw in the viewport.
Definition: viewport.cpp:1311
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...
Definition: viewport.cpp:3450
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.
Definition: viewport.cpp:2973
static bool ViewportSortParentSpritesChecker()
This fallback sprite checker always exists.
Definition: viewport.cpp:1525
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.
Definition: viewport.cpp:2756
static ViewportSSCSS _vp_sprite_sorters[]
List of sorters ordered from best to worst.
Definition: viewport.cpp:3522
void UpdateTileSelection()
Updates tile highlighting for all cases.
Definition: viewport.cpp:2602
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:890
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.
Definition: viewport.cpp:3618
void SetViewportCatchmentTown(const Town *t, bool sel)
Select or deselect town for coverage area highlight.
Definition: viewport.cpp:3658
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.
Definition: viewport.cpp:2850
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
Definition: viewport.cpp:2997
static HighLightStyle Check2x1AutoRail(int mode)
returns information about the 2x1 piece to be build.
Definition: viewport.cpp:2806
static bool CheckClickOnViewportSign(const Viewport *vp, int x, int y, const ViewportSign *sign)
Test whether a sign is below the mouse.
Definition: viewport.cpp:2199
static void ViewportDrawDirtyBlocks()
Draw/colour the blocks that have been redrawn.
Definition: viewport.cpp:1691
void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process)
highlighting tiles while only going over them with the mouse
Definition: viewport.cpp:2722
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.
Definition: viewport.cpp:1189
const Station * _viewport_highlight_station
Currently selected station for coverage area highlight.
Definition: viewport.cpp:1005
const Town * _viewport_highlight_town
Currently selected town for coverage area highlight.
Definition: viewport.cpp:1007
void UpdateViewportPosition(Window *w, uint32_t delta_ms)
Update the viewport position being displayed.
Definition: viewport.cpp:1916
const Waypoint * _viewport_highlight_waypoint
Currently selected waypoint for coverage area highlight.
Definition: viewport.cpp:1006
bool ScrollWindowTo(int x, int y, int z, Window *w, bool instant)
Scrolls the viewport in a window to a given location.
Definition: viewport.cpp:2469
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.
Definition: viewport_func.h:20
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.
ViewportDragDropSelectionProcess
Drag and drop selection process, or, what to do with an area of land when you've selected it.
@ ZOOM_IN
Zoom in (get more detailed view).
Definition: viewport_type.h:77
@ ZOOM_OUT
Zoom out (get helicopter view).
Definition: viewport_type.h:78
ViewportPlaceMethod
Viewport place method (type of highlighted area and placed objects)
Definition: viewport_type.h:92
@ VPM_FIX_Y
drag only in Y axis
Definition: viewport_type.h:95
@ VPM_Y_LIMITED
Drag only in Y axis with limited size.
@ VPM_X_AND_Y_LIMITED
area of land of limited size
Definition: viewport_type.h:97
@ VPM_FIX_VERTICAL
drag only in vertical direction
Definition: viewport_type.h:99
@ VPM_X_LIMITED
Drag only in X axis with limited size.
@ VPM_X_AND_Y
area of land in X and Y directions
Definition: viewport_type.h:96
@ VPM_FIX_HORIZONTAL
drag only in horizontal direction
Definition: viewport_type.h:98
@ VPM_FIX_X
drag only in X axis
Definition: viewport_type.h:94
@ VPM_SIGNALDIRS
similar to VMP_RAILDIRS, but with different cursor
@ VPM_X_OR_Y
drag in X or Y direction
Definition: viewport_type.h:93
@ VPM_RAILDIRS
all rail directions
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:281
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:1140
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition: window.cpp:1098
Window * GetMainWindow()
Get the main window, i.e.
Definition: window.cpp:1127
Window * FindWindowFromPt(int x, int y)
Do a search for a window at specific coordinates.
Definition: window.cpp:1768
SpecialMouseMode _special_mouse_mode
Mode of the mouse.
Definition: window.cpp:93
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition: window.cpp:3093
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.
@ WSM_DRAGDROP
Drag&drop an object.
Definition: window_gui.h:1041
@ WSM_DRAGGING
Dragging mode (trees).
Definition: window_gui.h:1044
@ WSM_PRESIZE
Presizing mode (docks, tunnels).
Definition: window_gui.h:1043
@ WSM_NONE
No special mouse mode.
Definition: window_gui.h:1040
@ WSM_SIZING
Sizing mode.
Definition: window_gui.h:1042
@ FR_TRANSPARENT
Makes the background transparent if set.
Definition: window_gui.h:26
int WidgetID
Widget ID.
Definition: window_type.h:18
int32_t WindowNumber
Number to differentiate different windows of the same class.
Definition: window_type.h:737
EventState
State of handling an event.
Definition: window_type.h:743
@ ES_HANDLED
The passed event is handled.
Definition: window_type.h:744
@ ES_NOT_HANDLED
The passed event is not handled.
Definition: window_type.h:745
WindowClass
Window classes.
Definition: window_type.h:44
@ WC_INVALID
Invalid window.
Definition: window_type.h:724
@ WC_WAYPOINT_VIEW
Waypoint view; Window numbers:
Definition: window_type.h:357
@ WC_STATION_VIEW
Station view; Window numbers:
Definition: window_type.h:345
@ WC_MAIN_WINDOW
Main window; Window numbers:
Definition: window_type.h:51
@ WC_TOWN_VIEW
Town view; Window numbers:
Definition: window_type.h:333
@ WC_TOOLTIPS
Tooltip window; Window numbers:
Definition: window_type.h:116
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