2005-07-24 14:12:37 +00:00
/* $Id$ */
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 .
* This time the nothern most tile on the map at height level 0 defines 0 and
* everything south of that has a positive number . In theory this works the
* same as for columns with the massive difference that due to the isometric
* projection the actual row where the tile is visible differs from the row
* where the tile would be if it were at height level 0. Strictly speaking ,
* if you know the row of the tile at height level 0 , then the row number
* where it is actually drawn is tile height / 2 lower than the row number
* of the same tile at height level 0.
*/
2004-08-09 17:04:08 +00:00
# include "stdafx.h"
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"
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"
# include <map>
2008-04-16 14:15:00 +00:00
2008-01-13 01:21:35 +00:00
# include "table/strings.h"
2012-03-25 19:06:59 +00:00
# include "table/palettes.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
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 ;
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.
} ;
2008-04-16 19:01:09 +00:00
typedef SmallVector < TileSpriteToDraw , 64 > TileSpriteToDrawVector ;
typedef SmallVector < StringSpriteToDraw , 4 > StringSpriteToDrawVector ;
2008-04-16 20:01:04 +00:00
typedef SmallVector < ParentSpriteToDraw , 64 > ParentSpriteToDrawVector ;
2008-04-16 20:39:35 +00:00
typedef SmallVector < ChildScreenSpriteToDraw , 16 > 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
2012-03-25 19:30:05 +00:00
static void MarkViewportDirty ( const ViewPort * vp , int left , int top , int right , int bottom ) ;
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 ;
2004-08-09 17:04:08 +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 ;
2014-01-02 16:48:16 +00:00
static VpSpriteSorter _vp_sprite_sorter = NULL ;
2004-08-09 17:04:08 +00:00
2009-03-15 15:25:18 +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 )
{
2013-06-30 20:40:49 +00:00
if ( w - > viewport = = NULL ) return ;
delete w - > viewport - > overlay ;
2008-05-11 15:08:44 +00:00
free ( w - > viewport ) ;
2006-11-18 13:54:33 +00:00
w - > viewport = NULL ;
}
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
{
2008-04-17 09:42:44 +00:00
assert ( w - > viewport = = NULL ) ;
2004-08-09 17:04:08 +00:00
2008-05-11 15:08:44 +00:00
ViewportData * vp = CallocT < ViewportData > ( 1 ) ;
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 {
2006-04-03 05:32:11 +00:00
uint x = TileX ( follow_flags ) * TILE_SIZE ;
uint 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
2013-05-19 14:49:25 +00:00
vp - > overlay = NULL ;
2004-08-09 17:04:08 +00:00
w - > viewport = vp ;
2014-10-15 18:31:37 +00:00
vp - > virtual_left = 0 ; // pt.x;
vp - > virtual_top = 0 ; // pt.y;
2004-08-09 17:04:08 +00:00
}
static Point _vp_move_offs ;
2009-01-07 16:11:27 +00:00
static void DoSetViewportPosition ( const Window * w , int left , int top , int width , int height )
2004-08-09 17:04:08 +00:00
{
2009-01-07 18:59:46 +00:00
FOR_ALL_WINDOWS_FROM_BACK_FROM ( w , w ) {
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 ) {
2009-01-07 16:11:27 +00:00
DoSetViewportPosition ( w , left , top , w - > left - left , height ) ;
DoSetViewportPosition ( w , 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 ) {
2009-01-07 16:11:27 +00:00
DoSetViewportPosition ( w , left , top , ( w - > left + w - > width - left ) , height ) ;
2009-10-02 15:13:15 +00:00
DoSetViewportPosition ( w , 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 ) {
2009-01-07 16:11:27 +00:00
DoSetViewportPosition ( w , left , top , width , ( w - > top - top ) ) ;
DoSetViewportPosition ( w , 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 ) {
2009-01-07 16:11:27 +00:00
DoSetViewportPosition ( w , left , top , width , ( w - > top + w - > height - top ) ) ;
2009-10-02 15:13:15 +00:00
DoSetViewportPosition ( w , 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
{
ViewPort * vp = w - > viewport ;
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
2009-01-07 16:11:27 +00:00
if ( height > 0 ) DoSetViewportPosition ( w - > z_front , 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 ,
* otherwise \ c NULL is returned .
*/
2005-07-17 20:14:58 +00:00
ViewPort * IsPtInWindowViewport ( const Window * w , int x , int y )
2004-08-09 17:04:08 +00:00
{
ViewPort * vp = w - > viewport ;
2005-07-17 20:14:58 +00:00
2004-08-09 17:04:08 +00:00
if ( vp ! = NULL & &
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 ;
return NULL ;
}
2008-10-13 03:26:48 +00:00
/**
* Translate screen coordinate in a viewport to a tile coordinate
* @ param vp Viewport that contains the ( \ a x , \ a y ) screen coordinate
* @ param x Screen x coordinate
* @ param y Screen y coordinate
2015-02-22 14:10:44 +00:00
* @ param clamp_to_map Clamp the coordinate outside of the map to the closest tile within the map .
2010-08-01 19:44:49 +00:00
* @ return Tile coordinate
*/
2015-02-22 14:10:44 +00:00
Point TranslateXYToTileCoord ( const ViewPort * vp , int x , int y , bool clamp_to_map )
2005-01-22 22:47:58 +00:00
{
2004-08-09 17:04:08 +00:00
Point pt ;
2009-01-09 22:56:28 +00:00
int a , b ;
2011-11-04 11:52:19 +00:00
int z ;
2004-08-09 17:04:08 +00:00
if ( ( uint ) ( x - = vp - > left ) > = ( uint ) vp - > width | |
( uint ) ( y - = vp - > top ) > = ( uint ) vp - > height ) {
Point pt = { - 1 , - 1 } ;
return pt ;
}
2011-11-24 12:38:48 +00:00
x = ( ScaleByZoom ( x , vp - > zoom ) + vp - > virtual_left ) > > ( 2 + ZOOM_LVL_SHIFT ) ;
y = ( ScaleByZoom ( y , vp - > zoom ) + vp - > virtual_top ) > > ( 1 + ZOOM_LVL_SHIFT ) ;
2004-08-09 17:04:08 +00:00
2009-01-09 22:56:28 +00:00
a = y - x ;
b = y + x ;
2004-08-09 17:04:08 +00:00
2015-02-22 14:10:44 +00:00
if ( clamp_to_map ) {
/* Bring the coordinates near to a valid range. This is mostly due to the
* tiles on the north side of the map possibly being drawn too high due to
* the extra height levels . So at the top we allow a number of extra tiles .
* This number is based on the tile height and pixels . */
int extra_tiles = CeilDiv ( _settings_game . construction . max_heightlevel * TILE_HEIGHT , TILE_PIXELS ) ;
a = Clamp ( a , - extra_tiles * TILE_SIZE , MapMaxX ( ) * TILE_SIZE - 1 ) ;
b = Clamp ( b , - extra_tiles * TILE_SIZE , MapMaxY ( ) * TILE_SIZE - 1 ) ;
}
2004-09-10 19:02:27 +00:00
2007-10-20 16:50:48 +00:00
/* (a, b) is the X/Y-world coordinate that belongs to (x,y) if the landscape would be completely flat on height 0.
* Now find the Z - world coordinate by fix point iteration .
* This is a bit tricky because the tile height is non - continuous at foundations .
* The clicked point should be approached from the back , otherwise there are regions that are not clickable .
* ( FOUNDATION_HALFTILE_LOWER on SLOPE_STEEP_S hides north halftile completely )
* So give it a z - malus of 4 in the first iterations .
*/
z = 0 ;
2006-08-06 16:32:49 +00:00
2009-01-21 02:31:55 +00:00
int min_coord = _settings_game . construction . freeform_edges ? TILE_SIZE : 0 ;
2011-11-04 11:52:19 +00:00
for ( int i = 0 ; i < 5 ; i + + ) z = GetSlopePixelZ ( Clamp ( a + max ( z , 4 ) - 4 , min_coord , MapMaxX ( ) * TILE_SIZE - 1 ) , Clamp ( b + max ( z , 4 ) - 4 , min_coord , MapMaxY ( ) * TILE_SIZE - 1 ) ) / 2 ;
for ( int malus = 3 ; malus > 0 ; malus - - ) z = GetSlopePixelZ ( Clamp ( a + max ( z , malus ) - malus , min_coord , MapMaxX ( ) * TILE_SIZE - 1 ) , Clamp ( b + max ( z , malus ) - malus , min_coord , MapMaxY ( ) * TILE_SIZE - 1 ) ) / 2 ;
for ( int i = 0 ; i < 5 ; i + + ) z = GetSlopePixelZ ( Clamp ( a + z , min_coord , MapMaxX ( ) * TILE_SIZE - 1 ) , Clamp ( b + z , min_coord , MapMaxY ( ) * TILE_SIZE - 1 ) ) / 2 ;
2009-01-21 02:31:55 +00:00
2015-02-22 14:10:44 +00:00
if ( clamp_to_map ) {
pt . x = Clamp ( a + z , min_coord , MapMaxX ( ) * TILE_SIZE - 1 ) ;
pt . y = Clamp ( b + z , min_coord , MapMaxY ( ) * TILE_SIZE - 1 ) ;
} else {
pt . x = a + z ;
pt . y = b + z ;
}
2004-08-09 17:04:08 +00:00
return pt ;
}
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 ;
ViewPort * vp ;
Point pt ;
2004-09-10 19:02:27 +00:00
if ( ( w = FindWindowFromPt ( x , y ) ) ! = NULL & &
2004-08-09 17:04:08 +00:00
( vp = IsPtInWindowViewport ( w , x , y ) ) ! = NULL )
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 ;
2008-05-11 15:08:44 +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
*/
2006-11-07 13:06:02 +00:00
void HandleZoomMessage ( Window * w , const ViewPort * vp , byte widget_zoom_in , byte widget_zoom_out )
{
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
*/
2010-01-21 01:38:13 +00:00
static void AddTileSpriteToDraw ( SpriteID image , PaletteID pal , int32 x , int32 y , int z , const SubSprite * sub = NULL , 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
2008-04-16 21:06:21 +00:00
TileSpriteToDraw * ts = _vd . tile_sprites_to_draw . Append ( ) ;
2008-04-16 19:01:09 +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 ) ;
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
2011-11-25 23:06:17 +00:00
AddChildSpriteScreen ( image , pal , offs . x + extra_offs_x , offs . y + extra_offs_y , false , sub , 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 {
2011-11-24 12:38:48 +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
2008-04-16 21:06:21 +00:00
/* _vd.last_child == NULL if foundation sprite was clipped by the viewport bounds */
2008-06-19 09:32:25 +00:00
if ( _vd . last_child ! = NULL ) _vd . foundation [ _vd . foundation_part ] = _vd . parent_sprites_to_draw . Length ( ) - 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 ) ;
2009-01-10 00:31:47 +00:00
const Sprite * spr = GetSprite ( image & SPRITE_MASK , ST_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 ;
2008-04-16 21:06:21 +00:00
const ParentSpriteToDraw * pstd = _vd . parent_sprites_to_draw . End ( ) - 1 ;
2011-11-25 23:06:17 +00:00
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 ;
}
2008-04-16 21:06:21 +00:00
_vd . last_child = NULL ;
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 {
2008-09-02 15:20:38 +00:00
const Sprite * spr = GetSprite ( image & SPRITE_MASK , ST_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 */
2007-09-26 19:27:29 +00:00
left = min ( left , RemapCoords ( x + w , y + bb_offset_y , z + bb_offset_z ) . x ) ;
right = max ( right , RemapCoords ( x + bb_offset_x , y + h , z + bb_offset_z ) . x + 1 ) ;
top = min ( top , RemapCoords ( x + bb_offset_x , y + bb_offset_y , z + dz ) . y ) ;
bottom = 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 ;
}
2008-04-16 21:06:21 +00:00
ParentSpriteToDraw * ps = _vd . parent_sprites_to_draw . Append ( ) ;
2008-04-16 20:01:04 +00:00
ps - > x = tmp_x ;
ps - > y = tmp_y ;
ps - > left = tmp_left ;
ps - > top = tmp_top ;
2004-08-09 17:04:08 +00:00
ps - > image = image ;
2007-01-14 19:57:49 +00:00
ps - > pal = pal ;
2007-10-05 21:49:15 +00:00
ps - > sub = sub ;
2007-09-14 21:54:57 +00:00
ps - > xmin = x + bb_offset_x ;
2007-09-23 09:37:25 +00:00
ps - > xmax = x + max ( bb_offset_x , w ) - 1 ;
2004-08-09 17:04:08 +00:00
2007-09-14 21:54:57 +00:00
ps - > ymin = y + bb_offset_y ;
2007-09-23 09:37:25 +00:00
ps - > ymax = y + max ( bb_offset_y , h ) - 1 ;
2004-08-09 17:04:08 +00:00
2007-09-14 21:54:57 +00:00
ps - > zmin = z + bb_offset_z ;
2007-09-23 09:37:25 +00:00
ps - > zmax = z + max ( bb_offset_z , dz ) - 1 ;
2004-08-09 17:04:08 +00:00
2007-09-14 21:54:57 +00:00
ps - > comparison_done = false ;
2008-06-16 20:08:30 +00:00
ps - > first_child = - 1 ;
2004-08-09 17:04:08 +00:00
2008-06-16 20:08:30 +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 .
*/
2011-11-25 23:06:17 +00:00
void AddChildSpriteScreen ( SpriteID image , PaletteID pal , int x , int y , bool transparent , const SubSprite * sub , bool scale )
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 */
2008-04-16 21:06:21 +00:00
if ( _vd . last_child = = NULL ) 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 ;
}
2008-06-19 09:32:25 +00:00
* _vd . last_child = _vd . child_screen_sprites_to_draw . Length ( ) ;
2008-06-16 20:08:30 +00:00
2008-04-16 21:06:21 +00:00
ChildScreenSpriteToDraw * cs = _vd . child_screen_sprites_to_draw . Append ( ) ;
2004-08-09 17:04:08 +00:00
cs - > image = image ;
2007-01-14 19:57:49 +00:00
cs - > pal = pal ;
2007-10-05 21:49:15 +00:00
cs - > sub = sub ;
2011-11-25 23:06:17 +00:00
cs - > x = scale ? x * ZOOM_LVL_BASE : x ;
cs - > y = scale ? y * ZOOM_LVL_BASE : y ;
2008-06-16 20:08:30 +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 . */
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 ) ;
2008-04-16 21:06:21 +00:00
StringSpriteToDraw * ss = _vd . string_sprites_to_draw . Append ( ) ;
2008-04-16 19:01:09 +00:00
ss - > string = string ;
ss - > x = x ;
ss - > y = y ;
ss - > params [ 0 ] = params_1 ;
ss - > params [ 1 ] = params_2 ;
ss - > width = width ;
2009-02-09 02:57:15 +00:00
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 .
2007-10-14 19:57:15 +00:00
*/
2010-01-21 01:38:13 +00:00
static void DrawSelectionSprite ( SpriteID image , PaletteID pal , const TileInfo * ti , int z_offset , FoundationPart foundation_part )
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 */
2010-01-03 20:55:00 +00:00
AddTileSpriteToDraw ( image , pal , ti - > x , ti - > y , ti - > z + z_offset ) ;
2007-10-14 19:57:15 +00:00
} else {
/* draw on top of foundation */
2011-11-24 12:38:48 +00:00
AddChildSpriteToFoundation ( image , pal , NULL , foundation_part , 0 , - 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
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 )
{
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
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? */
2010-12-25 19:58:50 +00:00
if ( ! is_redsq & & _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
/**
* Given a screen coordinate ( x , y ) as e . g . stored in _vd . dpi , this function
* returns the tile coordinate of the tile which would be painted at ( x , y )
* if one assumes height zero at that position .
* @ param x Some x screen coordinate
* @ param y Some y screen coordinate
* @ return Tile coordinate assuming height zero as described
*/
static inline Point GetTileCoordFromScreenCoord ( int x , int y )
2004-08-09 17:04:08 +00:00
{
2014-09-21 17:27:37 +00:00
/* First convert from the screen coordinate system (where the width of tiles
* is twice their height ) to the tile coordinate system . That means , turn
* around by 45 degrees and make the tiles quadratic . */
Point tile_coord = InverseRemapCoords ( x , y ) ;
2004-08-09 17:04:08 +00:00
2014-09-21 17:27:37 +00:00
/* Scale from a 16x16-grid to a 1x1-grid as returned by TileX/TileY. */
tile_coord . x / = ( int ) TILE_SIZE ;
tile_coord . y / = ( int ) TILE_SIZE ;
2004-08-09 17:04:08 +00:00
2014-09-21 17:27:37 +00:00
return tile_coord ;
}
2007-01-03 09:45:07 +00:00
2014-09-21 17:27:37 +00:00
/**
* Assume a region , given by screen coordinates ( x1 , y1 , x2 , y2 ) , as defined in _vd . dpi .
* This function basically takes ( x1 , y1 ) ( i . e . the upper left corner of that region )
* and returns the tile coordinate of the tile , which would be painted at ( x1 , y1 )
* if one assumes height zero at that position .
*
* However , in detail : Imagine tiles being split up into their upper left , upper right ,
* etc . isometric sections . We return a tile where the upper left corner of the
* mentioned region is either in its lower right section or in a neighbor tile
* below / right of that section . By doing so , we want to enforce that we can
* travel to east or south from that point without leaving the region again .
*
* @ param x Some x screen coordinate , x1 in terms of the description above
* @ param y Some y screen coordinate , y1 in terms of the description above
* @ return Upper left corner of the region as tile coordinates .
*/
static Point GetMinTileCoordsIgnoringHeight ( int x , int y )
{
Point tile_coord = GetTileCoordFromScreenCoord ( x , y ) ;
/* Expand area to be painted in order to avoid situations
* where south or east of the to be painted point in dpi are tiles
* which will not be painted . */
tile_coord . y - - ;
return tile_coord ;
}
/**
* Assume a region , given by screen coordinates ( x1 , y1 , x2 , y2 ) , as defined in _vd . dpi .
* This function basically takes ( x2 , y2 ) ( i . e . the lower right corner of that region )
* and returns the tile coordinate of the tile , which would be painted at ( x2 , y2 )
* if one assumes height zero at that position .
*
* However , in detail : Imagine tiles being split up into their upper left , upper right ,
* etc . isometric sections . We return a tile where the lower right corner of the
* mentioned region is either in its upper left section or in a neighbor tile
* above / left of that section . By doing so , we want to enforce that we can
* travel to north or west from that point without leaving the region again .
*
* @ param x Some x screen coordinate , x2 in terms of the description above
* @ param y Some y screen coordinate , y2 in terms of the description above
* @ return Upper left corner of the region as tile coordinates .
*/
static Point GetMaxTileCoordsIgnoringHeight ( int x , int y )
{
Point tile_coord = GetTileCoordFromScreenCoord ( x , y ) ;
/* Expand area to be painted to southeast in order to avoid situations
* where north or east of the given to be painted point in dpi are
* tiles which will not be repainted . */
tile_coord . y + + ;
return tile_coord ;
}
/**
* 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 .
*/
static int GetViewportY ( Point tile )
{
return ( tile . y * TILE_SIZE + tile . x * TILE_SIZE - GetTileMaxPixelZOutsideMap ( tile . x , tile . y ) ) < < ZOOM_LVL_SHIFT ;
}
/**
* Given a tile coordinate as returned by TileX / TileY , this returns its column .
*
* @ param tile_coord The coordinate of the tile .
* @ return The column index .
* @ ingroup vp_column_row
*/
static int GetTileColumnFromTileCoord ( Point tile_coord )
{
return tile_coord . y - tile_coord . x ;
}
/**
* Returns the position of the tile at the northern end of the column of the
* given tile .
* @ param tile Any tile .
* @ return Position of the tile at the northern end of the column as described .
* @ ingroup vp_column_row
*/
static Point GetNorthernEndOfColumn ( Point tile )
{
Point northern_end ;
if ( tile . x < tile . y ) {
northern_end . x = 0 ;
northern_end . y = tile . y - tile . x ;
} else {
northern_end . x = tile . x - tile . y ;
northern_end . y = 0 ;
2004-08-09 17:04:08 +00:00
}
2014-09-21 17:27:37 +00:00
return northern_end ;
}
2004-09-10 19:02:27 +00:00
2014-09-21 17:27:37 +00:00
/**
* Returns the position of the tile at the southern end of the column of the
* given tile , if it is within the given limit expressed in number of tiles
* @ param tile Any tile .
* @ param limit Number of tiles to go to south at most , if the southern end is
* further away , stop after that number of tiles
* @ return Position of the tile at the soutern end of the column as described .
* @ ingroup vp_column_row
*/
static Point GetSouthernEndOfColumnWithLimit ( Point tile , uint limit )
{
Point distance_to_end ;
distance_to_end . x = ( int ) MapMaxX ( ) - tile . x ;
distance_to_end . y = ( int ) MapMaxY ( ) - tile . y ;
Point southern_end ;
if ( distance_to_end . x < distance_to_end . y ) {
int number_of_steps = min ( limit , distance_to_end . x ) ;
southern_end . x = tile . x + number_of_steps ;
southern_end . y = tile . y + number_of_steps ;
} else {
int number_of_steps = min ( limit , distance_to_end . y ) ;
southern_end . x = tile . x + number_of_steps ;
southern_end . y = tile . y + number_of_steps ;
}
2004-08-09 17:04:08 +00:00
2014-09-21 17:27:37 +00:00
return southern_end ;
}
/**
* Returns the position of the tile at the southern end of the column of the
* given tile .
* @ param tile Any tile .
* @ return Position of the tile at the soutern end of the column as described .
* @ ingroup vp_column_row
*/
static Point GetSouthernEndOfColumn ( Point tile )
{
return GetSouthernEndOfColumnWithLimit ( tile , UINT32_MAX ) ;
}
/**
* Returns the tile exactly in the middle between two given tiles .
*
* @ param tile Point upper_tile , any tile .
* @ param tile Point lower_tile , any tile .
* @ return The tile in the middle of Point upper_tile and Point lower_tile .
*/
static Point GetMiddleTile ( Point upper_tile , Point lower_tile )
{
Point middle_tile ;
middle_tile . x = ( lower_tile . x + upper_tile . x ) / 2 ;
middle_tile . y = ( lower_tile . y + upper_tile . y ) / 2 ;
return middle_tile ;
}
/**
* Given a tile coordinate assuming height zero , this returns the row actually
* painted at this tile coordinate if one recognizes height .
*
* The problem concerning this calculation is that we have not enough
* information to calculate this in one closed formula . Which row we
* search rather depends on the height distribution on the map . So
* we have to search .
*
* First , the searched tile may be located outside map . Then , we know
* that we are not too far outside map , so we can step tile by tile ,
* starting at the given tile , until we have passed the searched tile .
*
* If the searched tile is inside map , searching is more difficult . A
* linear search on some thousand tiles would be not that efficient . But ,
* we can solve the problem by interval intersection . We know for sure ,
* that the searched tile is south of the given tile , simply because
* mountains of height > 0 ( and we have only such mountains ) are always
* painted north of their tile . So we choose a tile half way between the
* given tile and the southern end of the map , have a look whether it is
* north or south of the given position , and intersect again . Until
* our interval has length 1 , then we take the upper one .
*
* @ param viewport_y The viewport y corresponding to tile , if one assumes height zero for that tile
* @ param tile Some tile coordinate assuming height zero .
* @ param bridge_correct If true , consider bridges south of the calculated tile , and if the bridge
* visually intersect the calculated tile , shift it southwards .
* @ return The row which is painted at this coordinate , according to the discussion above .
* @ ingroup vp_column_row
*/
int GetRowAtTile ( int viewport_y , Point tile , bool bridge_correct )
{
Point northern_tile = GetNorthernEndOfColumn ( tile ) ;
Point southern_tile = GetSouthernEndOfColumn ( tile ) ;
int northern_tile_viewport_y = GetViewportY ( northern_tile ) ;
int southern_tile_viewport_y = GetViewportY ( southern_tile ) ;
if ( northern_tile_viewport_y > = viewport_y ) {
/* We are north of the map, search tile by tile with direction north. */
while ( northern_tile_viewport_y > = viewport_y ) {
northern_tile . x - - ;
northern_tile . y - - ;
northern_tile_viewport_y = GetViewportY ( northern_tile ) ;
}
return northern_tile . x + northern_tile . y ;
}
if ( southern_tile_viewport_y < = viewport_y ) {
/* We are south of the map, search tile by tile with direction south. */
while ( southern_tile_viewport_y < = viewport_y ) {
southern_tile . x + + ;
southern_tile . y + + ;
southern_tile_viewport_y = GetViewportY ( southern_tile ) ;
}
return southern_tile . x + southern_tile . y ;
}
/*
* We are inside the map . The searched tile is at most
* < maximum heightlevel / 4 > tiles south of the given tile ( as one tile
* painted on the screen needs as much vertical space as painting a tile
* by 4 heightlevels ascended ) . Add one to avoid rounding errors to the
* wrong side .
*
* Invariant in the code below : The searched tile shown at viewport_y
* always is between upper_tile and lower_tile .
*/
Point upper_tile = tile ;
Point lower_tile = GetSouthernEndOfColumnWithLimit ( upper_tile , _settings_game . construction . max_heightlevel / 4 + 1 ) ;
int middle_bound ;
2014-09-21 08:19:32 +00:00
2004-08-09 17:04:08 +00:00
do {
2014-09-21 17:27:37 +00:00
Point middle_tile = GetMiddleTile ( upper_tile , lower_tile ) ;
middle_bound = GetViewportY ( middle_tile ) ;
if ( middle_bound > = viewport_y ) {
/* The tile shown at viewport_y is somewhere in the upper half of
* the currently observed section . */
lower_tile = middle_tile ;
} else {
/* The tile shown at viewport_y is somewhere in the lower half of
* the currently observed section . */
upper_tile = middle_tile ;
}
}
while ( lower_tile . y - upper_tile . y > 1 ) ;
/* Now our interval has length 1, so only contains two tiles, and we take the upper one.
* However , there is one problem left : Tiles being located southwards , containing a high bridge .
* They may , though not high enough in terms of landscape , intersect the drawing area with parts
* of the bridge .
* Luckily , there is a guaranteed upper bound for bridge height , thus we know how far we have to
* search southwards whether such a bridge exists .
*/
int correction_step = 0 ;
if ( bridge_correct ) {
/* Calculate, how many tiles below upper_tile, a worst case bridge intersecting upper_tile in
* terms of painting can be located . Lets inspect that formula in detail :
* . . . - 5 : The magic constant near the beginning of ViewportAddLandscape accounts for 5 harmless heightlevels a bridge can have . Thus subtract them .
* . . . / 2 : Four heightlevels account for one tile height . On the other hand , if landscape ascends from upper_tile southwards , this can account for
* as many additional heightlevels as we step southwards . In combination : A division by two gains the number of tiles to step southwards .
* . . . + 1 : Avoid rounding errors , and fall back to the safe side .
*/
int worst_case_steps_southwards = max ( 0 , ( ( int ) _settings_game . construction . max_bridge_height - 5 ) / 2 + 1 ) ;
for ( int n = 0 ; n < worst_case_steps_southwards ; n + + ) {
TileIndex potential_bridge_tile = TileXY ( upper_tile . x + n , upper_tile . y + n ) ;
if ( IsValidTile ( potential_bridge_tile ) & & IsBridgeAbove ( potential_bridge_tile ) ) {
/* There is a bridge. */
TileIndex bridge_start = GetNorthernBridgeEnd ( potential_bridge_tile ) ;
int bridge_height = GetBridgeHeight ( bridge_start ) ;
int upper_tile_height = GetTileZ ( TileXY ( upper_tile . x , upper_tile . y ) ) ;
/* Start at the bridge level, descend by the number of heightlevels equivalent to our steps southwards (in worst case), subtract the harmless
* bridge heightlevels , and compare whether we are still above the height of the upper_tile . If yes , we need to paint that tile , to avoid glitches .
*/
if ( bridge_height - 2 * n - 1 > upper_tile_height ) {
correction_step = n ;
}
2006-08-07 17:32:29 +00:00
}
2014-09-21 17:27:37 +00:00
}
}
2006-08-07 17:32:29 +00:00
2014-09-21 17:27:37 +00:00
/* The biggest recorded correction_step defines, which tile we actually return. */
upper_tile . x + = correction_step ;
upper_tile . y + = correction_step ;
2004-09-10 19:02:27 +00:00
2014-09-21 17:27:37 +00:00
/* Returns its row. */
return upper_tile . x + upper_tile . y ;
}
2009-01-21 02:31:55 +00:00
2014-09-21 17:27:37 +00:00
/**
* Returns the bottom tile of the column of upper_tile shown on the viewport ,
* given upper_tile and the lower right tile shown on the viewport .
*
* @ param upper_tile Sny tile inside the map .
* @ param lower_right_tile The tile shown at the southeast edge of the viewport
* ( ignoring height ) . Note that this tile may be located
* northeast of the upper_tile , because upper_tile is usually
* calculated by shifting a tile southwards until we reach
* the northern map border .
* @ return The lowest existing tile located in the column defined by upper_tile ,
* which is in the same row as lower_right_tile or above that row
* If lower_right_tile was northeast of upper_tile , ( - 1 , - 1 ) is returned .
* @ ingroup vp_column_row
*/
static Point GetBottomTileOfColumn ( Point upper_tile , Point lower_right_tile )
{
int upper_row = upper_tile . x + upper_tile . y ;
int lower_row = lower_right_tile . x + lower_right_tile . y ;
assert ( upper_row < = lower_row ) ;
2009-01-21 02:31:55 +00:00
2014-09-21 17:27:37 +00:00
int number_of_rows = lower_row - upper_row ;
if ( number_of_rows % 2 ! = 0 ) {
/* Avoid 0.5 being rounded down to zero; painting too much is better than
* painting too little . */
number_of_rows + + ;
}
Point bottom_tile ;
bottom_tile . x = upper_tile . x + number_of_rows / 2 ;
bottom_tile . y = upper_tile . y + number_of_rows / 2 ;
int bottom_row = bottom_tile . x + bottom_tile . y ;
assert ( bottom_row > = lower_row ) ;
return bottom_tile ;
}
2004-09-10 19:02:27 +00:00
2014-09-21 17:27:37 +00:00
/**
* Add the landscape to the viewport , i . e . all ground tiles and buildings .
*/
static void ViewportAddLandscape ( )
{
assert ( _vd . dpi . top < = _vd . dpi . top + _vd . dpi . height ) ;
assert ( _vd . dpi . left < = _vd . dpi . left + _vd . dpi . width ) ;
/* The upper and lower edge of the viewport part to paint. Add some number
* of pixels to the lower end in order to ensure that we also take tiles
* south of the given area , but with high buildings intersecting the area .
* Subtract some pixels from the upper end in order to avoid glitches at the
* upper end of the top be painted area . */
int viewport_top = _vd . dpi . top - 16 ;
int viewport_bottom = _vd . dpi . top + _vd . dpi . height + 116 ;
/* First get the position of the tile at the upper left / lower right edge,
* for now ignoring the height . ( i . e . assuming height zero . ) */
Point upper_left_tile = GetMinTileCoordsIgnoringHeight ( _vd . dpi . left , viewport_top ) ;
Point lower_right_tile = GetMaxTileCoordsIgnoringHeight ( _vd . dpi . left + _vd . dpi . width , viewport_bottom ) ;
/* Calculate the bounding columns. We won't need to draw anything
* left / right of them . */
int left_column = GetTileColumnFromTileCoord ( upper_left_tile ) ;
/* Correction to avoid glitches when approaching the left edge of the map. */
left_column - - ;
int right_column = GetTileColumnFromTileCoord ( lower_right_tile ) ;
right_column + + ;
/* For each column, calculate the top and the bottom row. These are the
* bounding rows for that specific column . */
int * top_row = AllocaM ( int , right_column - left_column + 1 ) ; // Pre-allocate memory for visual studio/express to be able to compile.
int * bottom_row = AllocaM ( int , right_column - left_column + 1 ) ; // Pre-allocate memory for visual studio/express to be able to compile.
int min_top_row = MapMaxX ( ) + MapMaxY ( ) ;
int max_bottom_row = 0 ;
Point top_tile_of_column = upper_left_tile ;
/* And now for each column, determine the top and the bottom row we must paint. */
bool south_east_direction = false ;
for ( int x = left_column ; x < = right_column ; x + + ) {
Point bottom_tile_of_column = GetBottomTileOfColumn ( top_tile_of_column , lower_right_tile ) ;
/* And then actually find out the top and the bottom row. Note that
* top_tile_of_column and bottom_tile_of_column may be outside the map here .
* This possibility is needed , otherwise we couldn ' t paint the black area
* outside the map ( and in particular the edge of map ) properly .
* Subtract three / add one to avoid glitches . */
top_row [ x - left_column ] = GetRowAtTile ( viewport_top , top_tile_of_column , false ) ;
top_row [ x - left_column ] - = 3 ;
bottom_row [ x - left_column ] = GetRowAtTile ( viewport_bottom , bottom_tile_of_column , true ) ;
bottom_row [ x - left_column ] + + ;
/* We never paint things in rows < min_top_row or > max_bottom_row. */
min_top_row = min ( min_top_row , top_row [ x - left_column ] ) ;
max_bottom_row = max ( max_bottom_row , bottom_row [ x - left_column ] ) ;
/* Go to next column in the east. */
if ( south_east_direction ) {
top_tile_of_column . y + + ;
2007-01-03 09:45:07 +00:00
} else {
2014-09-21 17:27:37 +00:00
top_tile_of_column . x - - ;
}
/* Switch between directions southeast and northeast. */
south_east_direction = ! south_east_direction ;
}
for ( int row = min_top_row ; row < = max_bottom_row ; row + + ) {
for ( int column = left_column ; column < = right_column ; column + + ) {
/* For each column, we only paint the interval top_row .. bottom_row.
* Due to the division by two below , even and odd values of row + column map to
* the same ( x , y ) combinations . Thus , we only paint one of them . */
if ( ( ( row + column ) % 2 = = 0 ) & &
( top_row [ column - left_column ] < = row ) & &
( row < = bottom_row [ column - left_column ] ) ) {
TileType tile_type ;
TileInfo tile_info ;
_cur_ti = & tile_info ;
/* column = y - x; row = x + y; now solve the equation system
* for x and y . */
int x = ( row - column ) / 2 ;
int y = ( row + column ) / 2 ;
tile_info . x = x ;
tile_info . y = y ;
/* For some strange reason, those fields inside tile_info are uints. However,
* in the old code their copies in an int variable where compared against zero . */
if ( 0 < x & & x < ( int ) MapMaxX ( ) & & 0 < y & & y < ( int ) MapMaxY ( ) ) {
/* We are inside the map => paint landscape. */
tile_info . tile = TileXY ( tile_info . x , tile_info . y ) ;
tile_info . tileh = GetTilePixelSlope ( tile_info . tile , & tile_info . z ) ;
tile_type = GetTileType ( tile_info . tile ) ;
} else {
/* We are outside the map => paint black. */
tile_info . tile = INVALID_TILE ;
tile_info . tileh = GetTilePixelSlopeOutsideMap ( tile_info . x , tile_info . y , & tile_info . z ) ;
tile_type = MP_VOID ;
}
/* Scale to 16x16 tiles, needed for the drawing procedures called below. */
tile_info . x * = TILE_SIZE ;
tile_info . y * = TILE_SIZE ;
_vd . foundation_part = FOUNDATION_PART_NONE ;
_vd . foundation [ 0 ] = - 1 ;
_vd . foundation [ 1 ] = - 1 ;
_vd . last_foundation_child [ 0 ] = NULL ;
_vd . last_foundation_child [ 1 ] = NULL ;
_tile_type_procs [ tile_type ] - > draw_tile_proc ( & tile_info ) ;
DrawTileSelection ( & tile_info ) ;
}
2007-01-03 09:45:07 +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
2009-12-22 12:50:41 +00:00
int sign_height = ScaleByZoom ( VPSM_TOP + FONT_HEIGHT_NORMAL + VPSM_BOTTOM , dpi - > zoom ) ;
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 ;
AddStringToDraw ( sign - > center - sign_half_width + shadow_offset , sign - > top , string_small_shadow , params_1 , params_2 , INVALID_COLOUR , sign - > width_small ) ;
}
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
}
}
2009-11-22 19:53:49 +00:00
static void ViewportAddTownNames ( DrawPixelInfo * dpi )
2006-11-05 11:17:33 +00:00
{
2009-11-22 19:53:49 +00:00
if ( ! HasBit ( _display_opt , DO_SHOW_TOWN_NAMES ) | | _game_mode = = GM_MENU ) return ;
const Town * t ;
FOR_ALL_TOWNS ( t ) {
2012-04-25 20:50:13 +00:00
ViewportAddString ( dpi , ZOOM_LVL_OUT_16X , & t - > cache . sign ,
2009-11-22 19:53:49 +00:00
_settings_client . gui . population_in_label ? STR_VIEWPORT_TOWN_POP : STR_VIEWPORT_TOWN ,
STR_VIEWPORT_TOWN_TINY_WHITE , STR_VIEWPORT_TOWN_TINY_BLACK ,
2012-04-25 20:50:13 +00:00
t - > index , t - > cache . population ) ;
2009-11-22 19:53:49 +00:00
}
2006-11-05 11:17:33 +00:00
}
2004-11-14 19:44:06 +00:00
static void ViewportAddStationNames ( DrawPixelInfo * dpi )
2004-08-09 17:04:08 +00:00
{
2009-11-22 19:53:49 +00:00
if ( ! ( HasBit ( _display_opt , DO_SHOW_STATION_NAMES ) | | HasBit ( _display_opt , DO_SHOW_WAYPOINT_NAMES ) ) | | _game_mode = = GM_MENU ) return ;
2004-08-09 17:04:08 +00:00
2009-07-25 10:56:36 +00:00
const BaseStation * st ;
2009-11-22 19:53:49 +00:00
FOR_ALL_BASE_STATIONS ( st ) {
/* Check whether the base station is a station or a waypoint */
bool is_station = Station : : IsExpected ( st ) ;
2004-08-09 17:04:08 +00:00
2009-11-22 19:53:49 +00:00
/* Don't draw if the display options are disabled */
if ( ! HasBit ( _display_opt , is_station ? DO_SHOW_STATION_NAMES : DO_SHOW_WAYPOINT_NAMES ) ) continue ;
2007-05-19 22:48:04 +00:00
2011-08-01 18:41:21 +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 ( ! HasBit ( _display_opt , DO_SHOW_COMPETITOR_SIGNS ) & & _local_company ! = st - > owner & & st - > owner ! = OWNER_NONE ) continue ;
2011-11-24 12:38:48 +00:00
ViewportAddString ( dpi , ZOOM_LVL_OUT_16X , & st - > sign ,
2009-11-22 19:53:49 +00:00
is_station ? STR_VIEWPORT_STATION : STR_VIEWPORT_WAYPOINT ,
( is_station ? STR_VIEWPORT_STATION : STR_VIEWPORT_WAYPOINT ) + 1 , STR_NULL ,
2009-11-29 19:27:53 +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
}
}
2006-11-05 11:17:33 +00:00
2004-11-14 19:44:06 +00:00
static void ViewportAddSigns ( DrawPixelInfo * dpi )
2004-08-09 17:04:08 +00:00
{
2008-04-03 19:55:40 +00:00
/* Signs are turned off or are invisible */
if ( ! HasBit ( _display_opt , DO_SHOW_SIGNS ) | | IsInvisibilitySet ( TO_SIGNS ) ) return ;
2004-08-09 17:04:08 +00:00
2009-11-22 19:53:49 +00:00
const Sign * si ;
FOR_ALL_SIGNS ( si ) {
2011-08-01 18:41:21 +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 . */
2011-12-19 20:59:29 +00:00
if ( ! HasBit ( _display_opt , DO_SHOW_COMPETITOR_SIGNS ) & & _local_company ! = si - > owner & & si - > owner ! = OWNER_DEITY ) continue ;
2011-08-01 18:41:21 +00:00
2011-11-24 12:38:48 +00:00
ViewportAddString ( dpi , ZOOM_LVL_OUT_16X , & si - > sign ,
2009-11-22 19:53:49 +00:00
STR_WHITE_SIGN ,
2011-12-19 20:59:29 +00:00
( 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 ] ) ) ;
2004-08-09 17:04:08 +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
*/
void ViewportSign : : UpdatePosition ( int center , int top , StringID str )
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
2009-07-07 16:51:20 +00:00
char buffer [ DRAW_STRING_BUFFER ] ;
2004-08-09 17:04:08 +00:00
2006-10-21 23:31:34 +00:00
GetString ( buffer , str , lastof ( buffer ) ) ;
2009-12-22 12:50:41 +00:00
this - > width_normal = VPSM_LEFT + Align ( GetStringBoundingBox ( buffer ) . width , 2 ) + VPSM_RIGHT ;
this - > center = center ;
2004-08-09 17:04:08 +00:00
2006-09-16 13:20:14 +00:00
/* zoomed out version */
2010-05-30 15:32:37 +00:00
this - > width_small = VPSM_LEFT + Align ( GetStringBoundingBox ( buffer , FS_SMALL ) . width , 2 ) + VPSM_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 ) ;
zoomlevels [ zoom ] . bottom = this - > top + ScaleByZoom ( VPSM_TOP + FONT_HEIGHT_NORMAL + VPSM_BOTTOM + 1 , zoom ) ;
}
Window * w ;
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
ViewPort * vp = w - > viewport ;
if ( vp ! = NULL & & vp - > zoom < = maxzoom ) {
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
{
2008-04-16 19:01:09 +00:00
const TileSpriteToDraw * tsend = tstdv - > End ( ) ;
for ( const TileSpriteToDraw * ts = tstdv - > Begin ( ) ; ts ! = tsend ; + + ts ) {
2011-11-24 12:38:48 +00:00
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 ;
}
2008-10-13 03:26:48 +00:00
/** Sort parent sprites pointer array */
2008-04-16 20:01:04 +00:00
static void ViewportSortParentSprites ( ParentSpriteToSortVector * psdv )
2004-08-09 17:04:08 +00:00
{
2008-04-16 20:01:04 +00:00
ParentSpriteToDraw * * psdvend = psdv - > End ( ) ;
ParentSpriteToDraw * * psd = psdv - > Begin ( ) ;
while ( psd ! = psdvend ) {
2008-04-16 18:28:05 +00:00
ParentSpriteToDraw * ps = * psd ;
2004-08-09 17:04:08 +00:00
2008-04-16 19:36:30 +00:00
if ( ps - > comparison_done ) {
psd + + ;
continue ;
}
2005-07-17 20:14:58 +00:00
2008-04-16 18:28:05 +00:00
ps - > comparison_done = true ;
2004-09-10 19:02:27 +00:00
2008-04-16 20:01:04 +00:00
for ( ParentSpriteToDraw * * psd2 = psd + 1 ; psd2 ! = psdvend ; psd2 + + ) {
2008-04-16 18:28:05 +00:00
ParentSpriteToDraw * ps2 = * psd2 ;
2005-06-15 14:04:48 +00:00
2008-04-16 18:28:05 +00:00
if ( ps2 - > comparison_done ) continue ;
2004-08-09 17:04:08 +00:00
2008-04-16 18:28:05 +00:00
/* Decide which comparator to use, based on whether the bounding
* boxes overlap
*/
if ( ps - > xmax > = ps2 - > xmin & & ps - > xmin < = ps2 - > xmax & & // overlap in X?
ps - > ymax > = ps2 - > ymin & & ps - > ymin < = ps2 - > ymax & & // overlap in Y?
ps - > zmax > = ps2 - > zmin & & ps - > zmin < = ps2 - > zmax ) { // overlap in Z?
/* Use X+Y+Z as the sorting order, so sprites closer to the bottom of
* the screen and with higher Z elevation , are drawn in front .
* Here X , Y , Z are the coordinates of the " center of mass " of the sprite ,
* i . e . X = ( left + right ) / 2 , etc .
* However , since we only care about order , don ' t actually divide / 2
*/
if ( ps - > xmin + ps - > xmax + ps - > ymin + ps - > ymax + ps - > zmin + ps - > zmax < =
ps2 - > xmin + ps2 - > xmax + ps2 - > ymin + ps2 - > ymax + ps2 - > zmin + ps2 - > zmax ) {
continue ;
}
} else {
/* We only change the order, if it is definite.
* I . e . every single order of X , Y , Z says ps2 is behind ps or they overlap .
* That is : If one partial order says ps behind ps2 , do not change the order .
2006-07-29 10:18:59 +00:00
*/
2008-04-16 18:28:05 +00:00
if ( ps - > xmax < ps2 - > xmin | |
ps - > ymax < ps2 - > ymin | |
ps - > zmax < ps2 - > zmin ) {
continue ;
2005-06-15 14:04:48 +00:00
}
2008-04-16 18:28:05 +00:00
}
2005-07-17 20:14:58 +00:00
2008-09-28 12:38:56 +00:00
/* Move ps2 in front of ps */
ParentSpriteToDraw * temp = ps2 ;
for ( ParentSpriteToDraw * * psd3 = psd2 ; psd3 > psd ; psd3 - - ) {
* psd3 = * ( psd3 - 1 ) ;
}
* psd = temp ;
2004-08-09 17:04:08 +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
{
2008-04-16 20:01:04 +00:00
const ParentSpriteToDraw * const * psd_end = psd - > End ( ) ;
for ( const ParentSpriteToDraw * const * it = psd - > Begin ( ) ; it ! = psd_end ; it + + ) {
const ParentSpriteToDraw * ps = * it ;
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 ) {
const ChildScreenSpriteToDraw * cs = csstdv - > Get ( child_idx ) ;
child_idx = cs - > next ;
2011-11-24 12:38:48 +00:00
DrawSpriteViewport ( cs - > image , cs - > pal , ps - > left + cs - > x , ps - > top + 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
{
2008-04-16 20:01:04 +00:00
const ParentSpriteToDraw * const * psd_end = psd - > End ( ) ;
for ( const ParentSpriteToDraw * const * it = psd - > Begin ( ) ; it ! = psd_end ; it + + ) {
const ParentSpriteToDraw * ps = * it ;
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
{
2008-04-16 19:01:09 +00:00
const StringSpriteToDraw * ssend = sstdv - > End ( ) ;
for ( const StringSpriteToDraw * ss = sstdv - > Begin ( ) ; ss ! = ssend ; + + ss ) {
2009-09-11 22:21:54 +00:00
TextColour colour = TC_BLACK ;
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 ) ;
2009-12-22 12:50:41 +00:00
int h = VPSM_TOP + ( small ? FONT_HEIGHT_SMALL : FONT_HEIGHT_NORMAL ) + VPSM_BOTTOM ;
2009-09-11 22:21:54 +00:00
SetDParam ( 0 , ss - > params [ 0 ] ) ;
SetDParam ( 1 , ss - > params [ 1 ] ) ;
2006-11-05 11:17:33 +00:00
2009-12-22 12:50:41 +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 */
2009-08-05 17:59:21 +00:00
if ( IsInvisibilitySet ( TO_SIGNS ) & & ss - > string ! = STR_WHITE_SIGN ) continue ;
2008-04-03 19:55:40 +00:00
2009-09-11 22:21:54 +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 . */
2010-12-25 19:47:15 +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 (
2009-12-22 12:50:41 +00:00
x , y , x + w , y + h , 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
2010-07-02 13:53:05 +00:00
DrawString ( x + VPSM_LEFT , x + w - 1 - VPSM_RIGHT , y + VPSM_TOP , ss - > string , colour , SA_HOR_CENTER ) ;
2008-04-16 14:18:15 +00:00
}
2004-08-09 17:04:08 +00:00
}
2004-11-15 19:25:59 +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
DrawPixelInfo * old_dpi = _cur_dpi ;
_cur_dpi = & _vd . dpi ;
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 ;
_vd . dpi . pitch = old_dpi - > pitch ;
_vd . last_child = NULL ;
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
2014-01-02 22:41:58 +00:00
_vd . dpi . dst_ptr = BlitterFactory : : GetCurrentBlitter ( ) - > MoveTo ( old_dpi - > dst_ptr , x - old_dpi - > left , y - old_dpi - > top ) ;
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
2008-04-16 21:06:21 +00:00
ViewportAddTownNames ( & _vd . dpi ) ;
ViewportAddStationNames ( & _vd . dpi ) ;
ViewportAddSigns ( & _vd . dpi ) ;
2004-08-09 17:04:08 +00:00
2008-08-04 14:40:50 +00:00
DrawTextEffects ( & _vd . dpi ) ;
2008-06-19 09:32:25 +00:00
if ( _vd . tile_sprites_to_draw . Length ( ) ! = 0 ) ViewportDrawTileSprites ( & _vd . tile_sprites_to_draw ) ;
2004-08-09 17:04:08 +00:00
2008-04-16 21:06:21 +00:00
ParentSpriteToDraw * psd_end = _vd . parent_sprites_to_draw . End ( ) ;
for ( ParentSpriteToDraw * it = _vd . parent_sprites_to_draw . Begin ( ) ; it ! = psd_end ; it + + ) {
* _vd . parent_sprites_to_sort . Append ( ) = it ;
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 ;
2014-02-15 12:19:46 +00:00
if ( vp - > overlay ! = NULL & & vp - > overlay - > GetCargoMask ( ) ! = 0 & & vp - > overlay - > GetCompanyMask ( ) ! = 0 ) {
/* translate to window coordinates */
dp . left = x ;
dp . top = y ;
vp - > overlay - > Draw ( & dp ) ;
}
2013-05-19 14:49:25 +00:00
2014-02-15 12:19:46 +00:00
if ( _vd . string_sprites_to_draw . Length ( ) ! = 0 ) {
/* 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
2004-08-09 17:04:08 +00:00
_cur_dpi = old_dpi ;
2008-04-16 21:06:21 +00:00
2008-06-19 09:32:25 +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
}
2010-08-01 19:22:34 +00:00
/**
* Make sure we don ' t draw a too big area at a time .
2010-08-01 19:44:49 +00:00
* If we do , the sprite memory will overflow .
*/
2006-11-18 00:14:43 +00:00
static void ViewportDrawChk ( const ViewPort * vp , int left , int top , int right , int bottom )
2004-08-09 17:04:08 +00:00
{
2011-11-24 12:38:48 +00:00
if ( ScaleByZoom ( bottom - top , vp - > zoom ) * ScaleByZoom ( right - left , vp - > zoom ) > 180000 * ZOOM_LVL_BASE * ZOOM_LVL_BASE ) {
2004-08-09 17:04:08 +00:00
if ( ( bottom - top ) > ( right - left ) ) {
int t = ( top + bottom ) > > 1 ;
ViewportDrawChk ( vp , left , top , right , t ) ;
ViewportDrawChk ( vp , left , t , right , bottom ) ;
} else {
int t = ( left + right ) > > 1 ;
ViewportDrawChk ( vp , left , top , t , bottom ) ;
ViewportDrawChk ( vp , t , top , right , bottom ) ;
}
} else {
2004-09-10 19:02:27 +00:00
ViewportDoDraw ( vp ,
2007-05-15 16:08:46 +00:00
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
) ;
}
}
2006-11-18 00:14:43 +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
ViewportDrawChk ( vp , left , top , right , bottom ) ;
}
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
{
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
/**
* Continue criteria for the SearchMapEdge function .
* @ param iter Value to check .
* @ param iter_limit Maximum value for the iter
* @ param sy Screen y coordinate calculated for the tile at hand
* @ param sy_limit Limit to the screen y coordinate
* @ return True when we should continue searching .
*/
typedef bool ContinueMapEdgeSearch ( int iter , int iter_limit , int sy , int sy_limit ) ;
/** Continue criteria for searching a no-longer-visible tile in negative direction, starting at some tile. */
static inline bool ContinueLowerMapEdgeSearch ( int iter , int iter_limit , int sy , int sy_limit ) { return iter > 0 & & sy > sy_limit ; }
/** Continue criteria for searching a no-longer-visible tile in positive direction, starting at some tile. */
static inline bool ContinueUpperMapEdgeSearch ( int iter , int iter_limit , int sy , int sy_limit ) { return iter < iter_limit & & sy < sy_limit ; }
/**
* Searches , starting at the given tile , by applying the given offset to iter , for a no longer visible tile .
* The whole sense of this function is keeping the to - be - written code small , thus it is a little bit abstracted
* so the same function can be used for both the X and Y locations . As such a reference to one of the elements
* in curr_tile was needed .
* @ param curr_tile A tile
* @ param iter Reference to either the X or Y of curr_tile .
* @ param iter_limit Upper search limit for the iter value .
* @ param offset Search in steps of this size
* @ param sy_limit Search limit to be passed to the criteria
* @ param continue_criteria Search as long as this criteria is true
* @ return The final value of iter .
*/
static int SearchMapEdge ( Point & curr_tile , int & iter , int iter_limit , int offset , int sy_limit , ContinueMapEdgeSearch continue_criteria )
{
int sy ;
do {
iter = Clamp ( iter + offset , 0 , iter_limit ) ;
sy = GetViewportY ( curr_tile ) ;
} while ( continue_criteria ( iter , iter_limit , sy , sy_limit ) ) ;
return iter ;
}
/**
* Determine the clamping of either the X or Y coordinate to the map .
* @ param curr_tile A tile
* @ param iter Reference to either the X or Y of curr_tile .
* @ param iter_limit Upper search limit for the iter value .
* @ param start Start value for the iteration .
* @ param other_ref Reference to the opposite axis in curr_tile than of iter .
* @ param other_value Start value for of the opposite axis
* @ param vp_value Value of the viewport location in the opposite axis as for iter .
* @ param other_limit Limit for the other value , so if iter is X , then other_limit is for Y .
* @ param vp_top Top of the viewport .
* @ param vp_bottom Bottom of the viewport .
* @ return Clamped version of vp_value .
*/
static inline int ClampXYToMap ( Point & curr_tile , int & iter , int iter_limit , int start , int & other_ref , int other_value , int vp_value , int other_limit , int vp_top , int vp_bottom )
{
bool upper_edge = other_value < _settings_game . construction . max_heightlevel / 4 ;
/*
* First get an estimate of the tiles relevant for us at that edge . Relevant in the sense
2014-10-12 18:26:54 +00:00
* " at least close to the visible area " . Thus , we don ' t look at exactly each tile , inspecting
2014-09-22 18:14:44 +00:00
* e . g . every tenth should be enough . After all , the desired screen limit is set such that
* the bordermost tiles are painted in the middle of the screen when one hits the limit ,
* i . e . it is no harm if there is some small error in that calculation
*/
other_ref = upper_edge ? 0 : other_limit ;
iter = start ;
int min_iter = SearchMapEdge ( curr_tile , iter , iter_limit , upper_edge ? - 10 : + 10 , vp_top , upper_edge ? ContinueLowerMapEdgeSearch : ContinueUpperMapEdgeSearch ) ;
iter = start ;
int max_iter = SearchMapEdge ( curr_tile , iter , iter_limit , upper_edge ? + 10 : - 10 , vp_bottom , upper_edge ? ContinueUpperMapEdgeSearch : ContinueLowerMapEdgeSearch ) ;
max_iter = min ( max_iter + _settings_game . construction . max_heightlevel / 4 , iter_limit ) ;
min_iter = min ( min_iter , max_iter ) ;
/* Now, calculate the highest heightlevel of these tiles. Again just as an estimate. */
int max_heightlevel_at_edge = 0 ;
for ( iter = min_iter ; iter < = max_iter ; iter + = 10 ) {
max_heightlevel_at_edge = max ( max_heightlevel_at_edge , ( int ) TileHeight ( TileXY ( curr_tile . x , curr_tile . y ) ) ) ;
}
/* Based on that heightlevel, calculate the limit. For the upper edge a tile with height zero would
* get a limit of zero , on the other side it depends on the number of tiles along the axis . */
return upper_edge ?
max ( vp_value , - max_heightlevel_at_edge * ( int ) ( TILE_HEIGHT * 2 * ZOOM_LVL_BASE ) ) :
min ( vp_value , ( other_limit * TILE_SIZE * 4 - max_heightlevel_at_edge * TILE_HEIGHT * 2 ) * ZOOM_LVL_BASE ) ;
}
2007-06-25 21:50:36 +00:00
static inline void ClampViewportToMap ( const ViewPort * vp , int & x , int & y )
{
2014-09-22 18:14:44 +00:00
int original_y = y ;
2007-06-25 21:50:36 +00:00
/* Centre of the viewport is hot spot */
x + = vp - > virtual_width / 2 ;
y + = vp - > virtual_height / 2 ;
/* Convert viewport coordinates to map coordinates
* Calculation is scaled by 4 to avoid rounding errors */
int vx = - x + y * 2 ;
int vy = x + y * 2 ;
2014-09-22 18:14:44 +00:00
/* Find out which tile corresponds to (vx,vy) if one assumes height zero. The cast is necessary to prevent C++ from
* converting the result to an uint , which gives an overflow instead of a negative result . . . */
int tx = vx / ( int ) ( TILE_SIZE * 4 * ZOOM_LVL_BASE ) ;
int ty = vy / ( int ) ( TILE_SIZE * 4 * ZOOM_LVL_BASE ) ;
Point curr_tile ;
vx = ClampXYToMap ( curr_tile , curr_tile . y , MapMaxY ( ) , ty , curr_tile . x , tx , vx , MapMaxX ( ) , original_y , original_y + vp - > virtual_height ) ;
vy = ClampXYToMap ( curr_tile , curr_tile . x , MapMaxX ( ) , tx , curr_tile . y , ty , vy , MapMaxY ( ) , original_y , original_y + vp - > virtual_height ) ;
2007-06-25 21:50:36 +00:00
/* Convert map coordinates to viewport coordinates */
x = ( - vx + vy ) / 2 ;
y = ( vx + vy ) / 4 ;
2010-05-30 13:05:36 +00:00
/* Remove centering */
2007-06-25 21:50:36 +00:00
x - = vp - > virtual_width / 2 ;
y - = vp - > virtual_height / 2 ;
}
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 )
{
2005-07-17 20:14:58 +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 */
2008-05-11 15:08:44 +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 ) {
2011-11-24 12:38:48 +00:00
int max_scroll = ScaleByMapSize1D ( 512 * ZOOM_LVL_BASE ) ;
2010-05-30 13:05:36 +00:00
/* Not at our desired position yet... */
2008-05-11 15:08:44 +00:00
w - > viewport - > scrollpos_x + = Clamp ( delta_x / 4 , - max_scroll , max_scroll ) ;
w - > viewport - > scrollpos_y + = Clamp ( 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
}
2008-05-11 15:08:44 +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
2007-09-09 10:13:17 +00:00
* @ ingroup dirty
*/
2005-07-17 20:14:58 +00:00
static void 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 ;
if ( right < = 0 ) return ;
2004-08-09 17:04:08 +00:00
2005-07-17 20:14:58 +00:00
bottom - = vp - > virtual_top ;
if ( bottom < = 0 ) return ;
2004-08-09 17:04:08 +00:00
2005-07-17 20:14:58 +00:00
left = max ( 0 , left - vp - > virtual_left ) ;
2004-08-09 17:04:08 +00:00
2005-07-17 20:14:58 +00:00
if ( left > = vp - > virtual_width ) return ;
2004-08-09 17:04:08 +00:00
2005-07-17 20:14:58 +00:00
top = max ( 0 , top - vp - > virtual_top ) ;
2004-08-09 17:04:08 +00:00
2005-07-17 20:14:58 +00:00
if ( top > = vp - > virtual_height ) return ;
2004-08-09 17:04:08 +00:00
SetDirtyBlocks (
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
) ;
}
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 )
2008-04-19 13:05:05 +00:00
* @ ingroup dirty
*/
2004-08-09 17:04:08 +00:00
void MarkAllViewportsDirty ( int left , int top , int right , int bottom )
{
2009-01-06 22:37:42 +00:00
Window * w ;
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
ViewPort * vp = w - > viewport ;
2008-04-17 09:42:44 +00:00
if ( vp ! = NULL ) {
2004-08-09 17:04:08 +00:00
assert ( vp - > width ! = 0 ) ;
MarkViewportDirty ( vp , left , top , right , bottom ) ;
}
2008-04-17 09:42:44 +00:00
}
2004-08-09 17:04:08 +00:00
}
2011-11-24 12:20:14 +00:00
void ConstrainAllViewportsZoom ( )
{
Window * w ;
FOR_ALL_WINDOWS_FROM_FRONT ( w ) {
if ( w - > viewport = = NULL ) continue ;
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 . )
2011-01-18 22:31:06 +00:00
* @ ingroup dirty
*/
2015-02-22 14:01:24 +00:00
void MarkTileDirtyByTile ( TileIndex tile , int bridge_level_offset )
2005-07-17 20:14:58 +00:00
{
2015-02-14 12:53:07 +00:00
Point pt = RemapCoords ( TileX ( tile ) * TILE_SIZE , TileY ( tile ) * TILE_SIZE , TilePixelHeight ( tile ) ) ;
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
2015-02-14 12:53:07 +00:00
/**
* Mark a ( virtual ) tile outside the map dirty for repaint .
* @ param x Tile X position .
* @ param y Tile Y position .
* @ ingroup dirty
*/
2014-09-21 17:29:48 +00:00
void MarkTileDirtyByTileOutsideMap ( int x , int y )
{
2015-02-14 12:53:07 +00:00
Point pt = RemapCoords ( x * TILE_SIZE , y * TILE_SIZE , TilePixelHeightOutsideMap ( x , y ) ) ;
2014-09-21 17:29:48 +00:00
MarkAllViewportsDirty (
2015-02-14 12:53:07 +00:00
pt . x - MAX_TILE_EXTENT_LEFT ,
pt . y , // no buildings outside of map
pt . x + MAX_TILE_EXTENT_RIGHT ,
pt . y + MAX_TILE_EXTENT_BOTTOM ) ;
2014-09-21 17:29:48 +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 ) ;
int x_end = Clamp ( x_start + x_size , 0 , MapSizeX ( ) * TILE_SIZE - TILE_SIZE ) ;
int y_end = Clamp ( y_start + y_size , 0 , MapSizeY ( ) * TILE_SIZE - TILE_SIZE ) ;
x_start = Clamp ( x_start , 0 , MapSizeX ( ) * TILE_SIZE - TILE_SIZE ) ;
y_start = Clamp ( y_start , 0 , MapSizeY ( ) * TILE_SIZE - TILE_SIZE ) ;
/* 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 ;
if ( x < MapMaxX ( ) & & y < MapMaxY ( ) ) {
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
*/
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 ) ;
int sign_height = ScaleByZoom ( VPSM_TOP + ( small ? FONT_HEIGHT_SMALL : FONT_HEIGHT_NORMAL ) + VPSM_BOTTOM , vp - > zoom ) ;
x = ScaleByZoom ( x - vp - > left , vp - > zoom ) + vp - > virtual_left ;
y = ScaleByZoom ( y - vp - > top , vp - > zoom ) + vp - > virtual_top ;
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
}
2009-11-22 20:38:06 +00:00
static bool CheckClickOnTown ( const ViewPort * vp , int x , int y )
2009-07-25 10:56:36 +00:00
{
2009-11-22 20:38:06 +00:00
if ( ! HasBit ( _display_opt , DO_SHOW_TOWN_NAMES ) ) return false ;
2009-07-25 10:56:36 +00:00
2009-11-22 20:38:06 +00:00
const Town * t ;
FOR_ALL_TOWNS ( t ) {
2012-04-25 20:50:13 +00:00
if ( CheckClickOnViewportSign ( vp , x , y , & t - > cache . sign ) ) {
2009-11-22 20:38:06 +00:00
ShowTownViewWindow ( t - > index ) ;
return true ;
}
2009-07-25 10:56:36 +00:00
}
2009-11-22 20:38:06 +00:00
return false ;
2009-07-25 10:56:36 +00:00
}
2006-08-31 07:49:34 +00:00
2005-07-17 20:14:58 +00:00
static bool CheckClickOnStation ( const ViewPort * vp , int x , int y )
2004-08-09 17:04:08 +00:00
{
2009-11-22 20:38:06 +00:00
if ( ! ( HasBit ( _display_opt , DO_SHOW_STATION_NAMES ) | | HasBit ( _display_opt , DO_SHOW_WAYPOINT_NAMES ) ) | | IsInvisibilitySet ( TO_SIGNS ) ) return false ;
2004-08-09 17:04:08 +00:00
2009-07-25 10:56:36 +00:00
const BaseStation * st ;
2009-11-22 20:38:06 +00:00
FOR_ALL_BASE_STATIONS ( st ) {
/* Check whether the base station is a station or a waypoint */
bool is_station = Station : : IsExpected ( st ) ;
2006-08-31 07:49:34 +00:00
2009-11-22 20:38:06 +00:00
/* Don't check if the display options are disabled */
if ( ! HasBit ( _display_opt , is_station ? DO_SHOW_STATION_NAMES : DO_SHOW_WAYPOINT_NAMES ) ) continue ;
2007-05-19 22:48:04 +00:00
2011-08-19 20:54:15 +00:00
/* Don't check if competitor signs are not shown and the sign isn't owned by the local company */
if ( ! HasBit ( _display_opt , DO_SHOW_COMPETITOR_SIGNS ) & & _local_company ! = st - > owner & & st - > owner ! = OWNER_NONE ) continue ;
2009-11-22 20:38:06 +00:00
if ( CheckClickOnViewportSign ( vp , x , y , & st - > sign ) ) {
if ( is_station ) {
ShowStationViewWindow ( st - > index ) ;
} else {
ShowWaypointWindow ( Waypoint : : From ( st ) ) ;
2004-08-09 17:04:08 +00:00
}
2009-11-22 20:38:06 +00:00
return true ;
}
2004-08-09 17:04:08 +00:00
}
2009-11-22 20:38:06 +00:00
return false ;
2004-08-09 17:04:08 +00:00
}
2006-08-31 07:49:34 +00:00
2005-07-17 20:14:58 +00:00
static bool CheckClickOnSign ( const ViewPort * vp , int x , int y )
2004-08-09 17:04:08 +00:00
{
2008-09-30 20:39:50 +00:00
/* Signs are turned off, or they are transparent and invisibility is ON, or company is a spectator */
2010-05-01 14:20:56 +00:00
if ( ! HasBit ( _display_opt , DO_SHOW_SIGNS ) | | IsInvisibilitySet ( TO_SIGNS ) | | _local_company = = COMPANY_SPECTATOR ) return false ;
2004-08-09 17:04:08 +00:00
2009-11-22 20:38:06 +00:00
const Sign * si ;
FOR_ALL_SIGNS ( si ) {
2011-08-19 20:54:15 +00:00
/* If competitor signs are hidden, don't check signs that aren't owned by local company */
2012-01-22 13:54:02 +00:00
if ( ! HasBit ( _display_opt , DO_SHOW_COMPETITOR_SIGNS ) & & _local_company ! = si - > owner & & si - > owner ! = OWNER_DEITY ) continue ;
if ( si - > owner = = OWNER_DEITY & & _game_mode ! = GM_EDITOR ) continue ;
2011-08-19 20:54:15 +00:00
2009-11-22 20:38:06 +00:00
if ( CheckClickOnViewportSign ( vp , x , y , & si - > sign ) ) {
HandleClickOnSign ( si ) ;
return true ;
}
2004-08-09 17:04:08 +00:00
}
return false ;
}
2006-08-31 07:49:34 +00:00
2009-01-02 22:42:05 +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 ( ) ;
2010-09-07 11:05:22 +00:00
if ( w ! = NULL ) w - > OnPlaceObject ( pt , TileVirtXY ( pt . x , pt . y ) ) ;
}
2005-11-16 12:52:01 +00:00
2009-01-02 22:42:05 +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 ) {
if ( v ! = NULL & & VehicleClicked ( v ) ) return true ;
}
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
2009-01-02 22:42:05 +00:00
if ( CheckClickOnTown ( vp , x , y ) ) return true ;
if ( CheckClickOnStation ( vp , x , y ) ) return true ;
if ( CheckClickOnSign ( 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
2006-05-11 05:32:26 +00:00
if ( v ! = NULL ) {
2006-12-26 17:36:18 +00:00
DEBUG ( misc , 2 , " Vehicle %d (index %d) at %p " , v - > unitnumber , v - > index , 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 )
{
if ( w - > viewport - > overlay ! = NULL & &
w - > viewport - > overlay - > GetCompanyMask ( ) ! = 0 & &
w - > viewport - > overlay - > GetCargoMask ( ) ! = 0 ) {
w - > viewport - > overlay - > RebuildCache ( ) ;
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 ) {
if ( x > = 0 & & x < = ( int ) MapSizeX ( ) * ( int ) TILE_SIZE - 1
& & y > = 0 & & y < = ( int ) MapSizeY ( ) * ( int ) TILE_SIZE - 1 ) {
z = GetSlopePixelZ ( x , y ) ;
} else {
z = TileHeightOutsideMap ( x / TILE_SIZE , y / TILE_SIZE ) ;
}
}
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 .
* @ return The window that requested the current tile highlighting , or \ c NULL if not available .
*/
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
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 ( ) ;
break ;
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
*/
2010-10-03 12:20:50 +00:00
static inline void ShowMeasurementTooltips ( StringID str , uint paramcount , const uint64 params [ ] , TooltipCloseCondition close_cond = TCC_LEFT_CLICK )
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
}
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 ;
}
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 */
2010-10-03 12:20:50 +00:00
if ( distance > 1 ) ShowMeasurementTooltips ( STR_MEASURE_LENGTH , 1 , & distance , TCC_HOVER ) ;
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 ] ) ) ;
2010-08-01 20:52:11 +00:00
/* FALL THROUGH */
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 ] ) ) ) ;
2007-01-11 11:05:01 +00:00
h0 = 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 ] ) ) ) ;
2007-01-11 11:05:01 +00:00
h1 = 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 ) ;
CheckOverflow ( x , y , ( MapMaxX ( ) - 1 ) * TILE_SIZE , 1 ) ;
CheckOverflow ( y , x , ( MapMaxY ( ) - 1 ) * TILE_SIZE , 1 ) ;
2010-05-13 11:19:30 +00:00
assert ( x > = 0 & & y > = 0 & & x < = ( int ) ( MapMaxX ( ) * TILE_SIZE ) & & y < = ( int ) ( MapMaxY ( ) * 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 ) ;
CheckOverflow ( x , y , ( MapMaxX ( ) - 1 ) * TILE_SIZE , - 1 ) ;
CheckOverflow ( y , x , ( MapMaxY ( ) - 1 ) * TILE_SIZE , - 1 ) ;
2010-05-13 11:19:30 +00:00
assert ( x > = 0 & & y > = 0 & & x < = ( int ) ( MapMaxX ( ) * TILE_SIZE ) & & y < = ( int ) ( MapMaxY ( ) * 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 ;
2010-07-29 14:26:28 +00:00
/* FALL THROUGH */
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 ;
2010-07-29 14:26:28 +00:00
/* FALL THROUGH */
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 ) ;
2010-07-29 14:26:28 +00:00
/* FALL THROUGH */
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
{
2010-05-30 12:15:28 +00:00
if ( _special_mouse_mode ! = WSM_SIZING ) 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 ( ) ;
2004-08-09 17:04:08 +00:00
if ( w = = NULL ) {
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 ) {
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
}
2007-04-04 04:08:47 +00:00
/* mouse button released..
* keep the selected tool , but reset it to the original mode . */
2004-09-10 19:02:27 +00:00
_special_mouse_mode = WSM_NONE ;
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 ) ;
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
}
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"
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 ;
if ( w ! = NULL ) w - > OnPlaceObjectAbort ( ) ;
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
}
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
Point GetViewportStationMiddle ( const ViewPort * vp , const Station * st )
{
int x = TileX ( st - > xy ) * TILE_SIZE ;
int y = TileY ( st - > xy ) * TILE_SIZE ;
int z = GetSlopePixelZ ( Clamp ( x , 0 , MapSizeX ( ) * TILE_SIZE - 1 ) , Clamp ( y , 0 , MapSizeY ( ) * TILE_SIZE - 1 ) ) ;
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 ;
}
}
assert ( _vp_sprite_sorter ! = NULL ) ;
}