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 .
2015-02-22 14:42:34 +00:00
* This time the nothern most tile on the map defines 0 and
* everything south of that has a positive number .
2014-09-21 17:27:37 +00:00
*/
2004-08-09 17:04:08 +00:00
# include "stdafx.h"
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"
2018-04-24 17:19:01 +00:00
# include "company_base.h"
# include "command_func.h"
# include "network/network_func.h"
2018-07-19 19:17:07 +00:00
# include "framerate_type.h"
2018-09-16 16:26:04 +00:00
# include "core/sort_func.hpp"
2014-09-21 17:27:37 +00:00
# include <map>
2008-04-16 14:15:00 +00:00
2008-01-13 01:21:35 +00:00
# include "table/strings.h"
2015-07-26 09:47:17 +00:00
# include "table/string_colours.h"
2008-01-13 01:21:35 +00:00
2014-04-23 20:13:33 +00:00
# include "safeguards.h"
2008-01-09 09:45:45 +00:00
Point _tile_fract_coords ;
2007-05-15 14:08:39 +00:00
2015-02-14 12:53:07 +00:00
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
/**
* 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 )
{
2015-02-22 14:42:34 +00:00
/* Each increment in X or Y direction moves down by half a tile, i.e. TILE_PIXELS / 2. */
return ( tile . y * ( int ) ( TILE_PIXELS / 2 ) + tile . x * ( int ) ( TILE_PIXELS / 2 ) - TilePixelHeightOutsideMap ( tile . x , tile . y ) ) < < ZOOM_LVL_SHIFT ;
2014-09-21 17:27:37 +00:00
}
2004-09-10 19:02:27 +00:00
2014-09-21 17:27:37 +00:00
/**
2015-02-22 14:42:34 +00:00
* Add the landscape to the viewport , i . e . all ground tiles and buildings .
2014-09-21 17:27:37 +00:00
*/
2015-02-22 14:42:34 +00:00
static void ViewportAddLandscape ( )
2014-09-21 17:27:37 +00:00
{
2015-02-22 14:42:34 +00:00
assert ( _vd . dpi . top < = _vd . dpi . top + _vd . dpi . height ) ;
assert ( _vd . dpi . left < = _vd . dpi . left + _vd . dpi . width ) ;
2014-09-21 17:27:37 +00:00
2015-02-22 14:42:34 +00:00
Point upper_left = InverseRemapCoords ( _vd . dpi . left , _vd . dpi . top ) ;
Point upper_right = InverseRemapCoords ( _vd . dpi . left + _vd . dpi . width , _vd . dpi . top ) ;
2014-09-21 17:27:37 +00:00
2015-02-22 14:42:34 +00:00
/* Transformations between tile coordinates and viewport rows/columns: See vp_column_row
* column = y - x
* row = x + y
* x = ( row - column ) / 2
* y = ( row + column ) / 2
* Note : ( row , columns ) pairs are only valid , if they are both even or both odd .
*/
2014-09-21 17:27:37 +00:00
2015-02-22 14:42:34 +00:00
/* Columns overlap with neighbouring columns by a half tile.
* - Left column is column of upper_left ( rounded down ) and one column to the left .
* - Right column is column of upper_right ( rounded up ) and one column to the right .
* Note : Integer - division does not round down for negative numbers , so ensure rounding with another increment / decrement .
*/
int left_column = ( upper_left . y - upper_left . x ) / ( int ) TILE_SIZE - 2 ;
int right_column = ( upper_right . y - upper_right . x ) / ( int ) TILE_SIZE + 2 ;
2014-09-21 17:27:37 +00:00
2015-02-22 14:42:34 +00:00
int potential_bridge_height = ZOOM_LVL_BASE * TILE_HEIGHT * _settings_game . construction . max_bridge_height ;
2014-09-21 17:27:37 +00:00
2015-02-22 14:42:34 +00:00
/* Rows overlap with neighbouring rows by a half tile.
* The first row that could possibly be visible is the row above upper_left ( if it is at height 0 ) .
* Due to integer - division not rounding down for negative numbers , we need another decrement .
2014-09-21 17:27:37 +00:00
*/
2015-02-22 14:42:34 +00:00
int row = ( upper_left . x + upper_left . y ) / ( int ) TILE_SIZE - 2 ;
bool last_row = false ;
for ( ; ! last_row ; row + + ) {
last_row = true ;
for ( int column = left_column ; column < = right_column ; column + + ) {
/* Valid row/column? */
if ( ( row + column ) % 2 ! = 0 ) continue ;
Point tilecoord ;
tilecoord . x = ( row - column ) / 2 ;
tilecoord . y = ( row + column ) / 2 ;
assert ( column = = tilecoord . y - tilecoord . x ) ;
assert ( row = = tilecoord . y + tilecoord . x ) ;
TileType tile_type ;
TileInfo tile_info ;
_cur_ti = & tile_info ;
tile_info . x = tilecoord . x * TILE_SIZE ; // FIXME tile_info should use signed integers
tile_info . y = tilecoord . y * TILE_SIZE ;
2015-02-22 15:05:48 +00:00
if ( IsInsideBS ( tilecoord . x , 0 , MapSizeX ( ) ) & & IsInsideBS ( tilecoord . y , 0 , MapSizeY ( ) ) ) {
/* This includes the south border at MapMaxX / MapMaxY. When terraforming we still draw tile selections there. */
2015-02-22 14:42:34 +00:00
tile_info . tile = TileXY ( tilecoord . x , tilecoord . y ) ;
tile_type = GetTileType ( tile_info . tile ) ;
} else {
tile_info . tile = INVALID_TILE ;
tile_type = MP_VOID ;
2006-08-07 17:32:29 +00:00
}
2009-01-21 02:31:55 +00:00
2015-02-22 15:05:48 +00:00
if ( tile_type ! = MP_VOID ) {
/* We are inside the map => paint landscape. */
tile_info . tileh = GetTilePixelSlope ( tile_info . tile , & tile_info . z ) ;
} else {
/* We are outside the map => paint black. */
tile_info . tileh = GetTilePixelSlopeOutsideMap ( tilecoord . x , tilecoord . y , & tile_info . z ) ;
}
2015-02-22 14:42:34 +00:00
int viewport_y = GetViewportY ( tilecoord ) ;
2004-09-10 19:02:27 +00:00
2015-02-22 14:42:34 +00:00
if ( viewport_y + MAX_TILE_EXTENT_BOTTOM < _vd . dpi . top ) {
/* The tile in this column is not visible yet.
* Tiles in other columns may be visible , but we need more rows in any case . */
last_row = false ;
continue ;
}
2014-09-21 17:27:37 +00:00
2015-02-22 14:42:34 +00:00
int min_visible_height = viewport_y - ( _vd . dpi . top + _vd . dpi . height ) ;
bool tile_visible = min_visible_height < = 0 ;
2014-09-21 17:27:37 +00:00
2015-02-22 14:42:34 +00:00
if ( tile_type ! = MP_VOID ) {
/* Is tile with buildings visible? */
if ( min_visible_height < MAX_TILE_EXTENT_TOP ) tile_visible = true ;
2014-09-21 17:27:37 +00:00
2015-02-22 14:42:34 +00:00
if ( IsBridgeAbove ( tile_info . tile ) ) {
/* Is the bridge visible? */
TileIndex bridge_tile = GetNorthernBridgeEnd ( tile_info . tile ) ;
int bridge_height = ZOOM_LVL_BASE * ( GetBridgePixelHeight ( bridge_tile ) - TilePixelHeight ( tile_info . tile ) ) ;
if ( min_visible_height < bridge_height + MAX_TILE_EXTENT_TOP ) tile_visible = true ;
2014-09-21 17:27:37 +00:00
}
2015-02-22 14:42:34 +00:00
/* Would a higher bridge on a more southern tile be visible?
* If yes , we need to loop over more rows to possibly find one . */
if ( min_visible_height < potential_bridge_height + MAX_TILE_EXTENT_TOP ) last_row = false ;
} else {
/* Outside of map. If we are on the north border of the map, there may still be a bridge visible,
* so we need to loop over more rows to possibly find one . */
if ( ( tilecoord . x < = 0 | | tilecoord . y < = 0 ) & & min_visible_height < potential_bridge_height + MAX_TILE_EXTENT_TOP ) last_row = false ;
}
2014-09-21 17:27:37 +00:00
2015-02-22 14:42:34 +00:00
if ( tile_visible ) {
last_row = false ;
2014-09-21 17:27:37 +00:00
_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 ) ;
2015-02-22 15:05:48 +00:00
if ( tile_info . tile ! = INVALID_TILE ) DrawTileSelection ( & tile_info ) ;
2014-09-21 17:27:37 +00:00
}
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
2015-04-25 11:58:19 +00:00
* @ param str_small the string to show when zoomed out . STR_NULL means same as \ a str
2009-07-07 16:51:20 +00:00
*/
2015-04-25 11:58:19 +00:00
void ViewportSign : : UpdatePosition ( int center , int top , StringID str , StringID str_small )
2004-08-09 17:04:08 +00:00
{
2009-07-13 23:15:13 +00:00
if ( this - > width_normal ! = 0 ) this - > MarkDirty ( ) ;
2009-07-07 16:51:20 +00:00
this - > top = top ;
2004-08-09 17:04:08 +00:00
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 */
2015-04-25 11:58:19 +00:00
if ( str_small ! = STR_NULL ) {
GetString ( buffer , str_small , lastof ( buffer ) ) ;
}
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 ;
}
2018-09-16 16:26:04 +00:00
static int CDECL CompareParentSprites ( ParentSpriteToDraw * const * psd , ParentSpriteToDraw * const * psd2 )
{
const ParentSpriteToDraw * ps = * psd ;
const ParentSpriteToDraw * ps2 = * psd2 ;
return ps - > xmin - ps2 - > xmin ;
}
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 ( ) ;
2018-09-16 16:26:04 +00:00
/* pre-sort by xmin in ascending order */
QSortT ( psd , psdvend - psd , CompareParentSprites ) ;
2008-04-16 20:01:04 +00:00
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
*/
2018-09-16 16:26:04 +00:00
if ( ps - > xmax < ps2 - > xmin ) {
/* all following sprites have xmin >= ps2->xmin */
break ;
}
if ( ps - > ymax < ps2 - > ymin | | ps - > zmax < ps2 - > zmin ) {
2008-04-16 18:28:05 +00:00
continue ;
2005-06-15 14:04:48 +00:00
}
2008-04-16 18:28:05 +00:00
}
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
{
2018-07-19 19:17:07 +00:00
PerformanceAccumulator framerate ( PFE_DRAWWORLD ) ;
2004-08-09 17:04:08 +00:00
DrawPixelInfo * dpi = _cur_dpi ;
2008-05-17 12:48:06 +00:00
dpi - > left + = this - > left ;
dpi - > top + = this - > top ;
2004-08-09 17:04:08 +00:00
2008-05-17 12:48:06 +00:00
ViewportDraw ( this - > viewport , dpi - > left , dpi - > top , dpi - > left + dpi - > width , dpi - > top + dpi - > height ) ;
2004-08-09 17:04:08 +00:00
2008-05-17 12:48:06 +00:00
dpi - > left - = this - > left ;
dpi - > top - = this - > top ;
2004-08-09 17:04:08 +00:00
}
2014-09-22 18:14:44 +00:00
/**
* 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 {
2015-02-22 14:14:30 +00:00
z = TileHeightOutsideMap ( x / ( int ) TILE_SIZE , y / ( int ) TILE_SIZE ) ;
2014-09-22 18:14:44 +00:00
}
}
2009-03-15 15:25:18 +00:00
Point pt = MapXYZToViewport ( w - > viewport , x , y , z ) ;
2008-05-11 15:08:44 +00:00
w - > viewport - > follow_vehicle = INVALID_VEHICLE ;
2004-09-03 19:59:05 +00:00
2010-07-24 10:14:39 +00:00
if ( w - > viewport - > dest_scrollpos_x = = pt . x & & w - > viewport - > dest_scrollpos_y = = pt . y ) return false ;
2004-09-03 19:59:05 +00:00
2007-05-28 17:07:19 +00:00
if ( instant ) {
2008-05-11 15:08:44 +00:00
w - > viewport - > scrollpos_x = pt . x ;
w - > viewport - > scrollpos_y = pt . y ;
2013-05-19 14:49:25 +00:00
RebuildViewportOverlay ( w ) ;
2007-05-28 16:46:16 +00:00
}
2008-05-11 15:08:44 +00:00
w - > viewport - > dest_scrollpos_x = pt . x ;
w - > viewport - > dest_scrollpos_y = pt . y ;
2004-09-03 19:59:05 +00:00
return true ;
}
2010-10-23 20:39:21 +00:00
/**
* Scrolls the viewport in a window to a given location .
* @ param tile Desired tile to center on .
* @ param w % Window containing the viewport .
* @ param instant Jump to the location instead of slowly moving to it .
* @ return Destination of the viewport was changed ( to activate other actions when the viewport is already at the desired position ) .
*/
bool ScrollWindowToTile ( TileIndex tile , Window * w , bool instant )
{
return ScrollWindowTo ( TileX ( tile ) * TILE_SIZE , TileY ( tile ) * TILE_SIZE , - 1 , w , instant ) ;
}
2010-10-23 18:28:20 +00:00
/**
* Scrolls the viewport of the main window to a given location .
* @ param tile Desired tile to center on .
* @ param instant Jump to the location instead of slowly moving to it .
* @ return Destination of the viewport was changed ( to activate other actions when the viewport is already at the desired position ) .
*/
2007-05-28 16:46:16 +00:00
bool ScrollMainWindowToTile ( TileIndex tile , bool instant )
2004-08-09 17:04:08 +00:00
{
2009-03-15 15:25:18 +00:00
return ScrollMainWindowTo ( TileX ( tile ) * TILE_SIZE + TILE_SIZE / 2 , TileY ( tile ) * TILE_SIZE + TILE_SIZE / 2 , - 1 , instant ) ;
2004-08-09 17:04:08 +00:00
}
2010-10-23 18:28:20 +00:00
/**
* Set a tile to display a red error square .
* @ param tile Tile that should show the red error square .
*/
2004-08-09 17:04:08 +00:00
void SetRedErrorSquare ( TileIndex tile )
{
TileIndex old ;
old = _thd . redsq ;
_thd . redsq = tile ;
if ( tile ! = old ) {
2009-01-19 15:06:11 +00:00
if ( tile ! = INVALID_TILE ) MarkTileDirtyByTile ( tile ) ;
if ( old ! = INVALID_TILE ) MarkTileDirtyByTile ( old ) ;
2004-08-09 17:04:08 +00:00
}
}
2010-08-01 19:22:34 +00:00
/**
* Highlight \ a w by \ a h tiles at the cursor .
2009-08-14 18:41:03 +00:00
* @ param w Width of the highlighted tiles rectangle .
* @ param h Height of the highlighted tiles rectangle .
*/
2004-08-09 17:04:08 +00:00
void SetTileSelectSize ( int w , int h )
{
2006-04-03 05:32:11 +00:00
_thd . new_size . x = w * TILE_SIZE ;
_thd . new_size . y = h * TILE_SIZE ;
2005-05-27 15:05:54 +00:00
_thd . new_outersize . x = 0 ;
_thd . new_outersize . y = 0 ;
2004-08-09 17:04:08 +00:00
}
2005-07-17 20:14:58 +00:00
void SetTileSelectBigSize ( int ox , int oy , int sx , int sy )
{
2006-04-03 05:32:11 +00:00
_thd . offs . x = ox * TILE_SIZE ;
_thd . offs . y = oy * TILE_SIZE ;
_thd . new_outersize . x = sx * TILE_SIZE ;
_thd . new_outersize . y = sy * TILE_SIZE ;
2004-08-09 17:04:08 +00:00
}
2007-04-04 04:08:47 +00:00
/** returns the best autorail highlight type from map coordinates */
2008-01-09 09:45:45 +00:00
static HighLightStyle GetAutorailHT ( int x , int y )
2005-01-19 20:55:23 +00:00
{
2009-04-18 21:10:36 +00:00
return HT_RAIL | _autorail_piece [ x & TILE_UNIT_MASK ] [ y & TILE_UNIT_MASK ] ;
2005-01-19 20:55:23 +00:00
}
2004-08-09 17:04:08 +00:00
2010-12-30 15:32:31 +00:00
/**
* Reset tile highlighting .
*/
void TileHighlightData : : Reset ( )
{
this - > pos . x = 0 ;
this - > pos . y = 0 ;
this - > new_pos . x = 0 ;
this - > new_pos . y = 0 ;
}
2010-12-23 14:24:34 +00:00
/**
* Is the user dragging a ' diagonal rectangle ' ?
* @ return User is dragging a rotated rectangle .
*/
bool TileHighlightData : : IsDraggingDiagonal ( )
{
return ( this - > place_mode & HT_DIAGONAL ) ! = 0 & & _ctrl_pressed & & _left_button_down ;
}
2010-12-30 13:16:31 +00:00
/**
* Get the window that started the current highlighting .
* @ 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 ( ) ;
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 ] ) ) ;
2017-08-15 15:56:34 +00:00
FALLTHROUGH ;
2006-10-12 15:13:40 +00:00
}
2010-08-01 20:52:11 +00:00
2006-10-12 15:13:40 +00:00
case HT_POINT :
h0 = TileHeight ( start_tile ) ;
h1 = TileHeight ( end_tile ) ;
break ;
2009-03-15 00:32:18 +00:00
default : { // All other types, this is mostly only line/autorail
2006-10-12 15:13:40 +00:00
static const HighLightStyle flip_style_direction [ ] = {
HT_DIR_X , HT_DIR_Y , HT_DIR_HL , HT_DIR_HU , HT_DIR_VR , HT_DIR_VL
} ;
static const TileIndexDiffC heightdiff_line_by_dir [ ] = {
2009-03-15 00:32:18 +00:00
/* Start */ { 1 , 0 } , { 1 , 1 } , /* HT_DIR_X */ { 0 , 1 } , { 1 , 1 } , // HT_DIR_Y
/* Start */ { 1 , 0 } , { 0 , 0 } , /* HT_DIR_HU */ { 1 , 0 } , { 1 , 1 } , // HT_DIR_HL
/* Start */ { 1 , 0 } , { 1 , 1 } , /* HT_DIR_VL */ { 0 , 1 } , { 1 , 1 } , // HT_DIR_VR
2006-10-12 15:13:40 +00:00
2009-03-15 00:32:18 +00:00
/* Start */ { 0 , 1 } , { 0 , 0 } , /* HT_DIR_X */ { 1 , 0 } , { 0 , 0 } , // HT_DIR_Y
/* End */ { 0 , 1 } , { 0 , 0 } , /* HT_DIR_HU */ { 1 , 1 } , { 0 , 1 } , // HT_DIR_HL
/* End */ { 1 , 0 } , { 0 , 0 } , /* HT_DIR_VL */ { 0 , 0 } , { 0 , 1 } , // HT_DIR_VR
2006-10-12 15:13:40 +00:00
} ;
distance % = 2 ; // we're only interested if the distance is even or uneven
style & = HT_DIR_MASK ;
/* To handle autorail, we do some magic to be able to use a lookup table.
* Firstly if we drag the other way around , we switch start & end , and if needed
* also flip the drag - position . Eg if it was on the left , and the distance is even
* that means the end , which is now the start is on the right */
if ( swap & & distance = = 0 ) style = flip_style_direction [ style ] ;
/* Use lookup table for start-tile based on HighLightStyle direction */
2010-05-07 20:20:03 +00:00
byte style_t = style * 2 ;
2006-10-12 15:13:40 +00:00
assert ( style_t < lengthof ( heightdiff_line_by_dir ) - 13 ) ;
h0 = TileHeight ( TILE_ADD ( start_tile , ToTileIndexDiff ( heightdiff_line_by_dir [ style_t ] ) ) ) ;
2010-05-07 20:20:03 +00:00
uint ht = TileHeight ( TILE_ADD ( start_tile , ToTileIndexDiff ( heightdiff_line_by_dir [ style_t + 1 ] ) ) ) ;
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 ;
2017-08-13 18:38:42 +00:00
FALLTHROUGH ;
2010-02-01 23:13:15 +00:00
2009-03-15 00:32:18 +00:00
case VPM_FIX_X : // drag in Y direction
2006-10-12 15:13:40 +00:00
x = sx ;
style = HT_DIR_Y ;
goto calc_heightdiff_single_direction ;
2010-02-01 23:13:15 +00:00
case VPM_Y_LIMITED : // Drag in Y direction (limited size).
limit = ( _thd . sizelimit - 1 ) * TILE_SIZE ;
2017-08-13 18:38:42 +00:00
FALLTHROUGH ;
2010-02-01 23:13:15 +00:00
2009-03-15 00:32:18 +00:00
case VPM_FIX_Y : // drag in X direction
2006-10-12 15:13:40 +00:00
y = sy ;
style = HT_DIR_X ;
calc_heightdiff_single_direction : ;
2010-02-01 23:13:15 +00:00
if ( limit > 0 ) {
x = sx + Clamp ( x - sx , - limit , limit ) ;
y = sy + Clamp ( y - sy , - limit , limit ) ;
}
2008-05-29 15:13:28 +00:00
if ( _settings_client . gui . measure_tooltip ) {
2006-10-12 15:13:40 +00:00
TileIndex t0 = TileVirtXY ( sx , sy ) ;
TileIndex t1 = TileVirtXY ( x , y ) ;
uint distance = DistanceManhattan ( t0 , t1 ) + 1 ;
2006-10-18 14:20:10 +00:00
byte index = 0 ;
2007-06-21 19:08:47 +00:00
uint64 params [ 2 ] ;
2006-10-12 15:13:40 +00:00
2006-10-18 14:20:10 +00:00
if ( distance ! = 1 ) {
/* With current code passing a HT_LINE style to calculate the height
* difference is enough . However if / when a point - tool is created
* with this method , function should be called with new_style ( below )
* instead of HT_LINE | style case HT_POINT is handled specially
* new_style : = ( _thd . next_drawstyle & HT_RECT ) ? HT_LINE | style : _thd . next_drawstyle ; */
int heightdiff = CalcHeightdiff ( HT_LINE | style , 0 , t0 , t1 ) ;
params [ index + + ] = distance ;
if ( heightdiff ! = 0 ) params [ index + + ] = heightdiff ;
2006-10-12 15:13:40 +00:00
}
2006-10-18 14:20:10 +00:00
2008-08-02 11:26:25 +00:00
ShowMeasurementTooltips ( measure_strings_length [ index ] , index , params ) ;
2010-08-01 18:53:30 +00:00
}
2010-08-04 07:31:29 +00:00
break ;
2004-08-09 17:04:08 +00:00
2010-02-01 23:13:15 +00:00
case VPM_X_AND_Y_LIMITED : // Drag an X by Y constrained rect area.
limit = ( _thd . sizelimit - 1 ) * TILE_SIZE ;
2007-11-19 18:38:10 +00:00
x = sx + Clamp ( x - sx , - limit , limit ) ;
y = sy + Clamp ( y - sy , - limit , limit ) ;
2017-08-13 18:38:42 +00:00
FALLTHROUGH ;
2010-02-01 23:13:15 +00:00
2010-08-04 07:31:29 +00:00
case VPM_X_AND_Y : // drag an X by Y area
2009-02-08 15:45:34 +00:00
if ( _settings_client . gui . measure_tooltip ) {
2006-10-18 14:20:10 +00:00
static const StringID measure_strings_area [ ] = {
STR_NULL , STR_NULL , STR_MEASURE_AREA , STR_MEASURE_AREA_HEIGHTDIFF
} ;
2006-10-12 15:13:40 +00:00
TileIndex t0 = TileVirtXY ( sx , sy ) ;
TileIndex t1 = TileVirtXY ( x , y ) ;
2007-11-26 16:01:29 +00:00
uint dx = Delta ( TileX ( t0 ) , TileX ( t1 ) ) + 1 ;
uint dy = Delta ( TileY ( t0 ) , TileY ( t1 ) ) + 1 ;
2006-10-18 14:20:10 +00:00
byte index = 0 ;
2007-06-21 19:08:47 +00:00
uint64 params [ 3 ] ;
2006-10-12 15:13:40 +00:00
/* If dragging an area (eg dynamite tool) and it is actually a single
* row / column , change the type to ' line ' to get proper calculation for height */
2008-01-09 09:45:45 +00:00
style = ( HighLightStyle ) _thd . next_drawstyle ;
2010-12-23 14:24:34 +00:00
if ( _thd . IsDraggingDiagonal ( ) ) {
2010-12-13 15:11:03 +00:00
/* Determine the "area" of the diagonal dragged selection.
* We assume the area is the number of tiles along the X
* edge and the number of tiles along the Y edge . However ,
* multiplying these two numbers does not give the exact
* number of tiles ; basically we are counting the black
* squares on a chess board and ignore the white ones to
* make the tile counts at the edges match up . There is no
* other way to make a proper count though .
*
* First convert to the rotated coordinate system . */
int dist_x = TileX ( t0 ) - TileX ( t1 ) ;
int dist_y = TileY ( t0 ) - TileY ( t1 ) ;
int a_max = dist_x + dist_y ;
int b_max = dist_y - dist_x ;
/* Now determine the size along the edge, but due to the
* chess board principle this counts double . */
a_max = abs ( a_max + ( a_max > 0 ? 2 : - 2 ) ) / 2 ;
b_max = abs ( b_max + ( b_max > 0 ? 2 : - 2 ) ) / 2 ;
/* We get a 1x1 on normal 2x1 rectangles, due to it being
* a seen as two sides . As the result for actual building
* will be the same as non - diagonal dragging revert to that
* behaviour to give it a more normally looking size . */
if ( a_max ! = 1 | | b_max ! = 1 ) {
dx = a_max ;
dy = b_max ;
}
} else if ( style & HT_RECT ) {
2006-10-12 15:13:40 +00:00
if ( dx = = 1 ) {
style = HT_LINE | HT_DIR_Y ;
} else if ( dy = = 1 ) {
style = HT_LINE | HT_DIR_X ;
}
}
2011-08-13 10:43:11 +00:00
if ( dx ! = 1 | | dy ! = 1 ) {
2006-10-18 14:20:10 +00:00
int heightdiff = CalcHeightdiff ( style , 0 , t0 , t1 ) ;
2006-10-12 15:13:40 +00:00
2011-08-13 10:44:15 +00:00
params [ index + + ] = dx - ( style & HT_POINT ? 1 : 0 ) ;
params [ index + + ] = dy - ( style & HT_POINT ? 1 : 0 ) ;
2006-10-18 14:20:10 +00:00
if ( heightdiff ! = 0 ) params [ index + + ] = heightdiff ;
2006-10-12 15:13:40 +00:00
}
2006-10-18 14:20:10 +00:00
2008-08-02 11:26:25 +00:00
ShowMeasurementTooltips ( measure_strings_area [ index ] , index , params ) ;
2006-10-12 15:13:40 +00:00
}
2010-08-04 07:31:29 +00:00
break ;
2006-10-12 15:13:40 +00:00
default : NOT_REACHED ( ) ;
2004-08-09 17:04:08 +00:00
}
2005-05-27 15:05:54 +00:00
_thd . selend . x = x ;
_thd . selend . y = y ;
2004-08-09 17:04:08 +00:00
}
2008-05-19 09:24:03 +00:00
/**
* Handle the mouse while dragging for placement / resizing .
2010-05-30 12:15:28 +00:00
* @ return State of handling the event .
2008-05-19 09:24:03 +00:00
*/
2010-05-30 12:15:28 +00:00
EventState VpHandlePlaceSizingDrag ( )
2004-08-09 17:04:08 +00:00
{
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
}
2015-07-28 08:16:52 +00:00
/**
* Change the cursor and mouse click / drag handling to a mode for performing special operations like tile area selection , object placement , etc .
* @ param icon New shape of the mouse cursor .
* @ param pal Palette to use .
* @ param mode Mode to perform .
* @ param w % Window requesting the mode change .
*/
2010-01-21 01:38:13 +00:00
void SetObjectToPlaceWnd ( CursorID icon , PaletteID pal , HighLightStyle mode , Window * w )
2004-08-09 17:04:08 +00:00
{
2007-01-14 19:57:49 +00:00
SetObjectToPlace ( icon , pal , mode , w - > window_class , w - > window_number ) ;
2004-08-09 17:04:08 +00:00
}
# include "table/animcursors.h"
2015-07-28 08:16:52 +00:00
/**
* Change the cursor and mouse click / drag handling to a mode for performing special operations like tile area selection , object placement , etc .
* @ param icon New shape of the mouse cursor .
* @ param pal Palette to use .
* @ param mode Mode to perform .
* @ param window_class % Window class of the window requesting the mode change .
* @ param window_num Number of the window in its class requesting the mode change .
*/
2010-01-21 01:38:13 +00:00
void SetObjectToPlace ( CursorID icon , PaletteID pal , HighLightStyle mode , WindowClass window_class , WindowNumber window_num )
2004-08-09 17:04:08 +00:00
{
2010-12-25 12:47:05 +00:00
if ( _thd . window_class ! = WC_INVALID ) {
/* Undo clicking on button and drag & drop */
2010-12-30 13:18:04 +00:00
Window * w = _thd . GetCallbackWnd ( ) ;
2010-12-25 12:47:05 +00:00
/* Call the abort function, but set the window class to something
* that will never be used to avoid infinite loops . Setting it to
* the ' next ' window class must not be done because recursion into
* this function might in some cases reset the newly set object to
* place or not properly reset the original selection . */
_thd . window_class = WC_INVALID ;
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
}
2015-07-28 08:16:52 +00:00
/** Reset the cursor and mouse mode handling back to default (normal cursor, only clicking in windows). */
2007-03-07 11:47:46 +00:00
void ResetObjectToPlace ( )
2005-01-22 20:23:18 +00:00
{
2009-04-19 10:31:30 +00:00
SetObjectToPlace ( SPR_CURSOR_MOUSE , PAL_NONE , HT_NONE , WC_MAIN_WINDOW , 0 ) ;
2004-08-09 17:04:08 +00:00
}
2013-05-19 14:49:25 +00:00
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 ) ;
}
2018-04-24 17:19:01 +00:00
/**
* Scroll players main viewport .
* @ param tile tile to center viewport on
* @ param flags type of operation
* @ param p1 ViewportScrollTarget of scroll target
* @ param p2 company or client id depending on the target
* @ param text unused
* @ return the cost of this operation or an error
*/
CommandCost CmdScrollViewport ( TileIndex tile , DoCommandFlag flags , uint32 p1 , uint32 p2 , const char * text )
{
if ( _current_company ! = OWNER_DEITY ) return CMD_ERROR ;
ViewportScrollTarget target = ( ViewportScrollTarget ) p1 ;
switch ( target ) {
case VST_EVERYONE :
break ;
case VST_COMPANY :
if ( _local_company ! = ( CompanyID ) p2 ) return CommandCost ( ) ;
break ;
case VST_CLIENT :
# ifdef ENABLE_NETWORK
if ( _network_own_client_id ! = ( ClientID ) p2 ) return CommandCost ( ) ;
break ;
# else
return CommandCost ( ) ;
# endif
default :
return CMD_ERROR ;
}
if ( flags & DC_EXEC ) {
ResetObjectToPlace ( ) ;
ScrollMainWindowToTile ( tile ) ;
}
return CommandCost ( ) ;
}