2009-08-21 20:21:05 +00:00
/*
* This file is part of OpenTTD .
* 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.
* 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 .
* 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/>.
*/
2010-08-01 19:22:34 +00:00
/**
* @ file viewport . cpp Handling of all viewports .
2008-04-17 09:42:44 +00:00
*
* \ verbatim
* The in - game coordinate system looks like this *
* *
* ^ Z *
* | *
* | *
* | *
* | *
* / \ *
* / \ *
* / \ *
* / \ *
* X < > Y *
* \ endverbatim
*/
2007-02-23 01:48:53 +00:00
2014-09-21 17:27:37 +00:00
/**
* @ defgroup vp_column_row Rows and columns in the viewport
*
* Columns are vertical sections of the viewport that are half a tile wide .
* The origin , i . e . column 0 , is through the northern and southern most tile .
* This means that the column of e . g . Tile ( 0 , 0 ) and Tile ( 100 , 100 ) are in
* column number 0. The negative columns are towards the left of the screen ,
* or towards the west , whereas the positive ones are towards respectively
* the right and east .
* With half a tile wide is meant that the next column of tiles directly west
* or east of the centre line are respectively column - 1 and 1. Their tile
* centers are only half a tile from the center of their adjoining tile when
* looking only at the X - coordinate .
*
* \ verbatim
* ╳ *
* ╱ ╲ *
* ╳ 0 ╳ *
* ╱ ╲ ╱ ╲ *
* ╳ - 1 ╳ 1 ╳ *
* ╱ ╲ ╱ ╲ ╱ ╲ *
* ╳ - 2 ╳ 0 ╳ 2 ╳ *
* ╲ ╱ ╲ ╱ ╲ ╱ *
* ╳ - 1 ╳ 1 ╳ *
* ╲ ╱ ╲ ╱ *
* ╳ 0 ╳ *
* ╲ ╱ *
* ╳ *
* \ endverbatim
*
*
* Rows are horizontal sections of the viewport , also half a tile wide .
2019-01-17 22:01:07 +00:00
* This time the northern most tile on the map defines 0 and
2015-02-22 14:42:34 +00:00
* everything south of that has a positive number .
2014-09-21 17:27:37 +00:00
*/
2004-08-09 17:04:08 +00:00
# include "stdafx.h"
2023-01-26 18:21:36 +00:00
# include "core/backup_type.hpp"
2007-04-12 13:07:15 +00:00
# include "landscape.h"
2008-01-09 09:45:45 +00:00
# include "viewport_func.h"
2008-03-31 00:06:17 +00:00
# include "station_base.h"
2009-07-22 10:18:19 +00:00
# include "waypoint_base.h"
2004-08-09 17:04:08 +00:00
# include "town.h"
2008-03-31 07:25:49 +00:00
# include "signs_base.h"
# include "signs_func.h"
2009-01-31 20:16:06 +00:00
# include "vehicle_base.h"
2007-08-29 20:50:58 +00:00
# include "vehicle_gui.h"
2007-06-17 20:30:28 +00:00
# include "blitter/factory.hpp"
2007-12-21 19:49:27 +00:00
# include "strings_func.h"
2007-12-23 10:56:02 +00:00
# include "zoom_func.h"
2007-12-27 13:35:39 +00:00
# include "vehicle_func.h"
2008-09-30 20:51:04 +00:00
# include "company_func.h"
2009-07-22 10:18:19 +00:00
# include "waypoint_func.h"
2008-05-06 22:17:12 +00:00
# include "window_func.h"
2008-05-07 13:10:15 +00:00
# include "tilehighlight_func.h"
2008-05-17 13:01:30 +00:00
# include "window_gui.h"
2013-05-19 14:49:25 +00:00
# include "linkgraph/linkgraph_gui.h"
2019-02-22 16:57:28 +00:00
# include "viewport_kdtree.h"
2019-01-05 13:22:07 +00:00
# include "town_kdtree.h"
2014-01-02 16:48:16 +00:00
# include "viewport_sprite_sorter.h"
2014-09-21 17:27:37 +00:00
# include "bridge_map.h"
2018-04-24 17:19:01 +00:00
# include "company_base.h"
# include "command_func.h"
# include "network/network_func.h"
2018-07-19 19:17:07 +00:00
# include "framerate_type.h"
2021-10-05 20:02:27 +00:00
# include "viewport_cmd.h"
2014-09-21 17:27:37 +00:00
2020-01-25 21:53:16 +00:00
# include <forward_list>
# include <stack>
2008-04-16 14:15:00 +00:00
2008-01-13 01:21:35 +00:00
# include "table/strings.h"
2015-07-26 09:47:17 +00:00
# include "table/string_colours.h"
2008-01-13 01:21:35 +00:00
2014-04-23 20:13:33 +00:00
# include "safeguards.h"
2008-01-09 09:45:45 +00:00
Point _tile_fract_coords ;
2007-05-15 14:08:39 +00:00
2015-02-14 12:53:07 +00:00
2019-02-22 16:57:28 +00:00
ViewportSignKdtree _viewport_sign_kdtree ( & Kdtree_ViewportSignXYFunc ) ;
static int _viewport_sign_maxwidth = 0 ;
2015-02-14 12:53:07 +00:00
static const int MAX_TILE_EXTENT_LEFT = ZOOM_LVL_BASE * TILE_PIXELS ; ///< Maximum left extent of tile relative to north corner.
static const int MAX_TILE_EXTENT_RIGHT = ZOOM_LVL_BASE * TILE_PIXELS ; ///< Maximum right extent of tile relative to north corner.
static const int MAX_TILE_EXTENT_TOP = ZOOM_LVL_BASE * MAX_BUILDING_PIXELS ; ///< Maximum top extent of tile relative to north corner (not considering bridges).
static const int MAX_TILE_EXTENT_BOTTOM = ZOOM_LVL_BASE * ( TILE_PIXELS + 2 * TILE_HEIGHT ) ; ///< Maximum bottom extent of tile relative to north corner (worst case: #SLOPE_STEEP_N).
2007-03-07 12:11:48 +00:00
struct StringSpriteToDraw {
2009-02-09 02:57:15 +00:00
StringID string ;
2009-11-29 19:27:53 +00:00
Colours colour ;
2005-01-03 08:50:44 +00:00
int32 x ;
int32 y ;
2007-06-21 19:08:47 +00:00
uint64 params [ 2 ] ;
2004-08-09 17:04:08 +00:00
uint16 width ;
2007-03-07 12:11:48 +00:00
} ;
2004-08-09 17:04:08 +00:00
2007-03-07 12:11:48 +00:00
struct TileSpriteToDraw {
2007-01-14 19:57:49 +00:00
SpriteID image ;
2010-01-21 01:38:13 +00:00
PaletteID pal ;
2007-10-05 21:49:15 +00:00
const SubSprite * sub ; ///< only draw a rectangular part of the sprite
2009-06-07 13:25:21 +00:00
int32 x ; ///< screen X coordinate of sprite
int32 y ; ///< screen Y coordinate of sprite
2007-03-07 12:11:48 +00:00
} ;
2004-08-09 17:04:08 +00:00
2007-03-07 12:11:48 +00:00
struct ChildScreenSpriteToDraw {
2007-01-14 19:57:49 +00:00
SpriteID image ;
2010-01-21 01:38:13 +00:00
PaletteID pal ;
2007-10-05 21:49:15 +00:00
const SubSprite * sub ; ///< only draw a rectangular part of the sprite
2005-01-03 08:50:44 +00:00
int32 x ;
int32 y ;
2022-12-18 12:34:14 +00:00
bool relative ;
2008-06-16 20:08:30 +00:00
int next ; ///< next child to draw (-1 at the end)
2007-03-07 12:11:48 +00:00
} ;
2004-08-09 17:04:08 +00:00
2008-10-13 03:26:48 +00:00
/** Enumeration of multi-part foundations */
2007-10-20 20:25:43 +00:00
enum FoundationPart {
FOUNDATION_PART_NONE = 0xFF , ///< Neither foundation nor groundsprite drawn yet.
FOUNDATION_PART_NORMAL = 0 , ///< First part (normal foundation or no foundation)
FOUNDATION_PART_HALFTILE = 1 , ///< Second part (halftile foundation)
FOUNDATION_PART_END
} ;
2010-08-01 19:22:34 +00:00
/**
* Mode of " sprite combining "
2009-07-29 20:27:24 +00:00
* @ see StartSpriteCombine
*/
enum SpriteCombineMode {
SPRITE_COMBINE_NONE , ///< Every #AddSortableSpriteToDraw start its own bounding box
SPRITE_COMBINE_PENDING , ///< %Sprite combining will start with the next unclipped sprite.
SPRITE_COMBINE_ACTIVE , ///< %Sprite combining is active. #AddSortableSpriteToDraw outputs child sprites.
} ;
2019-03-03 17:30:09 +00:00
typedef std : : vector < TileSpriteToDraw > TileSpriteToDrawVector ;
typedef std : : vector < StringSpriteToDraw > StringSpriteToDrawVector ;
typedef std : : vector < ParentSpriteToDraw > ParentSpriteToDrawVector ;
typedef std : : vector < ChildScreenSpriteToDraw > ChildScreenSpriteToDrawVector ;
2008-04-16 14:15:00 +00:00
2008-10-13 03:26:48 +00:00
/** Data structure storing rendering information */
2007-03-07 12:11:48 +00:00
struct ViewportDrawer {
2004-08-09 17:04:08 +00:00
DrawPixelInfo dpi ;
2004-09-10 19:02:27 +00:00
2008-04-16 14:15:00 +00:00
StringSpriteToDrawVector string_sprites_to_draw ;
2008-04-16 14:18:15 +00:00
TileSpriteToDrawVector tile_sprites_to_draw ;
2008-04-16 20:01:04 +00:00
ParentSpriteToDrawVector parent_sprites_to_draw ;
2008-10-13 03:26:48 +00:00
ParentSpriteToSortVector parent_sprites_to_sort ; ///< Parent sprite pointer array used for sorting
2008-04-16 20:39:35 +00:00
ChildScreenSpriteToDrawVector child_screen_sprites_to_draw ;
2004-08-09 17:04:08 +00:00
2008-04-16 20:39:35 +00:00
int * last_child ;
2004-08-09 17:04:08 +00:00
2009-07-29 20:27:24 +00:00
SpriteCombineMode combine_sprites ; ///< Current mode of "sprite combining". @see StartSpriteCombine
2004-08-09 17:04:08 +00:00
2008-04-16 20:39:35 +00:00
int foundation [ FOUNDATION_PART_END ] ; ///< Foundation sprites (index into parent_sprites_to_draw).
FoundationPart foundation_part ; ///< Currently active foundation for ground sprite drawing.
int * last_foundation_child [ FOUNDATION_PART_END ] ; ///< Tail of ChildSprite list of the foundations. (index into child_screen_sprites_to_draw)
2010-05-30 13:05:36 +00:00
Point foundation_offset [ FOUNDATION_PART_END ] ; ///< Pixel offset for ground sprites on the foundations.
2007-03-07 12:11:48 +00:00
} ;
2004-08-09 17:04:08 +00:00
2021-01-17 18:57:16 +00:00
static bool MarkViewportDirty ( const Viewport * vp , int left , int top , int right , int bottom ) ;
2012-03-25 19:30:05 +00:00
2008-04-16 21:06:21 +00:00
static ViewportDrawer _vd ;
2004-08-09 17:04:08 +00:00
2005-05-27 15:05:54 +00:00
TileHighlightData _thd ;
2023-01-28 09:53:42 +00:00
static TileInfo _cur_ti ;
2008-04-18 16:51:54 +00:00
bool _draw_bounding_boxes = false ;
2012-03-25 19:06:59 +00:00
bool _draw_dirty_blocks = false ;
uint _dirty_block_colour = 0 ;
2019-04-10 21:07:06 +00:00
static VpSpriteSorter _vp_sprite_sorter = nullptr ;
2004-08-09 17:04:08 +00:00
2020-06-29 01:38:29 +00:00
static Point MapXYZToViewport ( const Viewport * vp , int x , int y , int z )
2004-08-09 17:04:08 +00:00
{
Point p = RemapCoords ( x , y , z ) ;
2005-07-17 20:14:58 +00:00
p . x - = vp - > virtual_width / 2 ;
p . y - = vp - > virtual_height / 2 ;
2004-08-09 17:04:08 +00:00
return p ;
}
2006-11-18 13:54:33 +00:00
void DeleteWindowViewport ( Window * w )
{
2023-03-31 15:06:36 +00:00
delete w - > viewport ;
2019-04-10 21:07:06 +00:00
w - > viewport = nullptr ;
2006-11-18 13:54:33 +00:00
}
2008-04-19 13:05:05 +00:00
/**
* Initialize viewport of the window for use .
* @ param w Window to use / display the viewport in
* @ param x Offset of left edge of viewport with respect to left edge window \ a w
* @ param y Offset of top edge of viewport with respect to top edge window \ a w
* @ param width Width of the viewport
* @ param height Height of the viewport
* @ param follow_flags Flags controlling the viewport .
2011-03-04 12:12:48 +00:00
* - If bit 31 is set , the lower 20 bits are the vehicle that the viewport should follow .
2010-10-23 18:12:38 +00:00
* - If bit 31 is clear , it is a # TileIndex .
2008-04-19 13:05:05 +00:00
* @ param zoom Zoomlevel to display
*/
2008-04-19 13:17:19 +00:00
void InitializeWindowViewport ( Window * w , int x , int y ,
2007-05-15 14:08:39 +00:00
int width , int height , uint32 follow_flags , ZoomLevel zoom )
2004-08-09 17:04:08 +00:00
{
2019-04-10 21:07:06 +00:00
assert ( w - > viewport = = nullptr ) ;
2004-08-09 17:04:08 +00:00
2023-03-31 15:06:36 +00:00
ViewportData * vp = new ViewportData ( ) ;
2004-08-09 17:04:08 +00:00
vp - > left = x + w - > left ;
vp - > top = y + w - > top ;
vp - > width = width ;
vp - > height = height ;
2004-09-10 19:02:27 +00:00
2011-11-24 12:20:14 +00:00
vp - > zoom = static_cast < ZoomLevel > ( Clamp ( zoom , _settings_client . gui . zoom_min , _settings_client . gui . zoom_max ) ) ;
2004-08-09 17:04:08 +00:00
2007-05-19 23:52:04 +00:00
vp - > virtual_width = ScaleByZoom ( width , zoom ) ;
vp - > virtual_height = ScaleByZoom ( height , zoom ) ;
2004-08-09 17:04:08 +00:00
2008-04-17 09:42:44 +00:00
Point pt ;
2004-08-09 17:04:08 +00:00
if ( follow_flags & 0x80000000 ) {
2005-07-17 20:14:58 +00:00
const Vehicle * veh ;
2011-03-04 12:12:48 +00:00
vp - > follow_vehicle = ( VehicleID ) ( follow_flags & 0xFFFFF ) ;
2009-05-16 23:34:14 +00:00
veh = Vehicle : : Get ( vp - > follow_vehicle ) ;
2004-08-09 17:04:08 +00:00
pt = MapXYZToViewport ( vp , veh - > x_pos , veh - > y_pos , veh - > z_pos ) ;
} else {
2021-05-26 18:51:17 +00:00
x = TileX ( follow_flags ) * TILE_SIZE ;
y = TileY ( follow_flags ) * TILE_SIZE ;
2005-07-17 20:14:58 +00:00
2008-05-11 15:08:44 +00:00
vp - > follow_vehicle = INVALID_VEHICLE ;
2011-11-04 10:18:13 +00:00
pt = MapXYZToViewport ( vp , x , y , GetSlopePixelZ ( x , y ) ) ;
2004-08-09 17:04:08 +00:00
}
2008-05-11 15:08:44 +00:00
vp - > scrollpos_x = pt . x ;
vp - > scrollpos_y = pt . y ;
vp - > dest_scrollpos_x = pt . x ;
vp - > dest_scrollpos_y = pt . y ;
2007-05-28 16:46:16 +00:00
2019-04-10 21:07:06 +00:00
vp - > overlay = nullptr ;
2013-05-19 14:49:25 +00:00
2004-08-09 17:04:08 +00:00
w - > viewport = vp ;
2023-01-26 21:50:43 +00:00
vp - > virtual_left = 0 ;
vp - > virtual_top = 0 ;
2004-08-09 17:04:08 +00:00
}
static Point _vp_move_offs ;
2021-05-09 14:42:36 +00:00
static void DoSetViewportPosition ( Window : : IteratorToFront it , int left , int top , int width , int height )
2004-08-09 17:04:08 +00:00
{
2021-05-09 14:42:36 +00:00
for ( ; ! it . IsEnd ( ) ; + + it ) {
const Window * w = * it ;
2004-08-09 17:04:08 +00:00
if ( left + width > w - > left & &
2005-07-17 20:14:58 +00:00
w - > left + w - > width > left & &
2004-08-09 17:04:08 +00:00
top + height > w - > top & &
2005-07-17 20:14:58 +00:00
w - > top + w - > height > top ) {
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
if ( left < w - > left ) {
2021-05-09 14:42:36 +00:00
DoSetViewportPosition ( it , left , top , w - > left - left , height ) ;
DoSetViewportPosition ( it , left + ( w - > left - left ) , top , width - ( w - > left - left ) , height ) ;
2004-08-09 17:04:08 +00:00
return ;
}
if ( left + width > w - > left + w - > width ) {
2021-05-09 14:42:36 +00:00
DoSetViewportPosition ( it , left , top , ( w - > left + w - > width - left ) , height ) ;
DoSetViewportPosition ( it , left + ( w - > left + w - > width - left ) , top , width - ( w - > left + w - > width - left ) , height ) ;
2004-08-09 17:04:08 +00:00
return ;
}
if ( top < w - > top ) {
2021-05-09 14:42:36 +00:00
DoSetViewportPosition ( it , left , top , width , ( w - > top - top ) ) ;
DoSetViewportPosition ( it , left , top + ( w - > top - top ) , width , height - ( w - > top - top ) ) ;
2004-08-09 17:04:08 +00:00
return ;
}
if ( top + height > w - > top + w - > height ) {
2021-05-09 14:42:36 +00:00
DoSetViewportPosition ( it , left , top , width , ( w - > top + w - > height - top ) ) ;
DoSetViewportPosition ( it , left , top + ( w - > top + w - > height - top ) , width , height - ( w - > top + w - > height - top ) ) ;
2004-08-09 17:04:08 +00:00
return ;
}
return ;
}
}
{
int xo = _vp_move_offs . x ;
int yo = _vp_move_offs . y ;
if ( abs ( xo ) > = width | | abs ( yo ) > = height ) {
/* fully_outside */
2005-07-17 20:14:58 +00:00
RedrawScreenRect ( left , top , left + width , top + height ) ;
2004-08-09 17:04:08 +00:00
return ;
}
GfxScroll ( left , top , width , height , xo , yo ) ;
if ( xo > 0 ) {
RedrawScreenRect ( left , top , xo + left , top + height ) ;
left + = xo ;
width - = xo ;
} else if ( xo < 0 ) {
2009-01-09 22:56:28 +00:00
RedrawScreenRect ( left + width + xo , top , left + width , top + height ) ;
2004-08-09 17:04:08 +00:00
width + = xo ;
}
if ( yo > 0 ) {
2009-01-09 22:56:28 +00:00
RedrawScreenRect ( left , top , width + left , top + yo ) ;
2004-08-09 17:04:08 +00:00
} else if ( yo < 0 ) {
2009-01-09 22:56:28 +00:00
RedrawScreenRect ( left , top + height + yo , width + left , top + height ) ;
2004-08-09 17:04:08 +00:00
}
}
}
2006-07-26 03:33:12 +00:00
static void SetViewportPosition ( Window * w , int x , int y )
2004-08-09 17:04:08 +00:00
{
2020-06-29 01:38:29 +00:00
Viewport * vp = w - > viewport ;
2004-08-09 17:04:08 +00:00
int old_left = vp - > virtual_left ;
int old_top = vp - > virtual_top ;
int i ;
int left , top , width , height ;
vp - > virtual_left = x ;
vp - > virtual_top = y ;
2010-05-30 13:05:36 +00:00
/* Viewport is bound to its left top corner, so it must be rounded down (UnScaleByZoomLower)
2007-11-18 13:13:59 +00:00
* else glitch described in FS # 1412 will happen ( offset by 1 pixel with zoom level > NORMAL )
*/
old_left = UnScaleByZoomLower ( old_left , vp - > zoom ) ;
old_top = UnScaleByZoomLower ( old_top , vp - > zoom ) ;
x = UnScaleByZoomLower ( x , vp - > zoom ) ;
y = UnScaleByZoomLower ( y , vp - > zoom ) ;
2004-08-09 17:04:08 +00:00
old_left - = x ;
old_top - = y ;
2005-07-17 20:14:58 +00:00
if ( old_top = = 0 & & old_left = = 0 ) return ;
2004-08-09 17:04:08 +00:00
_vp_move_offs . x = old_left ;
_vp_move_offs . y = old_top ;
left = vp - > left ;
top = vp - > top ;
width = vp - > width ;
height = vp - > height ;
if ( left < 0 ) {
width + = left ;
left = 0 ;
}
2005-07-17 20:14:58 +00:00
i = left + width - _screen . width ;
if ( i > = 0 ) width - = i ;
2004-08-09 17:04:08 +00:00
if ( width > 0 ) {
if ( top < 0 ) {
height + = top ;
top = 0 ;
}
2004-09-10 19:02:27 +00:00
2005-07-17 20:14:58 +00:00
i = top + height - _screen . height ;
if ( i > = 0 ) height - = i ;
2004-08-09 17:04:08 +00:00
2021-05-09 14:42:36 +00:00
if ( height > 0 ) {
Window : : IteratorToFront it ( w ) ;
+ + it ;
DoSetViewportPosition ( it , left , top , width , height ) ;
}
2004-08-09 17:04:08 +00:00
}
}
2008-04-19 13:05:05 +00:00
/**
* Is a xy position inside the viewport of the window ?
* @ param w Window to examine its viewport
* @ param x X coordinate of the xy position
* @ param y Y coordinate of the xy position
* @ return Pointer to the viewport if the xy position is in the viewport of the window ,
2019-04-10 21:07:06 +00:00
* otherwise \ c nullptr is returned .
2008-04-19 13:05:05 +00:00
*/
2020-06-29 01:38:29 +00:00
Viewport * IsPtInWindowViewport ( const Window * w , int x , int y )
2004-08-09 17:04:08 +00:00
{
2020-06-29 01:38:29 +00:00
Viewport * vp = w - > viewport ;
2005-07-17 20:14:58 +00:00
2019-04-10 21:07:06 +00:00
if ( vp ! = nullptr & &
2008-02-14 15:13:36 +00:00
IsInsideMM ( x , vp - > left , vp - > left + vp - > width ) & &
2007-11-24 10:38:43 +00:00
IsInsideMM ( y , vp - > top , vp - > top + vp - > height ) )
2004-08-09 17:04:08 +00:00
return vp ;
2019-04-10 21:07:06 +00:00
return nullptr ;
2004-08-09 17:04:08 +00:00
}
2008-10-13 03:26:48 +00:00
/**
2019-01-13 19:52:04 +00:00
* Translate screen coordinate in a viewport to underlying tile coordinate .
*
* Returns exact point of the map that is visible in the given place
* of the viewport ( 3 D perspective ) , height of tiles and foundations matter .
*
2008-10-13 03:26:48 +00:00
* @ param vp Viewport that contains the ( \ a x , \ a y ) screen coordinate
2019-01-13 19:52:04 +00:00
* @ param x Screen x coordinate , distance in pixels from the left edge of viewport frame
* @ param y Screen y coordinate , distance in pixels from the top edge of viewport frame
* @ param clamp_to_map Clamp the coordinate outside of the map to the closest , non - void tile within the map
* @ return Tile coordinate or ( - 1 , - 1 ) if given x or y is not within viewport frame
2010-08-01 19:44:49 +00:00
*/
2020-06-29 01:38:29 +00:00
Point TranslateXYToTileCoord ( const Viewport * vp , int x , int y , bool clamp_to_map )
2005-01-22 22:47:58 +00:00
{
2019-01-13 19:52:04 +00:00
if ( ! IsInsideBS ( x , vp - > left , vp - > width ) | | ! IsInsideBS ( y , vp - > top , vp - > height ) ) {
Point pt = { - 1 , - 1 } ;
return pt ;
2015-02-22 14:10:44 +00:00
}
2004-09-10 19:02:27 +00:00
2019-01-13 19:52:04 +00:00
return InverseRemapCoords2 (
ScaleByZoom ( x - vp - > left , vp - > zoom ) + vp - > virtual_left ,
ScaleByZoom ( y - vp - > top , vp - > zoom ) + vp - > virtual_top , clamp_to_map ) ;
2004-08-09 17:04:08 +00:00
}
2005-01-10 22:56:20 +00:00
/* When used for zooming, check area below current coordinates (x,y)
* and return the tile of the zoomed out / in position ( zoom_x , zoom_y )
* when you just want the tile , make x = zoom_x and y = zoom_y */
static Point GetTileFromScreenXY ( int x , int y , int zoom_x , int zoom_y )
2004-09-10 19:02:27 +00:00
{
2004-08-09 17:04:08 +00:00
Window * w ;
2020-06-29 01:38:29 +00:00
Viewport * vp ;
2004-08-09 17:04:08 +00:00
Point pt ;
2004-09-10 19:02:27 +00:00
2019-04-10 21:07:06 +00:00
if ( ( w = FindWindowFromPt ( x , y ) ) ! = nullptr & &
( vp = IsPtInWindowViewport ( w , x , y ) ) ! = nullptr )
2005-01-10 22:56:20 +00:00
return TranslateXYToTileCoord ( vp , zoom_x , zoom_y ) ;
2004-08-09 17:04:08 +00:00
pt . y = pt . x = - 1 ;
return pt ;
}
2007-03-07 11:47:46 +00:00
Point GetTileBelowCursor ( )
2004-08-09 17:04:08 +00:00
{
2005-01-10 22:56:20 +00:00
return GetTileFromScreenXY ( _cursor . pos . x , _cursor . pos . y , _cursor . pos . x , _cursor . pos . y ) ;
2004-08-09 17:04:08 +00:00
}
2004-09-03 19:59:05 +00:00
Point GetTileZoomCenterWindow ( bool in , Window * w )
2004-08-09 17:04:08 +00:00
{
int x , y ;
2020-06-29 01:38:29 +00:00
Viewport * vp = w - > viewport ;
2004-09-03 19:59:05 +00:00
2005-07-08 22:25:24 +00:00
if ( in ) {
2005-07-17 20:14:58 +00:00
x = ( ( _cursor . pos . x - vp - > left ) > > 1 ) + ( vp - > width > > 2 ) ;
y = ( ( _cursor . pos . y - vp - > top ) > > 1 ) + ( vp - > height > > 2 ) ;
} else {
2004-09-03 19:59:05 +00:00
x = vp - > width - ( _cursor . pos . x - vp - > left ) ;
y = vp - > height - ( _cursor . pos . y - vp - > top ) ;
2004-08-09 17:04:08 +00:00
}
2005-01-10 22:56:20 +00:00
/* Get the tile below the cursor and center on the zoomed-out center */
return GetTileFromScreenXY ( _cursor . pos . x , _cursor . pos . y , x + vp - > left , y + vp - > top ) ;
2004-08-09 17:04:08 +00:00
}
2010-08-01 19:22:34 +00:00
/**
* Update the status of the zoom - buttons according to the zoom - level
2006-11-07 13:06:02 +00:00
* of the viewport . This will update their status and invalidate accordingly
2007-04-09 15:06:24 +00:00
* @ param w Window pointer to the window that has the zoom buttons
2006-11-07 13:06:02 +00:00
* @ param vp pointer to the viewport whose zoom - level the buttons represent
* @ param widget_zoom_in widget index for window with zoom - in button
2010-08-01 19:44:49 +00:00
* @ param widget_zoom_out widget index for window with zoom - out button
*/
2020-06-29 01:38:29 +00:00
void HandleZoomMessage ( Window * w , const Viewport * vp , byte widget_zoom_in , byte widget_zoom_out )
2006-11-07 13:06:02 +00:00
{
2011-11-24 12:20:14 +00:00
w - > SetWidgetDisabledState ( widget_zoom_in , vp - > zoom < = _settings_client . gui . zoom_min ) ;
2009-09-13 19:15:59 +00:00
w - > SetWidgetDirty ( widget_zoom_in ) ;
2006-11-07 13:06:02 +00:00
2011-11-24 12:20:14 +00:00
w - > SetWidgetDisabledState ( widget_zoom_out , vp - > zoom > = _settings_client . gui . zoom_max ) ;
2009-09-13 19:15:59 +00:00
w - > SetWidgetDirty ( widget_zoom_out ) ;
2006-11-07 13:06:02 +00:00
}
2007-10-05 21:49:15 +00:00
/**
2010-05-30 13:05:36 +00:00
* Schedules a tile sprite for drawing .
2007-10-05 21:49:15 +00:00
*
* @ param image the image to draw .
* @ param pal the provided palette .
2009-06-07 13:25:21 +00:00
* @ param x position x ( world coordinates ) of the sprite .
* @ param y position y ( world coordinates ) of the sprite .
* @ param z position z ( world coordinates ) of the sprite .
2007-10-05 21:49:15 +00:00
* @ param sub Only draw a part of the sprite .
2009-06-07 13:25:21 +00:00
* @ param extra_offs_x Pixel X offset for the sprite position .
* @ param extra_offs_y Pixel Y offset for the sprite position .
2007-10-05 21:49:15 +00:00
*/
2019-04-10 21:07:06 +00:00
static void AddTileSpriteToDraw ( SpriteID image , PaletteID pal , int32 x , int32 y , int z , const SubSprite * sub = nullptr , int extra_offs_x = 0 , int extra_offs_y = 0 )
2004-08-09 17:04:08 +00:00
{
2005-07-24 15:56:31 +00:00
assert ( ( image & SPRITE_MASK ) < MAX_SPRITES ) ;
2004-08-09 17:04:08 +00:00
2020-12-14 23:22:04 +00:00
TileSpriteToDraw & ts = _vd . tile_sprites_to_draw . emplace_back ( ) ;
2019-02-18 22:39:06 +00:00
ts . image = image ;
ts . pal = pal ;
ts . sub = sub ;
2009-06-07 13:25:21 +00:00
Point pt = RemapCoords ( x , y , z ) ;
2019-02-18 22:39:06 +00:00
ts . x = pt . x + extra_offs_x ;
ts . y = pt . y + extra_offs_y ;
2004-08-09 17:04:08 +00:00
}
2007-10-14 19:57:15 +00:00
/**
* Adds a child sprite to the active foundation .
*
* The pixel offset of the sprite relative to the ParentSprite is the sum of the offset passed to OffsetGroundSprite ( ) and extra_offs_ ? .
*
* @ param image the image to draw .
* @ param pal the provided palette .
* @ param sub Only draw a part of the sprite .
2007-10-20 20:25:43 +00:00
* @ param foundation_part Foundation part .
2007-10-14 19:57:15 +00:00
* @ param extra_offs_x Pixel X offset for the sprite position .
* @ param extra_offs_y Pixel Y offset for the sprite position .
*/
2010-01-21 01:38:13 +00:00
static void AddChildSpriteToFoundation ( SpriteID image , PaletteID pal , const SubSprite * sub , FoundationPart foundation_part , int extra_offs_x , int extra_offs_y )
2007-10-14 19:57:15 +00:00
{
2007-11-24 10:38:43 +00:00
assert ( IsInsideMM ( foundation_part , 0 , FOUNDATION_PART_END ) ) ;
2008-04-16 21:06:21 +00:00
assert ( _vd . foundation [ foundation_part ] ! = - 1 ) ;
Point offs = _vd . foundation_offset [ foundation_part ] ;
2007-10-14 19:57:15 +00:00
/* Change the active ChildSprite list to the one of the foundation */
2008-04-16 21:06:21 +00:00
int * old_child = _vd . last_child ;
_vd . last_child = _vd . last_foundation_child [ foundation_part ] ;
2007-10-14 19:57:15 +00:00
2022-12-18 12:35:08 +00:00
AddChildSpriteScreen ( image , pal , offs . x + extra_offs_x , offs . y + extra_offs_y , false , sub , false , false ) ;
2007-10-14 19:57:15 +00:00
/* Switch back to last ChildSprite list */
2008-04-16 21:06:21 +00:00
_vd . last_child = old_child ;
2007-10-14 19:57:15 +00:00
}
2007-10-05 21:49:15 +00:00
/**
2010-01-03 20:55:00 +00:00
* Draws a ground sprite at a specific world - coordinate relative to the current tile .
2007-10-05 21:49:15 +00:00
* If the current tile is drawn on top of a foundation the sprite is added as child sprite to the " foundation " - ParentSprite .
*
* @ param image the image to draw .
* @ param pal the provided palette .
2010-01-03 20:55:00 +00:00
* @ param x position x ( world coordinates ) of the sprite relative to current tile .
* @ param y position y ( world coordinates ) of the sprite relative to current tile .
* @ param z position z ( world coordinates ) of the sprite relative to current tile .
2007-10-05 21:49:15 +00:00
* @ param sub Only draw a part of the sprite .
2009-06-07 13:25:21 +00:00
* @ param extra_offs_x Pixel X offset for the sprite position .
* @ param extra_offs_y Pixel Y offset for the sprite position .
2007-10-05 21:49:15 +00:00
*/
2010-01-21 01:38:13 +00:00
void DrawGroundSpriteAt ( SpriteID image , PaletteID pal , int32 x , int32 y , int z , const SubSprite * sub , int extra_offs_x , int extra_offs_y )
2004-08-09 17:04:08 +00:00
{
2007-10-20 20:25:43 +00:00
/* Switch to first foundation part, if no foundation was drawn */
2008-04-16 21:06:21 +00:00
if ( _vd . foundation_part = = FOUNDATION_PART_NONE ) _vd . foundation_part = FOUNDATION_PART_NORMAL ;
2007-10-20 20:25:43 +00:00
2008-04-16 21:06:21 +00:00
if ( _vd . foundation [ _vd . foundation_part ] ! = - 1 ) {
2010-01-03 20:55:00 +00:00
Point pt = RemapCoords ( x , y , z ) ;
2011-11-24 12:38:48 +00:00
AddChildSpriteToFoundation ( image , pal , sub , _vd . foundation_part , pt . x + extra_offs_x * ZOOM_LVL_BASE , pt . y + extra_offs_y * ZOOM_LVL_BASE ) ;
2004-08-09 17:04:08 +00:00
} else {
2023-01-28 09:53:42 +00:00
AddTileSpriteToDraw ( image , pal , _cur_ti . x + x , _cur_ti . y + y , _cur_ti . z + z , sub , extra_offs_x * ZOOM_LVL_BASE , extra_offs_y * ZOOM_LVL_BASE ) ;
2004-08-09 17:04:08 +00:00
}
}
2010-01-03 20:55:00 +00:00
/**
* Draws a ground sprite for the current tile .
* If the current tile is drawn on top of a foundation the sprite is added as child sprite to the " foundation " - ParentSprite .
*
* @ param image the image to draw .
* @ param pal the provided palette .
* @ param sub Only draw a part of the sprite .
* @ param extra_offs_x Pixel X offset for the sprite position .
* @ param extra_offs_y Pixel Y offset for the sprite position .
*/
2010-01-21 01:38:13 +00:00
void DrawGroundSprite ( SpriteID image , PaletteID pal , const SubSprite * sub , int extra_offs_x , int extra_offs_y )
2010-01-03 20:55:00 +00:00
{
DrawGroundSpriteAt ( image , pal , 0 , 0 , 0 , sub , extra_offs_x , extra_offs_y ) ;
}
2004-08-09 17:04:08 +00:00
2007-10-14 19:57:15 +00:00
/**
* Called when a foundation has been drawn for the current tile .
* Successive ground sprites for the current tile will be drawn as child sprites of the " foundation " - ParentSprite , not as TileSprites .
*
* @ param x sprite x - offset ( screen coordinates ) of ground sprites relative to the " foundation " - ParentSprite .
* @ param y sprite y - offset ( screen coordinates ) of ground sprites relative to the " foundation " - ParentSprite .
*/
2004-08-09 17:04:08 +00:00
void OffsetGroundSprite ( int x , int y )
{
2007-10-20 20:25:43 +00:00
/* Switch to next foundation part */
2008-04-16 21:06:21 +00:00
switch ( _vd . foundation_part ) {
2007-10-20 20:25:43 +00:00
case FOUNDATION_PART_NONE :
2008-04-16 21:06:21 +00:00
_vd . foundation_part = FOUNDATION_PART_NORMAL ;
2007-10-20 20:25:43 +00:00
break ;
case FOUNDATION_PART_NORMAL :
2008-04-16 21:06:21 +00:00
_vd . foundation_part = FOUNDATION_PART_HALFTILE ;
2007-10-20 20:25:43 +00:00
break ;
default : NOT_REACHED ( ) ;
}
2007-10-14 19:57:15 +00:00
2019-04-10 21:07:06 +00:00
/* _vd.last_child == nullptr if foundation sprite was clipped by the viewport bounds */
if ( _vd . last_child ! = nullptr ) _vd . foundation [ _vd . foundation_part ] = ( uint ) _vd . parent_sprites_to_draw . size ( ) - 1 ;
2007-10-14 19:57:15 +00:00
2011-11-24 12:38:48 +00:00
_vd . foundation_offset [ _vd . foundation_part ] . x = x * ZOOM_LVL_BASE ;
_vd . foundation_offset [ _vd . foundation_part ] . y = y * ZOOM_LVL_BASE ;
2008-04-16 21:06:21 +00:00
_vd . last_foundation_child [ _vd . foundation_part ] = _vd . last_child ;
2004-08-09 17:04:08 +00:00
}
2007-10-05 21:49:15 +00:00
/**
* Adds a child sprite to a parent sprite .
* In contrast to " AddChildSpriteScreen() " the sprite position is in world coordinates
*
* @ param image the image to draw .
* @ param pal the provided palette .
* @ param x position x of the sprite .
* @ param y position y of the sprite .
* @ param z position z of the sprite .
* @ param sub Only draw a part of the sprite .
*/
2011-11-04 13:40:59 +00:00
static void AddCombinedSprite ( SpriteID image , PaletteID pal , int x , int y , int z , const SubSprite * sub )
2004-08-09 17:04:08 +00:00
{
Point pt = RemapCoords ( x , y , z ) ;
2023-04-16 19:00:55 +00:00
const Sprite * spr = GetSprite ( image & SPRITE_MASK , SpriteType : : Normal ) ;
2004-08-09 17:04:08 +00:00
2008-04-16 21:06:21 +00:00
if ( pt . x + spr - > x_offs > = _vd . dpi . left + _vd . dpi . width | |
pt . x + spr - > x_offs + spr - > width < = _vd . dpi . left | |
pt . y + spr - > y_offs > = _vd . dpi . top + _vd . dpi . height | |
pt . y + spr - > y_offs + spr - > height < = _vd . dpi . top )
2004-08-09 17:04:08 +00:00
return ;
2019-02-17 11:20:52 +00:00
const ParentSpriteToDraw & pstd = _vd . parent_sprites_to_draw . back ( ) ;
AddChildSpriteScreen ( image , pal , pt . x - pstd . left , pt . y - pstd . top , false , sub , false ) ;
2004-08-09 17:04:08 +00:00
}
2010-08-01 19:22:34 +00:00
/**
* Draw a ( transparent ) sprite at given coordinates with a given bounding box .
2007-09-14 21:54:57 +00:00
* The bounding box extends from ( x + bb_offset_x , y + bb_offset_y , z + bb_offset_z ) to ( x + w - 1 , y + h - 1 , z + dz - 1 ) , both corners included .
2007-09-23 09:37:25 +00:00
* Bounding boxes with bb_offset_x = = w or bb_offset_y = = h or bb_offset_z = = dz are allowed and produce thin slices .
2007-09-14 21:54:57 +00:00
*
* @ note Bounding boxes are normally specified with bb_offset_x = bb_offset_y = bb_offset_z = 0. The extent of the bounding box in negative direction is
* defined by the sprite offset in the grf file .
* However if modifying the sprite offsets is not suitable ( e . g . when using existing graphics ) , the bounding box can be tuned by bb_offset .
*
2007-09-23 09:37:25 +00:00
* @ pre w > = bb_offset_x , h > = bb_offset_y , dz > = bb_offset_z . Else w , h or dz are ignored .
2007-09-14 21:54:57 +00:00
*
2007-07-26 14:07:11 +00:00
* @ param image the image to combine and draw ,
* @ param pal the provided palette ,
2007-09-14 21:54:57 +00:00
* @ param x position X ( world ) of the sprite ,
* @ param y position Y ( world ) of the sprite ,
* @ param w bounding box extent towards positive X ( world ) ,
* @ param h bounding box extent towards positive Y ( world ) ,
* @ param dz bounding box extent towards positive Z ( world ) ,
* @ param z position Z ( world ) of the sprite ,
* @ param transparent if true , switch the palette between the provided palette and the transparent palette ,
* @ param bb_offset_x bounding box extent towards negative X ( world ) ,
* @ param bb_offset_y bounding box extent towards negative Y ( world ) ,
* @ param bb_offset_z bounding box extent towards negative Z ( world )
2007-10-05 21:49:15 +00:00
* @ param sub Only draw a part of the sprite .
2007-07-26 14:07:11 +00:00
*/
2010-01-21 01:38:13 +00:00
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 )
2004-08-09 17:04:08 +00:00
{
2007-09-26 19:27:29 +00:00
int32 left , right , top , bottom ;
2004-08-09 17:04:08 +00:00
2005-07-24 15:56:31 +00:00
assert ( ( image & SPRITE_MASK ) < MAX_SPRITES ) ;
2004-08-09 17:04:08 +00:00
2007-07-26 14:07:11 +00:00
/* make the sprites transparent with the right palette */
if ( transparent ) {
2007-11-20 13:35:54 +00:00
SetBit ( image , PALETTE_MODIFIER_TRANSPARENT ) ;
2007-07-26 14:07:11 +00:00
pal = PALETTE_TO_TRANSPARENT ;
}
2009-07-29 20:27:24 +00:00
if ( _vd . combine_sprites = = SPRITE_COMBINE_ACTIVE ) {
2007-10-05 21:49:15 +00:00
AddCombinedSprite ( image , pal , x , y , z , sub ) ;
2004-08-09 17:04:08 +00:00
return ;
}
2019-04-10 21:07:06 +00:00
_vd . last_child = nullptr ;
2004-08-09 17:04:08 +00:00
2008-04-16 20:01:04 +00:00
Point pt = RemapCoords ( x , y , z ) ;
int tmp_left , tmp_top , tmp_x = pt . x , tmp_y = pt . y ;
2007-09-26 19:27:29 +00:00
/* Compute screen extents of sprite */
2007-09-19 16:36:42 +00:00
if ( image = = SPR_EMPTY_BOUNDING_BOX ) {
2008-04-16 20:01:04 +00:00
left = tmp_left = RemapCoords ( x + w , y + bb_offset_y , z + bb_offset_z ) . x ;
2007-09-26 19:27:29 +00:00
right = RemapCoords ( x + bb_offset_x , y + h , z + bb_offset_z ) . x + 1 ;
2008-04-16 20:01:04 +00:00
top = tmp_top = RemapCoords ( x + bb_offset_x , y + bb_offset_y , z + dz ) . y ;
2007-09-26 19:27:29 +00:00
bottom = RemapCoords ( x + w , y + h , z + bb_offset_z ) . y + 1 ;
2007-09-19 16:36:42 +00:00
} else {
2023-04-16 19:00:55 +00:00
const Sprite * spr = GetSprite ( image & SPRITE_MASK , SpriteType : : Normal ) ;
2008-04-16 20:01:04 +00:00
left = tmp_left = ( pt . x + = spr - > x_offs ) ;
2007-09-26 19:27:29 +00:00
right = ( pt . x + spr - > width ) ;
2008-04-16 20:01:04 +00:00
top = tmp_top = ( pt . y + = spr - > y_offs ) ;
2007-09-26 19:27:29 +00:00
bottom = ( pt . y + spr - > height ) ;
}
2008-02-14 15:13:36 +00:00
2007-09-26 19:27:29 +00:00
if ( _draw_bounding_boxes & & ( image ! = SPR_EMPTY_BOUNDING_BOX ) ) {
2010-04-12 14:12:47 +00:00
/* Compute maximal extents of sprite and its bounding box */
2021-01-08 10:16:18 +00:00
left = std : : min ( left , RemapCoords ( x + w , y + bb_offset_y , z + bb_offset_z ) . x ) ;
right = std : : max ( right , RemapCoords ( x + bb_offset_x , y + h , z + bb_offset_z ) . x + 1 ) ;
top = std : : min ( top , RemapCoords ( x + bb_offset_x , y + bb_offset_y , z + dz ) . y ) ;
bottom = std : : max ( bottom , RemapCoords ( x + w , y + h , z + bb_offset_z ) . y + 1 ) ;
2007-09-19 16:36:42 +00:00
}
2008-02-14 15:13:36 +00:00
2007-09-26 19:27:29 +00:00
/* Do not add the sprite to the viewport, if it is outside */
2008-04-16 21:06:21 +00:00
if ( left > = _vd . dpi . left + _vd . dpi . width | |
right < = _vd . dpi . left | |
top > = _vd . dpi . top + _vd . dpi . height | |
bottom < = _vd . dpi . top ) {
2006-07-29 13:06:00 +00:00
return ;
}
2020-12-14 23:22:04 +00:00
ParentSpriteToDraw & ps = _vd . parent_sprites_to_draw . emplace_back ( ) ;
2019-02-18 22:39:06 +00:00
ps . x = tmp_x ;
ps . y = tmp_y ;
2008-04-16 20:01:04 +00:00
2019-02-18 22:39:06 +00:00
ps . left = tmp_left ;
ps . top = tmp_top ;
2004-08-09 17:04:08 +00:00
2019-02-18 22:39:06 +00:00
ps . image = image ;
ps . pal = pal ;
ps . sub = sub ;
ps . xmin = x + bb_offset_x ;
2021-01-08 10:16:18 +00:00
ps . xmax = x + std : : max ( bb_offset_x , w ) - 1 ;
2004-08-09 17:04:08 +00:00
2019-02-18 22:39:06 +00:00
ps . ymin = y + bb_offset_y ;
2021-01-08 10:16:18 +00:00
ps . ymax = y + std : : max ( bb_offset_y , h ) - 1 ;
2004-08-09 17:04:08 +00:00
2019-02-18 22:39:06 +00:00
ps . zmin = z + bb_offset_z ;
2021-01-08 10:16:18 +00:00
ps . zmax = z + std : : max ( bb_offset_z , dz ) - 1 ;
2004-08-09 17:04:08 +00:00
2019-02-18 22:39:06 +00:00
ps . first_child = - 1 ;
2004-08-09 17:04:08 +00:00
2019-02-18 22:39:06 +00:00
_vd . last_child = & ps . first_child ;
2004-08-09 17:04:08 +00:00
2009-07-29 20:27:24 +00:00
if ( _vd . combine_sprites = = SPRITE_COMBINE_PENDING ) _vd . combine_sprites = SPRITE_COMBINE_ACTIVE ;
2004-08-09 17:04:08 +00:00
}
2009-07-29 20:27:24 +00:00
/**
* Starts a block of sprites , which are " combined " into a single bounding box .
*
* Subsequent calls to # AddSortableSpriteToDraw will be drawn into the same bounding box .
* That is : The first sprite that is not clipped by the viewport defines the bounding box , and
* the following sprites will be child sprites to that one .
*
* That implies :
* - The drawing order is definite . No other sprites will be sorted between those of the block .
* - You have to provide a valid bounding box for all sprites ,
* as you won ' t know which one is the first non - clipped one .
* Preferable you use the same bounding box for all .
* - You cannot use # AddChildSpriteScreen inside the block , as its result will be indefinite .
*
* The block is terminated by # EndSpriteCombine .
*
* You cannot nest " combined " blocks .
*/
2007-03-07 11:47:46 +00:00
void StartSpriteCombine ( )
2004-08-09 17:04:08 +00:00
{
2009-07-29 20:27:24 +00:00
assert ( _vd . combine_sprites = = SPRITE_COMBINE_NONE ) ;
_vd . combine_sprites = SPRITE_COMBINE_PENDING ;
2004-08-09 17:04:08 +00:00
}
2009-07-29 20:27:24 +00:00
/**
* Terminates a block of sprites started by # StartSpriteCombine .
* Take a look there for details .
*/
2007-03-07 11:47:46 +00:00
void EndSpriteCombine ( )
2004-08-09 17:04:08 +00:00
{
2009-07-29 20:27:24 +00:00
assert ( _vd . combine_sprites ! = SPRITE_COMBINE_NONE ) ;
_vd . combine_sprites = SPRITE_COMBINE_NONE ;
2004-08-09 17:04:08 +00:00
}
2010-12-13 15:09:59 +00:00
/**
* Check if the parameter " check " is inside the interval between
* begin and end , including both begin and end .
* @ note Whether \ c begin or \ c end is the biggest does not matter .
* This method will account for that .
* @ param begin The begin of the interval .
* @ param end The end of the interval .
* @ param check The value to check .
*/
static bool IsInRangeInclusive ( int begin , int end , int check )
{
if ( begin > end ) Swap ( begin , end ) ;
return begin < = check & & check < = end ;
}
/**
* Checks whether a point is inside the selected a diagonal rectangle given by _thd . size and _thd . pos
* @ param x The x coordinate of the point to be checked .
* @ param y The y coordinate of the point to be checked .
* @ return True if the point is inside the rectangle , else false .
*/
bool IsInsideRotatedRectangle ( int x , int y )
{
int dist_a = ( _thd . size . x + _thd . size . y ) ; // Rotated coordinate system for selected rectangle.
int dist_b = ( _thd . size . x - _thd . size . y ) ; // We don't have to divide by 2. It's all relative!
int a = ( ( x - _thd . pos . x ) + ( y - _thd . pos . y ) ) ; // Rotated coordinate system for the point under scrutiny.
int b = ( ( x - _thd . pos . x ) - ( y - _thd . pos . y ) ) ;
/* Check if a and b are between 0 and dist_a or dist_b respectively. */
return IsInRangeInclusive ( dist_a , 0 , a ) & & IsInRangeInclusive ( dist_b , 0 , b ) ;
}
2007-10-05 21:49:15 +00:00
/**
* Add a child sprite to a parent sprite .
*
* @ param image the image to draw .
* @ param pal the provided palette .
* @ param x sprite x - offset ( screen coordinates ) relative to parent sprite .
* @ param y sprite y - offset ( screen coordinates ) relative to parent sprite .
* @ param transparent if true , switch the palette between the provided palette and the transparent palette ,
* @ param sub Only draw a part of the sprite .
2022-12-18 12:34:14 +00:00
* @ param scale if true , scale offsets to base zoom level .
* @ param relative if true , draw sprite relative to parent sprite offsets .
2007-10-05 21:49:15 +00:00
*/
2022-12-18 12:34:14 +00:00
void AddChildSpriteScreen ( SpriteID image , PaletteID pal , int x , int y , bool transparent , const SubSprite * sub , bool scale , bool relative )
2004-08-09 17:04:08 +00:00
{
2005-07-24 15:56:31 +00:00
assert ( ( image & SPRITE_MASK ) < MAX_SPRITES ) ;
2004-08-09 17:04:08 +00:00
2008-04-16 20:39:35 +00:00
/* If the ParentSprite was clipped by the viewport bounds, do not draw the ChildSprites either */
2019-04-10 21:07:06 +00:00
if ( _vd . last_child = = nullptr ) return ;
2008-04-16 20:39:35 +00:00
2007-09-30 19:33:40 +00:00
/* make the sprites transparent with the right palette */
if ( transparent ) {
2007-11-20 13:35:54 +00:00
SetBit ( image , PALETTE_MODIFIER_TRANSPARENT ) ;
2007-09-30 19:33:40 +00:00
pal = PALETTE_TO_TRANSPARENT ;
}
2019-03-27 23:09:33 +00:00
* _vd . last_child = ( uint ) _vd . child_screen_sprites_to_draw . size ( ) ;
2008-06-16 20:08:30 +00:00
2020-12-14 23:22:04 +00:00
ChildScreenSpriteToDraw & cs = _vd . child_screen_sprites_to_draw . emplace_back ( ) ;
2019-02-18 22:39:06 +00:00
cs . image = image ;
cs . pal = pal ;
cs . sub = sub ;
cs . x = scale ? x * ZOOM_LVL_BASE : x ;
cs . y = scale ? y * ZOOM_LVL_BASE : y ;
2022-12-18 12:34:14 +00:00
cs . relative = relative ;
2019-02-18 22:39:06 +00:00
cs . next = - 1 ;
2008-04-16 20:39:35 +00:00
2008-06-16 20:08:30 +00:00
/* Append the sprite to the active ChildSprite list.
* If the active ParentSprite is a foundation , update last_foundation_child as well .
* Note : ChildSprites of foundations are NOT sequential in the vector , as selection sprites are added at last . */
2019-02-18 22:39:06 +00:00
if ( _vd . last_foundation_child [ 0 ] = = _vd . last_child ) _vd . last_foundation_child [ 0 ] = & cs . next ;
if ( _vd . last_foundation_child [ 1 ] = = _vd . last_child ) _vd . last_foundation_child [ 1 ] = & cs . next ;
_vd . last_child = & cs . next ;
2004-08-09 17:04:08 +00:00
}
2009-12-22 12:50:41 +00:00
static void AddStringToDraw ( int x , int y , StringID string , uint64 params_1 , uint64 params_2 , Colours colour , uint16 width )
2004-08-09 17:04:08 +00:00
{
2009-12-22 12:50:41 +00:00
assert ( width ! = 0 ) ;
2020-12-14 23:22:04 +00:00
StringSpriteToDraw & ss = _vd . string_sprites_to_draw . emplace_back ( ) ;
2019-02-18 22:39:06 +00:00
ss . string = string ;
ss . x = x ;
ss . y = y ;
ss . params [ 0 ] = params_1 ;
ss . params [ 1 ] = params_2 ;
ss . width = width ;
ss . colour = colour ;
2004-08-09 17:04:08 +00:00
}
2006-06-10 08:37:41 +00:00
2007-10-14 19:57:15 +00:00
/**
* Draws sprites between ground sprite and everything above .
*
* The sprite is either drawn as TileSprite or as ChildSprite of the active foundation .
*
* @ param image the image to draw .
* @ param pal the provided palette .
* @ param ti TileInfo Tile that is being drawn
* @ param z_offset Z offset relative to the groundsprite . Only used for the sprite position , not for sprite sorting .
2007-10-20 20:25:43 +00:00
* @ param foundation_part Foundation part the sprite belongs to .
2022-12-18 12:35:08 +00:00
* @ param extra_offs_x Pixel X offset for the sprite position .
* @ param extra_offs_y Pixel Y offset for the sprite position .
2007-10-14 19:57:15 +00:00
*/
2022-12-18 12:35:08 +00:00
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 )
2004-08-09 17:04:08 +00:00
{
2010-10-30 17:51:07 +00:00
/* FIXME: This is not totally valid for some autorail highlights that extend over the edges of the tile. */
2008-04-16 21:06:21 +00:00
if ( _vd . foundation [ foundation_part ] = = - 1 ) {
2007-10-14 19:57:15 +00:00
/* draw on real ground */
2022-12-18 12:35:08 +00:00
AddTileSpriteToDraw ( image , pal , ti - > x , ti - > y , ti - > z + z_offset , nullptr , extra_offs_x , extra_offs_y ) ;
2007-10-14 19:57:15 +00:00
} else {
/* draw on top of foundation */
2022-12-18 12:35:08 +00:00
AddChildSpriteToFoundation ( image , pal , nullptr , foundation_part , extra_offs_x , extra_offs_y - z_offset * ZOOM_LVL_BASE ) ;
2005-01-22 09:17:58 +00:00
}
2004-08-09 17:04:08 +00:00
}
2007-10-05 19:57:20 +00:00
/**
* Draws a selection rectangle on a tile .
*
* @ param ti TileInfo Tile that is being drawn
* @ param pal Palette to apply .
*/
2010-01-21 01:38:13 +00:00
static void DrawTileSelectionRect ( const TileInfo * ti , PaletteID pal )
2007-10-05 19:57:20 +00:00
{
2009-01-19 13:50:56 +00:00
if ( ! IsValidTile ( ti - > tile ) ) return ;
2007-10-20 20:25:43 +00:00
SpriteID sel ;
if ( IsHalftileSlope ( ti - > tileh ) ) {
Corner halftile_corner = GetHalftileSlopeCorner ( ti - > tileh ) ;
SpriteID sel2 = SPR_HALFTILE_SELECTION_FLAT + halftile_corner ;
DrawSelectionSprite ( sel2 , pal , ti , 7 + TILE_HEIGHT , FOUNDATION_PART_HALFTILE ) ;
Corner opposite_corner = OppositeCorner ( halftile_corner ) ;
if ( IsSteepSlope ( ti - > tileh ) ) {
sel = SPR_HALFTILE_SELECTION_DOWN ;
} else {
sel = ( ( ti - > tileh & SlopeWithOneCornerRaised ( opposite_corner ) ) ! = 0 ? SPR_HALFTILE_SELECTION_UP : SPR_HALFTILE_SELECTION_FLAT ) ;
}
sel + = opposite_corner ;
} else {
2010-07-19 17:00:54 +00:00
sel = SPR_SELECT_TILE + SlopeToSpriteOffset ( ti - > tileh ) ;
2007-10-20 20:25:43 +00:00
}
DrawSelectionSprite ( sel , pal , ti , 7 , FOUNDATION_PART_NORMAL ) ;
2007-10-05 19:57:20 +00:00
}
2004-08-09 17:04:08 +00:00
static bool IsPartOfAutoLine ( int px , int py )
{
2005-05-27 15:05:54 +00:00
px - = _thd . selstart . x ;
py - = _thd . selstart . y ;
2004-08-09 17:04:08 +00:00
2010-12-24 14:55:31 +00:00
if ( ( _thd . drawstyle & HT_DRAG_MASK ) ! = HT_LINE ) return false ;
2004-09-10 19:02:27 +00:00
2008-01-09 09:45:45 +00:00
switch ( _thd . drawstyle & HT_DIR_MASK ) {
case HT_DIR_X : return py = = 0 ; // x direction
case HT_DIR_Y : return px = = 0 ; // y direction
case HT_DIR_HU : return px = = - py | | px = = - py - 16 ; // horizontal upper
case HT_DIR_HL : return px = = - py | | px = = - py + 16 ; // horizontal lower
2010-05-30 13:05:36 +00:00
case HT_DIR_VL : return px = = py | | px = = py + 16 ; // vertical left
2008-01-09 09:45:45 +00:00
case HT_DIR_VR : return px = = py | | px = = py - 16 ; // vertical right
default :
NOT_REACHED ( ) ;
}
2004-08-09 17:04:08 +00:00
}
2009-03-15 00:32:18 +00:00
/* [direction][side] */
2008-01-09 09:45:45 +00:00
static const HighLightStyle _autorail_type [ 6 ] [ 2 ] = {
2005-01-19 20:55:23 +00:00
{ HT_DIR_X , HT_DIR_X } ,
{ HT_DIR_Y , HT_DIR_Y } ,
{ HT_DIR_HU , HT_DIR_HL } ,
{ HT_DIR_HL , HT_DIR_HU } ,
{ HT_DIR_VL , HT_DIR_VR } ,
{ HT_DIR_VR , HT_DIR_VL }
} ;
# include "table/autorail.h"
2007-06-26 16:58:40 +00:00
/**
2007-10-05 19:57:20 +00:00
* Draws autorail highlights .
*
2007-06-26 16:58:40 +00:00
* @ param * ti TileInfo Tile that is being drawn
2007-10-05 19:57:20 +00:00
* @ param autorail_type Offset into _AutorailTilehSprite [ ] [ ]
2007-06-26 16:58:40 +00:00
*/
2007-10-05 19:57:20 +00:00
static void DrawAutorailSelection ( const TileInfo * ti , uint autorail_type )
2004-08-09 17:04:08 +00:00
{
2007-01-14 19:57:49 +00:00
SpriteID image ;
2010-01-21 01:38:13 +00:00
PaletteID pal ;
2007-10-05 19:57:20 +00:00
int offset ;
2007-10-20 20:25:43 +00:00
FoundationPart foundation_part = FOUNDATION_PART_NORMAL ;
2008-01-25 15:47:58 +00:00
Slope autorail_tileh = RemoveHalftileSlope ( ti - > tileh ) ;
2007-10-20 20:25:43 +00:00
if ( IsHalftileSlope ( ti - > tileh ) ) {
static const uint _lower_rail [ 4 ] = { 5U , 2U , 4U , 3U } ;
Corner halftile_corner = GetHalftileSlopeCorner ( ti - > tileh ) ;
if ( autorail_type ! = _lower_rail [ halftile_corner ] ) {
foundation_part = FOUNDATION_PART_HALFTILE ;
/* Here we draw the highlights of the "three-corners-raised"-slope. That looks ok to me. */
autorail_tileh = SlopeWithThreeCornersRaised ( OppositeCorner ( halftile_corner ) ) ;
}
}
offset = _AutorailTilehSprite [ autorail_tileh ] [ autorail_type ] ;
2007-10-05 19:57:20 +00:00
if ( offset > = 0 ) {
image = SPR_AUTORAIL_BASE + offset ;
pal = PAL_NONE ;
} else {
image = SPR_AUTORAIL_BASE - offset ;
pal = PALETTE_SEL_TILE_RED ;
}
2007-10-20 20:25:43 +00:00
DrawSelectionSprite ( image , _thd . make_square_red ? PALETTE_SEL_TILE_RED : pal , ti , 7 , foundation_part ) ;
2007-10-05 19:57:20 +00:00
}
2004-08-09 17:04:08 +00:00
2019-02-22 18:14:06 +00:00
enum TileHighlightType {
THT_NONE ,
THT_WHITE ,
THT_BLUE ,
2019-03-30 16:59:43 +00:00
THT_RED ,
2019-02-22 18:14:06 +00:00
} ;
const Station * _viewport_highlight_station ; ///< Currently selected station for coverage area highlight
2019-03-30 16:59:43 +00:00
const Town * _viewport_highlight_town ; ///< Currently selected town for coverage area highlight
2019-02-22 18:14:06 +00:00
/**
* Get tile highlight type of coverage area for a given tile .
* @ param t Tile that is being drawn
* @ return Tile highlight type to draw
*/
static TileHighlightType GetTileHighlightType ( TileIndex t )
{
if ( _viewport_highlight_station ! = nullptr ) {
if ( IsTileType ( t , MP_STATION ) & & GetStationIndex ( t ) = = _viewport_highlight_station - > index ) return THT_WHITE ;
if ( _viewport_highlight_station - > TileIsInCatchment ( t ) ) return THT_BLUE ;
}
2019-03-30 16:59:43 +00:00
if ( _viewport_highlight_town ! = nullptr ) {
if ( IsTileType ( t , MP_HOUSE ) ) {
if ( GetTownIndex ( t ) = = _viewport_highlight_town - > index ) {
TileHighlightType type = THT_RED ;
for ( const Station * st : _viewport_highlight_town - > stations_near ) {
if ( st - > owner ! = _current_company ) continue ;
if ( st - > TileIsInCatchment ( t ) ) return THT_BLUE ;
}
return type ;
}
} else if ( IsTileType ( t , MP_STATION ) ) {
for ( const Station * st : _viewport_highlight_town - > stations_near ) {
if ( st - > owner ! = _current_company ) continue ;
if ( GetStationIndex ( t ) = = st - > index ) return THT_WHITE ;
}
}
}
2019-02-22 18:14:06 +00:00
return THT_NONE ;
}
/**
* Draw tile highlight for coverage area highlight .
* @ param * ti TileInfo Tile that is being drawn
* @ param tht Highlight type to draw .
*/
static void DrawTileHighlightType ( const TileInfo * ti , TileHighlightType tht )
{
switch ( tht ) {
default :
case THT_NONE : break ;
case THT_WHITE : DrawTileSelectionRect ( ti , PAL_NONE ) ; break ;
case THT_BLUE : DrawTileSelectionRect ( ti , PALETTE_SEL_TILE_BLUE ) ; break ;
2021-02-07 15:20:26 +00:00
case THT_RED : DrawTileSelectionRect ( ti , PALETTE_SEL_TILE_RED ) ; break ;
2019-02-22 18:14:06 +00:00
}
}
2019-01-05 13:22:07 +00:00
/**
* Highlights tiles insede local authority of selected towns .
* @ param * ti TileInfo Tile that is being drawn
*/
static void HighlightTownLocalAuthorityTiles ( const TileInfo * ti )
{
/* Going through cases in order of computational time. */
if ( _town_local_authority_kdtree . Count ( ) = = 0 ) return ;
/* Tile belongs to town regardless of distance from town. */
if ( GetTileType ( ti - > tile ) = = MP_HOUSE ) {
if ( ! Town : : GetByTile ( ti - > tile ) - > show_zone ) return ;
DrawTileSelectionRect ( ti , PALETTE_CRASH ) ;
return ;
}
/* If the closest town in the highlighted list is far, we can stop searching. */
TownID tid = _town_local_authority_kdtree . FindNearest ( TileX ( ti - > tile ) , TileY ( ti - > tile ) ) ;
Town * closest_highlighted_town = Town : : Get ( tid ) ;
if ( DistanceManhattan ( ti - > tile , closest_highlighted_town - > xy ) > = _settings_game . economy . dist_local_authority ) return ;
/* Tile is inside of the local autrhority distance of a highlighted town,
but it is possible that a non - highlighted town is even closer . */
Town * closest_town = ClosestTownFromTile ( ti - > tile , _settings_game . economy . dist_local_authority ) ;
if ( closest_town - > show_zone ) {
DrawTileSelectionRect ( ti , PALETTE_CRASH ) ;
}
}
2007-10-05 19:57:20 +00:00
/**
* Checks if the specified tile is selected and if so draws selection using correct selectionstyle .
* @ param * ti TileInfo Tile that is being drawn
*/
static void DrawTileSelection ( const TileInfo * ti )
{
2019-01-05 13:22:07 +00:00
/* Highlight tiles insede local authority of selected towns. */
HighlightTownLocalAuthorityTiles ( ti ) ;
2007-04-04 04:08:47 +00:00
/* Draw a red error square? */
2009-01-19 15:06:11 +00:00
bool is_redsq = _thd . redsq = = ti - > tile ;
2008-03-27 14:10:09 +00:00
if ( is_redsq ) DrawTileSelectionRect ( ti , PALETTE_TILE_RED_PULSATING ) ;
2004-08-09 17:04:08 +00:00
2019-02-22 18:14:06 +00:00
TileHighlightType tht = GetTileHighlightType ( ti - > tile ) ;
DrawTileHighlightType ( ti , tht ) ;
2010-12-24 14:55:31 +00:00
/* No tile selection active? */
if ( ( _thd . drawstyle & HT_DRAG_MASK ) = = HT_NONE ) return ;
2004-08-09 17:04:08 +00:00
2010-12-13 15:09:59 +00:00
if ( _thd . diagonal ) { // We're drawing a 45 degrees rotated (diagonal) rectangle
2010-12-27 18:21:19 +00:00
if ( IsInsideRotatedRectangle ( ( int ) ti - > x , ( int ) ti - > y ) ) goto draw_inner ;
2010-12-13 15:09:59 +00:00
return ;
}
2007-04-04 04:08:47 +00:00
/* Inside the inner area? */
2007-11-24 10:38:43 +00:00
if ( IsInsideBS ( ti - > x , _thd . pos . x , _thd . size . x ) & &
IsInsideBS ( ti - > y , _thd . pos . y , _thd . size . y ) ) {
2010-12-27 18:21:19 +00:00
draw_inner :
2005-05-27 15:05:54 +00:00
if ( _thd . drawstyle & HT_RECT ) {
2008-03-27 14:10:09 +00:00
if ( ! is_redsq ) DrawTileSelectionRect ( ti , _thd . make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE ) ;
2005-05-27 15:05:54 +00:00
} else if ( _thd . drawstyle & HT_POINT ) {
2007-04-04 04:08:47 +00:00
/* Figure out the Z coordinate for the single dot. */
2011-11-04 13:40:59 +00:00
int z = 0 ;
2007-10-20 20:25:43 +00:00
FoundationPart foundation_part = FOUNDATION_PART_NORMAL ;
2006-04-23 13:48:16 +00:00
if ( ti - > tileh & SLOPE_N ) {
2006-04-23 19:35:36 +00:00
z + = TILE_HEIGHT ;
2008-01-25 15:47:58 +00:00
if ( RemoveHalftileSlope ( ti - > tileh ) = = SLOPE_STEEP_N ) z + = TILE_HEIGHT ;
2007-10-20 20:25:43 +00:00
}
if ( IsHalftileSlope ( ti - > tileh ) ) {
Corner halftile_corner = GetHalftileSlopeCorner ( ti - > tileh ) ;
if ( ( halftile_corner = = CORNER_W ) | | ( halftile_corner = = CORNER_E ) ) z + = TILE_HEIGHT ;
if ( halftile_corner ! = CORNER_S ) {
foundation_part = FOUNDATION_PART_HALFTILE ;
if ( IsSteepSlope ( ti - > tileh ) ) z - = TILE_HEIGHT ;
}
2004-08-09 17:04:08 +00:00
}
2007-10-20 20:25:43 +00:00
DrawSelectionSprite ( _cur_dpi - > zoom < = ZOOM_LVL_DETAIL ? SPR_DOT : SPR_DOT_SMALL , PAL_NONE , ti , z , foundation_part ) ;
2009-04-19 21:26:06 +00:00
} else if ( _thd . drawstyle & HT_RAIL ) {
2007-04-04 04:08:47 +00:00
/* autorail highlight piece under cursor */
2009-04-18 21:10:36 +00:00
HighLightStyle type = _thd . drawstyle & HT_DIR_MASK ;
assert ( type < HT_DIR_END ) ;
2008-01-09 09:45:45 +00:00
DrawAutorailSelection ( ti , _autorail_type [ type ] [ 0 ] ) ;
2005-07-17 20:14:58 +00:00
} else if ( IsPartOfAutoLine ( ti - > x , ti - > y ) ) {
2007-04-04 04:08:47 +00:00
/* autorail highlighting long line */
2009-04-18 21:10:36 +00:00
HighLightStyle dir = _thd . drawstyle & HT_DIR_MASK ;
2005-07-17 20:14:58 +00:00
uint side ;
2005-01-23 13:09:35 +00:00
2009-04-18 21:10:36 +00:00
if ( dir = = HT_DIR_X | | dir = = HT_DIR_Y ) {
2005-07-17 20:14:58 +00:00
side = 0 ;
} else {
TileIndex start = TileVirtXY ( _thd . selstart . x , _thd . selstart . y ) ;
2007-11-26 16:01:29 +00:00
side = Delta ( Delta ( TileX ( start ) , TileX ( ti - > tile ) ) , Delta ( TileY ( start ) , TileY ( ti - > tile ) ) ) ;
2005-07-17 20:14:58 +00:00
}
2005-01-19 20:55:23 +00:00
2008-01-09 09:45:45 +00:00
DrawAutorailSelection ( ti , _autorail_type [ dir ] [ side ] ) ;
2005-07-17 20:14:58 +00:00
}
2004-08-09 17:04:08 +00:00
return ;
}
2007-04-04 04:08:47 +00:00
/* Check if it's inside the outer area? */
2019-03-30 16:59:43 +00:00
if ( ! is_redsq & & ( tht = = THT_NONE | | tht = = THT_RED ) & & _thd . outersize . x > 0 & &
2007-11-24 10:38:43 +00:00
IsInsideBS ( ti - > x , _thd . pos . x + _thd . offs . x , _thd . size . x + _thd . outersize . x ) & &
IsInsideBS ( ti - > y , _thd . pos . y + _thd . offs . y , _thd . size . y + _thd . outersize . y ) ) {
2007-04-04 04:08:47 +00:00
/* Draw a blue rect. */
2007-10-05 19:57:20 +00:00
DrawTileSelectionRect ( ti , PALETTE_SEL_TILE_BLUE ) ;
2004-08-09 17:04:08 +00:00
return ;
}
}
2014-09-21 17:27:37 +00:00
/**
2019-02-03 19:15:10 +00:00
* Returns the y coordinate in the viewport coordinate system where the given
* tile is painted .
* @ param tile Any tile .
* @ return The viewport y coordinate where the tile is painted .
2014-09-21 17:27:37 +00:00
*/
2019-02-03 19:15:10 +00:00
static int GetViewportY ( Point tile )
2014-09-21 17:27:37 +00:00
{
2019-02-03 19:15:10 +00:00
/* Each increment in X or Y direction moves down by half a tile, i.e. TILE_PIXELS / 2. */
return ( tile . y * ( int ) ( TILE_PIXELS / 2 ) + tile . x * ( int ) ( TILE_PIXELS / 2 ) - TilePixelHeightOutsideMap ( tile . x , tile . y ) ) < < ZOOM_LVL_SHIFT ;
2019-01-13 20:00:40 +00:00
}
2014-09-21 17:27:37 +00:00
2019-01-13 20:00:40 +00:00
/**
2019-02-03 19:15:10 +00:00
* Add the landscape to the viewport , i . e . all ground tiles and buildings .
2019-01-13 20:00:40 +00:00
*/
static void ViewportAddLandscape ( )
{
2019-02-03 19:15:10 +00:00
assert ( _vd . dpi . top < = _vd . dpi . top + _vd . dpi . height ) ;
assert ( _vd . dpi . left < = _vd . dpi . left + _vd . dpi . width ) ;
Point upper_left = InverseRemapCoords ( _vd . dpi . left , _vd . dpi . top ) ;
Point upper_right = InverseRemapCoords ( _vd . dpi . left + _vd . dpi . width , _vd . dpi . top ) ;
/* Transformations between tile coordinates and viewport rows/columns: See vp_column_row
2015-02-22 14:42:34 +00:00
* column = y - x
* row = x + y
* x = ( row - column ) / 2
* y = ( row + column ) / 2
* Note : ( row , columns ) pairs are only valid , if they are both even or both odd .
*/
2014-09-21 17:27:37 +00:00
2019-02-03 19:15:10 +00:00
/* Columns overlap with neighbouring columns by a half tile.
* - Left column is column of upper_left ( rounded down ) and one column to the left .
* - Right column is column of upper_right ( rounded up ) and one column to the right .
* Note : Integer - division does not round down for negative numbers , so ensure rounding with another increment / decrement .
*/
int left_column = ( upper_left . y - upper_left . x ) / ( int ) TILE_SIZE - 2 ;
int right_column = ( upper_right . y - upper_right . x ) / ( int ) TILE_SIZE + 2 ;
int potential_bridge_height = ZOOM_LVL_BASE * TILE_HEIGHT * _settings_game . construction . max_bridge_height ;
/* Rows overlap with neighbouring rows by a half tile.
* The first row that could possibly be visible is the row above upper_left ( if it is at height 0 ) .
* Due to integer - division not rounding down for negative numbers , we need another decrement .
*/
int row = ( upper_left . x + upper_left . y ) / ( int ) TILE_SIZE - 2 ;
bool last_row = false ;
for ( ; ! last_row ; row + + ) {
last_row = true ;
for ( int column = left_column ; column < = right_column ; column + + ) {
/* Valid row/column? */
if ( ( row + column ) % 2 ! = 0 ) continue ;
Point tilecoord ;
tilecoord . x = ( row - column ) / 2 ;
tilecoord . y = ( row + column ) / 2 ;
assert ( column = = tilecoord . y - tilecoord . x ) ;
assert ( row = = tilecoord . y + tilecoord . x ) ;
2015-02-22 14:42:34 +00:00
TileType tile_type ;
2023-01-28 09:53:42 +00:00
_cur_ti . x = tilecoord . x * TILE_SIZE ;
_cur_ti . y = tilecoord . y * TILE_SIZE ;
2015-02-22 14:42:34 +00:00
2023-01-21 09:43:03 +00:00
if ( IsInsideBS ( tilecoord . x , 0 , Map : : SizeX ( ) ) & & IsInsideBS ( tilecoord . y , 0 , Map : : SizeY ( ) ) ) {
/* This includes the south border at Map::MaxX / Map::MaxY. When terraforming we still draw tile selections there. */
2023-01-28 09:53:42 +00:00
_cur_ti . tile = TileXY ( tilecoord . x , tilecoord . y ) ;
tile_type = GetTileType ( _cur_ti . tile ) ;
2015-02-22 14:42:34 +00:00
} else {
2023-01-28 09:53:42 +00:00
_cur_ti . tile = INVALID_TILE ;
2015-02-22 14:42:34 +00:00
tile_type = MP_VOID ;
2006-08-07 17:32:29 +00:00
}
2009-01-21 02:31:55 +00:00
2019-02-03 19:15:10 +00:00
if ( tile_type ! = MP_VOID ) {
/* We are inside the map => paint landscape. */
2023-01-28 09:53:42 +00:00
_cur_ti . tileh = GetTilePixelSlope ( _cur_ti . tile , & _cur_ti . z ) ;
2019-02-03 19:15:10 +00:00
} else {
/* We are outside the map => paint black. */
2023-01-28 09:53:42 +00:00
_cur_ti . tileh = GetTilePixelSlopeOutsideMap ( tilecoord . x , tilecoord . y , & _cur_ti . z ) ;
2015-02-22 14:42:34 +00:00
}
2014-09-21 17:27:37 +00:00
2019-02-03 19:15:10 +00:00
int viewport_y = GetViewportY ( tilecoord ) ;
if ( viewport_y + MAX_TILE_EXTENT_BOTTOM < _vd . dpi . top ) {
/* The tile in this column is not visible yet.
* Tiles in other columns may be visible , but we need more rows in any case . */
last_row = false ;
continue ;
}
2014-09-21 17:27:37 +00:00
2019-02-03 19:15:10 +00:00
int min_visible_height = viewport_y - ( _vd . dpi . top + _vd . dpi . height ) ;
bool tile_visible = min_visible_height < = 0 ;
if ( tile_type ! = MP_VOID ) {
/* Is tile with buildings visible? */
if ( min_visible_height < MAX_TILE_EXTENT_TOP ) tile_visible = true ;
2023-01-28 09:53:42 +00:00
if ( IsBridgeAbove ( _cur_ti . tile ) ) {
2019-02-03 19:15:10 +00:00
/* Is the bridge visible? */
2023-01-28 09:53:42 +00:00
TileIndex bridge_tile = GetNorthernBridgeEnd ( _cur_ti . tile ) ;
int bridge_height = ZOOM_LVL_BASE * ( GetBridgePixelHeight ( bridge_tile ) - TilePixelHeight ( _cur_ti . tile ) ) ;
2019-02-03 19:15:10 +00:00
if ( min_visible_height < bridge_height + MAX_TILE_EXTENT_TOP ) tile_visible = true ;
}
2019-01-13 20:00:40 +00:00
2019-02-03 19:15:10 +00:00
/* Would a higher bridge on a more southern tile be visible?
* If yes , we need to loop over more rows to possibly find one . */
if ( min_visible_height < potential_bridge_height + MAX_TILE_EXTENT_TOP ) last_row = false ;
} else {
/* Outside of map. If we are on the north border of the map, there may still be a bridge visible,
* so we need to loop over more rows to possibly find one . */
if ( ( tilecoord . x < = 0 | | tilecoord . y < = 0 ) & & min_visible_height < potential_bridge_height + MAX_TILE_EXTENT_TOP ) last_row = false ;
}
if ( tile_visible ) {
last_row = false ;
_vd . foundation_part = FOUNDATION_PART_NONE ;
_vd . foundation [ 0 ] = - 1 ;
_vd . foundation [ 1 ] = - 1 ;
2019-04-10 21:07:06 +00:00
_vd . last_foundation_child [ 0 ] = nullptr ;
_vd . last_foundation_child [ 1 ] = nullptr ;
2019-02-03 19:15:10 +00:00
2023-01-28 09:53:42 +00:00
_tile_type_procs [ tile_type ] - > draw_tile_proc ( & _cur_ti ) ;
if ( _cur_ti . tile ! = INVALID_TILE ) DrawTileSelection ( & _cur_ti ) ;
2019-02-03 19:15:10 +00:00
}
}
2014-09-21 17:27:37 +00:00
}
2004-08-09 17:04:08 +00:00
}
2009-11-22 19:53:49 +00:00
/**
* Add a string to draw in the viewport
* @ param dpi current viewport area
2009-12-22 12:50:41 +00:00
* @ param small_from Zoomlevel from when the small font should be used
2009-11-22 19:53:49 +00:00
* @ param sign sign position and dimension
* @ param string_normal String for normal and 2 x zoom level
* @ param string_small String for 4 x and 8 x zoom level
2010-05-30 13:05:36 +00:00
* @ param string_small_shadow Shadow string for 4 x and 8 x zoom level ; or # STR_NULL if no shadow
2011-12-19 20:59:29 +00:00
* @ param colour colour of the sign background ; or INVALID_COLOUR if transparent
2009-11-22 19:53:49 +00:00
*/
2009-12-22 12:50:41 +00:00
void ViewportAddString ( const DrawPixelInfo * dpi , ZoomLevel small_from , const ViewportSign * sign , StringID string_normal , StringID string_small , StringID string_small_shadow , uint64 params_1 , uint64 params_2 , Colours colour )
2004-08-09 17:04:08 +00:00
{
2009-12-22 12:50:41 +00:00
bool small = dpi - > zoom > = small_from ;
int left = dpi - > left ;
int top = dpi - > top ;
int right = left + dpi - > width ;
2009-11-22 19:53:49 +00:00
int bottom = top + dpi - > height ;
2004-08-09 17:04:08 +00:00
2022-12-21 22:21:50 +00:00
int sign_height = ScaleByZoom ( WidgetDimensions : : scaled . fullbevel . top + FONT_HEIGHT_NORMAL + WidgetDimensions : : scaled . fullbevel . bottom , dpi - > zoom ) ;
2009-12-22 12:50:41 +00:00
int sign_half_width = ScaleByZoom ( ( small ? sign - > width_small : sign - > width_normal ) / 2 , dpi - > zoom ) ;
2006-11-05 12:22:46 +00:00
2009-12-23 17:59:34 +00:00
if ( bottom < sign - > top | |
top > sign - > top + sign_height | |
right < sign - > center - sign_half_width | |
left > sign - > center + sign_half_width ) {
2009-12-22 12:50:41 +00:00
return ;
}
2007-05-19 22:48:04 +00:00
2009-12-22 12:50:41 +00:00
if ( ! small ) {
AddStringToDraw ( sign - > center - sign_half_width , sign - > top , string_normal , params_1 , params_2 , colour , sign - > width_normal ) ;
} else {
int shadow_offset = 0 ;
if ( string_small_shadow ! = STR_NULL ) {
shadow_offset = 4 ;
2022-12-25 13:29:38 +00:00
AddStringToDraw ( sign - > center - sign_half_width + shadow_offset , sign - > top , string_small_shadow , params_1 , params_2 , INVALID_COLOUR , sign - > width_small | 0x8000 ) ;
2009-12-22 12:50:41 +00:00
}
AddStringToDraw ( sign - > center - sign_half_width , sign - > top - shadow_offset , string_small , params_1 , params_2 ,
colour , sign - > width_small | 0x8000 ) ;
2004-08-09 17:04:08 +00:00
}
}
2019-02-22 16:57:28 +00:00
static Rect ExpandRectWithViewportSignMargins ( Rect r , ZoomLevel zoom )
2006-11-05 11:17:33 +00:00
{
2019-02-22 16:57:28 +00:00
/* Pessimistically always use normal font, but also assume small font is never larger in either dimension */
const int fh = FONT_HEIGHT_NORMAL ;
const int max_tw = _viewport_sign_maxwidth / 2 + 1 ;
2022-12-21 22:21:50 +00:00
const int expand_y = ScaleByZoom ( WidgetDimensions : : scaled . fullbevel . top + fh + WidgetDimensions : : scaled . fullbevel . bottom , zoom ) ;
const int expand_x = ScaleByZoom ( WidgetDimensions : : scaled . fullbevel . left + max_tw + WidgetDimensions : : scaled . fullbevel . right , zoom ) ;
2009-11-22 19:53:49 +00:00
2019-02-22 16:57:28 +00:00
r . left - = expand_x ;
r . right + = expand_x ;
r . top - = expand_y ;
r . bottom + = expand_y ;
2006-11-05 11:17:33 +00:00
2019-02-22 16:57:28 +00:00
return r ;
}
2006-11-05 11:17:33 +00:00
2019-02-22 16:57:28 +00:00
static void ViewportAddKdtreeSigns ( DrawPixelInfo * dpi )
2004-08-09 17:04:08 +00:00
{
2019-02-22 16:57:28 +00:00
Rect search_rect { dpi - > left , dpi - > top , dpi - > left + dpi - > width , dpi - > top + dpi - > height } ;
search_rect = ExpandRectWithViewportSignMargins ( search_rect , dpi - > zoom ) ;
bool show_stations = HasBit ( _display_opt , DO_SHOW_STATION_NAMES ) & & _game_mode ! = GM_MENU ;
bool show_waypoints = HasBit ( _display_opt , DO_SHOW_WAYPOINT_NAMES ) & & _game_mode ! = GM_MENU ;
bool show_towns = HasBit ( _display_opt , DO_SHOW_TOWN_NAMES ) & & _game_mode ! = GM_MENU ;
bool show_signs = HasBit ( _display_opt , DO_SHOW_SIGNS ) & & ! IsInvisibilitySet ( TO_SIGNS ) ;
bool show_competitors = HasBit ( _display_opt , DO_SHOW_COMPETITOR_SIGNS ) ;
2004-08-09 17:04:08 +00:00
2019-02-22 16:57:28 +00:00
/* Collect all the items first and draw afterwards, to ensure layering */
std : : vector < const BaseStation * > stations ;
std : : vector < const Town * > towns ;
std : : vector < const Sign * > signs ;
2007-05-19 22:48:04 +00:00
2019-02-22 16:57:28 +00:00
_viewport_sign_kdtree . FindContained ( search_rect . left , search_rect . top , search_rect . right , search_rect . bottom , [ & ] ( const ViewportSignKdtreeItem & item ) {
switch ( item . type ) {
2023-01-28 19:06:51 +00:00
case ViewportSignKdtreeItem : : VKI_STATION : {
2019-02-22 16:57:28 +00:00
if ( ! show_stations ) break ;
2023-01-28 19:06:51 +00:00
const BaseStation * st = BaseStation : : Get ( item . id . station ) ;
2011-08-01 18:41:21 +00:00
2019-02-22 16:57:28 +00:00
/* Don't draw if station is owned by another company and competitor station names are hidden. Stations owned by none are never ignored. */
if ( ! show_competitors & & _local_company ! = st - > owner & & st - > owner ! = OWNER_NONE ) break ;
2004-08-09 17:04:08 +00:00
2019-02-22 16:57:28 +00:00
stations . push_back ( st ) ;
break ;
2023-01-28 19:06:51 +00:00
}
2006-11-05 11:17:33 +00:00
2023-01-28 19:06:51 +00:00
case ViewportSignKdtreeItem : : VKI_WAYPOINT : {
2019-02-22 16:57:28 +00:00
if ( ! show_waypoints ) break ;
2023-01-28 19:06:51 +00:00
const BaseStation * st = BaseStation : : Get ( item . id . station ) ;
2004-08-09 17:04:08 +00:00
2019-02-22 16:57:28 +00:00
/* Don't draw if station is owned by another company and competitor station names are hidden. Stations owned by none are never ignored. */
if ( ! show_competitors & & _local_company ! = st - > owner & & st - > owner ! = OWNER_NONE ) break ;
stations . push_back ( st ) ;
break ;
2023-01-28 19:06:51 +00:00
}
2019-02-22 16:57:28 +00:00
case ViewportSignKdtreeItem : : VKI_TOWN :
if ( ! show_towns ) break ;
towns . push_back ( Town : : Get ( item . id . town ) ) ;
break ;
2023-01-28 19:06:51 +00:00
case ViewportSignKdtreeItem : : VKI_SIGN : {
2019-02-22 16:57:28 +00:00
if ( ! show_signs ) break ;
2023-01-28 19:06:51 +00:00
const Sign * si = Sign : : Get ( item . id . sign ) ;
2019-02-22 16:57:28 +00:00
/* Don't draw if sign is owned by another company and competitor signs should be hidden.
* Note : It is intentional that also signs owned by OWNER_NONE are hidden . Bankrupt
* companies can leave OWNER_NONE signs after them . */
if ( ! show_competitors & & _local_company ! = si - > owner & & si - > owner ! = OWNER_DEITY ) break ;
signs . push_back ( si ) ;
break ;
2023-01-28 19:06:51 +00:00
}
2019-02-22 16:57:28 +00:00
default :
NOT_REACHED ( ) ;
}
} ) ;
/* Layering order (bottom to top): Town names, signs, stations */
for ( const auto * t : towns ) {
ViewportAddString ( dpi , ZOOM_LVL_OUT_16X , & t - > cache . sign ,
_settings_client . gui . population_in_label ? STR_VIEWPORT_TOWN_POP : STR_VIEWPORT_TOWN ,
STR_VIEWPORT_TOWN_TINY_WHITE , STR_VIEWPORT_TOWN_TINY_BLACK ,
t - > index , t - > cache . population ) ;
}
2011-08-01 18:41:21 +00:00
2019-02-22 16:57:28 +00:00
for ( const auto * si : signs ) {
2011-11-24 12:38:48 +00:00
ViewportAddString ( dpi , ZOOM_LVL_OUT_16X , & si - > sign ,
2019-02-22 16:57:28 +00:00
STR_WHITE_SIGN ,
( IsTransparencySet ( TO_SIGNS ) | | si - > owner = = OWNER_DEITY ) ? STR_VIEWPORT_SIGN_SMALL_WHITE : STR_VIEWPORT_SIGN_SMALL_BLACK , STR_NULL ,
si - > index , 0 , ( si - > owner = = OWNER_NONE ) ? COLOUR_GREY : ( si - > owner = = OWNER_DEITY ? INVALID_COLOUR : _company_colours [ si - > owner ] ) ) ;
}
for ( const auto * st : stations ) {
if ( Station : : IsExpected ( st ) ) {
/* Station */
ViewportAddString ( dpi , ZOOM_LVL_OUT_16X , & st - > sign ,
2021-08-28 15:08:01 +00:00
STR_VIEWPORT_STATION , STR_VIEWPORT_STATION_TINY , STR_NULL ,
2019-02-22 16:57:28 +00:00
st - > index , st - > facilities , ( st - > owner = = OWNER_NONE | | ! st - > IsInUse ( ) ) ? COLOUR_GREY : _company_colours [ st - > owner ] ) ;
} else {
/* Waypoint */
ViewportAddString ( dpi , ZOOM_LVL_OUT_16X , & st - > sign ,
2021-08-28 15:08:01 +00:00
STR_VIEWPORT_WAYPOINT , STR_VIEWPORT_WAYPOINT_TINY , STR_NULL ,
2019-02-22 16:57:28 +00:00
st - > index , st - > facilities , ( st - > owner = = OWNER_NONE | | ! st - > IsInUse ( ) ) ? COLOUR_GREY : _company_colours [ st - > owner ] ) ;
}
2004-08-09 17:04:08 +00:00
}
}
2019-02-22 16:57:28 +00:00
2009-07-07 16:51:20 +00:00
/**
* Update the position of the viewport sign .
* @ param center the ( preferred ) center of the viewport sign
* @ param top the new top of the sign
* @ param str the string to show in the sign
2015-04-25 11:58:19 +00:00
* @ param str_small the string to show when zoomed out . STR_NULL means same as \ a str
2009-07-07 16:51:20 +00:00
*/
2015-04-25 11:58:19 +00:00
void ViewportSign : : UpdatePosition ( int center , int top , StringID str , StringID str_small )
2004-08-09 17:04:08 +00:00
{
2009-07-13 23:15:13 +00:00
if ( this - > width_normal ! = 0 ) this - > MarkDirty ( ) ;
2009-07-07 16:51:20 +00:00
this - > top = top ;
2004-08-09 17:04:08 +00:00
2023-05-19 12:35:53 +00:00
std : : string name = GetString ( str ) ;
this - > width_normal = WidgetDimensions : : scaled . fullbevel . left + Align ( GetStringBoundingBox ( name ) . width , 2 ) + WidgetDimensions : : scaled . fullbevel . right ;
2009-12-22 12:50:41 +00:00
this - > center = center ;
2004-08-09 17:04:08 +00:00
2006-09-16 13:20:14 +00:00
/* zoomed out version */
2015-04-25 11:58:19 +00:00
if ( str_small ! = STR_NULL ) {
2023-05-19 12:35:53 +00:00
name = GetString ( str_small ) ;
2015-04-25 11:58:19 +00:00
}
2023-05-19 12:35:53 +00:00
this - > width_small = WidgetDimensions : : scaled . fullbevel . left + Align ( GetStringBoundingBox ( name , FS_SMALL ) . width , 2 ) + WidgetDimensions : : scaled . fullbevel . right ;
2009-07-13 23:15:13 +00:00
this - > MarkDirty ( ) ;
2004-08-09 17:04:08 +00:00
}
2009-07-08 08:30:35 +00:00
/**
* Mark the sign dirty in all viewports .
2012-03-25 19:30:05 +00:00
* @ param maxzoom Maximum % ZoomLevel at which the text is visible .
2009-07-08 08:30:35 +00:00
*
* @ ingroup dirty
*/
2012-03-25 19:30:05 +00:00
void ViewportSign : : MarkDirty ( ZoomLevel maxzoom ) const
2009-07-08 08:30:35 +00:00
{
2012-03-25 19:30:05 +00:00
Rect zoomlevels [ ZOOM_LVL_COUNT ] ;
for ( ZoomLevel zoom = ZOOM_LVL_BEGIN ; zoom ! = ZOOM_LVL_END ; zoom + + ) {
/* FIXME: This doesn't switch to width_small when appropriate. */
zoomlevels [ zoom ] . left = this - > center - ScaleByZoom ( this - > width_normal / 2 + 1 , zoom ) ;
zoomlevels [ zoom ] . top = this - > top - ScaleByZoom ( 1 , zoom ) ;
zoomlevels [ zoom ] . right = this - > center + ScaleByZoom ( this - > width_normal / 2 + 1 , zoom ) ;
2022-12-21 22:21:50 +00:00
zoomlevels [ zoom ] . bottom = this - > top + ScaleByZoom ( WidgetDimensions : : scaled . fullbevel . top + FONT_HEIGHT_NORMAL + WidgetDimensions : : scaled . fullbevel . bottom + 1 , zoom ) ;
2012-03-25 19:30:05 +00:00
}
2021-05-09 15:10:07 +00:00
for ( const Window * w : Window : : Iterate ( ) ) {
2020-06-29 01:38:29 +00:00
Viewport * vp = w - > viewport ;
2019-04-10 21:07:06 +00:00
if ( vp ! = nullptr & & vp - > zoom < = maxzoom ) {
2012-03-25 19:30:05 +00:00
assert ( vp - > width ! = 0 ) ;
Rect & zl = zoomlevels [ vp - > zoom ] ;
MarkViewportDirty ( vp , zl . left , zl . top , zl . right , zl . bottom ) ;
}
}
2009-07-08 08:30:35 +00:00
}
2004-08-09 17:04:08 +00:00
2008-04-16 14:18:15 +00:00
static void ViewportDrawTileSprites ( const TileSpriteToDrawVector * tstdv )
2004-09-10 19:02:27 +00:00
{
2019-02-17 11:20:52 +00:00
for ( const TileSpriteToDraw & ts : * tstdv ) {
DrawSpriteViewport ( ts . image , ts . pal , ts . x , ts . y , ts . sub ) ;
2008-04-16 14:18:15 +00:00
}
2004-08-09 17:04:08 +00:00
}
2014-01-02 16:48:16 +00:00
/** This fallback sprite checker always exists. */
static bool ViewportSortParentSpritesChecker ( )
{
return true ;
}
2020-01-25 21:53:16 +00:00
/** Sort parent sprites pointer array replicating the way original sorter did it. */
2008-04-16 20:01:04 +00:00
static void ViewportSortParentSprites ( ParentSpriteToSortVector * psdv )
2004-08-09 17:04:08 +00:00
{
2020-01-25 21:53:16 +00:00
if ( psdv - > size ( ) < 2 ) return ;
/* We rely on sprites being, for the most part, already ordered.
* So we don ' t need to move many of them and can keep track of their
* order efficiently by using stack . We always move sprites to the front
* of the current position , i . e . to the top of the stack .
* Also use special constants to indicate sorting state without
* adding extra fields to ParentSpriteToDraw structure .
*/
const uint32 ORDER_COMPARED = UINT32_MAX ; // Sprite was compared but we still need to compare the ones preceding it
const uint32 ORDER_RETURNED = UINT32_MAX - 1 ; // Makr sorted sprite in case there are other occurrences of it in the stack
std : : stack < ParentSpriteToDraw * > sprite_order ;
uint32 next_order = 0 ;
std : : forward_list < std : : pair < int64 , ParentSpriteToDraw * > > sprite_list ; // We store sprites in a list sorted by xmin+ymin
/* Initialize sprite list and order. */
for ( auto p = psdv - > rbegin ( ) ; p ! = psdv - > rend ( ) ; p + + ) {
sprite_list . push_front ( std : : make_pair ( ( * p ) - > xmin + ( * p ) - > ymin , * p ) ) ;
sprite_order . push ( * p ) ;
( * p ) - > order = next_order + + ;
}
sprite_list . sort ( ) ;
2021-02-05 10:00:36 +00:00
std : : vector < ParentSpriteToDraw * > preceding ; // Temporarily stores sprites that precede current and their position in the list
2020-01-25 21:53:16 +00:00
auto preceding_prev = sprite_list . begin ( ) ; // Store iterator in case we need to delete a single preciding sprite
auto out = psdv - > begin ( ) ; // Iterator to output sorted sprites
while ( ! sprite_order . empty ( ) ) {
auto s = sprite_order . top ( ) ;
sprite_order . pop ( ) ;
2004-08-09 17:04:08 +00:00
2020-01-25 21:53:16 +00:00
/* Sprite is already sorted, ignore it. */
if ( s - > order = = ORDER_RETURNED ) continue ;
/* Sprite was already compared, just need to output it. */
if ( s - > order = = ORDER_COMPARED ) {
* ( out + + ) = s ;
s - > order = ORDER_RETURNED ;
2008-04-16 19:36:30 +00:00
continue ;
}
2005-07-17 20:14:58 +00:00
2020-01-25 21:53:16 +00:00
preceding . clear ( ) ;
/* We only need sprites with xmin <= s->xmax && ymin <= s->ymax && zmin <= s->zmax
* So by iterating sprites with xmin + ymin < = s - > xmax + s - > ymax
* we get all we need and some more that we filter out later .
* We don ' t include zmin into the sum as there are usually more neighbors on x and y than z
* so including it will actually increase the amount of false positives .
* Also min coordinates can be > max so using max ( xmin , xmax ) + max ( ymin , ymax )
* to ensure that we iterate the current sprite as we need to remove it from the list .
*/
2021-01-08 10:16:18 +00:00
auto ssum = std : : max ( s - > xmax , s - > xmin ) + std : : max ( s - > ymax , s - > ymin ) ;
2020-01-25 21:53:16 +00:00
auto prev = sprite_list . before_begin ( ) ;
auto x = sprite_list . begin ( ) ;
while ( x ! = sprite_list . end ( ) & & ( ( * x ) . first < = ssum ) ) {
auto p = ( * x ) . second ;
if ( p = = s ) {
/* We found the current sprite, remove it and move on. */
x = sprite_list . erase_after ( prev ) ;
continue ;
}
auto p_prev = prev ;
prev = x + + ;
if ( s - > xmax < p - > xmin | | s - > ymax < p - > ymin | | s - > zmax < p - > zmin ) continue ;
if ( s - > xmin < = p - > xmax & & // overlap in X?
s - > ymin < = p - > ymax & & // overlap in Y?
s - > zmin < = p - > zmax ) { // overlap in Z?
if ( s - > xmin + s - > xmax + s - > ymin + s - > ymax + s - > zmin + s - > zmax < =
p - > xmin + p - > xmax + p - > ymin + p - > ymax + p - > zmin + p - > zmax ) {
2008-04-16 18:28:05 +00:00
continue ;
2005-06-15 14:04:48 +00:00
}
2008-04-16 18:28:05 +00:00
}
2020-01-25 21:53:16 +00:00
preceding . push_back ( p ) ;
preceding_prev = p_prev ;
}
if ( preceding . empty ( ) ) {
/* No preceding sprites, add current one to the output */
* ( out + + ) = s ;
s - > order = ORDER_RETURNED ;
continue ;
}
2005-07-17 20:14:58 +00:00
2020-01-25 21:53:16 +00:00
/* Optimization for the case when we only have 1 sprite to move. */
if ( preceding . size ( ) = = 1 ) {
auto p = preceding [ 0 ] ;
/* We can only output the preceding sprite if there can't be any other sprites preceding it. */
if ( p - > xmax < = s - > xmax & & p - > ymax < = s - > ymax & & p - > zmax < = s - > zmax ) {
p - > order = ORDER_RETURNED ;
s - > order = ORDER_RETURNED ;
sprite_list . erase_after ( preceding_prev ) ;
* ( out + + ) = p ;
* ( out + + ) = s ;
continue ;
2008-09-28 12:38:56 +00:00
}
2020-01-25 21:53:16 +00:00
}
/* Sort all preceding sprites by order and assign new orders in reverse (as original sorter did). */
std : : sort ( preceding . begin ( ) , preceding . end ( ) , [ ] ( const ParentSpriteToDraw * a , const ParentSpriteToDraw * b ) {
2021-02-05 10:00:36 +00:00
return a - > order > b - > order ;
2020-01-25 21:53:16 +00:00
} ) ;
s - > order = ORDER_COMPARED ;
sprite_order . push ( s ) ; // Still need to output so push it back for now
for ( auto p : preceding ) {
p - > order = next_order + + ;
sprite_order . push ( p ) ;
2004-08-09 17:04:08 +00:00
}
}
}
2020-01-25 21:53:16 +00:00
2008-04-16 20:39:35 +00:00
static void ViewportDrawParentSprites ( const ParentSpriteToSortVector * psd , const ChildScreenSpriteToDrawVector * csstdv )
2004-08-09 17:04:08 +00:00
{
2019-02-17 11:20:52 +00:00
for ( const ParentSpriteToDraw * ps : * psd ) {
2011-11-24 12:38:48 +00:00
if ( ps - > image ! = SPR_EMPTY_BOUNDING_BOX ) DrawSpriteViewport ( ps - > image , ps - > pal , ps - > x , ps - > y , ps - > sub ) ;
2004-08-09 17:04:08 +00:00
2008-06-16 20:08:30 +00:00
int child_idx = ps - > first_child ;
while ( child_idx > = 0 ) {
2018-09-25 20:20:24 +00:00
const ChildScreenSpriteToDraw * cs = csstdv - > data ( ) + child_idx ;
2008-06-16 20:08:30 +00:00
child_idx = cs - > next ;
2022-12-18 12:34:14 +00:00
if ( cs - > relative ) {
DrawSpriteViewport ( cs - > image , cs - > pal , ps - > left + cs - > x , ps - > top + cs - > y , cs - > sub ) ;
} else {
DrawSpriteViewport ( cs - > image , cs - > pal , ps - > x + cs - > x , ps - > y + cs - > y , cs - > sub ) ;
}
2004-08-09 17:04:08 +00:00
}
}
}
2007-09-26 19:27:29 +00:00
/**
* Draws the bounding boxes of all ParentSprites
* @ param psd Array of ParentSprites
*/
2008-04-16 20:01:04 +00:00
static void ViewportDrawBoundingBoxes ( const ParentSpriteToSortVector * psd )
2007-09-26 19:27:29 +00:00
{
2019-02-17 11:20:52 +00:00
for ( const ParentSpriteToDraw * ps : * psd ) {
2007-09-26 19:27:29 +00:00
Point pt1 = RemapCoords ( ps - > xmax + 1 , ps - > ymax + 1 , ps - > zmax + 1 ) ; // top front corner
Point pt2 = RemapCoords ( ps - > xmin , ps - > ymax + 1 , ps - > zmax + 1 ) ; // top left corner
Point pt3 = RemapCoords ( ps - > xmax + 1 , ps - > ymin , ps - > zmax + 1 ) ; // top right corner
Point pt4 = RemapCoords ( ps - > xmax + 1 , ps - > ymax + 1 , ps - > zmin ) ; // bottom front corner
DrawBox ( pt1 . x , pt1 . y ,
pt2 . x - pt1 . x , pt2 . y - pt1 . y ,
pt3 . x - pt1 . x , pt3 . y - pt1 . y ,
pt4 . x - pt1 . x , pt4 . y - pt1 . y ) ;
}
}
2012-03-25 19:06:59 +00:00
/**
* Draw / colour the blocks that have been redrawn .
*/
static void ViewportDrawDirtyBlocks ( )
{
2014-01-02 22:41:58 +00:00
Blitter * blitter = BlitterFactory : : GetCurrentBlitter ( ) ;
2012-03-25 19:06:59 +00:00
const DrawPixelInfo * dpi = _cur_dpi ;
void * dst ;
int right = UnScaleByZoom ( dpi - > width , dpi - > zoom ) ;
int bottom = UnScaleByZoom ( dpi - > height , dpi - > zoom ) ;
int colour = _string_colourmap [ _dirty_block_colour & 0xF ] ;
dst = dpi - > dst_ptr ;
byte bo = UnScaleByZoom ( dpi - > left + dpi - > top , dpi - > zoom ) & 1 ;
do {
for ( int i = ( bo ^ = 1 ) ; i < right ; i + = 2 ) blitter - > SetPixel ( dst , i , 0 , ( uint8 ) colour ) ;
dst = blitter - > MoveTo ( dst , 0 , 1 ) ;
} while ( - - bottom > 0 ) ;
}
2013-05-19 14:49:25 +00:00
static void ViewportDrawStrings ( ZoomLevel zoom , const StringSpriteToDrawVector * sstdv )
2004-08-09 17:04:08 +00:00
{
2019-02-17 11:20:52 +00:00
for ( const StringSpriteToDraw & ss : * sstdv ) {
2009-09-11 22:21:54 +00:00
TextColour colour = TC_BLACK ;
2019-02-17 11:20:52 +00:00
bool small = HasBit ( ss . width , 15 ) ;
int w = GB ( ss . width , 0 , 15 ) ;
int x = UnScaleByZoom ( ss . x , zoom ) ;
int y = UnScaleByZoom ( ss . y , zoom ) ;
2022-12-21 22:21:50 +00:00
int h = WidgetDimensions : : scaled . fullbevel . top + ( small ? FONT_HEIGHT_SMALL : FONT_HEIGHT_NORMAL ) + WidgetDimensions : : scaled . fullbevel . bottom ;
2009-09-11 22:21:54 +00:00
2019-02-17 11:20:52 +00:00
SetDParam ( 0 , ss . params [ 0 ] ) ;
SetDParam ( 1 , ss . params [ 1 ] ) ;
2006-11-05 11:17:33 +00:00
2019-02-17 11:20:52 +00:00
if ( ss . colour ! = INVALID_COLOUR ) {
2008-04-03 19:55:40 +00:00
/* Do not draw signs nor station names if they are set invisible */
2019-02-17 11:20:52 +00:00
if ( IsInvisibilitySet ( TO_SIGNS ) & & ss . string ! = STR_WHITE_SIGN ) continue ;
2008-04-03 19:55:40 +00:00
2019-02-17 11:20:52 +00:00
if ( IsTransparencySet ( TO_SIGNS ) & & ss . string ! = STR_WHITE_SIGN ) {
2013-10-06 20:18:53 +00:00
/* Don't draw the rectangle.
* Real colours need the TC_IS_PALETTE_COLOUR flag .
* Otherwise colours from _string_colourmap are assumed . */
2019-02-17 11:20:52 +00:00
colour = ( TextColour ) _colour_gradient [ ss . colour ] [ 6 ] | TC_IS_PALETTE_COLOUR ;
2013-10-06 20:18:53 +00:00
} else {
/* Draw the rectangle if 'transparent station signs' is off,
* or if we are drawing a general text sign ( STR_WHITE_SIGN ) . */
2005-07-17 20:14:58 +00:00
DrawFrameRect (
2022-09-23 08:36:22 +00:00
x , y , x + w - 1 , y + h - 1 , ss . colour ,
2007-11-10 01:17:15 +00:00
IsTransparencySet ( TO_SIGNS ) ? FR_TRANSPARENT : FR_NONE
2005-07-17 20:14:58 +00:00
) ;
2007-04-05 07:49:04 +00:00
}
2009-03-22 23:54:36 +00:00
}
2009-03-22 09:43:00 +00:00
2022-12-25 13:29:38 +00:00
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 ) ;
2008-04-16 14:18:15 +00:00
}
2004-08-09 17:04:08 +00:00
}
2020-06-29 01:38:29 +00:00
void ViewportDoDraw ( const Viewport * vp , int left , int top , int right , int bottom )
2004-08-09 17:04:08 +00:00
{
2008-04-16 21:06:21 +00:00
_vd . dpi . zoom = vp - > zoom ;
int mask = ScaleByZoom ( - 1 , vp - > zoom ) ;
2004-08-09 17:04:08 +00:00
2009-07-29 20:27:24 +00:00
_vd . combine_sprites = SPRITE_COMBINE_NONE ;
2004-08-09 17:04:08 +00:00
2008-04-16 21:06:21 +00:00
_vd . dpi . width = ( right - left ) & mask ;
_vd . dpi . height = ( bottom - top ) & mask ;
_vd . dpi . left = left & mask ;
_vd . dpi . top = top & mask ;
2023-01-26 18:21:36 +00:00
_vd . dpi . pitch = _cur_dpi - > pitch ;
2019-04-10 21:07:06 +00:00
_vd . last_child = nullptr ;
2004-08-09 17:04:08 +00:00
2008-04-16 21:06:21 +00:00
int x = UnScaleByZoom ( _vd . dpi . left - ( vp - > virtual_left & mask ) , vp - > zoom ) + vp - > left ;
int y = UnScaleByZoom ( _vd . dpi . top - ( vp - > virtual_top & mask ) , vp - > zoom ) + vp - > top ;
2004-08-09 17:04:08 +00:00
2023-01-26 18:21:36 +00:00
_vd . dpi . dst_ptr = BlitterFactory : : GetCurrentBlitter ( ) - > MoveTo ( _cur_dpi - > dst_ptr , x - _cur_dpi - > left , y - _cur_dpi - > top ) ;
AutoRestoreBackup dpi_backup ( _cur_dpi , & _vd . dpi ) ;
2004-08-09 17:04:08 +00:00
ViewportAddLandscape ( ) ;
2008-04-16 21:06:21 +00:00
ViewportAddVehicles ( & _vd . dpi ) ;
2004-08-09 17:04:08 +00:00
2019-02-22 16:57:28 +00:00
ViewportAddKdtreeSigns ( & _vd . dpi ) ;
2004-08-09 17:04:08 +00:00
2008-08-04 14:40:50 +00:00
DrawTextEffects ( & _vd . dpi ) ;
2018-09-23 11:23:54 +00:00
if ( _vd . tile_sprites_to_draw . size ( ) ! = 0 ) ViewportDrawTileSprites ( & _vd . tile_sprites_to_draw ) ;
2004-08-09 17:04:08 +00:00
2019-02-17 11:20:52 +00:00
for ( auto & psd : _vd . parent_sprites_to_draw ) {
_vd . parent_sprites_to_sort . push_back ( & psd ) ;
2008-04-16 20:01:04 +00:00
}
2004-08-09 17:04:08 +00:00
2014-01-02 16:48:16 +00:00
_vp_sprite_sorter ( & _vd . parent_sprites_to_sort ) ;
2008-04-16 21:06:21 +00:00
ViewportDrawParentSprites ( & _vd . parent_sprites_to_sort , & _vd . child_screen_sprites_to_draw ) ;
2008-02-14 15:13:36 +00:00
2008-04-16 21:06:21 +00:00
if ( _draw_bounding_boxes ) ViewportDrawBoundingBoxes ( & _vd . parent_sprites_to_sort ) ;
2012-03-25 19:06:59 +00:00
if ( _draw_dirty_blocks ) ViewportDrawDirtyBlocks ( ) ;
2004-09-10 19:02:27 +00:00
2013-05-19 14:49:25 +00:00
DrawPixelInfo dp = _vd . dpi ;
ZoomLevel zoom = _vd . dpi . zoom ;
dp . zoom = ZOOM_LVL_NORMAL ;
dp . width = UnScaleByZoom ( dp . width , zoom ) ;
dp . height = UnScaleByZoom ( dp . height , zoom ) ;
_cur_dpi = & dp ;
2019-04-10 21:07:06 +00:00
if ( vp - > overlay ! = nullptr & & vp - > overlay - > GetCargoMask ( ) ! = 0 & & vp - > overlay - > GetCompanyMask ( ) ! = 0 ) {
2014-02-15 12:19:46 +00:00
/* translate to window coordinates */
dp . left = x ;
dp . top = y ;
vp - > overlay - > Draw ( & dp ) ;
}
2013-05-19 14:49:25 +00:00
2018-09-23 11:23:54 +00:00
if ( _vd . string_sprites_to_draw . size ( ) ! = 0 ) {
2014-02-15 12:19:46 +00:00
/* translate to world coordinates */
dp . left = UnScaleByZoom ( _vd . dpi . left , zoom ) ;
dp . top = UnScaleByZoom ( _vd . dpi . top , zoom ) ;
ViewportDrawStrings ( zoom , & _vd . string_sprites_to_draw ) ;
}
2004-09-10 19:02:27 +00:00
2018-09-20 22:44:14 +00:00
_vd . string_sprites_to_draw . clear ( ) ;
_vd . tile_sprites_to_draw . clear ( ) ;
_vd . parent_sprites_to_draw . clear ( ) ;
_vd . parent_sprites_to_sort . clear ( ) ;
_vd . child_screen_sprites_to_draw . clear ( ) ;
2004-08-09 17:04:08 +00:00
}
2020-06-29 01:38:29 +00:00
static inline void ViewportDraw ( const Viewport * vp , int left , int top , int right , int bottom )
2004-08-09 17:04:08 +00:00
{
2005-07-17 20:14:58 +00:00
if ( right < = vp - > left | | bottom < = vp - > top ) return ;
2004-08-09 17:04:08 +00:00
2005-07-17 20:14:58 +00:00
if ( left > = vp - > left + vp - > width ) return ;
2004-08-09 17:04:08 +00:00
if ( left < vp - > left ) left = vp - > left ;
2005-07-17 20:14:58 +00:00
if ( right > vp - > left + vp - > width ) right = vp - > left + vp - > width ;
2004-08-09 17:04:08 +00:00
2005-07-17 20:14:58 +00:00
if ( top > = vp - > top + vp - > height ) return ;
2004-08-09 17:04:08 +00:00
if ( top < vp - > top ) top = vp - > top ;
2005-07-17 20:14:58 +00:00
if ( bottom > vp - > top + vp - > height ) bottom = vp - > top + vp - > height ;
2004-08-09 17:04:08 +00:00
2020-02-14 22:10:04 +00:00
ViewportDoDraw ( vp ,
ScaleByZoom ( left - vp - > left , vp - > zoom ) + vp - > virtual_left ,
ScaleByZoom ( top - vp - > top , vp - > zoom ) + vp - > virtual_top ,
ScaleByZoom ( right - vp - > left , vp - > zoom ) + vp - > virtual_left ,
ScaleByZoom ( bottom - vp - > top , vp - > zoom ) + vp - > virtual_top
) ;
2004-08-09 17:04:08 +00:00
}
2008-10-14 19:27:08 +00:00
/**
* Draw the viewport of this window .
*/
2008-05-17 12:48:06 +00:00
void Window : : DrawViewport ( ) const
2005-07-17 20:14:58 +00:00
{
2018-07-19 19:17:07 +00:00
PerformanceAccumulator framerate ( PFE_DRAWWORLD ) ;
2004-08-09 17:04:08 +00:00
DrawPixelInfo * dpi = _cur_dpi ;
2008-05-17 12:48:06 +00:00
dpi - > left + = this - > left ;
dpi - > top + = this - > top ;
2004-08-09 17:04:08 +00:00
2008-05-17 12:48:06 +00:00
ViewportDraw ( this - > viewport , dpi - > left , dpi - > top , dpi - > left + dpi - > width , dpi - > top + dpi - > height ) ;
2004-08-09 17:04:08 +00:00
2008-05-17 12:48:06 +00:00
dpi - > left - = this - > left ;
dpi - > top - = this - > top ;
2004-08-09 17:04:08 +00:00
}
2014-09-22 18:14:44 +00:00
/**
2019-01-13 19:54:21 +00:00
* Ensure that a given viewport has a valid scroll position .
*
* There must be a visible piece of the map in the center of the viewport .
* If there isn ' t , the viewport will be scrolled to nearest such location .
*
* @ param vp The viewport .
* @ param [ in , out ] scroll_x Viewport X scroll .
* @ param [ in , out ] scroll_y Viewport Y scroll .
2014-09-22 18:14:44 +00:00
*/
2020-06-29 01:38:29 +00:00
static inline void ClampViewportToMap ( const Viewport * vp , int * scroll_x , int * scroll_y )
2014-09-22 18:14:44 +00:00
{
2019-01-13 19:54:21 +00:00
/* Centre of the viewport is hot spot. */
Point pt = {
* scroll_x + vp - > virtual_width / 2 ,
* scroll_y + vp - > virtual_height / 2
} ;
/* Find nearest tile that is within borders of the map. */
bool clamped ;
pt = InverseRemapCoords2 ( pt . x , pt . y , true , & clamped ) ;
if ( clamped ) {
/* Convert back to viewport coordinates and remove centering. */
pt = RemapCoords2 ( pt . x , pt . y ) ;
* scroll_x = pt . x - vp - > virtual_width / 2 ;
* scroll_y = pt . y - vp - > virtual_height / 2 ;
2014-09-22 18:14:44 +00:00
}
2007-06-25 21:50:36 +00:00
}
2009-07-26 12:49:26 +00:00
/**
* Update the viewport position being displayed .
* @ param w % Window owning the viewport .
*/
2004-08-09 17:04:08 +00:00
void UpdateViewportPosition ( Window * w )
{
2020-06-29 01:38:29 +00:00
const Viewport * vp = w - > viewport ;
2004-08-09 17:04:08 +00:00
2008-05-11 15:08:44 +00:00
if ( w - > viewport - > follow_vehicle ! = INVALID_VEHICLE ) {
2009-05-16 23:34:14 +00:00
const Vehicle * veh = Vehicle : : Get ( w - > viewport - > follow_vehicle ) ;
2005-07-17 20:14:58 +00:00
Point pt = MapXYZToViewport ( vp , veh - > x_pos , veh - > y_pos , veh - > z_pos ) ;
2004-08-09 17:04:08 +00:00
2009-01-09 23:30:02 +00:00
w - > viewport - > scrollpos_x = pt . x ;
w - > viewport - > scrollpos_y = pt . y ;
2004-08-09 17:04:08 +00:00
SetViewportPosition ( w , pt . x , pt . y ) ;
} else {
2007-06-25 21:50:36 +00:00
/* Ensure the destination location is within the map */
2019-01-13 19:54:21 +00:00
ClampViewportToMap ( vp , & w - > viewport - > dest_scrollpos_x , & w - > viewport - > dest_scrollpos_y ) ;
2004-12-13 18:33:47 +00:00
2008-05-11 15:08:44 +00:00
int delta_x = w - > viewport - > dest_scrollpos_x - w - > viewport - > scrollpos_x ;
int delta_y = w - > viewport - > dest_scrollpos_y - w - > viewport - > scrollpos_y ;
2007-05-28 16:46:16 +00:00
2013-05-19 14:49:25 +00:00
bool update_overlay = false ;
2007-05-28 16:46:16 +00:00
if ( delta_x ! = 0 | | delta_y ! = 0 ) {
2008-05-29 15:13:28 +00:00
if ( _settings_client . gui . smooth_scroll ) {
2023-01-21 09:43:03 +00:00
int max_scroll = Map : : ScaleBySize1D ( 512 * ZOOM_LVL_BASE ) ;
2010-05-30 13:05:36 +00:00
/* Not at our desired position yet... */
2019-01-12 08:28:43 +00:00
w - > viewport - > scrollpos_x + = Clamp ( DivAwayFromZero ( delta_x , 4 ) , - max_scroll , max_scroll ) ;
w - > viewport - > scrollpos_y + = Clamp ( DivAwayFromZero ( delta_y , 4 ) , - max_scroll , max_scroll ) ;
2007-05-28 17:07:19 +00:00
} else {
2008-05-11 15:08:44 +00:00
w - > viewport - > scrollpos_x = w - > viewport - > dest_scrollpos_x ;
w - > viewport - > scrollpos_y = w - > viewport - > dest_scrollpos_y ;
2007-05-28 17:07:19 +00:00
}
2013-05-19 14:49:25 +00:00
update_overlay = ( w - > viewport - > scrollpos_x = = w - > viewport - > dest_scrollpos_x & &
w - > viewport - > scrollpos_y = = w - > viewport - > dest_scrollpos_y ) ;
2007-05-28 16:46:16 +00:00
}
2019-01-13 19:54:21 +00:00
ClampViewportToMap ( vp , & w - > viewport - > scrollpos_x , & w - > viewport - > scrollpos_y ) ;
2008-02-14 15:13:36 +00:00
2008-05-11 15:08:44 +00:00
SetViewportPosition ( w , w - > viewport - > scrollpos_x , w - > viewport - > scrollpos_y ) ;
2013-05-19 14:49:25 +00:00
if ( update_overlay ) RebuildViewportOverlay ( w ) ;
2004-08-09 17:04:08 +00:00
}
}
2007-09-09 10:13:17 +00:00
/**
2008-04-19 13:05:05 +00:00
* Marks a viewport as dirty for repaint if it displays ( a part of ) the area the needs to be repainted .
* @ param vp The viewport to mark as dirty
* @ param left Left edge of area to repaint
* @ param top Top edge of area to repaint
* @ param right Right edge of area to repaint
* @ param bottom Bottom edge of area to repaint
2021-01-17 18:57:16 +00:00
* @ return true if the viewport contains a dirty block
2007-09-09 10:13:17 +00:00
* @ ingroup dirty
*/
2021-01-17 18:57:16 +00:00
static bool MarkViewportDirty ( const Viewport * vp , int left , int top , int right , int bottom )
2004-08-09 17:04:08 +00:00
{
2015-02-14 12:53:07 +00:00
/* Rounding wrt. zoom-out level */
right + = ( 1 < < vp - > zoom ) - 1 ;
bottom + = ( 1 < < vp - > zoom ) - 1 ;
2005-07-17 20:14:58 +00:00
right - = vp - > virtual_left ;
2021-01-17 18:57:16 +00:00
if ( right < = 0 ) return false ;
2004-08-09 17:04:08 +00:00
2005-07-17 20:14:58 +00:00
bottom - = vp - > virtual_top ;
2021-01-17 18:57:16 +00:00
if ( bottom < = 0 ) return false ;
2004-08-09 17:04:08 +00:00
2021-01-08 10:16:18 +00:00
left = std : : max ( 0 , left - vp - > virtual_left ) ;
2004-08-09 17:04:08 +00:00
2021-01-17 18:57:16 +00:00
if ( left > = vp - > virtual_width ) return false ;
2004-08-09 17:04:08 +00:00
2021-01-08 10:16:18 +00:00
top = std : : max ( 0 , top - vp - > virtual_top ) ;
2004-08-09 17:04:08 +00:00
2021-01-17 18:57:16 +00:00
if ( top > = vp - > virtual_height ) return false ;
2004-08-09 17:04:08 +00:00
2020-06-07 22:38:06 +00:00
AddDirtyBlock (
2009-01-03 15:03:28 +00:00
UnScaleByZoomLower ( left , vp - > zoom ) + vp - > left ,
UnScaleByZoomLower ( top , vp - > zoom ) + vp - > top ,
UnScaleByZoom ( right , vp - > zoom ) + vp - > left + 1 ,
UnScaleByZoom ( bottom , vp - > zoom ) + vp - > top + 1
2004-08-09 17:04:08 +00:00
) ;
2021-01-17 18:57:16 +00:00
return true ;
2004-08-09 17:04:08 +00:00
}
2008-04-19 13:05:05 +00:00
/**
* Mark all viewports that display an area as dirty ( in need of repaint ) .
2015-02-14 12:53:07 +00:00
* @ param left Left edge of area to repaint . ( viewport coordinates , that is wrt . # ZOOM_LVL_NORMAL )
* @ param top Top edge of area to repaint . ( viewport coordinates , that is wrt . # ZOOM_LVL_NORMAL )
* @ param right Right edge of area to repaint . ( viewport coordinates , that is wrt . # ZOOM_LVL_NORMAL )
* @ param bottom Bottom edge of area to repaint . ( viewport coordinates , that is wrt . # ZOOM_LVL_NORMAL )
2021-01-17 18:57:16 +00:00
* @ return true if at least one viewport has a dirty block
2008-04-19 13:05:05 +00:00
* @ ingroup dirty
*/
2021-01-17 18:57:16 +00:00
bool MarkAllViewportsDirty ( int left , int top , int right , int bottom )
2004-08-09 17:04:08 +00:00
{
2021-01-17 18:57:16 +00:00
bool dirty = false ;
2021-05-09 15:10:07 +00:00
for ( const Window * w : Window : : Iterate ( ) ) {
2020-06-29 01:38:29 +00:00
Viewport * vp = w - > viewport ;
2019-04-10 21:07:06 +00:00
if ( vp ! = nullptr ) {
2004-08-09 17:04:08 +00:00
assert ( vp - > width ! = 0 ) ;
2021-01-17 18:57:16 +00:00
if ( MarkViewportDirty ( vp , left , top , right , bottom ) ) dirty = true ;
2004-08-09 17:04:08 +00:00
}
2008-04-17 09:42:44 +00:00
}
2021-01-17 18:57:16 +00:00
return dirty ;
2004-08-09 17:04:08 +00:00
}
2011-11-24 12:20:14 +00:00
void ConstrainAllViewportsZoom ( )
{
2021-05-09 15:10:07 +00:00
for ( Window * w : Window : : Iterate ( ) ) {
2019-04-10 21:07:06 +00:00
if ( w - > viewport = = nullptr ) continue ;
2011-11-24 12:20:14 +00:00
ZoomLevel zoom = static_cast < ZoomLevel > ( Clamp ( w - > viewport - > zoom , _settings_client . gui . zoom_min , _settings_client . gui . zoom_max ) ) ;
if ( zoom ! = w - > viewport - > zoom ) {
while ( w - > viewport - > zoom < zoom ) DoZoomInOutWindow ( ZOOM_OUT , w ) ;
while ( w - > viewport - > zoom > zoom ) DoZoomInOutWindow ( ZOOM_IN , w ) ;
}
}
}
2011-01-18 22:31:06 +00:00
/**
* Mark a tile given by its index dirty for repaint .
* @ param tile The tile to mark dirty .
2015-02-22 14:01:24 +00:00
* @ param bridge_level_offset Height of bridge on tile to also mark dirty . ( Height level relative to north corner . )
2019-01-13 19:56:10 +00:00
* @ param tile_height_override Height of the tile ( # TileHeight ) .
2011-01-18 22:31:06 +00:00
* @ ingroup dirty
*/
2019-01-13 19:56:10 +00:00
void MarkTileDirtyByTile ( TileIndex tile , int bridge_level_offset , int tile_height_override )
2005-07-17 20:14:58 +00:00
{
2019-01-13 19:56:10 +00:00
Point pt = RemapCoords ( TileX ( tile ) * TILE_SIZE , TileY ( tile ) * TILE_SIZE , tile_height_override * TILE_HEIGHT ) ;
2004-08-09 17:04:08 +00:00
MarkAllViewportsDirty (
2015-02-14 12:53:07 +00:00
pt . x - MAX_TILE_EXTENT_LEFT ,
2015-02-22 14:01:24 +00:00
pt . y - MAX_TILE_EXTENT_TOP - ZOOM_LVL_BASE * TILE_HEIGHT * bridge_level_offset ,
2015-02-14 12:53:07 +00:00
pt . x + MAX_TILE_EXTENT_RIGHT ,
pt . y + MAX_TILE_EXTENT_BOTTOM ) ;
2004-09-10 19:02:27 +00:00
}
2004-08-09 17:04:08 +00:00
2007-09-09 10:13:17 +00:00
/**
* Marks the selected tiles as dirty .
*
* This function marks the selected tiles as dirty for repaint
*
* @ ingroup dirty
*/
2007-03-07 11:47:46 +00:00
static void SetSelectionTilesDirty ( )
2004-08-09 17:04:08 +00:00
{
2009-04-25 22:04:13 +00:00
int x_size = _thd . size . x ;
int y_size = _thd . size . y ;
2013-01-08 22:46:42 +00:00
if ( ! _thd . diagonal ) { // Selecting in a straight rectangle (or a single square)
2010-12-13 15:09:59 +00:00
int x_start = _thd . pos . x ;
int y_start = _thd . pos . y ;
2004-08-09 17:04:08 +00:00
2010-12-13 15:09:59 +00:00
if ( _thd . outersize . x ! = 0 ) {
x_size + = _thd . outersize . x ;
x_start + = _thd . offs . x ;
y_size + = _thd . outersize . y ;
y_start + = _thd . offs . y ;
}
2004-08-09 17:04:08 +00:00
2010-12-13 15:09:59 +00:00
x_size - = TILE_SIZE ;
y_size - = TILE_SIZE ;
assert ( x_size > = 0 ) ;
assert ( y_size > = 0 ) ;
2023-01-21 09:43:03 +00:00
int x_end = Clamp ( x_start + x_size , 0 , Map : : SizeX ( ) * TILE_SIZE - TILE_SIZE ) ;
int y_end = Clamp ( y_start + y_size , 0 , Map : : SizeY ( ) * TILE_SIZE - TILE_SIZE ) ;
2010-12-13 15:09:59 +00:00
2023-01-21 09:43:03 +00:00
x_start = Clamp ( x_start , 0 , Map : : SizeX ( ) * TILE_SIZE - TILE_SIZE ) ;
y_start = Clamp ( y_start , 0 , Map : : SizeY ( ) * TILE_SIZE - TILE_SIZE ) ;
2010-12-13 15:09:59 +00:00
/* make sure everything is multiple of TILE_SIZE */
assert ( ( x_end | y_end | x_start | y_start ) % TILE_SIZE = = 0 ) ;
/* How it works:
* Suppose we have to mark dirty rectangle of 3 x4 tiles :
* x
* xxx
* xxxxx
* xxxxx
* xxx
* x
* This algorithm marks dirty columns of tiles , so it is done in 3 + 4 - 1 steps :
* 1 ) x 2 ) x
* xxx Oxx
* Oxxxx xOxxx
* xxxxx Oxxxx
* xxx xxx
* x x
* And so forth . . .
*/
int top_x = x_end ; // coordinates of top dirty tile
int top_y = y_start ;
int bot_x = top_x ; // coordinates of bottom dirty tile
int bot_y = top_y ;
2004-08-09 17:04:08 +00:00
2010-12-13 15:09:59 +00:00
do {
2011-08-16 21:25:33 +00:00
/* topmost dirty point */
TileIndex top_tile = TileVirtXY ( top_x , top_y ) ;
2011-11-04 10:18:13 +00:00
Point top = RemapCoords ( top_x , top_y , GetTileMaxPixelZ ( top_tile ) ) ;
2011-08-16 21:25:33 +00:00
/* bottommost point */
TileIndex bottom_tile = TileVirtXY ( bot_x , bot_y ) ;
2011-11-04 10:18:13 +00:00
Point bot = RemapCoords ( bot_x + TILE_SIZE , bot_y + TILE_SIZE , GetTilePixelZ ( bottom_tile ) ) ; // bottommost point
2009-04-25 22:04:13 +00:00
2010-12-13 15:09:59 +00:00
/* the 'x' coordinate of 'top' and 'bot' is the same (and always in the same distance from tile middle),
* tile height / slope affects only the ' y ' on - screen coordinate ! */
2009-04-25 22:04:13 +00:00
2015-02-14 12:53:07 +00:00
int l = top . x - TILE_PIXELS * ZOOM_LVL_BASE ; // 'x' coordinate of left side of the dirty rectangle
int t = top . y ; // 'y' coordinate of top side of the dirty rectangle
int r = top . x + TILE_PIXELS * ZOOM_LVL_BASE ; // 'x' coordinate of right side of the dirty rectangle
int b = bot . y ; // 'y' coordinate of bottom side of the dirty rectangle
2009-04-25 22:04:13 +00:00
2015-02-14 12:53:07 +00:00
static const int OVERLAY_WIDTH = 4 * ZOOM_LVL_BASE ; // part of selection sprites is drawn outside the selected area (in particular: terraforming)
2009-04-25 22:04:13 +00:00
2010-12-13 15:09:59 +00:00
/* For halftile foundations on SLOPE_STEEP_S the sprite extents some more towards the top */
2015-02-14 12:53:07 +00:00
MarkAllViewportsDirty ( l - OVERLAY_WIDTH , t - OVERLAY_WIDTH - TILE_HEIGHT * ZOOM_LVL_BASE , r + OVERLAY_WIDTH , b + OVERLAY_WIDTH ) ;
2009-04-25 22:04:13 +00:00
2010-12-13 15:09:59 +00:00
/* haven't we reached the topmost tile yet? */
if ( top_x ! = x_start ) {
top_x - = TILE_SIZE ;
} else {
top_y + = TILE_SIZE ;
}
2009-04-25 22:04:13 +00:00
2010-12-13 15:09:59 +00:00
/* the way the bottom tile changes is different when we reach the bottommost tile */
if ( bot_y ! = y_end ) {
bot_y + = TILE_SIZE ;
} else {
bot_x - = TILE_SIZE ;
}
} while ( bot_x > = top_x ) ;
} else { // Selecting in a 45 degrees rotated (diagonal) rectangle.
/* a_size, b_size describe a rectangle with rotated coordinates */
int a_size = x_size + y_size , b_size = x_size - y_size ;
2010-12-16 14:03:21 +00:00
int interval_a = a_size < 0 ? - ( int ) TILE_SIZE : ( int ) TILE_SIZE ;
int interval_b = b_size < 0 ? - ( int ) TILE_SIZE : ( int ) TILE_SIZE ;
2010-12-13 15:09:59 +00:00
for ( int a = - interval_a ; a ! = a_size + interval_a ; a + = interval_a ) {
for ( int b = - interval_b ; b ! = b_size + interval_b ; b + = interval_b ) {
uint x = ( _thd . pos . x + ( a + b ) / 2 ) / TILE_SIZE ;
uint y = ( _thd . pos . y + ( a - b ) / 2 ) / TILE_SIZE ;
2023-01-21 09:43:03 +00:00
if ( x < Map : : MaxX ( ) & & y < Map : : MaxY ( ) ) {
2010-12-13 15:09:59 +00:00
MarkTileDirtyByTile ( TileXY ( x , y ) ) ;
}
}
2009-04-25 22:04:13 +00:00
}
2010-12-13 15:09:59 +00:00
}
2004-08-09 17:04:08 +00:00
}
2005-06-29 08:14:09 +00:00
void SetSelectionRed ( bool b )
{
_thd . make_square_red = b ;
SetSelectionTilesDirty ( ) ;
}
2009-11-22 20:38:06 +00:00
/**
* Test whether a sign is below the mouse
* @ param vp the clicked viewport
* @ param x X position of click
* @ param y Y position of click
* @ param sign the sign to check
* @ return true if the sign was hit
*/
2020-06-29 01:38:29 +00:00
static bool CheckClickOnViewportSign ( const Viewport * vp , int x , int y , const ViewportSign * sign )
2004-08-09 17:04:08 +00:00
{
2011-11-24 12:38:48 +00:00
bool small = ( vp - > zoom > = ZOOM_LVL_OUT_16X ) ;
2009-12-22 12:50:41 +00:00
int sign_half_width = ScaleByZoom ( ( small ? sign - > width_small : sign - > width_normal ) / 2 , vp - > zoom ) ;
2022-12-21 22:21:50 +00:00
int sign_height = ScaleByZoom ( WidgetDimensions : : scaled . fullbevel . top + ( small ? FONT_HEIGHT_SMALL : FONT_HEIGHT_NORMAL ) + WidgetDimensions : : scaled . fullbevel . bottom , vp - > zoom ) ;
2009-12-22 12:50:41 +00:00
2010-11-20 15:44:24 +00:00
return y > = sign - > top & & y < sign - > top + sign_height & &
x > = sign - > center - sign_half_width & & x < sign - > center + sign_half_width ;
2004-08-09 17:04:08 +00:00
}
2019-02-22 16:57:28 +00:00
/**
* Check whether any viewport sign was clicked , and dispatch the click .
* @ param vp the clicked viewport
* @ param x X position of click
* @ param y Y position of click
* @ return true if the sign was hit
*/
2020-06-29 01:38:29 +00:00
static bool CheckClickOnViewportSign ( const Viewport * vp , int x , int y )
2009-07-25 10:56:36 +00:00
{
2019-04-20 16:49:54 +00:00
if ( _game_mode = = GM_MENU ) return false ;
2019-02-22 16:57:28 +00:00
x = ScaleByZoom ( x - vp - > left , vp - > zoom ) + vp - > virtual_left ;
y = ScaleByZoom ( y - vp - > top , vp - > zoom ) + vp - > virtual_top ;
2009-07-25 10:56:36 +00:00
2019-02-22 16:57:28 +00:00
Rect search_rect { x - 1 , y - 1 , x + 1 , y + 1 } ;
search_rect = ExpandRectWithViewportSignMargins ( search_rect , vp - > zoom ) ;
2019-04-20 16:49:54 +00:00
bool show_stations = HasBit ( _display_opt , DO_SHOW_STATION_NAMES ) & & ! IsInvisibilitySet ( TO_SIGNS ) ;
bool show_waypoints = HasBit ( _display_opt , DO_SHOW_WAYPOINT_NAMES ) & & ! IsInvisibilitySet ( TO_SIGNS ) ;
bool show_towns = HasBit ( _display_opt , DO_SHOW_TOWN_NAMES ) ;
2019-02-22 16:57:28 +00:00
bool show_signs = HasBit ( _display_opt , DO_SHOW_SIGNS ) & & ! IsInvisibilitySet ( TO_SIGNS ) ;
bool show_competitors = HasBit ( _display_opt , DO_SHOW_COMPETITOR_SIGNS ) ;
/* Topmost of each type that was hit */
2019-04-10 21:07:06 +00:00
BaseStation * st = nullptr , * last_st = nullptr ;
Town * t = nullptr , * last_t = nullptr ;
Sign * si = nullptr , * last_si = nullptr ;
2019-02-22 16:57:28 +00:00
/* See ViewportAddKdtreeSigns() for details on the search logic */
_viewport_sign_kdtree . FindContained ( search_rect . left , search_rect . top , search_rect . right , search_rect . bottom , [ & ] ( const ViewportSignKdtreeItem & item ) {
switch ( item . type ) {
case ViewportSignKdtreeItem : : VKI_STATION :
if ( ! show_stations ) break ;
st = BaseStation : : Get ( item . id . station ) ;
if ( ! show_competitors & & _local_company ! = st - > owner & & st - > owner ! = OWNER_NONE ) break ;
if ( CheckClickOnViewportSign ( vp , x , y , & st - > sign ) ) last_st = st ;
break ;
case ViewportSignKdtreeItem : : VKI_WAYPOINT :
if ( ! show_waypoints ) break ;
st = BaseStation : : Get ( item . id . station ) ;
if ( ! show_competitors & & _local_company ! = st - > owner & & st - > owner ! = OWNER_NONE ) break ;
if ( CheckClickOnViewportSign ( vp , x , y , & st - > sign ) ) last_st = st ;
break ;
case ViewportSignKdtreeItem : : VKI_TOWN :
if ( ! show_towns ) break ;
t = Town : : Get ( item . id . town ) ;
if ( CheckClickOnViewportSign ( vp , x , y , & t - > cache . sign ) ) last_t = t ;
break ;
case ViewportSignKdtreeItem : : VKI_SIGN :
if ( ! show_signs ) break ;
si = Sign : : Get ( item . id . sign ) ;
if ( ! show_competitors & & _local_company ! = si - > owner & & si - > owner ! = OWNER_DEITY ) break ;
if ( CheckClickOnViewportSign ( vp , x , y , & si - > sign ) ) last_si = si ;
break ;
default :
NOT_REACHED ( ) ;
2009-11-22 20:38:06 +00:00
}
2019-02-22 16:57:28 +00:00
} ) ;
/* Select which hit to handle based on priority */
2019-04-10 21:07:06 +00:00
if ( last_st ! = nullptr ) {
2019-02-22 16:57:28 +00:00
if ( Station : : IsExpected ( last_st ) ) {
ShowStationViewWindow ( last_st - > index ) ;
} else {
ShowWaypointWindow ( Waypoint : : From ( last_st ) ) ;
}
return true ;
2019-04-10 21:07:06 +00:00
} else if ( last_t ! = nullptr ) {
2019-02-22 16:57:28 +00:00
ShowTownViewWindow ( last_t - > index ) ;
return true ;
2019-04-10 21:07:06 +00:00
} else if ( last_si ! = nullptr ) {
2019-02-22 16:57:28 +00:00
HandleClickOnSign ( last_si ) ;
return true ;
} else {
return false ;
2009-07-25 10:56:36 +00:00
}
2019-02-22 16:57:28 +00:00
}
ViewportSignKdtreeItem ViewportSignKdtreeItem : : MakeStation ( StationID id )
{
ViewportSignKdtreeItem item ;
item . type = VKI_STATION ;
item . id . station = id ;
const Station * st = Station : : Get ( id ) ;
2019-12-01 22:17:33 +00:00
assert ( st - > sign . kdtree_valid ) ;
item . center = st - > sign . center ;
item . top = st - > sign . top ;
2019-02-22 16:57:28 +00:00
/* Assume the sign can be a candidate for drawing, so measure its width */
2021-01-08 10:16:18 +00:00
_viewport_sign_maxwidth = std : : max < int > ( _viewport_sign_maxwidth , st - > sign . width_normal ) ;
2019-02-22 16:57:28 +00:00
return item ;
2009-07-25 10:56:36 +00:00
}
2006-08-31 07:49:34 +00:00
2019-02-22 16:57:28 +00:00
ViewportSignKdtreeItem ViewportSignKdtreeItem : : MakeWaypoint ( StationID id )
2004-08-09 17:04:08 +00:00
{
2019-02-22 16:57:28 +00:00
ViewportSignKdtreeItem item ;
item . type = VKI_WAYPOINT ;
item . id . station = id ;
2004-08-09 17:04:08 +00:00
2019-02-22 16:57:28 +00:00
const Waypoint * st = Waypoint : : Get ( id ) ;
2019-12-01 22:17:33 +00:00
assert ( st - > sign . kdtree_valid ) ;
item . center = st - > sign . center ;
item . top = st - > sign . top ;
2011-08-19 20:54:15 +00:00
2019-02-22 16:57:28 +00:00
/* Assume the sign can be a candidate for drawing, so measure its width */
2021-01-08 10:16:18 +00:00
_viewport_sign_maxwidth = std : : max < int > ( _viewport_sign_maxwidth , st - > sign . width_normal ) ;
2004-08-09 17:04:08 +00:00
2019-02-22 16:57:28 +00:00
return item ;
2004-08-09 17:04:08 +00:00
}
2019-02-22 16:57:28 +00:00
ViewportSignKdtreeItem ViewportSignKdtreeItem : : MakeTown ( TownID id )
{
ViewportSignKdtreeItem item ;
item . type = VKI_TOWN ;
item . id . town = id ;
const Town * town = Town : : Get ( id ) ;
2019-12-01 22:17:33 +00:00
assert ( town - > cache . sign . kdtree_valid ) ;
item . center = town - > cache . sign . center ;
item . top = town - > cache . sign . top ;
2019-02-22 16:57:28 +00:00
/* Assume the sign can be a candidate for drawing, so measure its width */
2021-01-08 10:16:18 +00:00
_viewport_sign_maxwidth = std : : max < int > ( _viewport_sign_maxwidth , town - > cache . sign . width_normal ) ;
2019-02-22 16:57:28 +00:00
return item ;
}
ViewportSignKdtreeItem ViewportSignKdtreeItem : : MakeSign ( SignID id )
2004-08-09 17:04:08 +00:00
{
2019-02-22 16:57:28 +00:00
ViewportSignKdtreeItem item ;
item . type = VKI_SIGN ;
item . id . sign = id ;
2004-08-09 17:04:08 +00:00
2019-02-22 16:57:28 +00:00
const Sign * sign = Sign : : Get ( id ) ;
2019-12-01 22:17:33 +00:00
assert ( sign - > sign . kdtree_valid ) ;
item . center = sign - > sign . center ;
item . top = sign - > sign . top ;
2019-02-22 16:57:28 +00:00
/* Assume the sign can be a candidate for drawing, so measure its width */
2021-01-08 10:16:18 +00:00
_viewport_sign_maxwidth = std : : max < int > ( _viewport_sign_maxwidth , sign - > sign . width_normal ) ;
2019-02-22 16:57:28 +00:00
return item ;
}
void RebuildViewportKdtree ( )
{
/* Reset biggest size sign seen */
_viewport_sign_maxwidth = 0 ;
std : : vector < ViewportSignKdtreeItem > items ;
items . reserve ( BaseStation : : GetNumItems ( ) + Town : : GetNumItems ( ) + Sign : : GetNumItems ( ) ) ;
2019-12-15 04:55:59 +00:00
for ( const Station * st : Station : : Iterate ( ) ) {
2019-12-01 22:17:33 +00:00
if ( st - > sign . kdtree_valid ) items . push_back ( ViewportSignKdtreeItem : : MakeStation ( st - > index ) ) ;
2019-02-22 16:57:28 +00:00
}
2019-12-15 04:55:59 +00:00
for ( const Waypoint * wp : Waypoint : : Iterate ( ) ) {
2019-12-01 22:17:33 +00:00
if ( wp - > sign . kdtree_valid ) items . push_back ( ViewportSignKdtreeItem : : MakeWaypoint ( wp - > index ) ) ;
2019-02-22 16:57:28 +00:00
}
2019-12-17 21:04:09 +00:00
for ( const Town * town : Town : : Iterate ( ) ) {
2019-12-01 22:17:33 +00:00
if ( town - > cache . sign . kdtree_valid ) items . push_back ( ViewportSignKdtreeItem : : MakeTown ( town - > index ) ) ;
2019-02-22 16:57:28 +00:00
}
2019-12-17 18:14:42 +00:00
for ( const Sign * sign : Sign : : Iterate ( ) ) {
2019-12-01 22:17:33 +00:00
if ( sign - > sign . kdtree_valid ) items . push_back ( ViewportSignKdtreeItem : : MakeSign ( sign - > index ) ) ;
2004-08-09 17:04:08 +00:00
}
2019-02-22 16:57:28 +00:00
_viewport_sign_kdtree . Build ( items . begin ( ) , items . end ( ) ) ;
2004-08-09 17:04:08 +00:00
}
2006-08-31 07:49:34 +00:00
2020-06-29 01:38:29 +00:00
static bool CheckClickOnLandscape ( const Viewport * vp , int x , int y )
2004-08-09 17:04:08 +00:00
{
2005-07-17 20:14:58 +00:00
Point pt = TranslateXYToTileCoord ( vp , x , y ) ;
2005-06-24 12:38:35 +00:00
2009-01-02 22:42:05 +00:00
if ( pt . x ! = - 1 ) return ClickTile ( TileVirtXY ( pt . x , pt . y ) ) ;
return true ;
2004-08-09 17:04:08 +00:00
}
2010-09-07 11:05:22 +00:00
static void PlaceObject ( )
{
Point pt ;
Window * w ;
pt = GetTileBelowCursor ( ) ;
if ( pt . x = = - 1 ) return ;
2010-12-23 14:15:05 +00:00
if ( ( _thd . place_mode & HT_DRAG_MASK ) = = HT_POINT ) {
2011-11-06 15:54:55 +00:00
pt . x + = TILE_SIZE / 2 ;
pt . y + = TILE_SIZE / 2 ;
2010-09-07 11:05:22 +00:00
}
_tile_fract_coords . x = pt . x & TILE_UNIT_MASK ;
_tile_fract_coords . y = pt . y & TILE_UNIT_MASK ;
2010-12-30 13:16:31 +00:00
w = _thd . GetCallbackWnd ( ) ;
2019-04-10 21:07:06 +00:00
if ( w ! = nullptr ) w - > OnPlaceObject ( pt , TileVirtXY ( pt . x , pt . y ) ) ;
2010-09-07 11:05:22 +00:00
}
2005-11-16 12:52:01 +00:00
2020-06-29 01:38:29 +00:00
bool HandleViewportClicked ( const Viewport * vp , int x , int y )
2004-08-09 17:04:08 +00:00
{
2010-09-06 14:14:09 +00:00
const Vehicle * v = CheckClickOnVehicle ( vp , x , y ) ;
if ( _thd . place_mode & HT_VEHICLE ) {
2019-04-10 21:07:06 +00:00
if ( v ! = nullptr & & VehicleClicked ( v ) ) return true ;
2010-09-06 14:14:09 +00:00
}
2010-12-24 14:55:31 +00:00
/* Vehicle placement mode already handled above. */
if ( ( _thd . place_mode & HT_DRAG_MASK ) ! = HT_NONE ) {
2010-09-07 11:00:42 +00:00
PlaceObject ( ) ;
return true ;
}
2004-08-09 17:04:08 +00:00
2019-02-22 16:57:28 +00:00
if ( CheckClickOnViewportSign ( vp , x , y ) ) return true ;
2010-12-20 09:34:30 +00:00
bool result = CheckClickOnLandscape ( vp , x , y ) ;
2004-08-09 17:04:08 +00:00
2019-04-10 21:07:06 +00:00
if ( v ! = nullptr ) {
2021-06-12 07:10:17 +00:00
Debug ( misc , 2 , " Vehicle {} (index {}) at {} " , v - > unitnumber , v - > index , fmt : : ptr ( v ) ) ;
2010-04-24 20:55:51 +00:00
if ( IsCompanyBuildableVehicleType ( v ) ) {
v = v - > First ( ) ;
if ( _ctrl_pressed & & v - > owner = = _local_company ) {
StartStopVehicle ( v , true ) ;
} else {
ShowVehicleViewWindow ( v ) ;
}
}
2009-01-02 22:42:05 +00:00
return true ;
2006-05-11 05:32:26 +00:00
}
2010-12-20 09:34:30 +00:00
return result ;
2004-08-09 17:04:08 +00:00
}
2013-05-19 14:49:25 +00:00
void RebuildViewportOverlay ( Window * w )
{
2019-04-10 21:07:06 +00:00
if ( w - > viewport - > overlay ! = nullptr & &
2013-05-19 14:49:25 +00:00
w - > viewport - > overlay - > GetCompanyMask ( ) ! = 0 & &
w - > viewport - > overlay - > GetCargoMask ( ) ! = 0 ) {
2019-02-23 19:19:41 +00:00
w - > viewport - > overlay - > SetDirty ( ) ;
2013-05-19 14:49:25 +00:00
w - > SetDirty ( ) ;
}
}
2004-09-03 19:59:05 +00:00
2010-08-01 19:22:34 +00:00
/**
* Scrolls the viewport in a window to a given location .
2009-07-26 12:49:26 +00:00
* @ param x Desired x location of the map to scroll to ( world coordinate ) .
* @ param y Desired y location of the map to scroll to ( world coordinate ) .
* @ param z Desired z location of the map to scroll to ( world coordinate ) . Use \ c - 1 to scroll to the height of the map at the \ a x , \ a y location .
* @ param w % Window containing the viewport .
* @ param instant Jump to the location instead of slowly moving to it .
* @ return Destination of the viewport was changed ( to activate other actions when the viewport is already at the desired position ) .
*/
2009-03-15 15:25:18 +00:00
bool ScrollWindowTo ( int x , int y , int z , Window * w , bool instant )
2004-09-03 19:59:05 +00:00
{
2008-02-02 21:09:05 +00:00
/* The slope cannot be acquired outside of the map, so make sure we are always within the map. */
2014-09-22 18:14:44 +00:00
if ( z = = - 1 ) {
2023-01-21 09:43:03 +00:00
if ( x > = 0 & & x < = ( int ) Map : : SizeX ( ) * ( int ) TILE_SIZE - 1
& & y > = 0 & & y < = ( int ) Map : : SizeY ( ) * ( int ) TILE_SIZE - 1 ) {
2014-09-22 18:14:44 +00:00
z = GetSlopePixelZ ( x , y ) ;
} else {
2015-02-22 14:14:30 +00:00
z = TileHeightOutsideMap ( x / ( int ) TILE_SIZE , y / ( int ) TILE_SIZE ) ;
2014-09-22 18:14:44 +00:00
}
}
2009-03-15 15:25:18 +00:00
Point pt = MapXYZToViewport ( w - > viewport , x , y , z ) ;
2008-05-11 15:08:44 +00:00
w - > viewport - > follow_vehicle = INVALID_VEHICLE ;
2004-09-03 19:59:05 +00:00
2010-07-24 10:14:39 +00:00
if ( w - > viewport - > dest_scrollpos_x = = pt . x & & w - > viewport - > dest_scrollpos_y = = pt . y ) return false ;
2004-09-03 19:59:05 +00:00
2007-05-28 17:07:19 +00:00
if ( instant ) {
2008-05-11 15:08:44 +00:00
w - > viewport - > scrollpos_x = pt . x ;
w - > viewport - > scrollpos_y = pt . y ;
2013-05-19 14:49:25 +00:00
RebuildViewportOverlay ( w ) ;
2007-05-28 16:46:16 +00:00
}
2008-05-11 15:08:44 +00:00
w - > viewport - > dest_scrollpos_x = pt . x ;
w - > viewport - > dest_scrollpos_y = pt . y ;
2004-09-03 19:59:05 +00:00
return true ;
}
2010-10-23 20:39:21 +00:00
/**
* Scrolls the viewport in a window to a given location .
* @ param tile Desired tile to center on .
* @ param w % Window containing the viewport .
* @ param instant Jump to the location instead of slowly moving to it .
* @ return Destination of the viewport was changed ( to activate other actions when the viewport is already at the desired position ) .
*/
bool ScrollWindowToTile ( TileIndex tile , Window * w , bool instant )
{
return ScrollWindowTo ( TileX ( tile ) * TILE_SIZE , TileY ( tile ) * TILE_SIZE , - 1 , w , instant ) ;
}
2010-10-23 18:28:20 +00:00
/**
* Scrolls the viewport of the main window to a given location .
* @ param tile Desired tile to center on .
* @ param instant Jump to the location instead of slowly moving to it .
* @ return Destination of the viewport was changed ( to activate other actions when the viewport is already at the desired position ) .
*/
2007-05-28 16:46:16 +00:00
bool ScrollMainWindowToTile ( TileIndex tile , bool instant )
2004-08-09 17:04:08 +00:00
{
2009-03-15 15:25:18 +00:00
return ScrollMainWindowTo ( TileX ( tile ) * TILE_SIZE + TILE_SIZE / 2 , TileY ( tile ) * TILE_SIZE + TILE_SIZE / 2 , - 1 , instant ) ;
2004-08-09 17:04:08 +00:00
}
2010-10-23 18:28:20 +00:00
/**
* Set a tile to display a red error square .
* @ param tile Tile that should show the red error square .
*/
2004-08-09 17:04:08 +00:00
void SetRedErrorSquare ( TileIndex tile )
{
TileIndex old ;
old = _thd . redsq ;
_thd . redsq = tile ;
if ( tile ! = old ) {
2009-01-19 15:06:11 +00:00
if ( tile ! = INVALID_TILE ) MarkTileDirtyByTile ( tile ) ;
if ( old ! = INVALID_TILE ) MarkTileDirtyByTile ( old ) ;
2004-08-09 17:04:08 +00:00
}
}
2010-08-01 19:22:34 +00:00
/**
* Highlight \ a w by \ a h tiles at the cursor .
2009-08-14 18:41:03 +00:00
* @ param w Width of the highlighted tiles rectangle .
* @ param h Height of the highlighted tiles rectangle .
*/
2004-08-09 17:04:08 +00:00
void SetTileSelectSize ( int w , int h )
{
2006-04-03 05:32:11 +00:00
_thd . new_size . x = w * TILE_SIZE ;
_thd . new_size . y = h * TILE_SIZE ;
2005-05-27 15:05:54 +00:00
_thd . new_outersize . x = 0 ;
_thd . new_outersize . y = 0 ;
2004-08-09 17:04:08 +00:00
}
2005-07-17 20:14:58 +00:00
void SetTileSelectBigSize ( int ox , int oy , int sx , int sy )
{
2006-04-03 05:32:11 +00:00
_thd . offs . x = ox * TILE_SIZE ;
_thd . offs . y = oy * TILE_SIZE ;
_thd . new_outersize . x = sx * TILE_SIZE ;
_thd . new_outersize . y = sy * TILE_SIZE ;
2004-08-09 17:04:08 +00:00
}
2007-04-04 04:08:47 +00:00
/** returns the best autorail highlight type from map coordinates */
2008-01-09 09:45:45 +00:00
static HighLightStyle GetAutorailHT ( int x , int y )
2005-01-19 20:55:23 +00:00
{
2009-04-18 21:10:36 +00:00
return HT_RAIL | _autorail_piece [ x & TILE_UNIT_MASK ] [ y & TILE_UNIT_MASK ] ;
2005-01-19 20:55:23 +00:00
}
2004-08-09 17:04:08 +00:00
2010-12-30 15:32:31 +00:00
/**
* Reset tile highlighting .
*/
void TileHighlightData : : Reset ( )
{
this - > pos . x = 0 ;
this - > pos . y = 0 ;
this - > new_pos . x = 0 ;
this - > new_pos . y = 0 ;
}
2010-12-23 14:24:34 +00:00
/**
* Is the user dragging a ' diagonal rectangle ' ?
* @ return User is dragging a rotated rectangle .
*/
bool TileHighlightData : : IsDraggingDiagonal ( )
{
return ( this - > place_mode & HT_DIAGONAL ) ! = 0 & & _ctrl_pressed & & _left_button_down ;
}
2010-12-30 13:16:31 +00:00
/**
* Get the window that started the current highlighting .
2019-04-10 21:07:06 +00:00
* @ return The window that requested the current tile highlighting , or \ c nullptr if not available .
2010-12-30 13:16:31 +00:00
*/
Window * TileHighlightData : : GetCallbackWnd ( )
{
return FindWindowById ( this - > window_class , this - > window_number ) ;
}
2007-06-26 16:58:40 +00:00
/**
* Updates tile highlighting for all cases .
* Uses _thd . selstart and _thd . selend and _thd . place_mode ( set elsewhere ) to determine _thd . pos and _thd . size
* Also drawstyle is determined . Uses _thd . new . * as a buffer and calls SetSelectionTilesDirty ( ) twice ,
* Once for the old and once for the new selection .
* _thd is TileHighlightData , found in viewport . h
*/
2007-03-07 11:47:46 +00:00
void UpdateTileSelection ( )
2004-08-09 17:04:08 +00:00
{
2005-07-17 20:14:58 +00:00
int x1 ;
int y1 ;
2004-08-09 17:04:08 +00:00
2019-03-30 16:58:40 +00:00
if ( _thd . freeze ) return ;
2010-12-22 19:24:36 +00:00
HighLightStyle new_drawstyle = HT_NONE ;
bool new_diagonal = false ;
2004-08-09 17:04:08 +00:00
2010-12-23 14:15:05 +00:00
if ( ( _thd . place_mode & HT_DRAG_MASK ) = = HT_SPECIAL ) {
2005-05-27 15:05:54 +00:00
x1 = _thd . selend . x ;
y1 = _thd . selend . y ;
2004-08-09 17:04:08 +00:00
if ( x1 ! = - 1 ) {
2009-04-18 21:10:36 +00:00
int x2 = _thd . selstart . x & ~ TILE_UNIT_MASK ;
int y2 = _thd . selstart . y & ~ TILE_UNIT_MASK ;
x1 & = ~ TILE_UNIT_MASK ;
y1 & = ~ TILE_UNIT_MASK ;
2004-09-10 19:02:27 +00:00
2010-12-23 14:24:34 +00:00
if ( _thd . IsDraggingDiagonal ( ) ) {
2010-12-22 19:24:36 +00:00
new_diagonal = true ;
2010-12-13 15:09:59 +00:00
} else {
if ( x1 > = x2 ) Swap ( x1 , x2 ) ;
if ( y1 > = y2 ) Swap ( y1 , y2 ) ;
}
2005-05-27 15:05:54 +00:00
_thd . new_pos . x = x1 ;
_thd . new_pos . y = y1 ;
2010-12-13 15:09:59 +00:00
_thd . new_size . x = x2 - x1 ;
_thd . new_size . y = y2 - y1 ;
2010-12-22 19:24:36 +00:00
if ( ! new_diagonal ) {
2010-12-13 15:09:59 +00:00
_thd . new_size . x + = TILE_SIZE ;
_thd . new_size . y + = TILE_SIZE ;
}
2010-12-22 19:24:36 +00:00
new_drawstyle = _thd . next_drawstyle ;
2004-08-09 17:04:08 +00:00
}
2010-09-06 14:14:09 +00:00
} else if ( ( _thd . place_mode & HT_DRAG_MASK ) ! = HT_NONE ) {
2005-07-17 20:14:58 +00:00
Point pt = GetTileBelowCursor ( ) ;
2004-08-09 17:04:08 +00:00
x1 = pt . x ;
y1 = pt . y ;
if ( x1 ! = - 1 ) {
2009-04-19 21:26:06 +00:00
switch ( _thd . place_mode & HT_DRAG_MASK ) {
2009-04-19 10:31:30 +00:00
case HT_RECT :
2010-12-22 19:24:36 +00:00
new_drawstyle = HT_RECT ;
2005-01-19 20:55:23 +00:00
break ;
2009-04-19 10:31:30 +00:00
case HT_POINT :
2010-12-22 19:24:36 +00:00
new_drawstyle = HT_POINT ;
2009-04-18 21:10:36 +00:00
x1 + = TILE_SIZE / 2 ;
y1 + = TILE_SIZE / 2 ;
2005-01-19 20:55:23 +00:00
break ;
2009-04-19 10:31:30 +00:00
case HT_RAIL :
2009-04-19 21:26:06 +00:00
/* Draw one highlighted tile in any direction */
2010-12-22 19:24:36 +00:00
new_drawstyle = GetAutorailHT ( pt . x , pt . y ) ;
2009-04-19 21:26:06 +00:00
break ;
case HT_LINE :
switch ( _thd . place_mode & HT_DIR_MASK ) {
2010-12-22 19:24:36 +00:00
case HT_DIR_X : new_drawstyle = HT_LINE | HT_DIR_X ; break ;
case HT_DIR_Y : new_drawstyle = HT_LINE | HT_DIR_Y ; break ;
2009-04-19 21:26:06 +00:00
case HT_DIR_HU :
case HT_DIR_HL :
2010-12-22 19:24:36 +00:00
new_drawstyle = ( pt . x & TILE_UNIT_MASK ) + ( pt . y & TILE_UNIT_MASK ) < = TILE_SIZE ? HT_LINE | HT_DIR_HU : HT_LINE | HT_DIR_HL ;
2009-04-19 21:26:06 +00:00
break ;
case HT_DIR_VL :
case HT_DIR_VR :
2010-12-22 19:24:36 +00:00
new_drawstyle = ( pt . x & TILE_UNIT_MASK ) > ( pt . y & TILE_UNIT_MASK ) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR ;
2009-04-19 21:26:06 +00:00
break ;
default : NOT_REACHED ( ) ;
}
_thd . selstart . x = x1 & ~ TILE_UNIT_MASK ;
_thd . selstart . y = y1 & ~ TILE_UNIT_MASK ;
2007-11-18 23:13:53 +00:00
break ;
default :
NOT_REACHED ( ) ;
2004-08-09 17:04:08 +00:00
}
2009-04-18 21:10:36 +00:00
_thd . new_pos . x = x1 & ~ TILE_UNIT_MASK ;
_thd . new_pos . y = y1 & ~ TILE_UNIT_MASK ;
2004-08-09 17:04:08 +00:00
}
}
2007-04-04 04:08:47 +00:00
/* redraw selection */
2010-12-22 19:24:36 +00:00
if ( _thd . drawstyle ! = new_drawstyle | |
2005-05-27 15:05:54 +00:00
_thd . pos . x ! = _thd . new_pos . x | | _thd . pos . y ! = _thd . new_pos . y | |
2006-09-03 22:39:02 +00:00
_thd . size . x ! = _thd . new_size . x | | _thd . size . y ! = _thd . new_size . y | |
2007-06-26 16:58:40 +00:00
_thd . outersize . x ! = _thd . new_outersize . x | |
2010-12-13 15:09:59 +00:00
_thd . outersize . y ! = _thd . new_outersize . y | |
2010-12-22 19:24:36 +00:00
_thd . diagonal ! = new_diagonal ) {
2010-12-24 14:55:31 +00:00
/* Clear the old tile selection? */
if ( ( _thd . drawstyle & HT_DRAG_MASK ) ! = HT_NONE ) SetSelectionTilesDirty ( ) ;
2004-08-09 17:04:08 +00:00
2010-12-22 19:24:36 +00:00
_thd . drawstyle = new_drawstyle ;
2009-02-08 15:45:34 +00:00
_thd . pos = _thd . new_pos ;
_thd . size = _thd . new_size ;
2005-05-27 15:05:54 +00:00
_thd . outersize = _thd . new_outersize ;
2010-12-22 19:24:36 +00:00
_thd . diagonal = new_diagonal ;
2009-02-08 15:45:34 +00:00
_thd . dirty = 0xff ;
2004-08-09 17:04:08 +00:00
2010-12-24 14:55:31 +00:00
/* Draw the new tile selection? */
if ( ( new_drawstyle & HT_DRAG_MASK ) ! = HT_NONE ) SetSelectionTilesDirty ( ) ;
2004-08-09 17:04:08 +00:00
}
}
2010-08-01 19:22:34 +00:00
/**
* Displays the measurement tooltips when selecting multiple tiles
2008-08-02 11:26:25 +00:00
* @ param str String to be displayed
* @ param paramcount number of params to deal with
* @ param params ( optional ) up to 5 pieces of additional information that may be added to a tooltip
2010-10-03 12:20:50 +00:00
* @ param close_cond Condition for closing this tooltip .
2008-08-02 11:26:25 +00:00
*/
2021-04-05 11:30:58 +00:00
static inline void ShowMeasurementTooltips ( StringID str , uint paramcount , const uint64 params [ ] , TooltipCloseCondition close_cond = TCC_EXIT_VIEWPORT )
2008-08-02 11:26:25 +00:00
{
if ( ! _settings_client . gui . measure_tooltip ) return ;
2010-12-30 13:18:04 +00:00
GuiShowTooltips ( _thd . GetCallbackWnd ( ) , str , paramcount , params , close_cond ) ;
2008-08-02 11:26:25 +00:00
}
2019-03-21 18:03:39 +00:00
static void HideMeasurementTooltips ( )
2019-03-20 01:21:10 +00:00
{
2021-05-17 13:46:38 +00:00
CloseWindowById ( WC_TOOLTIPS , 0 ) ;
2019-03-20 01:21:10 +00:00
}
2007-04-04 04:08:47 +00:00
/** highlighting tiles while only going over them with the mouse */
2008-05-08 13:21:55 +00:00
void VpStartPlaceSizing ( TileIndex tile , ViewportPlaceMethod method , ViewportDragDropSelectionProcess process )
2004-08-09 17:04:08 +00:00
{
2007-05-23 12:45:56 +00:00
_thd . select_method = method ;
_thd . select_proc = process ;
2006-04-03 05:32:11 +00:00
_thd . selend . x = TileX ( tile ) * TILE_SIZE ;
_thd . selstart . x = TileX ( tile ) * TILE_SIZE ;
_thd . selend . y = TileY ( tile ) * TILE_SIZE ;
_thd . selstart . y = TileY ( tile ) * TILE_SIZE ;
2007-11-18 20:05:44 +00:00
/* Needed so several things (road, autoroad, bridges, ...) are placed correctly.
* In effect , placement starts from the centre of a tile
*/
if ( method = = VPM_X_OR_Y | | method = = VPM_FIX_X | | method = = VPM_FIX_Y ) {
_thd . selend . x + = TILE_SIZE / 2 ;
_thd . selend . y + = TILE_SIZE / 2 ;
_thd . selstart . x + = TILE_SIZE / 2 ;
_thd . selstart . y + = TILE_SIZE / 2 ;
}
2010-12-24 14:48:38 +00:00
HighLightStyle others = _thd . place_mode & ~ ( HT_DRAG_MASK | HT_DIR_MASK ) ;
2010-12-23 14:15:05 +00:00
if ( ( _thd . place_mode & HT_DRAG_MASK ) = = HT_RECT ) {
_thd . place_mode = HT_SPECIAL | others ;
_thd . next_drawstyle = HT_RECT | others ;
2009-04-24 17:23:32 +00:00
} else if ( _thd . place_mode & ( HT_RAIL | HT_LINE ) ) {
2010-12-23 14:15:05 +00:00
_thd . place_mode = HT_SPECIAL | others ;
_thd . next_drawstyle = _thd . drawstyle | others ;
2004-08-09 17:04:08 +00:00
} else {
2010-12-23 14:15:05 +00:00
_thd . place_mode = HT_SPECIAL | others ;
_thd . next_drawstyle = HT_POINT | others ;
2004-08-09 17:04:08 +00:00
}
_special_mouse_mode = WSM_SIZING ;
}
2020-06-26 19:30:18 +00:00
/** Drag over the map while holding the left mouse down. */
void VpStartDragging ( ViewportDragDropSelectionProcess process )
{
_thd . select_method = VPM_X_AND_Y ;
_thd . select_proc = process ;
_thd . selstart . x = 0 ;
_thd . selstart . y = 0 ;
_thd . next_drawstyle = HT_RECT ;
_special_mouse_mode = WSM_DRAGGING ;
}
2004-08-09 17:04:08 +00:00
void VpSetPlaceSizingLimit ( int limit )
{
_thd . sizelimit = limit ;
}
2006-10-12 15:13:40 +00:00
/**
2009-03-14 18:16:29 +00:00
* Highlights all tiles between a set of two tiles . Used in dock and tunnel placement
* @ param from TileIndex of the first tile to highlight
2010-08-01 19:44:49 +00:00
* @ param to TileIndex of the last tile to highlight
*/
2006-10-12 15:13:40 +00:00
void VpSetPresizeRange ( TileIndex from , TileIndex to )
2004-08-09 17:04:08 +00:00
{
2007-06-21 19:08:47 +00:00
uint64 distance = DistanceManhattan ( from , to ) + 1 ;
2006-10-12 15:13:40 +00:00
2006-04-03 05:32:11 +00:00
_thd . selend . x = TileX ( to ) * TILE_SIZE ;
_thd . selend . y = TileY ( to ) * TILE_SIZE ;
_thd . selstart . x = TileX ( from ) * TILE_SIZE ;
_thd . selstart . y = TileY ( from ) * TILE_SIZE ;
2005-05-27 15:05:54 +00:00
_thd . next_drawstyle = HT_RECT ;
2006-10-12 15:13:40 +00:00
/* show measurement only if there is any length to speak of */
2019-03-20 01:21:10 +00:00
if ( distance > 1 ) {
ShowMeasurementTooltips ( STR_MEASURE_LENGTH , 1 , & distance ) ;
} else {
HideMeasurementTooltips ( ) ;
}
2004-08-09 17:04:08 +00:00
}
2007-03-07 11:47:46 +00:00
static void VpStartPreSizing ( )
2004-08-09 17:04:08 +00:00
{
_thd . selend . x = - 1 ;
_special_mouse_mode = WSM_PRESIZE ;
}
2010-08-01 19:22:34 +00:00
/**
* returns information about the 2 x1 piece to be build .
2010-08-01 19:44:49 +00:00
* The lower bits ( 0 - 3 ) are the track type .
*/
2008-01-09 09:45:45 +00:00
static HighLightStyle Check2x1AutoRail ( int mode )
2005-01-19 20:55:23 +00:00
{
int fxpy = _tile_fract_coords . x + _tile_fract_coords . y ;
2009-04-18 21:10:36 +00:00
int sxpy = ( _thd . selend . x & TILE_UNIT_MASK ) + ( _thd . selend . y & TILE_UNIT_MASK ) ;
2005-01-19 20:55:23 +00:00
int fxmy = _tile_fract_coords . x - _tile_fract_coords . y ;
2009-04-18 21:10:36 +00:00
int sxmy = ( _thd . selend . x & TILE_UNIT_MASK ) - ( _thd . selend . y & TILE_UNIT_MASK ) ;
2005-01-19 20:55:23 +00:00
2006-02-01 07:36:15 +00:00
switch ( mode ) {
2008-01-09 09:45:45 +00:00
default : NOT_REACHED ( ) ;
case 0 : // end piece is lower right
2009-04-18 21:10:36 +00:00
if ( fxpy > = 20 & & sxpy < = 12 ) return HT_DIR_HL ;
if ( fxmy < - 3 & & sxmy > 3 ) return HT_DIR_VR ;
2008-01-09 09:45:45 +00:00
return HT_DIR_Y ;
2005-01-19 20:55:23 +00:00
2008-01-09 09:45:45 +00:00
case 1 :
2009-04-18 21:10:36 +00:00
if ( fxmy > 3 & & sxmy < - 3 ) return HT_DIR_VL ;
if ( fxpy < = 12 & & sxpy > = 20 ) return HT_DIR_HU ;
2008-01-09 09:45:45 +00:00
return HT_DIR_Y ;
2005-01-19 20:55:23 +00:00
2008-01-09 09:45:45 +00:00
case 2 :
2009-04-18 21:10:36 +00:00
if ( fxmy > 3 & & sxmy < - 3 ) return HT_DIR_VL ;
if ( fxpy > = 20 & & sxpy < = 12 ) return HT_DIR_HL ;
2008-01-09 09:45:45 +00:00
return HT_DIR_X ;
2005-01-19 20:55:23 +00:00
2008-01-09 09:45:45 +00:00
case 3 :
2009-04-18 21:10:36 +00:00
if ( fxmy < - 3 & & sxmy > 3 ) return HT_DIR_VR ;
if ( fxpy < = 12 & & sxpy > = 20 ) return HT_DIR_HU ;
2008-01-09 09:45:45 +00:00
return HT_DIR_X ;
2005-01-19 20:55:23 +00:00
}
}
2010-08-01 19:22:34 +00:00
/**
* Check if the direction of start and end tile should be swapped based on
2006-10-12 15:13:40 +00:00
* the dragging - style . Default directions are :
* in the case of a line ( HT_RAIL , HT_LINE ) : DIR_NE , DIR_NW , DIR_N , DIR_E
* in the case of a rect ( HT_RECT , HT_POINT ) : DIR_S , DIR_E
* For example dragging a rectangle area from south to north should be swapped to
* north - south ( DIR_S ) to obtain the same results with less code . This is what
* the return value signifies .
* @ param style HighLightStyle dragging style
2007-04-18 00:41:09 +00:00
* @ param start_tile start tile of drag
* @ param end_tile end tile of drag
2010-08-01 19:44:49 +00:00
* @ return boolean value which when true means start / end should be swapped
*/
2006-10-12 15:13:40 +00:00
static bool SwapDirection ( HighLightStyle style , TileIndex start_tile , TileIndex end_tile )
{
uint start_x = TileX ( start_tile ) ;
uint start_y = TileY ( start_tile ) ;
uint end_x = TileX ( end_tile ) ;
uint end_y = TileY ( end_tile ) ;
switch ( style & HT_DRAG_MASK ) {
case HT_RAIL :
case HT_LINE : return ( end_x > start_x | | ( end_x = = start_x & & end_y > start_y ) ) ;
case HT_RECT :
case HT_POINT : return ( end_x ! = start_x & & end_y < start_y ) ;
default : NOT_REACHED ( ) ;
}
return false ;
}
2010-08-01 19:22:34 +00:00
/**
* Calculates height difference between one tile and another .
2010-05-07 20:39:00 +00:00
* Multiplies the result to suit the standard given by # TILE_HEIGHT_STEP .
*
2009-03-14 18:16:29 +00:00
* To correctly get the height difference we need the direction we are dragging
* in , as well as with what kind of tool we are dragging . For example a horizontal
* autorail tool that starts in bottom and ends at the top of a tile will need the
* maximum of SW , S and SE , N corners respectively . This is handled by the lookup table below
2010-05-07 20:20:03 +00:00
* See # _tileoffs_by_dir in map . cpp for the direction enums if you can ' t figure out the values yourself .
* @ param style Highlighting style of the drag . This includes direction and style ( autorail , rect , etc . )
* @ param distance Number of tiles dragged , important for horizontal / vertical drags , ignored for others .
* @ param start_tile Start tile of the drag operation .
* @ param end_tile End tile of the drag operation .
* @ return Height difference between two tiles . The tile measurement tool utilizes this value in its tooltip .
*/
2006-10-12 15:13:40 +00:00
static int CalcHeightdiff ( HighLightStyle style , uint distance , TileIndex start_tile , TileIndex end_tile )
{
bool swap = SwapDirection ( style , start_tile , end_tile ) ;
2010-05-07 20:20:03 +00:00
uint h0 , h1 ; // Start height and end height.
2006-10-12 15:13:40 +00:00
if ( start_tile = = end_tile ) return 0 ;
2007-01-19 11:47:48 +00:00
if ( swap ) Swap ( start_tile , end_tile ) ;
2006-10-12 15:13:40 +00:00
switch ( style & HT_DRAG_MASK ) {
case HT_RECT : {
static const TileIndexDiffC heightdiff_area_by_dir [ ] = {
2009-03-15 00:32:18 +00:00
/* Start */ { 1 , 0 } , /* Dragging east */ { 0 , 0 } , // Dragging south
/* End */ { 0 , 1 } , /* Dragging east */ { 1 , 1 } // Dragging south
2006-10-12 15:13:40 +00:00
} ;
/* In the case of an area we can determine whether we were dragging south or
* east by checking the X - coordinates of the tiles */
2010-05-07 20:20:03 +00:00
byte style_t = ( byte ) ( TileX ( end_tile ) > TileX ( start_tile ) ) ;
2006-10-12 15:13:40 +00:00
start_tile = TILE_ADD ( start_tile , ToTileIndexDiff ( heightdiff_area_by_dir [ style_t ] ) ) ;
end_tile = TILE_ADD ( end_tile , ToTileIndexDiff ( heightdiff_area_by_dir [ 2 + style_t ] ) ) ;
2017-08-15 15:56:34 +00:00
FALLTHROUGH ;
2006-10-12 15:13:40 +00:00
}
2010-08-01 20:52:11 +00:00
2006-10-12 15:13:40 +00:00
case HT_POINT :
h0 = TileHeight ( start_tile ) ;
h1 = TileHeight ( end_tile ) ;
break ;
2009-03-15 00:32:18 +00:00
default : { // All other types, this is mostly only line/autorail
2006-10-12 15:13:40 +00:00
static const HighLightStyle flip_style_direction [ ] = {
HT_DIR_X , HT_DIR_Y , HT_DIR_HL , HT_DIR_HU , HT_DIR_VR , HT_DIR_VL
} ;
static const TileIndexDiffC heightdiff_line_by_dir [ ] = {
2009-03-15 00:32:18 +00:00
/* Start */ { 1 , 0 } , { 1 , 1 } , /* HT_DIR_X */ { 0 , 1 } , { 1 , 1 } , // HT_DIR_Y
/* Start */ { 1 , 0 } , { 0 , 0 } , /* HT_DIR_HU */ { 1 , 0 } , { 1 , 1 } , // HT_DIR_HL
/* Start */ { 1 , 0 } , { 1 , 1 } , /* HT_DIR_VL */ { 0 , 1 } , { 1 , 1 } , // HT_DIR_VR
2006-10-12 15:13:40 +00:00
2009-03-15 00:32:18 +00:00
/* Start */ { 0 , 1 } , { 0 , 0 } , /* HT_DIR_X */ { 1 , 0 } , { 0 , 0 } , // HT_DIR_Y
/* End */ { 0 , 1 } , { 0 , 0 } , /* HT_DIR_HU */ { 1 , 1 } , { 0 , 1 } , // HT_DIR_HL
/* End */ { 1 , 0 } , { 0 , 0 } , /* HT_DIR_VL */ { 0 , 0 } , { 0 , 1 } , // HT_DIR_VR
2006-10-12 15:13:40 +00:00
} ;
distance % = 2 ; // we're only interested if the distance is even or uneven
style & = HT_DIR_MASK ;
/* To handle autorail, we do some magic to be able to use a lookup table.
* Firstly if we drag the other way around , we switch start & end , and if needed
* also flip the drag - position . Eg if it was on the left , and the distance is even
* that means the end , which is now the start is on the right */
if ( swap & & distance = = 0 ) style = flip_style_direction [ style ] ;
/* Use lookup table for start-tile based on HighLightStyle direction */
2010-05-07 20:20:03 +00:00
byte style_t = style * 2 ;
2006-10-12 15:13:40 +00:00
assert ( style_t < lengthof ( heightdiff_line_by_dir ) - 13 ) ;
h0 = TileHeight ( TILE_ADD ( start_tile , ToTileIndexDiff ( heightdiff_line_by_dir [ style_t ] ) ) ) ;
2010-05-07 20:20:03 +00:00
uint ht = TileHeight ( TILE_ADD ( start_tile , ToTileIndexDiff ( heightdiff_line_by_dir [ style_t + 1 ] ) ) ) ;
2021-01-08 10:16:18 +00:00
h0 = std : : max ( h0 , ht ) ;
2006-10-12 15:13:40 +00:00
/* Use lookup table for end-tile based on HighLightStyle direction
* flip around side ( lower / upper , left / right ) based on distance */
if ( distance = = 0 ) style_t = flip_style_direction [ style ] * 2 ;
assert ( style_t < lengthof ( heightdiff_line_by_dir ) - 13 ) ;
h1 = TileHeight ( TILE_ADD ( end_tile , ToTileIndexDiff ( heightdiff_line_by_dir [ 12 + style_t ] ) ) ) ;
ht = TileHeight ( TILE_ADD ( end_tile , ToTileIndexDiff ( heightdiff_line_by_dir [ 12 + style_t + 1 ] ) ) ) ;
2021-01-08 10:16:18 +00:00
h1 = std : : max ( h1 , ht ) ;
2010-08-01 18:53:30 +00:00
break ;
}
2006-10-12 15:13:40 +00:00
}
2007-01-19 11:47:48 +00:00
if ( swap ) Swap ( h0 , h1 ) ;
2010-05-07 20:39:00 +00:00
return ( int ) ( h1 - h0 ) * TILE_HEIGHT_STEP ;
2006-10-12 15:13:40 +00:00
}
2005-01-19 20:55:23 +00:00
2006-10-18 14:42:19 +00:00
static const StringID measure_strings_length [ ] = { STR_NULL , STR_MEASURE_LENGTH , STR_MEASURE_LENGTH_HEIGHTDIFF } ;
2006-10-18 14:20:10 +00:00
2009-04-19 21:26:06 +00:00
/**
* Check for underflowing the map .
* @ param test the variable to test for underflowing
* @ param other the other variable to update to keep the line
* @ param mult the constant to multiply the difference by for \ c other
*/
static void CheckUnderflow ( int & test , int & other , int mult )
{
if ( test > = 0 ) return ;
other + = mult * test ;
test = 0 ;
}
/**
* Check for overflowing the map .
* @ param test the variable to test for overflowing
* @ param other the other variable to update to keep the line
* @ param max the maximum value for the \ c test variable
* @ param mult the constant to multiply the difference by for \ c other
*/
static void CheckOverflow ( int & test , int & other , int max , int mult )
{
if ( test < = max ) return ;
other + = mult * ( test - max ) ;
test = max ;
}
2007-04-04 04:08:47 +00:00
/** while dragging */
2010-12-24 17:51:46 +00:00
static void CalcRaildirsDrawstyle ( int x , int y , int method )
2004-08-09 17:04:08 +00:00
{
2006-10-10 14:05:39 +00:00
HighLightStyle b ;
2004-09-10 19:02:27 +00:00
2010-12-24 17:51:46 +00:00
int dx = _thd . selstart . x - ( _thd . selend . x & ~ TILE_UNIT_MASK ) ;
int dy = _thd . selstart . y - ( _thd . selend . y & ~ TILE_UNIT_MASK ) ;
2009-04-18 21:10:36 +00:00
uint w = abs ( dx ) + TILE_SIZE ;
uint h = abs ( dy ) + TILE_SIZE ;
2005-01-19 20:55:23 +00:00
2009-04-19 21:26:06 +00:00
if ( method & ~ ( VPM_RAILDIRS | VPM_SIGNALDIRS ) ) {
/* We 'force' a selection direction; first four rail buttons. */
method & = ~ ( VPM_RAILDIRS | VPM_SIGNALDIRS ) ;
2010-12-24 17:51:46 +00:00
int raw_dx = _thd . selstart . x - _thd . selend . x ;
int raw_dy = _thd . selstart . y - _thd . selend . y ;
2009-04-19 21:26:06 +00:00
switch ( method ) {
case VPM_FIX_X :
b = HT_LINE | HT_DIR_Y ;
2010-12-24 17:51:46 +00:00
x = _thd . selstart . x ;
2009-04-19 21:26:06 +00:00
break ;
case VPM_FIX_Y :
b = HT_LINE | HT_DIR_X ;
2010-12-24 17:51:46 +00:00
y = _thd . selstart . y ;
2009-04-19 21:26:06 +00:00
break ;
case VPM_FIX_HORIZONTAL :
if ( dx = = - dy ) {
/* We are on a straight horizontal line. Determine the 'rail'
* to build based the sub tile location . */
b = ( x & TILE_UNIT_MASK ) + ( y & TILE_UNIT_MASK ) > = TILE_SIZE ? HT_LINE | HT_DIR_HL : HT_LINE | HT_DIR_HU ;
} else {
/* We are not on a straight line. Determine the rail to build
* based on whether we are above or below it . */
2010-05-13 11:19:30 +00:00
b = dx + dy > = ( int ) TILE_SIZE ? HT_LINE | HT_DIR_HU : HT_LINE | HT_DIR_HL ;
2009-04-19 21:26:06 +00:00
/* Calculate where a horizontal line through the start point and
* a vertical line from the selected end point intersect and
* use that point as the end point . */
int offset = ( raw_dx - raw_dy ) / 2 ;
2010-12-24 17:51:46 +00:00
x = _thd . selstart . x - ( offset & ~ TILE_UNIT_MASK ) ;
y = _thd . selstart . y + ( offset & ~ TILE_UNIT_MASK ) ;
2009-04-19 21:26:06 +00:00
/* 'Build' the last half rail tile if needed */
if ( ( offset & TILE_UNIT_MASK ) > ( TILE_SIZE / 2 ) ) {
2010-05-13 11:19:30 +00:00
if ( dx + dy > = ( int ) TILE_SIZE ) {
2010-05-21 05:42:41 +00:00
x + = ( dx + dy < 0 ) ? ( int ) TILE_SIZE : - ( int ) TILE_SIZE ;
2009-04-19 21:26:06 +00:00
} else {
2010-05-21 05:42:41 +00:00
y + = ( dx + dy < 0 ) ? ( int ) TILE_SIZE : - ( int ) TILE_SIZE ;
2009-04-19 21:26:06 +00:00
}
}
/* Make sure we do not overflow the map! */
CheckUnderflow ( x , y , 1 ) ;
CheckUnderflow ( y , x , 1 ) ;
2023-01-21 09:43:03 +00:00
CheckOverflow ( x , y , ( Map : : MaxX ( ) - 1 ) * TILE_SIZE , 1 ) ;
CheckOverflow ( y , x , ( Map : : MaxY ( ) - 1 ) * TILE_SIZE , 1 ) ;
assert ( x > = 0 & & y > = 0 & & x < = ( int ) ( Map : : MaxX ( ) * TILE_SIZE ) & & y < = ( int ) ( Map : : MaxY ( ) * TILE_SIZE ) ) ;
2009-04-19 21:26:06 +00:00
}
break ;
case VPM_FIX_VERTICAL :
if ( dx = = dy ) {
/* We are on a straight vertical line. Determine the 'rail'
* to build based the sub tile location . */
b = ( x & TILE_UNIT_MASK ) > ( y & TILE_UNIT_MASK ) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR ;
} else {
/* We are not on a straight line. Determine the rail to build
* based on whether we are left or right from it . */
b = dx < dy ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR ;
/* Calculate where a vertical line through the start point and
* a horizontal line from the selected end point intersect and
* use that point as the end point . */
2010-06-07 19:43:40 +00:00
int offset = ( raw_dx + raw_dy + ( int ) TILE_SIZE ) / 2 ;
2010-12-24 17:51:46 +00:00
x = _thd . selstart . x - ( offset & ~ TILE_UNIT_MASK ) ;
y = _thd . selstart . y - ( offset & ~ TILE_UNIT_MASK ) ;
2009-04-19 21:26:06 +00:00
/* 'Build' the last half rail tile if needed */
if ( ( offset & TILE_UNIT_MASK ) > ( TILE_SIZE / 2 ) ) {
if ( dx - dy < 0 ) {
2010-05-21 05:42:41 +00:00
y + = ( dx > dy ) ? ( int ) TILE_SIZE : - ( int ) TILE_SIZE ;
2009-04-19 21:26:06 +00:00
} else {
2010-05-21 05:42:41 +00:00
x + = ( dx < dy ) ? ( int ) TILE_SIZE : - ( int ) TILE_SIZE ;
2009-04-19 21:26:06 +00:00
}
}
/* Make sure we do not overflow the map! */
CheckUnderflow ( x , y , - 1 ) ;
CheckUnderflow ( y , x , - 1 ) ;
2023-01-21 09:43:03 +00:00
CheckOverflow ( x , y , ( Map : : MaxX ( ) - 1 ) * TILE_SIZE , - 1 ) ;
CheckOverflow ( y , x , ( Map : : MaxY ( ) - 1 ) * TILE_SIZE , - 1 ) ;
assert ( x > = 0 & & y > = 0 & & x < = ( int ) ( Map : : MaxX ( ) * TILE_SIZE ) & & y < = ( int ) ( Map : : MaxY ( ) * TILE_SIZE ) ) ;
2009-04-19 21:26:06 +00:00
}
break ;
default :
NOT_REACHED ( ) ;
}
2010-12-24 17:51:46 +00:00
} else if ( TileVirtXY ( _thd . selstart . x , _thd . selstart . y ) = = TileVirtXY ( x , y ) ) { // check if we're only within one tile
2009-04-19 21:26:06 +00:00
if ( method & VPM_RAILDIRS ) {
2005-07-17 20:14:58 +00:00
b = GetAutorailHT ( x , y ) ;
2006-06-27 21:25:53 +00:00
} else { // rect for autosignals on one tile
2005-07-17 20:14:58 +00:00
b = HT_RECT ;
2006-06-27 21:25:53 +00:00
}
2009-04-18 21:10:36 +00:00
} else if ( h = = TILE_SIZE ) { // Is this in X direction?
2010-05-13 11:19:30 +00:00
if ( dx = = ( int ) TILE_SIZE ) { // 2x1 special handling
2005-01-19 20:55:23 +00:00
b = ( Check2x1AutoRail ( 3 ) ) | HT_LINE ;
2010-05-13 11:19:30 +00:00
} else if ( dx = = - ( int ) TILE_SIZE ) {
2005-01-19 20:55:23 +00:00
b = ( Check2x1AutoRail ( 2 ) ) | HT_LINE ;
2006-06-27 21:25:53 +00:00
} else {
2005-01-19 20:55:23 +00:00
b = HT_LINE | HT_DIR_X ;
2006-06-27 21:25:53 +00:00
}
2010-12-24 17:51:46 +00:00
y = _thd . selstart . y ;
2009-04-18 21:10:36 +00:00
} else if ( w = = TILE_SIZE ) { // Or Y direction?
2010-05-13 11:19:30 +00:00
if ( dy = = ( int ) TILE_SIZE ) { // 2x1 special handling
2005-01-19 20:55:23 +00:00
b = ( Check2x1AutoRail ( 1 ) ) | HT_LINE ;
2010-05-13 11:19:30 +00:00
} else if ( dy = = - ( int ) TILE_SIZE ) { // 2x1 other direction
2005-01-19 20:55:23 +00:00
b = ( Check2x1AutoRail ( 0 ) ) | HT_LINE ;
2006-06-27 21:25:53 +00:00
} else {
2005-01-19 20:55:23 +00:00
b = HT_LINE | HT_DIR_Y ;
2006-06-27 21:25:53 +00:00
}
2010-12-24 17:51:46 +00:00
x = _thd . selstart . x ;
2005-01-19 20:55:23 +00:00
} else if ( w > h * 2 ) { // still count as x dir?
2005-07-17 20:14:58 +00:00
b = HT_LINE | HT_DIR_X ;
2010-12-24 17:51:46 +00:00
y = _thd . selstart . y ;
2005-01-19 20:55:23 +00:00
} else if ( h > w * 2 ) { // still count as y dir?
2005-07-17 20:14:58 +00:00
b = HT_LINE | HT_DIR_Y ;
2010-12-24 17:51:46 +00:00
x = _thd . selstart . x ;
2005-01-19 20:55:23 +00:00
} else { // complicated direction
2006-10-10 14:05:39 +00:00
int d = w - h ;
2010-12-24 17:51:46 +00:00
_thd . selend . x = _thd . selend . x & ~ TILE_UNIT_MASK ;
_thd . selend . y = _thd . selend . y & ~ TILE_UNIT_MASK ;
2004-08-09 17:04:08 +00:00
2009-03-15 00:32:18 +00:00
/* four cases. */
2010-12-24 17:51:46 +00:00
if ( x > _thd . selstart . x ) {
if ( y > _thd . selstart . y ) {
2009-03-15 00:32:18 +00:00
/* south */
2005-07-17 20:14:58 +00:00
if ( d = = 0 ) {
2009-04-18 21:10:36 +00:00
b = ( x & TILE_UNIT_MASK ) > ( y & TILE_UNIT_MASK ) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR ;
2005-07-17 20:14:58 +00:00
} else if ( d > = 0 ) {
2010-12-24 17:51:46 +00:00
x = _thd . selstart . x + h ;
2005-07-17 20:14:58 +00:00
b = HT_LINE | HT_DIR_VL ;
} else {
2010-12-24 17:51:46 +00:00
y = _thd . selstart . y + w ;
2005-07-17 20:14:58 +00:00
b = HT_LINE | HT_DIR_VR ;
2009-04-18 21:10:36 +00:00
}
2004-08-09 17:04:08 +00:00
} else {
2009-03-15 00:32:18 +00:00
/* west */
2005-07-17 20:14:58 +00:00
if ( d = = 0 ) {
2009-04-18 21:10:36 +00:00
b = ( x & TILE_UNIT_MASK ) + ( y & TILE_UNIT_MASK ) > = TILE_SIZE ? HT_LINE | HT_DIR_HL : HT_LINE | HT_DIR_HU ;
2005-07-17 20:14:58 +00:00
} else if ( d > = 0 ) {
2010-12-24 17:51:46 +00:00
x = _thd . selstart . x + h ;
2005-07-17 20:14:58 +00:00
b = HT_LINE | HT_DIR_HL ;
} else {
2010-12-24 17:51:46 +00:00
y = _thd . selstart . y - w ;
2005-07-17 20:14:58 +00:00
b = HT_LINE | HT_DIR_HU ;
}
2004-08-09 17:04:08 +00:00
}
} else {
2010-12-24 17:51:46 +00:00
if ( y > _thd . selstart . y ) {
2009-03-15 00:32:18 +00:00
/* east */
2005-07-17 20:14:58 +00:00
if ( d = = 0 ) {
2009-04-18 21:10:36 +00:00
b = ( x & TILE_UNIT_MASK ) + ( y & TILE_UNIT_MASK ) > = TILE_SIZE ? HT_LINE | HT_DIR_HL : HT_LINE | HT_DIR_HU ;
2005-07-17 20:14:58 +00:00
} else if ( d > = 0 ) {
2010-12-24 17:51:46 +00:00
x = _thd . selstart . x - h ;
2005-07-17 20:14:58 +00:00
b = HT_LINE | HT_DIR_HU ;
} else {
2010-12-24 17:51:46 +00:00
y = _thd . selstart . y + w ;
2005-07-17 20:14:58 +00:00
b = HT_LINE | HT_DIR_HL ;
2009-04-18 21:10:36 +00:00
}
2004-08-09 17:04:08 +00:00
} else {
2009-03-15 00:32:18 +00:00
/* north */
2005-07-17 20:14:58 +00:00
if ( d = = 0 ) {
2009-04-18 21:10:36 +00:00
b = ( x & TILE_UNIT_MASK ) > ( y & TILE_UNIT_MASK ) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR ;
2005-07-17 20:14:58 +00:00
} else if ( d > = 0 ) {
2010-12-24 17:51:46 +00:00
x = _thd . selstart . x - h ;
2005-07-17 20:14:58 +00:00
b = HT_LINE | HT_DIR_VR ;
} else {
2010-12-24 17:51:46 +00:00
y = _thd . selstart . y - w ;
2005-07-17 20:14:58 +00:00
b = HT_LINE | HT_DIR_VL ;
2009-04-18 21:10:36 +00:00
}
2004-08-09 17:04:08 +00:00
}
}
}
2006-10-12 15:13:40 +00:00
2008-05-29 15:13:28 +00:00
if ( _settings_client . gui . measure_tooltip ) {
2010-12-24 17:51:46 +00:00
TileIndex t0 = TileVirtXY ( _thd . selstart . x , _thd . selstart . y ) ;
2006-10-12 15:13:40 +00:00
TileIndex t1 = TileVirtXY ( x , y ) ;
uint distance = DistanceManhattan ( t0 , t1 ) + 1 ;
2006-10-18 14:20:10 +00:00
byte index = 0 ;
2007-06-21 19:08:47 +00:00
uint64 params [ 2 ] ;
2006-10-12 15:13:40 +00:00
2006-10-18 14:20:10 +00:00
if ( distance ! = 1 ) {
int heightdiff = CalcHeightdiff ( b , distance , t0 , t1 ) ;
/* If we are showing a tooltip for horizontal or vertical drags,
* 2 tiles have a length of 1. To bias towards the ceiling we add
* one before division . It feels more natural to count 3 lengths as 2 */
if ( ( b & HT_DIR_MASK ) ! = HT_DIR_X & & ( b & HT_DIR_MASK ) ! = HT_DIR_Y ) {
2010-04-18 14:56:05 +00:00
distance = CeilDiv ( distance , 2 ) ;
2006-10-18 14:20:10 +00:00
}
2006-10-12 15:13:40 +00:00
2006-10-18 14:20:10 +00:00
params [ index + + ] = distance ;
if ( heightdiff ! = 0 ) params [ index + + ] = heightdiff ;
2006-10-12 15:13:40 +00:00
}
2006-10-18 14:20:10 +00:00
2008-08-02 11:26:25 +00:00
ShowMeasurementTooltips ( measure_strings_length [ index ] , index , params ) ;
2006-10-12 15:13:40 +00:00
}
2010-12-24 17:51:46 +00:00
_thd . selend . x = x ;
_thd . selend . y = y ;
_thd . next_drawstyle = b ;
2004-08-09 17:04:08 +00:00
}
2006-10-10 14:05:39 +00:00
/**
* Selects tiles while dragging
* @ param x X coordinate of end of selection
* @ param y Y coordinate of end of selection
* @ param method modifies the way tiles are selected . Possible
2010-08-01 19:44:49 +00:00
* methods are VPM_ * in viewport . h
*/
2007-11-15 18:21:59 +00:00
void VpSelectTilesWithMethod ( int x , int y , ViewportPlaceMethod method )
2004-08-09 17:04:08 +00:00
{
2006-10-10 14:05:39 +00:00
int sx , sy ;
2006-10-12 15:13:40 +00:00
HighLightStyle style ;
2005-05-27 15:05:54 +00:00
2004-08-09 17:04:08 +00:00
if ( x = = - 1 ) {
2005-05-27 15:05:54 +00:00
_thd . selend . x = - 1 ;
2004-08-09 17:04:08 +00:00
return ;
}
2006-10-10 14:05:39 +00:00
/* Special handling of drag in any (8-way) direction */
2009-04-19 21:26:06 +00:00
if ( method & ( VPM_RAILDIRS | VPM_SIGNALDIRS ) ) {
2005-05-27 15:05:54 +00:00
_thd . selend . x = x ;
_thd . selend . y = y ;
2010-12-24 17:51:46 +00:00
CalcRaildirsDrawstyle ( x , y , method ) ;
2004-08-09 17:04:08 +00:00
return ;
}
2007-11-18 20:05:44 +00:00
/* Needed so level-land is placed correctly */
2010-12-23 20:25:55 +00:00
if ( ( _thd . next_drawstyle & HT_DRAG_MASK ) = = HT_POINT ) {
2006-10-10 14:05:39 +00:00
x + = TILE_SIZE / 2 ;
y + = TILE_SIZE / 2 ;
2005-07-17 20:14:58 +00:00
}
2004-08-09 17:04:08 +00:00
2005-05-27 15:05:54 +00:00
sx = _thd . selstart . x ;
sy = _thd . selstart . y ;
2004-08-09 17:04:08 +00:00
2010-02-01 23:13:15 +00:00
int limit = 0 ;
2005-07-17 20:14:58 +00:00
switch ( method ) {
2009-03-15 00:32:18 +00:00
case VPM_X_OR_Y : // drag in X or Y direction
2007-11-19 18:58:04 +00:00
if ( abs ( sy - y ) < abs ( sx - x ) ) {
2006-06-27 21:25:53 +00:00
y = sy ;
2006-10-12 15:13:40 +00:00
style = HT_DIR_X ;
2006-06-27 21:25:53 +00:00
} else {
x = sx ;
2006-10-12 15:13:40 +00:00
style = HT_DIR_Y ;
2006-06-27 21:25:53 +00:00
}
2006-10-12 15:13:40 +00:00
goto calc_heightdiff_single_direction ;
2010-02-01 23:13:15 +00:00
case VPM_X_LIMITED : // Drag in X direction (limited size).
limit = ( _thd . sizelimit - 1 ) * TILE_SIZE ;
2017-08-13 18:38:42 +00:00
FALLTHROUGH ;
2010-02-01 23:13:15 +00:00
2009-03-15 00:32:18 +00:00
case VPM_FIX_X : // drag in Y direction
2006-10-12 15:13:40 +00:00
x = sx ;
style = HT_DIR_Y ;
goto calc_heightdiff_single_direction ;
2010-02-01 23:13:15 +00:00
case VPM_Y_LIMITED : // Drag in Y direction (limited size).
limit = ( _thd . sizelimit - 1 ) * TILE_SIZE ;
2017-08-13 18:38:42 +00:00
FALLTHROUGH ;
2010-02-01 23:13:15 +00:00
2009-03-15 00:32:18 +00:00
case VPM_FIX_Y : // drag in X direction
2006-10-12 15:13:40 +00:00
y = sy ;
style = HT_DIR_X ;
calc_heightdiff_single_direction : ;
2010-02-01 23:13:15 +00:00
if ( limit > 0 ) {
x = sx + Clamp ( x - sx , - limit , limit ) ;
y = sy + Clamp ( y - sy , - limit , limit ) ;
}
2008-05-29 15:13:28 +00:00
if ( _settings_client . gui . measure_tooltip ) {
2006-10-12 15:13:40 +00:00
TileIndex t0 = TileVirtXY ( sx , sy ) ;
TileIndex t1 = TileVirtXY ( x , y ) ;
uint distance = DistanceManhattan ( t0 , t1 ) + 1 ;
2006-10-18 14:20:10 +00:00
byte index = 0 ;
2007-06-21 19:08:47 +00:00
uint64 params [ 2 ] ;
2006-10-12 15:13:40 +00:00
2006-10-18 14:20:10 +00:00
if ( distance ! = 1 ) {
/* With current code passing a HT_LINE style to calculate the height
* difference is enough . However if / when a point - tool is created
* with this method , function should be called with new_style ( below )
* instead of HT_LINE | style case HT_POINT is handled specially
* new_style : = ( _thd . next_drawstyle & HT_RECT ) ? HT_LINE | style : _thd . next_drawstyle ; */
int heightdiff = CalcHeightdiff ( HT_LINE | style , 0 , t0 , t1 ) ;
params [ index + + ] = distance ;
if ( heightdiff ! = 0 ) params [ index + + ] = heightdiff ;
2006-10-12 15:13:40 +00:00
}
2006-10-18 14:20:10 +00:00
2008-08-02 11:26:25 +00:00
ShowMeasurementTooltips ( measure_strings_length [ index ] , index , params ) ;
2010-08-01 18:53:30 +00:00
}
2010-08-04 07:31:29 +00:00
break ;
2004-08-09 17:04:08 +00:00
2010-02-01 23:13:15 +00:00
case VPM_X_AND_Y_LIMITED : // Drag an X by Y constrained rect area.
limit = ( _thd . sizelimit - 1 ) * TILE_SIZE ;
2007-11-19 18:38:10 +00:00
x = sx + Clamp ( x - sx , - limit , limit ) ;
y = sy + Clamp ( y - sy , - limit , limit ) ;
2017-08-13 18:38:42 +00:00
FALLTHROUGH ;
2010-02-01 23:13:15 +00:00
2010-08-04 07:31:29 +00:00
case VPM_X_AND_Y : // drag an X by Y area
2009-02-08 15:45:34 +00:00
if ( _settings_client . gui . measure_tooltip ) {
2006-10-18 14:20:10 +00:00
static const StringID measure_strings_area [ ] = {
STR_NULL , STR_NULL , STR_MEASURE_AREA , STR_MEASURE_AREA_HEIGHTDIFF
} ;
2006-10-12 15:13:40 +00:00
TileIndex t0 = TileVirtXY ( sx , sy ) ;
TileIndex t1 = TileVirtXY ( x , y ) ;
2007-11-26 16:01:29 +00:00
uint dx = Delta ( TileX ( t0 ) , TileX ( t1 ) ) + 1 ;
uint dy = Delta ( TileY ( t0 ) , TileY ( t1 ) ) + 1 ;
2006-10-18 14:20:10 +00:00
byte index = 0 ;
2007-06-21 19:08:47 +00:00
uint64 params [ 3 ] ;
2006-10-12 15:13:40 +00:00
/* If dragging an area (eg dynamite tool) and it is actually a single
* row / column , change the type to ' line ' to get proper calculation for height */
2008-01-09 09:45:45 +00:00
style = ( HighLightStyle ) _thd . next_drawstyle ;
2010-12-23 14:24:34 +00:00
if ( _thd . IsDraggingDiagonal ( ) ) {
2010-12-13 15:11:03 +00:00
/* Determine the "area" of the diagonal dragged selection.
* We assume the area is the number of tiles along the X
* edge and the number of tiles along the Y edge . However ,
* multiplying these two numbers does not give the exact
* number of tiles ; basically we are counting the black
* squares on a chess board and ignore the white ones to
* make the tile counts at the edges match up . There is no
* other way to make a proper count though .
*
* First convert to the rotated coordinate system . */
int dist_x = TileX ( t0 ) - TileX ( t1 ) ;
int dist_y = TileY ( t0 ) - TileY ( t1 ) ;
int a_max = dist_x + dist_y ;
int b_max = dist_y - dist_x ;
/* Now determine the size along the edge, but due to the
* chess board principle this counts double . */
a_max = abs ( a_max + ( a_max > 0 ? 2 : - 2 ) ) / 2 ;
b_max = abs ( b_max + ( b_max > 0 ? 2 : - 2 ) ) / 2 ;
/* We get a 1x1 on normal 2x1 rectangles, due to it being
* a seen as two sides . As the result for actual building
* will be the same as non - diagonal dragging revert to that
* behaviour to give it a more normally looking size . */
if ( a_max ! = 1 | | b_max ! = 1 ) {
dx = a_max ;
dy = b_max ;
}
} else if ( style & HT_RECT ) {
2006-10-12 15:13:40 +00:00
if ( dx = = 1 ) {
style = HT_LINE | HT_DIR_Y ;
} else if ( dy = = 1 ) {
style = HT_LINE | HT_DIR_X ;
}
}
2011-08-13 10:43:11 +00:00
if ( dx ! = 1 | | dy ! = 1 ) {
2006-10-18 14:20:10 +00:00
int heightdiff = CalcHeightdiff ( style , 0 , t0 , t1 ) ;
2006-10-12 15:13:40 +00:00
2011-08-13 10:44:15 +00:00
params [ index + + ] = dx - ( style & HT_POINT ? 1 : 0 ) ;
params [ index + + ] = dy - ( style & HT_POINT ? 1 : 0 ) ;
2006-10-18 14:20:10 +00:00
if ( heightdiff ! = 0 ) params [ index + + ] = heightdiff ;
2006-10-12 15:13:40 +00:00
}
2006-10-18 14:20:10 +00:00
2008-08-02 11:26:25 +00:00
ShowMeasurementTooltips ( measure_strings_area [ index ] , index , params ) ;
2006-10-12 15:13:40 +00:00
}
2010-08-04 07:31:29 +00:00
break ;
2006-10-12 15:13:40 +00:00
default : NOT_REACHED ( ) ;
2004-08-09 17:04:08 +00:00
}
2005-05-27 15:05:54 +00:00
_thd . selend . x = x ;
_thd . selend . y = y ;
2004-08-09 17:04:08 +00:00
}
2008-05-19 09:24:03 +00:00
/**
* Handle the mouse while dragging for placement / resizing .
2010-05-30 12:15:28 +00:00
* @ return State of handling the event .
2008-05-19 09:24:03 +00:00
*/
2010-05-30 12:15:28 +00:00
EventState VpHandlePlaceSizingDrag ( )
2004-08-09 17:04:08 +00:00
{
2020-06-26 19:30:18 +00:00
if ( _special_mouse_mode ! = WSM_SIZING & & _special_mouse_mode ! = WSM_DRAGGING ) return ES_NOT_HANDLED ;
2004-08-09 17:04:08 +00:00
2007-04-04 04:08:47 +00:00
/* stop drag mode if the window has been closed */
2010-12-30 13:18:04 +00:00
Window * w = _thd . GetCallbackWnd ( ) ;
2019-04-10 21:07:06 +00:00
if ( w = = nullptr ) {
2004-08-09 17:04:08 +00:00
ResetObjectToPlace ( ) ;
2010-05-30 12:15:28 +00:00
return ES_HANDLED ;
2004-08-09 17:04:08 +00:00
}
2007-04-04 04:08:47 +00:00
/* while dragging execute the drag procedure of the corresponding window (mostly VpSelectTilesWithMethod() ) */
2004-08-09 17:04:08 +00:00
if ( _left_button_down ) {
2020-06-26 19:30:18 +00:00
if ( _special_mouse_mode = = WSM_DRAGGING ) {
/* Only register a drag event when the mouse moved. */
if ( _thd . new_pos . x = = _thd . selstart . x & & _thd . new_pos . y = = _thd . selstart . y ) return ES_HANDLED ;
_thd . selstart . x = _thd . new_pos . x ;
_thd . selstart . y = _thd . new_pos . y ;
}
2008-05-10 13:46:36 +00:00
w - > OnPlaceDrag ( _thd . select_method , _thd . select_proc , GetTileBelowCursor ( ) ) ;
2010-05-30 12:15:28 +00:00
return ES_HANDLED ;
2004-08-09 17:04:08 +00:00
}
2020-06-26 19:30:18 +00:00
/* Mouse button released. */
2004-09-10 19:02:27 +00:00
_special_mouse_mode = WSM_NONE ;
2020-06-26 19:30:18 +00:00
if ( _special_mouse_mode = = WSM_DRAGGING ) return ES_HANDLED ;
/* Keep the selected tool, but reset it to the original mode. */
2010-12-24 14:48:38 +00:00
HighLightStyle others = _thd . place_mode & ~ ( HT_DRAG_MASK | HT_DIR_MASK ) ;
2010-12-23 14:15:05 +00:00
if ( ( _thd . next_drawstyle & HT_DRAG_MASK ) = = HT_RECT ) {
_thd . place_mode = HT_RECT | others ;
2009-04-19 21:26:06 +00:00
} else if ( _thd . select_method & VPM_SIGNALDIRS ) {
2010-12-23 14:15:05 +00:00
_thd . place_mode = HT_RECT | others ;
2009-04-19 21:26:06 +00:00
} else if ( _thd . select_method & VPM_RAILDIRS ) {
2010-12-23 14:15:05 +00:00
_thd . place_mode = ( _thd . select_method & ~ VPM_RAILDIRS ) ? _thd . next_drawstyle : ( HT_RAIL | others ) ;
2005-07-17 20:14:58 +00:00
} else {
2010-12-23 14:15:05 +00:00
_thd . place_mode = HT_POINT | others ;
2005-07-17 20:14:58 +00:00
}
2004-08-09 17:04:08 +00:00
SetTileSelectSize ( 1 , 1 ) ;
2019-03-21 18:03:39 +00:00
HideMeasurementTooltips ( ) ;
2008-05-10 13:46:36 +00:00
w - > OnPlaceMouseUp ( _thd . select_method , _thd . select_proc , _thd . selend , TileVirtXY ( _thd . selstart . x , _thd . selstart . y ) , TileVirtXY ( _thd . selend . x , _thd . selend . y ) ) ;
2004-09-10 19:02:27 +00:00
2010-05-30 12:15:28 +00:00
return ES_HANDLED ;
2004-08-09 17:04:08 +00:00
}
2015-07-28 08:16:52 +00:00
/**
* Change the cursor and mouse click / drag handling to a mode for performing special operations like tile area selection , object placement , etc .
* @ param icon New shape of the mouse cursor .
* @ param pal Palette to use .
* @ param mode Mode to perform .
* @ param w % Window requesting the mode change .
*/
2010-01-21 01:38:13 +00:00
void SetObjectToPlaceWnd ( CursorID icon , PaletteID pal , HighLightStyle mode , Window * w )
2004-08-09 17:04:08 +00:00
{
2007-01-14 19:57:49 +00:00
SetObjectToPlace ( icon , pal , mode , w - > window_class , w - > window_number ) ;
2004-08-09 17:04:08 +00:00
}
# include "table/animcursors.h"
2015-07-28 08:16:52 +00:00
/**
* Change the cursor and mouse click / drag handling to a mode for performing special operations like tile area selection , object placement , etc .
* @ param icon New shape of the mouse cursor .
* @ param pal Palette to use .
* @ param mode Mode to perform .
* @ param window_class % Window class of the window requesting the mode change .
* @ param window_num Number of the window in its class requesting the mode change .
*/
2010-01-21 01:38:13 +00:00
void SetObjectToPlace ( CursorID icon , PaletteID pal , HighLightStyle mode , WindowClass window_class , WindowNumber window_num )
2004-08-09 17:04:08 +00:00
{
2010-12-25 12:47:05 +00:00
if ( _thd . window_class ! = WC_INVALID ) {
/* Undo clicking on button and drag & drop */
2010-12-30 13:18:04 +00:00
Window * w = _thd . GetCallbackWnd ( ) ;
2010-12-25 12:47:05 +00:00
/* Call the abort function, but set the window class to something
* that will never be used to avoid infinite loops . Setting it to
* the ' next ' window class must not be done because recursion into
* this function might in some cases reset the newly set object to
* place or not properly reset the original selection . */
_thd . window_class = WC_INVALID ;
2019-04-10 21:07:06 +00:00
if ( w ! = nullptr ) {
2019-03-20 01:21:10 +00:00
w - > OnPlaceObjectAbort ( ) ;
HideMeasurementTooltips ( ) ;
}
2004-08-09 17:04:08 +00:00
}
2004-09-10 19:02:27 +00:00
2011-07-10 14:44:41 +00:00
/* Mark the old selection dirty, in case the selection shape or colour changes */
if ( ( _thd . drawstyle & HT_DRAG_MASK ) ! = HT_NONE ) SetSelectionTilesDirty ( ) ;
2004-08-09 17:04:08 +00:00
SetTileSelectSize ( 1 , 1 ) ;
2004-09-10 19:02:27 +00:00
2005-05-27 15:05:54 +00:00
_thd . make_square_red = false ;
2004-08-09 17:04:08 +00:00
2009-04-19 10:31:30 +00:00
if ( mode = = HT_DRAG ) { // HT_DRAG is for dragdropping trains in the depot window
mode = HT_NONE ;
2004-08-09 17:04:08 +00:00
_special_mouse_mode = WSM_DRAGDROP ;
} else {
_special_mouse_mode = WSM_NONE ;
}
2005-05-27 15:05:54 +00:00
_thd . place_mode = mode ;
_thd . window_class = window_class ;
_thd . window_number = window_num ;
2004-08-09 17:04:08 +00:00
2010-12-27 18:21:19 +00:00
if ( ( mode & HT_DRAG_MASK ) = = HT_SPECIAL ) { // special tools, like tunnels or docks start with presizing mode
2004-08-09 17:04:08 +00:00
VpStartPreSizing ( ) ;
2010-07-24 10:14:39 +00:00
}
2004-09-10 19:02:27 +00:00
2010-02-04 15:42:38 +00:00
if ( ( icon & ANIMCURSOR_FLAG ) ! = 0 ) {
SetAnimatedMouseCursor ( _animcursors [ icon & ~ ANIMCURSOR_FLAG ] ) ;
2008-05-15 14:41:56 +00:00
} else {
2007-01-14 19:57:49 +00:00
SetMouseCursor ( icon , pal ) ;
2008-05-15 14:41:56 +00:00
}
2004-08-09 17:04:08 +00:00
}
2015-07-28 08:16:52 +00:00
/** Reset the cursor and mouse mode handling back to default (normal cursor, only clicking in windows). */
2007-03-07 11:47:46 +00:00
void ResetObjectToPlace ( )
2005-01-22 20:23:18 +00:00
{
2009-04-19 10:31:30 +00:00
SetObjectToPlace ( SPR_CURSOR_MOUSE , PAL_NONE , HT_NONE , WC_MAIN_WINDOW , 0 ) ;
2004-08-09 17:04:08 +00:00
}
2013-05-19 14:49:25 +00:00
2020-06-29 01:38:29 +00:00
Point GetViewportStationMiddle ( const Viewport * vp , const Station * st )
2013-05-19 14:49:25 +00:00
{
int x = TileX ( st - > xy ) * TILE_SIZE ;
int y = TileY ( st - > xy ) * TILE_SIZE ;
2023-01-21 09:43:03 +00:00
int z = GetSlopePixelZ ( Clamp ( x , 0 , Map : : SizeX ( ) * TILE_SIZE - 1 ) , Clamp ( y , 0 , Map : : SizeY ( ) * TILE_SIZE - 1 ) ) ;
2013-05-19 14:49:25 +00:00
Point p = RemapCoords ( x , y , z ) ;
p . x = UnScaleByZoom ( p . x - vp - > virtual_left , vp - > zoom ) + vp - > left ;
p . y = UnScaleByZoom ( p . y - vp - > virtual_top , vp - > zoom ) + vp - > top ;
return p ;
}
2014-01-02 16:48:16 +00:00
/** Helper class for getting the best sprite sorter. */
struct ViewportSSCSS {
VpSorterChecker fct_checker ; ///< The check function.
VpSpriteSorter fct_sorter ; ///< The sorting function.
} ;
/** List of sorters ordered from best to worst. */
static ViewportSSCSS _vp_sprite_sorters [ ] = {
# ifdef WITH_SSE
{ & ViewportSortParentSpritesSSE41Checker , & ViewportSortParentSpritesSSE41 } ,
# endif
{ & ViewportSortParentSpritesChecker , & ViewportSortParentSprites }
} ;
/** Choose the "best" sprite sorter and set _vp_sprite_sorter. */
void InitializeSpriteSorter ( )
{
for ( uint i = 0 ; i < lengthof ( _vp_sprite_sorters ) ; i + + ) {
if ( _vp_sprite_sorters [ i ] . fct_checker ( ) ) {
_vp_sprite_sorter = _vp_sprite_sorters [ i ] . fct_sorter ;
break ;
}
}
2019-04-10 21:07:06 +00:00
assert ( _vp_sprite_sorter ! = nullptr ) ;
2014-01-02 16:48:16 +00:00
}
2018-04-24 17:19:01 +00:00
/**
* Scroll players main viewport .
* @ param flags type of operation
2021-10-10 00:08:52 +00:00
* @ param tile tile to center viewport on
2021-11-21 22:03:44 +00:00
* @ param target ViewportScrollTarget of scroll target
* @ param ref company or client id depending on the target
2018-04-24 17:19:01 +00:00
* @ return the cost of this operation or an error
*/
2021-11-21 22:03:44 +00:00
CommandCost CmdScrollViewport ( DoCommandFlag flags , TileIndex tile , ViewportScrollTarget target , uint32 ref )
2018-04-24 17:19:01 +00:00
{
if ( _current_company ! = OWNER_DEITY ) return CMD_ERROR ;
switch ( target ) {
case VST_EVERYONE :
break ;
case VST_COMPANY :
2021-11-21 22:03:44 +00:00
if ( _local_company ! = ( CompanyID ) ref ) return CommandCost ( ) ;
2018-04-24 17:19:01 +00:00
break ;
case VST_CLIENT :
2021-11-21 22:03:44 +00:00
if ( _network_own_client_id ! = ( ClientID ) ref ) return CommandCost ( ) ;
2018-04-24 17:19:01 +00:00
break ;
default :
return CMD_ERROR ;
}
if ( flags & DC_EXEC ) {
ResetObjectToPlace ( ) ;
ScrollMainWindowToTile ( tile ) ;
}
return CommandCost ( ) ;
}
2019-02-22 18:14:06 +00:00
2022-02-22 21:00:37 +00:00
void MarkCatchmentTilesDirty ( )
2019-02-22 18:14:06 +00:00
{
2019-03-30 16:59:43 +00:00
if ( _viewport_highlight_town ! = nullptr ) {
MarkWholeScreenDirty ( ) ;
return ;
}
2019-02-22 18:14:06 +00:00
if ( _viewport_highlight_station ! = nullptr ) {
if ( _viewport_highlight_station - > catchment_tiles . tile = = INVALID_TILE ) {
MarkWholeScreenDirty ( ) ;
_viewport_highlight_station = nullptr ;
} else {
BitmapTileIterator it ( _viewport_highlight_station - > catchment_tiles ) ;
for ( TileIndex tile = it ; tile ! = INVALID_TILE ; tile = + + it ) {
MarkTileDirtyByTile ( tile ) ;
}
}
}
}
/**
* Select or deselect station for coverage area highlight .
2019-03-30 16:59:43 +00:00
* Selecting a station will deselect a town .
2019-02-22 18:14:06 +00:00
* @ param * st Station in question
* @ param sel Select or deselect given station
*/
void SetViewportCatchmentStation ( const Station * st , bool sel )
{
if ( _viewport_highlight_station ! = nullptr ) SetWindowDirty ( WC_STATION_VIEW , _viewport_highlight_station - > index ) ;
2019-03-30 16:59:43 +00:00
if ( _viewport_highlight_town ! = nullptr ) SetWindowDirty ( WC_TOWN_VIEW , _viewport_highlight_town - > index ) ;
2019-02-22 18:14:06 +00:00
if ( sel & & _viewport_highlight_station ! = st ) {
MarkCatchmentTilesDirty ( ) ;
_viewport_highlight_station = st ;
2019-03-30 16:59:43 +00:00
_viewport_highlight_town = nullptr ;
2019-02-22 18:14:06 +00:00
MarkCatchmentTilesDirty ( ) ;
} else if ( ! sel & & _viewport_highlight_station = = st ) {
MarkCatchmentTilesDirty ( ) ;
_viewport_highlight_station = nullptr ;
}
if ( _viewport_highlight_station ! = nullptr ) SetWindowDirty ( WC_STATION_VIEW , _viewport_highlight_station - > index ) ;
}
2019-03-30 16:59:43 +00:00
/**
* Select or deselect town for coverage area highlight .
* Selecting a town will deselect a station .
* @ param * t Town in question
* @ param sel Select or deselect given town
*/
void SetViewportCatchmentTown ( const Town * t , bool sel )
{
if ( _viewport_highlight_town ! = nullptr ) SetWindowDirty ( WC_TOWN_VIEW , _viewport_highlight_town - > index ) ;
if ( _viewport_highlight_station ! = nullptr ) SetWindowDirty ( WC_STATION_VIEW , _viewport_highlight_station - > index ) ;
if ( sel & & _viewport_highlight_town ! = t ) {
_viewport_highlight_station = nullptr ;
_viewport_highlight_town = t ;
MarkWholeScreenDirty ( ) ;
} else if ( ! sel & & _viewport_highlight_town = = t ) {
_viewport_highlight_town = nullptr ;
MarkWholeScreenDirty ( ) ;
}
if ( _viewport_highlight_town ! = nullptr ) SetWindowDirty ( WC_TOWN_VIEW , _viewport_highlight_town - > index ) ;
}