00001
00002
00003
00004
00005
00006
00007
00008
00009
00028 #include "stdafx.h"
00029 #include "openttd.h"
00030 #include "landscape.h"
00031 #include "viewport_func.h"
00032 #include "station_base.h"
00033 #include "waypoint_base.h"
00034 #include "town.h"
00035 #include "signs_base.h"
00036 #include "signs_func.h"
00037 #include "variables.h"
00038 #include "vehicle_base.h"
00039 #include "vehicle_gui.h"
00040 #include "blitter/factory.hpp"
00041 #include "transparency.h"
00042 #include "strings_func.h"
00043 #include "zoom_func.h"
00044 #include "vehicle_func.h"
00045 #include "company_func.h"
00046 #include "waypoint_func.h"
00047 #include "window_func.h"
00048 #include "tilehighlight_func.h"
00049 #include "window_gui.h"
00050
00051 #include "table/sprites.h"
00052 #include "table/strings.h"
00053
00054 PlaceProc *_place_proc;
00055 Point _tile_fract_coords;
00056
00057 struct StringSpriteToDraw {
00058 StringID string;
00059 Colours colour;
00060 int32 x;
00061 int32 y;
00062 uint64 params[2];
00063 uint16 width;
00064 };
00065
00066 struct TileSpriteToDraw {
00067 SpriteID image;
00068 SpriteID pal;
00069 const SubSprite *sub;
00070 int32 x;
00071 int32 y;
00072 };
00073
00074 struct ChildScreenSpriteToDraw {
00075 SpriteID image;
00076 SpriteID pal;
00077 const SubSprite *sub;
00078 int32 x;
00079 int32 y;
00080 int next;
00081 };
00082
00084 struct ParentSpriteToDraw {
00085 SpriteID image;
00086 SpriteID pal;
00087 const SubSprite *sub;
00088
00089 int32 x;
00090 int32 y;
00091
00092 int32 left;
00093 int32 top;
00094
00095 int32 xmin;
00096 int32 xmax;
00097 int32 ymin;
00098 int32 ymax;
00099 int zmin;
00100 int zmax;
00101
00102 int first_child;
00103 bool comparison_done;
00104 };
00105
00107 enum FoundationPart {
00108 FOUNDATION_PART_NONE = 0xFF,
00109 FOUNDATION_PART_NORMAL = 0,
00110 FOUNDATION_PART_HALFTILE = 1,
00111 FOUNDATION_PART_END
00112 };
00113
00117 enum SpriteCombineMode {
00118 SPRITE_COMBINE_NONE,
00119 SPRITE_COMBINE_PENDING,
00120 SPRITE_COMBINE_ACTIVE,
00121 };
00122
00123 typedef SmallVector<TileSpriteToDraw, 64> TileSpriteToDrawVector;
00124 typedef SmallVector<StringSpriteToDraw, 4> StringSpriteToDrawVector;
00125 typedef SmallVector<ParentSpriteToDraw, 64> ParentSpriteToDrawVector;
00126 typedef SmallVector<ParentSpriteToDraw*, 64> ParentSpriteToSortVector;
00127 typedef SmallVector<ChildScreenSpriteToDraw, 16> ChildScreenSpriteToDrawVector;
00128
00130 struct ViewportDrawer {
00131 DrawPixelInfo dpi;
00132
00133 StringSpriteToDrawVector string_sprites_to_draw;
00134 TileSpriteToDrawVector tile_sprites_to_draw;
00135 ParentSpriteToDrawVector parent_sprites_to_draw;
00136 ParentSpriteToSortVector parent_sprites_to_sort;
00137 ChildScreenSpriteToDrawVector child_screen_sprites_to_draw;
00138
00139 int *last_child;
00140
00141 SpriteCombineMode combine_sprites;
00142
00143 int foundation[FOUNDATION_PART_END];
00144 FoundationPart foundation_part;
00145 int *last_foundation_child[FOUNDATION_PART_END];
00146 Point foundation_offset[FOUNDATION_PART_END];
00147 };
00148
00149 static ViewportDrawer _vd;
00150
00151 TileHighlightData _thd;
00152 static TileInfo *_cur_ti;
00153 bool _draw_bounding_boxes = false;
00154
00155 static Point MapXYZToViewport(const ViewPort *vp, int x, int y, int z)
00156 {
00157 Point p = RemapCoords(x, y, z);
00158 p.x -= vp->virtual_width / 2;
00159 p.y -= vp->virtual_height / 2;
00160 return p;
00161 }
00162
00163 void DeleteWindowViewport(Window *w)
00164 {
00165 free(w->viewport);
00166 w->viewport = NULL;
00167 }
00168
00181 void InitializeWindowViewport(Window *w, int x, int y,
00182 int width, int height, uint32 follow_flags, ZoomLevel zoom)
00183 {
00184 assert(w->viewport == NULL);
00185
00186 ViewportData *vp = CallocT<ViewportData>(1);
00187
00188 vp->left = x + w->left;
00189 vp->top = y + w->top;
00190 vp->width = width;
00191 vp->height = height;
00192
00193 vp->zoom = zoom;
00194
00195 vp->virtual_width = ScaleByZoom(width, zoom);
00196 vp->virtual_height = ScaleByZoom(height, zoom);
00197
00198 Point pt;
00199
00200 if (follow_flags & 0x80000000) {
00201 const Vehicle *veh;
00202
00203 vp->follow_vehicle = (VehicleID)(follow_flags & 0xFFFF);
00204 veh = Vehicle::Get(vp->follow_vehicle);
00205 pt = MapXYZToViewport(vp, veh->x_pos, veh->y_pos, veh->z_pos);
00206 } else {
00207 uint x = TileX(follow_flags) * TILE_SIZE;
00208 uint y = TileY(follow_flags) * TILE_SIZE;
00209
00210 vp->follow_vehicle = INVALID_VEHICLE;
00211 pt = MapXYZToViewport(vp, x, y, GetSlopeZ(x, y));
00212 }
00213
00214 vp->scrollpos_x = pt.x;
00215 vp->scrollpos_y = pt.y;
00216 vp->dest_scrollpos_x = pt.x;
00217 vp->dest_scrollpos_y = pt.y;
00218
00219 w->viewport = vp;
00220 vp->virtual_left = 0;
00221 vp->virtual_top = 0;
00222 }
00223
00224 static Point _vp_move_offs;
00225
00226 static void DoSetViewportPosition(const Window *w, int left, int top, int width, int height)
00227 {
00228 FOR_ALL_WINDOWS_FROM_BACK_FROM(w, w) {
00229 if (left + width > w->left &&
00230 w->left + w->width > left &&
00231 top + height > w->top &&
00232 w->top + w->height > top) {
00233
00234 if (left < w->left) {
00235 DoSetViewportPosition(w, left, top, w->left - left, height);
00236 DoSetViewportPosition(w, left + (w->left - left), top, width - (w->left - left), height);
00237 return;
00238 }
00239
00240 if (left + width > w->left + w->width) {
00241 DoSetViewportPosition(w, left, top, (w->left + w->width - left), height);
00242 DoSetViewportPosition(w, left + (w->left + w->width - left), top, width - (w->left + w->width - left), height);
00243 return;
00244 }
00245
00246 if (top < w->top) {
00247 DoSetViewportPosition(w, left, top, width, (w->top - top));
00248 DoSetViewportPosition(w, left, top + (w->top - top), width, height - (w->top - top));
00249 return;
00250 }
00251
00252 if (top + height > w->top + w->height) {
00253 DoSetViewportPosition(w, left, top, width, (w->top + w->height - top));
00254 DoSetViewportPosition(w, left, top + (w->top + w->height - top), width, height - (w->top + w->height - top));
00255 return;
00256 }
00257
00258 return;
00259 }
00260 }
00261
00262 {
00263 int xo = _vp_move_offs.x;
00264 int yo = _vp_move_offs.y;
00265
00266 if (abs(xo) >= width || abs(yo) >= height) {
00267
00268 RedrawScreenRect(left, top, left + width, top + height);
00269 return;
00270 }
00271
00272 GfxScroll(left, top, width, height, xo, yo);
00273
00274 if (xo > 0) {
00275 RedrawScreenRect(left, top, xo + left, top + height);
00276 left += xo;
00277 width -= xo;
00278 } else if (xo < 0) {
00279 RedrawScreenRect(left + width + xo, top, left + width, top + height);
00280 width += xo;
00281 }
00282
00283 if (yo > 0) {
00284 RedrawScreenRect(left, top, width + left, top + yo);
00285 } else if (yo < 0) {
00286 RedrawScreenRect(left, top + height + yo, width + left, top + height);
00287 }
00288 }
00289 }
00290
00291 static void SetViewportPosition(Window *w, int x, int y)
00292 {
00293 ViewPort *vp = w->viewport;
00294 int old_left = vp->virtual_left;
00295 int old_top = vp->virtual_top;
00296 int i;
00297 int left, top, width, height;
00298
00299 vp->virtual_left = x;
00300 vp->virtual_top = y;
00301
00302
00303
00304
00305 old_left = UnScaleByZoomLower(old_left, vp->zoom);
00306 old_top = UnScaleByZoomLower(old_top, vp->zoom);
00307 x = UnScaleByZoomLower(x, vp->zoom);
00308 y = UnScaleByZoomLower(y, vp->zoom);
00309
00310 old_left -= x;
00311 old_top -= y;
00312
00313 if (old_top == 0 && old_left == 0) return;
00314
00315 _vp_move_offs.x = old_left;
00316 _vp_move_offs.y = old_top;
00317
00318 left = vp->left;
00319 top = vp->top;
00320 width = vp->width;
00321 height = vp->height;
00322
00323 if (left < 0) {
00324 width += left;
00325 left = 0;
00326 }
00327
00328 i = left + width - _screen.width;
00329 if (i >= 0) width -= i;
00330
00331 if (width > 0) {
00332 if (top < 0) {
00333 height += top;
00334 top = 0;
00335 }
00336
00337 i = top + height - _screen.height;
00338 if (i >= 0) height -= i;
00339
00340 if (height > 0) DoSetViewportPosition(w->z_front, left, top, width, height);
00341 }
00342 }
00343
00352 ViewPort *IsPtInWindowViewport(const Window *w, int x, int y)
00353 {
00354 ViewPort *vp = w->viewport;
00355
00356 if (vp != NULL &&
00357 IsInsideMM(x, vp->left, vp->left + vp->width) &&
00358 IsInsideMM(y, vp->top, vp->top + vp->height))
00359 return vp;
00360
00361 return NULL;
00362 }
00363
00370 static Point TranslateXYToTileCoord(const ViewPort *vp, int x, int y)
00371 {
00372 Point pt;
00373 int a, b;
00374 uint z;
00375
00376 if ( (uint)(x -= vp->left) >= (uint)vp->width ||
00377 (uint)(y -= vp->top) >= (uint)vp->height) {
00378 Point pt = {-1, -1};
00379 return pt;
00380 }
00381
00382 x = (ScaleByZoom(x, vp->zoom) + vp->virtual_left) >> 2;
00383 y = (ScaleByZoom(y, vp->zoom) + vp->virtual_top) >> 1;
00384
00385 a = y - x;
00386 b = y + x;
00387
00388
00389
00390
00391 a = Clamp(a, -4 * TILE_SIZE, (int)(MapMaxX() * TILE_SIZE) - 1);
00392 b = Clamp(b, -4 * TILE_SIZE, (int)(MapMaxY() * TILE_SIZE) - 1);
00393
00394
00395
00396
00397
00398
00399
00400
00401 z = 0;
00402
00403 int min_coord = _settings_game.construction.freeform_edges ? TILE_SIZE : 0;
00404
00405 for (int i = 0; i < 5; i++) z = GetSlopeZ(Clamp(a + (int)max(z, 4u) - 4, min_coord, MapMaxX() * TILE_SIZE - 1), Clamp(b + (int)max(z, 4u) - 4, min_coord, MapMaxY() * TILE_SIZE - 1)) / 2;
00406 for (uint malus = 3; malus > 0; malus--) z = GetSlopeZ(Clamp(a + (int)max(z, malus) - (int)malus, min_coord, MapMaxX() * TILE_SIZE - 1), Clamp(b + (int)max(z, malus) - (int)malus, min_coord, MapMaxY() * TILE_SIZE - 1)) / 2;
00407 for (int i = 0; i < 5; i++) z = GetSlopeZ(Clamp(a + (int)z, min_coord, MapMaxX() * TILE_SIZE - 1), Clamp(b + (int)z, min_coord, MapMaxY() * TILE_SIZE - 1)) / 2;
00408
00409 pt.x = Clamp(a + (int)z, min_coord, MapMaxX() * TILE_SIZE - 1);
00410 pt.y = Clamp(b + (int)z, min_coord, MapMaxY() * TILE_SIZE - 1);
00411
00412 return pt;
00413 }
00414
00415
00416
00417
00418 static Point GetTileFromScreenXY(int x, int y, int zoom_x, int zoom_y)
00419 {
00420 Window *w;
00421 ViewPort *vp;
00422 Point pt;
00423
00424 if ( (w = FindWindowFromPt(x, y)) != NULL &&
00425 (vp = IsPtInWindowViewport(w, x, y)) != NULL)
00426 return TranslateXYToTileCoord(vp, zoom_x, zoom_y);
00427
00428 pt.y = pt.x = -1;
00429 return pt;
00430 }
00431
00432 Point GetTileBelowCursor()
00433 {
00434 return GetTileFromScreenXY(_cursor.pos.x, _cursor.pos.y, _cursor.pos.x, _cursor.pos.y);
00435 }
00436
00437
00438 Point GetTileZoomCenterWindow(bool in, Window * w)
00439 {
00440 int x, y;
00441 ViewPort *vp = w->viewport;
00442
00443 if (in) {
00444 x = ((_cursor.pos.x - vp->left) >> 1) + (vp->width >> 2);
00445 y = ((_cursor.pos.y - vp->top) >> 1) + (vp->height >> 2);
00446 } else {
00447 x = vp->width - (_cursor.pos.x - vp->left);
00448 y = vp->height - (_cursor.pos.y - vp->top);
00449 }
00450
00451 return GetTileFromScreenXY(_cursor.pos.x, _cursor.pos.y, x + vp->left, y + vp->top);
00452 }
00453
00460 void HandleZoomMessage(Window *w, const ViewPort *vp, byte widget_zoom_in, byte widget_zoom_out)
00461 {
00462 w->SetWidgetDisabledState(widget_zoom_in, vp->zoom == ZOOM_LVL_MIN);
00463 w->SetWidgetDirty(widget_zoom_in);
00464
00465 w->SetWidgetDisabledState(widget_zoom_out, vp->zoom == ZOOM_LVL_MAX);
00466 w->SetWidgetDirty(widget_zoom_out);
00467 }
00468
00481 static void AddTileSpriteToDraw(SpriteID image, SpriteID pal, int32 x, int32 y, int z, const SubSprite *sub = NULL, int extra_offs_x = 0, int extra_offs_y = 0)
00482 {
00483 assert((image & SPRITE_MASK) < MAX_SPRITES);
00484
00485 TileSpriteToDraw *ts = _vd.tile_sprites_to_draw.Append();
00486 ts->image = image;
00487 ts->pal = pal;
00488 ts->sub = sub;
00489 Point pt = RemapCoords(x, y, z);
00490 ts->x = pt.x + extra_offs_x;
00491 ts->y = pt.y + extra_offs_y;
00492 }
00493
00506 static void AddChildSpriteToFoundation(SpriteID image, SpriteID pal, const SubSprite *sub, FoundationPart foundation_part, int extra_offs_x, int extra_offs_y)
00507 {
00508 assert(IsInsideMM(foundation_part, 0, FOUNDATION_PART_END));
00509 assert(_vd.foundation[foundation_part] != -1);
00510 Point offs = _vd.foundation_offset[foundation_part];
00511
00512
00513 int *old_child = _vd.last_child;
00514 _vd.last_child = _vd.last_foundation_child[foundation_part];
00515
00516 AddChildSpriteScreen(image, pal, offs.x + extra_offs_x, offs.y + extra_offs_y, false, sub);
00517
00518
00519 _vd.last_child = old_child;
00520 }
00521
00535 void DrawGroundSpriteAt(SpriteID image, SpriteID pal, int32 x, int32 y, int z, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
00536 {
00537
00538 if (_vd.foundation_part == FOUNDATION_PART_NONE) _vd.foundation_part = FOUNDATION_PART_NORMAL;
00539
00540 if (_vd.foundation[_vd.foundation_part] != -1) {
00541 Point pt = RemapCoords(x, y, z);
00542 AddChildSpriteToFoundation(image, pal, sub, _vd.foundation_part, pt.x + extra_offs_x, pt.y + extra_offs_y);
00543 } else {
00544 AddTileSpriteToDraw(image, pal, _cur_ti->x + x, _cur_ti->y + y, _cur_ti->z + z, sub, extra_offs_x, extra_offs_y);
00545 }
00546 }
00547
00558 void DrawGroundSprite(SpriteID image, SpriteID pal, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
00559 {
00560 DrawGroundSpriteAt(image, pal, 0, 0, 0, sub, extra_offs_x, extra_offs_y);
00561 }
00562
00570 void OffsetGroundSprite(int x, int y)
00571 {
00572
00573 switch (_vd.foundation_part) {
00574 case FOUNDATION_PART_NONE:
00575 _vd.foundation_part = FOUNDATION_PART_NORMAL;
00576 break;
00577 case FOUNDATION_PART_NORMAL:
00578 _vd.foundation_part = FOUNDATION_PART_HALFTILE;
00579 break;
00580 default: NOT_REACHED();
00581 }
00582
00583
00584 if (_vd.last_child != NULL) _vd.foundation[_vd.foundation_part] = _vd.parent_sprites_to_draw.Length() - 1;
00585
00586 _vd.foundation_offset[_vd.foundation_part].x = x;
00587 _vd.foundation_offset[_vd.foundation_part].y = y;
00588 _vd.last_foundation_child[_vd.foundation_part] = _vd.last_child;
00589 }
00590
00602 static void AddCombinedSprite(SpriteID image, SpriteID pal, int x, int y, byte z, const SubSprite *sub)
00603 {
00604 Point pt = RemapCoords(x, y, z);
00605 const Sprite *spr = GetSprite(image & SPRITE_MASK, ST_NORMAL);
00606
00607 if (pt.x + spr->x_offs >= _vd.dpi.left + _vd.dpi.width ||
00608 pt.x + spr->x_offs + spr->width <= _vd.dpi.left ||
00609 pt.y + spr->y_offs >= _vd.dpi.top + _vd.dpi.height ||
00610 pt.y + spr->y_offs + spr->height <= _vd.dpi.top)
00611 return;
00612
00613 const ParentSpriteToDraw *pstd = _vd.parent_sprites_to_draw.End() - 1;
00614 AddChildSpriteScreen(image, pal, pt.x - pstd->left, pt.y - pstd->top, false, sub);
00615 }
00616
00641 void AddSortableSpriteToDraw(SpriteID image, SpriteID 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)
00642 {
00643 int32 left, right, top, bottom;
00644
00645 assert((image & SPRITE_MASK) < MAX_SPRITES);
00646
00647
00648 if (transparent) {
00649 SetBit(image, PALETTE_MODIFIER_TRANSPARENT);
00650 pal = PALETTE_TO_TRANSPARENT;
00651 }
00652
00653 if (_vd.combine_sprites == SPRITE_COMBINE_ACTIVE) {
00654 AddCombinedSprite(image, pal, x, y, z, sub);
00655 return;
00656 }
00657
00658 _vd.last_child = NULL;
00659
00660 Point pt = RemapCoords(x, y, z);
00661 int tmp_left, tmp_top, tmp_x = pt.x, tmp_y = pt.y;
00662
00663
00664 if (image == SPR_EMPTY_BOUNDING_BOX) {
00665 left = tmp_left = RemapCoords(x + w , y + bb_offset_y, z + bb_offset_z).x;
00666 right = RemapCoords(x + bb_offset_x, y + h , z + bb_offset_z).x + 1;
00667 top = tmp_top = RemapCoords(x + bb_offset_x, y + bb_offset_y, z + dz ).y;
00668 bottom = RemapCoords(x + w , y + h , z + bb_offset_z).y + 1;
00669 } else {
00670 const Sprite *spr = GetSprite(image & SPRITE_MASK, ST_NORMAL);
00671 left = tmp_left = (pt.x += spr->x_offs);
00672 right = (pt.x + spr->width );
00673 top = tmp_top = (pt.y += spr->y_offs);
00674 bottom = (pt.y + spr->height);
00675 }
00676
00677 if (_draw_bounding_boxes && (image != SPR_EMPTY_BOUNDING_BOX)) {
00678
00679 left = min(left , RemapCoords(x + w , y + bb_offset_y, z + bb_offset_z).x);
00680 right = max(right , RemapCoords(x + bb_offset_x, y + h , z + bb_offset_z).x + 1);
00681 top = min(top , RemapCoords(x + bb_offset_x, y + bb_offset_y, z + dz ).y);
00682 bottom = max(bottom, RemapCoords(x + w , y + h , z + bb_offset_z).y + 1);
00683 }
00684
00685
00686 if (left >= _vd.dpi.left + _vd.dpi.width ||
00687 right <= _vd.dpi.left ||
00688 top >= _vd.dpi.top + _vd.dpi.height ||
00689 bottom <= _vd.dpi.top) {
00690 return;
00691 }
00692
00693 ParentSpriteToDraw *ps = _vd.parent_sprites_to_draw.Append();
00694 ps->x = tmp_x;
00695 ps->y = tmp_y;
00696
00697 ps->left = tmp_left;
00698 ps->top = tmp_top;
00699
00700 ps->image = image;
00701 ps->pal = pal;
00702 ps->sub = sub;
00703 ps->xmin = x + bb_offset_x;
00704 ps->xmax = x + max(bb_offset_x, w) - 1;
00705
00706 ps->ymin = y + bb_offset_y;
00707 ps->ymax = y + max(bb_offset_y, h) - 1;
00708
00709 ps->zmin = z + bb_offset_z;
00710 ps->zmax = z + max(bb_offset_z, dz) - 1;
00711
00712 ps->comparison_done = false;
00713 ps->first_child = -1;
00714
00715 _vd.last_child = &ps->first_child;
00716
00717 if (_vd.combine_sprites == SPRITE_COMBINE_PENDING) _vd.combine_sprites = SPRITE_COMBINE_ACTIVE;
00718 }
00719
00738 void StartSpriteCombine()
00739 {
00740 assert(_vd.combine_sprites == SPRITE_COMBINE_NONE);
00741 _vd.combine_sprites = SPRITE_COMBINE_PENDING;
00742 }
00743
00748 void EndSpriteCombine()
00749 {
00750 assert(_vd.combine_sprites != SPRITE_COMBINE_NONE);
00751 _vd.combine_sprites = SPRITE_COMBINE_NONE;
00752 }
00753
00764 void AddChildSpriteScreen(SpriteID image, SpriteID pal, int x, int y, bool transparent, const SubSprite *sub)
00765 {
00766 assert((image & SPRITE_MASK) < MAX_SPRITES);
00767
00768
00769 if (_vd.last_child == NULL) return;
00770
00771
00772 if (transparent) {
00773 SetBit(image, PALETTE_MODIFIER_TRANSPARENT);
00774 pal = PALETTE_TO_TRANSPARENT;
00775 }
00776
00777 *_vd.last_child = _vd.child_screen_sprites_to_draw.Length();
00778
00779 ChildScreenSpriteToDraw *cs = _vd.child_screen_sprites_to_draw.Append();
00780 cs->image = image;
00781 cs->pal = pal;
00782 cs->sub = sub;
00783 cs->x = x;
00784 cs->y = y;
00785 cs->next = -1;
00786
00787
00788
00789
00790 if (_vd.last_foundation_child[0] == _vd.last_child) _vd.last_foundation_child[0] = &cs->next;
00791 if (_vd.last_foundation_child[1] == _vd.last_child) _vd.last_foundation_child[1] = &cs->next;
00792 _vd.last_child = &cs->next;
00793 }
00794
00795 static void AddStringToDraw(int x, int y, StringID string, uint64 params_1, uint64 params_2, Colours colour, uint16 width)
00796 {
00797 assert(width != 0);
00798 StringSpriteToDraw *ss = _vd.string_sprites_to_draw.Append();
00799 ss->string = string;
00800 ss->x = x;
00801 ss->y = y;
00802 ss->params[0] = params_1;
00803 ss->params[1] = params_2;
00804 ss->width = width;
00805 ss->colour = colour;
00806 }
00807
00808
00820 static void DrawSelectionSprite(SpriteID image, SpriteID pal, const TileInfo *ti, int z_offset, FoundationPart foundation_part)
00821 {
00822
00823 if (_vd.foundation[foundation_part] == -1) {
00824
00825 AddTileSpriteToDraw(image, pal, ti->x, ti->y, ti->z + z_offset);
00826 } else {
00827
00828 AddChildSpriteToFoundation(image, pal, NULL, foundation_part, 0, -z_offset);
00829 }
00830 }
00831
00838 static void DrawTileSelectionRect(const TileInfo *ti, SpriteID pal)
00839 {
00840 if (!IsValidTile(ti->tile)) return;
00841
00842 SpriteID sel;
00843 if (IsHalftileSlope(ti->tileh)) {
00844 Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
00845 SpriteID sel2 = SPR_HALFTILE_SELECTION_FLAT + halftile_corner;
00846 DrawSelectionSprite(sel2, pal, ti, 7 + TILE_HEIGHT, FOUNDATION_PART_HALFTILE);
00847
00848 Corner opposite_corner = OppositeCorner(halftile_corner);
00849 if (IsSteepSlope(ti->tileh)) {
00850 sel = SPR_HALFTILE_SELECTION_DOWN;
00851 } else {
00852 sel = ((ti->tileh & SlopeWithOneCornerRaised(opposite_corner)) != 0 ? SPR_HALFTILE_SELECTION_UP : SPR_HALFTILE_SELECTION_FLAT);
00853 }
00854 sel += opposite_corner;
00855 } else {
00856 sel = SPR_SELECT_TILE + _tileh_to_sprite[ti->tileh];
00857 }
00858 DrawSelectionSprite(sel, pal, ti, 7, FOUNDATION_PART_NORMAL);
00859 }
00860
00861 static bool IsPartOfAutoLine(int px, int py)
00862 {
00863 px -= _thd.selstart.x;
00864 py -= _thd.selstart.y;
00865
00866 if ((_thd.drawstyle & ~HT_DIR_MASK) != HT_LINE) return false;
00867
00868 switch (_thd.drawstyle & HT_DIR_MASK) {
00869 case HT_DIR_X: return py == 0;
00870 case HT_DIR_Y: return px == 0;
00871 case HT_DIR_HU: return px == -py || px == -py - 16;
00872 case HT_DIR_HL: return px == -py || px == -py + 16;
00873 case HT_DIR_VL: return px == py || px == py + 16;
00874 case HT_DIR_VR: return px == py || px == py - 16;
00875 default:
00876 NOT_REACHED();
00877 }
00878 }
00879
00880
00881 static const HighLightStyle _autorail_type[6][2] = {
00882 { HT_DIR_X, HT_DIR_X },
00883 { HT_DIR_Y, HT_DIR_Y },
00884 { HT_DIR_HU, HT_DIR_HL },
00885 { HT_DIR_HL, HT_DIR_HU },
00886 { HT_DIR_VL, HT_DIR_VR },
00887 { HT_DIR_VR, HT_DIR_VL }
00888 };
00889
00890 #include "table/autorail.h"
00891
00898 static void DrawAutorailSelection(const TileInfo *ti, uint autorail_type)
00899 {
00900 SpriteID image;
00901 SpriteID pal;
00902 int offset;
00903
00904 FoundationPart foundation_part = FOUNDATION_PART_NORMAL;
00905 Slope autorail_tileh = RemoveHalftileSlope(ti->tileh);
00906 if (IsHalftileSlope(ti->tileh)) {
00907 static const uint _lower_rail[4] = { 5U, 2U, 4U, 3U };
00908 Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
00909 if (autorail_type != _lower_rail[halftile_corner]) {
00910 foundation_part = FOUNDATION_PART_HALFTILE;
00911
00912 autorail_tileh = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
00913 }
00914 }
00915
00916 offset = _AutorailTilehSprite[autorail_tileh][autorail_type];
00917 if (offset >= 0) {
00918 image = SPR_AUTORAIL_BASE + offset;
00919 pal = PAL_NONE;
00920 } else {
00921 image = SPR_AUTORAIL_BASE - offset;
00922 pal = PALETTE_SEL_TILE_RED;
00923 }
00924
00925 DrawSelectionSprite(image, _thd.make_square_red ? PALETTE_SEL_TILE_RED : pal, ti, 7, foundation_part);
00926 }
00927
00932 static void DrawTileSelection(const TileInfo *ti)
00933 {
00934
00935 bool is_redsq = _thd.redsq == ti->tile;
00936 if (is_redsq) DrawTileSelectionRect(ti, PALETTE_TILE_RED_PULSATING);
00937
00938
00939 if (_thd.drawstyle == 0) return;
00940
00941
00942 if (IsInsideBS(ti->x, _thd.pos.x, _thd.size.x) &&
00943 IsInsideBS(ti->y, _thd.pos.y, _thd.size.y)) {
00944 if (_thd.drawstyle & HT_RECT) {
00945 if (!is_redsq) DrawTileSelectionRect(ti, _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE);
00946 } else if (_thd.drawstyle & HT_POINT) {
00947
00948 byte z = 0;
00949 FoundationPart foundation_part = FOUNDATION_PART_NORMAL;
00950 if (ti->tileh & SLOPE_N) {
00951 z += TILE_HEIGHT;
00952 if (RemoveHalftileSlope(ti->tileh) == SLOPE_STEEP_N) z += TILE_HEIGHT;
00953 }
00954 if (IsHalftileSlope(ti->tileh)) {
00955 Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
00956 if ((halftile_corner == CORNER_W) || (halftile_corner == CORNER_E)) z += TILE_HEIGHT;
00957 if (halftile_corner != CORNER_S) {
00958 foundation_part = FOUNDATION_PART_HALFTILE;
00959 if (IsSteepSlope(ti->tileh)) z -= TILE_HEIGHT;
00960 }
00961 }
00962 DrawSelectionSprite(_cur_dpi->zoom <= ZOOM_LVL_DETAIL ? SPR_DOT : SPR_DOT_SMALL, PAL_NONE, ti, z, foundation_part);
00963 } else if (_thd.drawstyle & HT_RAIL) {
00964
00965 HighLightStyle type = _thd.drawstyle & HT_DIR_MASK;
00966 assert(type < HT_DIR_END);
00967 DrawAutorailSelection(ti, _autorail_type[type][0]);
00968 } else if (IsPartOfAutoLine(ti->x, ti->y)) {
00969
00970 HighLightStyle dir = _thd.drawstyle & HT_DIR_MASK;
00971 uint side;
00972
00973 if (dir == HT_DIR_X || dir == HT_DIR_Y) {
00974 side = 0;
00975 } else {
00976 TileIndex start = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
00977 side = Delta(Delta(TileX(start), TileX(ti->tile)), Delta(TileY(start), TileY(ti->tile)));
00978 }
00979
00980 DrawAutorailSelection(ti, _autorail_type[dir][side]);
00981 }
00982 return;
00983 }
00984
00985
00986 if (!is_redsq && _thd.outersize.x &&
00987 _thd.size.x < _thd.size.x + _thd.outersize.x &&
00988 IsInsideBS(ti->x, _thd.pos.x + _thd.offs.x, _thd.size.x + _thd.outersize.x) &&
00989 IsInsideBS(ti->y, _thd.pos.y + _thd.offs.y, _thd.size.y + _thd.outersize.y)) {
00990
00991 DrawTileSelectionRect(ti, PALETTE_SEL_TILE_BLUE);
00992 return;
00993 }
00994 }
00995
00996 static void ViewportAddLandscape()
00997 {
00998 int x, y, width, height;
00999 TileInfo ti;
01000 bool direction;
01001
01002 _cur_ti = &ti;
01003
01004
01005 x = ((_vd.dpi.top >> 1) - (_vd.dpi.left >> 2)) & ~TILE_UNIT_MASK;
01006 y = ((_vd.dpi.top >> 1) + (_vd.dpi.left >> 2) - TILE_SIZE) & ~TILE_UNIT_MASK;
01007
01008
01009 {
01010 Point pt = RemapCoords(x, y, 241);
01011 width = (_vd.dpi.left + _vd.dpi.width - pt.x + 95) >> 6;
01012 height = (_vd.dpi.top + _vd.dpi.height - pt.y) >> 5 << 1;
01013 }
01014
01015 assert(width > 0);
01016 assert(height > 0);
01017
01018 direction = false;
01019
01020 do {
01021 int width_cur = width;
01022 int x_cur = x;
01023 int y_cur = y;
01024
01025 do {
01026 TileType tt = MP_VOID;
01027
01028 ti.x = x_cur;
01029 ti.y = y_cur;
01030
01031 ti.z = 0;
01032
01033 ti.tileh = SLOPE_FLAT;
01034 ti.tile = INVALID_TILE;
01035
01036 if (0 <= x_cur && x_cur < (int)MapMaxX() * TILE_SIZE &&
01037 0 <= y_cur && y_cur < (int)MapMaxY() * TILE_SIZE) {
01038 TileIndex tile = TileVirtXY(x_cur, y_cur);
01039
01040 if (!_settings_game.construction.freeform_edges || (TileX(tile) != 0 && TileY(tile) != 0)) {
01041 if (x_cur == ((int)MapMaxX() - 1) * TILE_SIZE || y_cur == ((int)MapMaxY() - 1) * TILE_SIZE) {
01042 uint maxh = max<uint>(TileHeight(tile), 1);
01043 for (uint h = 0; h < maxh; h++) {
01044 AddTileSpriteToDraw(SPR_SHADOW_CELL, PAL_NONE, ti.x, ti.y, h * TILE_HEIGHT);
01045 }
01046 }
01047
01048 ti.tile = tile;
01049 ti.tileh = GetTileSlope(tile, &ti.z);
01050 tt = GetTileType(tile);
01051 }
01052 }
01053
01054 _vd.foundation_part = FOUNDATION_PART_NONE;
01055 _vd.foundation[0] = -1;
01056 _vd.foundation[1] = -1;
01057 _vd.last_foundation_child[0] = NULL;
01058 _vd.last_foundation_child[1] = NULL;
01059
01060 _tile_type_procs[tt]->draw_tile_proc(&ti);
01061
01062 if ((x_cur == (int)MapMaxX() * TILE_SIZE && IsInsideMM(y_cur, 0, MapMaxY() * TILE_SIZE + 1)) ||
01063 (y_cur == (int)MapMaxY() * TILE_SIZE && IsInsideMM(x_cur, 0, MapMaxX() * TILE_SIZE + 1))) {
01064 TileIndex tile = TileVirtXY(x_cur, y_cur);
01065 ti.tile = tile;
01066 ti.tileh = GetTileSlope(tile, &ti.z);
01067 tt = GetTileType(tile);
01068 }
01069 if (ti.tile != INVALID_TILE) DrawTileSelection(&ti);
01070
01071 y_cur += 0x10;
01072 x_cur -= 0x10;
01073 } while (--width_cur);
01074
01075 if ((direction ^= 1) != 0) {
01076 y += 0x10;
01077 } else {
01078 x += 0x10;
01079 }
01080 } while (--height);
01081 }
01082
01093 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)
01094 {
01095 bool small = dpi->zoom >= small_from;
01096
01097 int left = dpi->left;
01098 int top = dpi->top;
01099 int right = left + dpi->width;
01100 int bottom = top + dpi->height;
01101
01102 int sign_height = ScaleByZoom(VPSM_TOP + FONT_HEIGHT_NORMAL + VPSM_BOTTOM, dpi->zoom);
01103 int sign_half_width = ScaleByZoom((small ? sign->width_small : sign->width_normal) / 2, dpi->zoom);
01104
01105 if (bottom < sign->top ||
01106 top > sign->top + sign_height ||
01107 right < sign->center - sign_half_width ||
01108 left > sign->center + sign_half_width) {
01109 return;
01110 }
01111
01112 if (!small) {
01113 AddStringToDraw(sign->center - sign_half_width, sign->top, string_normal, params_1, params_2, colour, sign->width_normal);
01114 } else {
01115 int shadow_offset = 0;
01116 if (string_small_shadow != STR_NULL) {
01117 shadow_offset = 4;
01118 AddStringToDraw(sign->center - sign_half_width + shadow_offset, sign->top, string_small_shadow, params_1, params_2, INVALID_COLOUR, sign->width_small);
01119 }
01120 AddStringToDraw(sign->center - sign_half_width, sign->top - shadow_offset, string_small, params_1, params_2,
01121 colour, sign->width_small | 0x8000);
01122 }
01123 }
01124
01125 static void ViewportAddTownNames(DrawPixelInfo *dpi)
01126 {
01127 if (!HasBit(_display_opt, DO_SHOW_TOWN_NAMES) || _game_mode == GM_MENU) return;
01128
01129 const Town *t;
01130 FOR_ALL_TOWNS(t) {
01131 ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &t->sign,
01132 _settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_VIEWPORT_TOWN,
01133 STR_VIEWPORT_TOWN_TINY_WHITE, STR_VIEWPORT_TOWN_TINY_BLACK,
01134 t->index, t->population);
01135 }
01136 }
01137
01138
01139 static void ViewportAddStationNames(DrawPixelInfo *dpi)
01140 {
01141 if (!(HasBit(_display_opt, DO_SHOW_STATION_NAMES) || HasBit(_display_opt, DO_SHOW_WAYPOINT_NAMES)) || _game_mode == GM_MENU) return;
01142
01143 const BaseStation *st;
01144 FOR_ALL_BASE_STATIONS(st) {
01145
01146 bool is_station = Station::IsExpected(st);
01147
01148
01149 if (!HasBit(_display_opt, is_station ? DO_SHOW_STATION_NAMES : DO_SHOW_WAYPOINT_NAMES)) continue;
01150
01151 ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &st->sign,
01152 is_station ? STR_VIEWPORT_STATION : STR_VIEWPORT_WAYPOINT,
01153 (is_station ? STR_VIEWPORT_STATION : STR_VIEWPORT_WAYPOINT) + 1, STR_NULL,
01154 st->index, st->facilities, (st->owner == OWNER_NONE || !st->IsInUse()) ? COLOUR_GREY : _company_colours[st->owner]);
01155 }
01156 }
01157
01158
01159 static void ViewportAddSigns(DrawPixelInfo *dpi)
01160 {
01161
01162 if (!HasBit(_display_opt, DO_SHOW_SIGNS) || IsInvisibilitySet(TO_SIGNS)) return;
01163
01164 const Sign *si;
01165 FOR_ALL_SIGNS(si) {
01166 ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &si->sign,
01167 STR_WHITE_SIGN,
01168 IsTransparencySet(TO_SIGNS) ? STR_VIEWPORT_SIGN_SMALL_WHITE : STR_VIEWPORT_SIGN_SMALL_BLACK, STR_NULL,
01169 si->index, 0, (si->owner == OWNER_NONE) ? COLOUR_GREY : _company_colours[si->owner]);
01170 }
01171 }
01172
01179 void ViewportSign::UpdatePosition(int center, int top, StringID str)
01180 {
01181 if (this->width_normal != 0) this->MarkDirty();
01182
01183 this->top = top;
01184
01185 char buffer[DRAW_STRING_BUFFER];
01186
01187 GetString(buffer, str, lastof(buffer));
01188 this->width_normal = VPSM_LEFT + Align(GetStringBoundingBox(buffer).width, 2) + VPSM_RIGHT;
01189 this->center = center;
01190
01191
01192 _cur_fontsize = FS_SMALL;
01193 this->width_small = VPSM_LEFT + Align(GetStringBoundingBox(buffer).width, 2) + VPSM_RIGHT;
01194 _cur_fontsize = FS_NORMAL;
01195
01196 this->MarkDirty();
01197 }
01198
01204 void ViewportSign::MarkDirty() const
01205 {
01206
01207
01208
01209
01210 MarkAllViewportsDirty(
01211 this->center - ScaleByZoom(this->width_normal / 2 + 1, ZOOM_LVL_MAX),
01212 this->top - ScaleByZoom(1, ZOOM_LVL_MAX),
01213 this->center + ScaleByZoom(this->width_normal / 2 + 1, ZOOM_LVL_MAX),
01214 this->top + ScaleByZoom(VPSM_TOP + FONT_HEIGHT_NORMAL + VPSM_BOTTOM + 1, ZOOM_LVL_MAX));
01215 }
01216
01217 static void ViewportDrawTileSprites(const TileSpriteToDrawVector *tstdv)
01218 {
01219 const TileSpriteToDraw *tsend = tstdv->End();
01220 for (const TileSpriteToDraw *ts = tstdv->Begin(); ts != tsend; ++ts) {
01221 DrawSprite(ts->image, ts->pal, ts->x, ts->y, ts->sub);
01222 }
01223 }
01224
01226 static void ViewportSortParentSprites(ParentSpriteToSortVector *psdv)
01227 {
01228 ParentSpriteToDraw **psdvend = psdv->End();
01229 ParentSpriteToDraw **psd = psdv->Begin();
01230 while (psd != psdvend) {
01231 ParentSpriteToDraw *ps = *psd;
01232
01233 if (ps->comparison_done) {
01234 psd++;
01235 continue;
01236 }
01237
01238 ps->comparison_done = true;
01239
01240 for (ParentSpriteToDraw **psd2 = psd + 1; psd2 != psdvend; psd2++) {
01241 ParentSpriteToDraw *ps2 = *psd2;
01242
01243 if (ps2->comparison_done) continue;
01244
01245
01246
01247
01248 if (ps->xmax >= ps2->xmin && ps->xmin <= ps2->xmax &&
01249 ps->ymax >= ps2->ymin && ps->ymin <= ps2->ymax &&
01250 ps->zmax >= ps2->zmin && ps->zmin <= ps2->zmax) {
01251
01252
01253
01254
01255
01256
01257 if (ps->xmin + ps->xmax + ps->ymin + ps->ymax + ps->zmin + ps->zmax <=
01258 ps2->xmin + ps2->xmax + ps2->ymin + ps2->ymax + ps2->zmin + ps2->zmax) {
01259 continue;
01260 }
01261 } else {
01262
01263
01264
01265
01266 if (ps->xmax < ps2->xmin ||
01267 ps->ymax < ps2->ymin ||
01268 ps->zmax < ps2->zmin) {
01269 continue;
01270 }
01271 }
01272
01273
01274 ParentSpriteToDraw *temp = ps2;
01275 for (ParentSpriteToDraw **psd3 = psd2; psd3 > psd; psd3--) {
01276 *psd3 = *(psd3 - 1);
01277 }
01278 *psd = temp;
01279 }
01280 }
01281 }
01282
01283 static void ViewportDrawParentSprites(const ParentSpriteToSortVector *psd, const ChildScreenSpriteToDrawVector *csstdv)
01284 {
01285 const ParentSpriteToDraw * const *psd_end = psd->End();
01286 for (const ParentSpriteToDraw * const *it = psd->Begin(); it != psd_end; it++) {
01287 const ParentSpriteToDraw *ps = *it;
01288 if (ps->image != SPR_EMPTY_BOUNDING_BOX) DrawSprite(ps->image, ps->pal, ps->x, ps->y, ps->sub);
01289
01290 int child_idx = ps->first_child;
01291 while (child_idx >= 0) {
01292 const ChildScreenSpriteToDraw *cs = csstdv->Get(child_idx);
01293 child_idx = cs->next;
01294 DrawSprite(cs->image, cs->pal, ps->left + cs->x, ps->top + cs->y, cs->sub);
01295 }
01296 }
01297 }
01298
01303 static void ViewportDrawBoundingBoxes(const ParentSpriteToSortVector *psd)
01304 {
01305 const ParentSpriteToDraw * const *psd_end = psd->End();
01306 for (const ParentSpriteToDraw * const *it = psd->Begin(); it != psd_end; it++) {
01307 const ParentSpriteToDraw *ps = *it;
01308 Point pt1 = RemapCoords(ps->xmax + 1, ps->ymax + 1, ps->zmax + 1);
01309 Point pt2 = RemapCoords(ps->xmin , ps->ymax + 1, ps->zmax + 1);
01310 Point pt3 = RemapCoords(ps->xmax + 1, ps->ymin , ps->zmax + 1);
01311 Point pt4 = RemapCoords(ps->xmax + 1, ps->ymax + 1, ps->zmin );
01312
01313 DrawBox( pt1.x, pt1.y,
01314 pt2.x - pt1.x, pt2.y - pt1.y,
01315 pt3.x - pt1.x, pt3.y - pt1.y,
01316 pt4.x - pt1.x, pt4.y - pt1.y);
01317 }
01318 }
01319
01320 static void ViewportDrawStrings(DrawPixelInfo *dpi, const StringSpriteToDrawVector *sstdv)
01321 {
01322 DrawPixelInfo dp;
01323 ZoomLevel zoom;
01324
01325 _cur_dpi = &dp;
01326 dp = *dpi;
01327
01328 zoom = dp.zoom;
01329 dp.zoom = ZOOM_LVL_NORMAL;
01330
01331 dp.left = UnScaleByZoom(dp.left, zoom);
01332 dp.top = UnScaleByZoom(dp.top, zoom);
01333 dp.width = UnScaleByZoom(dp.width, zoom);
01334 dp.height = UnScaleByZoom(dp.height, zoom);
01335
01336 const StringSpriteToDraw *ssend = sstdv->End();
01337 for (const StringSpriteToDraw *ss = sstdv->Begin(); ss != ssend; ++ss) {
01338 TextColour colour = TC_BLACK;
01339 bool small = HasBit(ss->width, 15);
01340 int w = GB(ss->width, 0, 15);
01341 int x = UnScaleByZoom(ss->x, zoom);
01342 int y = UnScaleByZoom(ss->y, zoom);
01343 int h = VPSM_TOP + (small ? FONT_HEIGHT_SMALL : FONT_HEIGHT_NORMAL) + VPSM_BOTTOM;
01344
01345 SetDParam(0, ss->params[0]);
01346 SetDParam(1, ss->params[1]);
01347
01348 if (ss->colour != INVALID_COLOUR) {
01349
01350 if (IsInvisibilitySet(TO_SIGNS) && ss->string != STR_WHITE_SIGN) continue;
01351
01352
01353
01354 if (IsTransparencySet(TO_SIGNS) && ss->string != STR_WHITE_SIGN) {
01355
01356
01357 colour = (TextColour)_colour_gradient[ss->colour][6] | IS_PALETTE_COLOUR;
01358 }
01359
01360
01361
01362 if (!IsTransparencySet(TO_SIGNS) || ss->string == STR_WHITE_SIGN) {
01363 DrawFrameRect(
01364 x, y, x + w, y + h, ss->colour,
01365 IsTransparencySet(TO_SIGNS) ? FR_TRANSPARENT : FR_NONE
01366 );
01367 }
01368 }
01369
01370 DrawString(x + VPSM_LEFT, x + w - 1 - VPSM_RIGHT, y + VPSM_TOP, ss->string, colour, SA_CENTER);
01371 }
01372 }
01373
01374 void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom)
01375 {
01376 DrawPixelInfo *old_dpi = _cur_dpi;
01377 _cur_dpi = &_vd.dpi;
01378
01379 _vd.dpi.zoom = vp->zoom;
01380 int mask = ScaleByZoom(-1, vp->zoom);
01381
01382 _vd.combine_sprites = SPRITE_COMBINE_NONE;
01383
01384 _vd.dpi.width = (right - left) & mask;
01385 _vd.dpi.height = (bottom - top) & mask;
01386 _vd.dpi.left = left & mask;
01387 _vd.dpi.top = top & mask;
01388 _vd.dpi.pitch = old_dpi->pitch;
01389 _vd.last_child = NULL;
01390
01391 int x = UnScaleByZoom(_vd.dpi.left - (vp->virtual_left & mask), vp->zoom) + vp->left;
01392 int y = UnScaleByZoom(_vd.dpi.top - (vp->virtual_top & mask), vp->zoom) + vp->top;
01393
01394 _vd.dpi.dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(old_dpi->dst_ptr, x - old_dpi->left, y - old_dpi->top);
01395
01396 ViewportAddLandscape();
01397 ViewportAddVehicles(&_vd.dpi);
01398
01399 ViewportAddTownNames(&_vd.dpi);
01400 ViewportAddStationNames(&_vd.dpi);
01401 ViewportAddSigns(&_vd.dpi);
01402
01403 DrawTextEffects(&_vd.dpi);
01404
01405 if (_vd.tile_sprites_to_draw.Length() != 0) ViewportDrawTileSprites(&_vd.tile_sprites_to_draw);
01406
01407 ParentSpriteToDraw *psd_end = _vd.parent_sprites_to_draw.End();
01408 for (ParentSpriteToDraw *it = _vd.parent_sprites_to_draw.Begin(); it != psd_end; it++) {
01409 *_vd.parent_sprites_to_sort.Append() = it;
01410 }
01411
01412 ViewportSortParentSprites(&_vd.parent_sprites_to_sort);
01413 ViewportDrawParentSprites(&_vd.parent_sprites_to_sort, &_vd.child_screen_sprites_to_draw);
01414
01415 if (_draw_bounding_boxes) ViewportDrawBoundingBoxes(&_vd.parent_sprites_to_sort);
01416
01417 if (_vd.string_sprites_to_draw.Length() != 0) ViewportDrawStrings(&_vd.dpi, &_vd.string_sprites_to_draw);
01418
01419 _cur_dpi = old_dpi;
01420
01421 _vd.string_sprites_to_draw.Clear();
01422 _vd.tile_sprites_to_draw.Clear();
01423 _vd.parent_sprites_to_draw.Clear();
01424 _vd.parent_sprites_to_sort.Clear();
01425 _vd.child_screen_sprites_to_draw.Clear();
01426 }
01427
01430 static void ViewportDrawChk(const ViewPort *vp, int left, int top, int right, int bottom)
01431 {
01432 if (ScaleByZoom(bottom - top, vp->zoom) * ScaleByZoom(right - left, vp->zoom) > 180000) {
01433 if ((bottom - top) > (right - left)) {
01434 int t = (top + bottom) >> 1;
01435 ViewportDrawChk(vp, left, top, right, t);
01436 ViewportDrawChk(vp, left, t, right, bottom);
01437 } else {
01438 int t = (left + right) >> 1;
01439 ViewportDrawChk(vp, left, top, t, bottom);
01440 ViewportDrawChk(vp, t, top, right, bottom);
01441 }
01442 } else {
01443 ViewportDoDraw(vp,
01444 ScaleByZoom(left - vp->left, vp->zoom) + vp->virtual_left,
01445 ScaleByZoom(top - vp->top, vp->zoom) + vp->virtual_top,
01446 ScaleByZoom(right - vp->left, vp->zoom) + vp->virtual_left,
01447 ScaleByZoom(bottom - vp->top, vp->zoom) + vp->virtual_top
01448 );
01449 }
01450 }
01451
01452 static inline void ViewportDraw(const ViewPort *vp, int left, int top, int right, int bottom)
01453 {
01454 if (right <= vp->left || bottom <= vp->top) return;
01455
01456 if (left >= vp->left + vp->width) return;
01457
01458 if (left < vp->left) left = vp->left;
01459 if (right > vp->left + vp->width) right = vp->left + vp->width;
01460
01461 if (top >= vp->top + vp->height) return;
01462
01463 if (top < vp->top) top = vp->top;
01464 if (bottom > vp->top + vp->height) bottom = vp->top + vp->height;
01465
01466 ViewportDrawChk(vp, left, top, right, bottom);
01467 }
01468
01472 void Window::DrawViewport() const
01473 {
01474 DrawPixelInfo *dpi = _cur_dpi;
01475
01476 dpi->left += this->left;
01477 dpi->top += this->top;
01478
01479 ViewportDraw(this->viewport, dpi->left, dpi->top, dpi->left + dpi->width, dpi->top + dpi->height);
01480
01481 dpi->left -= this->left;
01482 dpi->top -= this->top;
01483 }
01484
01485 static inline void ClampViewportToMap(const ViewPort *vp, int &x, int &y)
01486 {
01487
01488 x += vp->virtual_width / 2;
01489 y += vp->virtual_height / 2;
01490
01491
01492
01493 int vx = -x + y * 2;
01494 int vy = x + y * 2;
01495
01496
01497 vx = Clamp(vx, 0, MapMaxX() * TILE_SIZE * 4);
01498 vy = Clamp(vy, 0, MapMaxY() * TILE_SIZE * 4);
01499
01500
01501 x = (-vx + vy) / 2;
01502 y = ( vx + vy) / 4;
01503
01504
01505 x -= vp->virtual_width / 2;
01506 y -= vp->virtual_height / 2;
01507 }
01508
01513 void UpdateViewportPosition(Window *w)
01514 {
01515 const ViewPort *vp = w->viewport;
01516
01517 if (w->viewport->follow_vehicle != INVALID_VEHICLE) {
01518 const Vehicle *veh = Vehicle::Get(w->viewport->follow_vehicle);
01519 Point pt = MapXYZToViewport(vp, veh->x_pos, veh->y_pos, veh->z_pos);
01520
01521 w->viewport->scrollpos_x = pt.x;
01522 w->viewport->scrollpos_y = pt.y;
01523 SetViewportPosition(w, pt.x, pt.y);
01524 } else {
01525
01526 ClampViewportToMap(vp, w->viewport->dest_scrollpos_x, w->viewport->dest_scrollpos_y);
01527
01528 int delta_x = w->viewport->dest_scrollpos_x - w->viewport->scrollpos_x;
01529 int delta_y = w->viewport->dest_scrollpos_y - w->viewport->scrollpos_y;
01530
01531 if (delta_x != 0 || delta_y != 0) {
01532 if (_settings_client.gui.smooth_scroll) {
01533 int max_scroll = ScaleByMapSize1D(512);
01534
01535 w->viewport->scrollpos_x += Clamp(delta_x / 4, -max_scroll, max_scroll);
01536 w->viewport->scrollpos_y += Clamp(delta_y / 4, -max_scroll, max_scroll);
01537 } else {
01538 w->viewport->scrollpos_x = w->viewport->dest_scrollpos_x;
01539 w->viewport->scrollpos_y = w->viewport->dest_scrollpos_y;
01540 }
01541 }
01542
01543 ClampViewportToMap(vp, w->viewport->scrollpos_x, w->viewport->scrollpos_y);
01544
01545 SetViewportPosition(w, w->viewport->scrollpos_x, w->viewport->scrollpos_y);
01546 }
01547 }
01548
01558 static void MarkViewportDirty(const ViewPort *vp, int left, int top, int right, int bottom)
01559 {
01560 right -= vp->virtual_left;
01561 if (right <= 0) return;
01562
01563 bottom -= vp->virtual_top;
01564 if (bottom <= 0) return;
01565
01566 left = max(0, left - vp->virtual_left);
01567
01568 if (left >= vp->virtual_width) return;
01569
01570 top = max(0, top - vp->virtual_top);
01571
01572 if (top >= vp->virtual_height) return;
01573
01574 SetDirtyBlocks(
01575 UnScaleByZoomLower(left, vp->zoom) + vp->left,
01576 UnScaleByZoomLower(top, vp->zoom) + vp->top,
01577 UnScaleByZoom(right, vp->zoom) + vp->left + 1,
01578 UnScaleByZoom(bottom, vp->zoom) + vp->top + 1
01579 );
01580 }
01581
01590 void MarkAllViewportsDirty(int left, int top, int right, int bottom)
01591 {
01592 Window *w;
01593 FOR_ALL_WINDOWS_FROM_BACK(w) {
01594 ViewPort *vp = w->viewport;
01595 if (vp != NULL) {
01596 assert(vp->width != 0);
01597 MarkViewportDirty(vp, left, top, right, bottom);
01598 }
01599 }
01600 }
01601
01602 void MarkTileDirtyByTile(TileIndex tile)
01603 {
01604 Point pt = RemapCoords(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE, GetTileZ(tile));
01605 MarkAllViewportsDirty(
01606 pt.x - 31,
01607 pt.y - 122,
01608 pt.x - 31 + 67,
01609 pt.y - 122 + 154
01610 );
01611 }
01612
01620 static void SetSelectionTilesDirty()
01621 {
01622 int x_start = _thd.pos.x;
01623 int y_start = _thd.pos.y;
01624
01625 int x_size = _thd.size.x;
01626 int y_size = _thd.size.y;
01627
01628 if (_thd.outersize.x != 0) {
01629 x_size += _thd.outersize.x;
01630 x_start += _thd.offs.x;
01631 y_size += _thd.outersize.y;
01632 y_start += _thd.offs.y;
01633 }
01634
01635 x_size -= TILE_SIZE;
01636 y_size -= TILE_SIZE;
01637
01638 assert(x_size >= 0);
01639 assert(y_size >= 0);
01640
01641 int x_end = Clamp(x_start + x_size, 0, MapSizeX() * TILE_SIZE - TILE_SIZE);
01642 int y_end = Clamp(y_start + y_size, 0, MapSizeY() * TILE_SIZE - TILE_SIZE);
01643
01644 x_start = Clamp(x_start, 0, MapSizeX() * TILE_SIZE - TILE_SIZE);
01645 y_start = Clamp(y_start, 0, MapSizeY() * TILE_SIZE - TILE_SIZE);
01646
01647
01648 assert((x_end | y_end | x_start | y_start) % TILE_SIZE == 0);
01649
01650
01651
01652
01653
01654
01655
01656
01657
01658
01659
01660
01661
01662
01663
01664
01665
01666
01667
01668 int top_x = x_end;
01669 int top_y = y_start;
01670 int bot_x = top_x;
01671 int bot_y = top_y;
01672
01673 do {
01674 Point top = RemapCoords2(top_x, top_y);
01675 Point bot = RemapCoords2(bot_x + TILE_SIZE - 1, bot_y + TILE_SIZE - 1);
01676
01677
01678
01679
01680 int l = top.x - (TILE_PIXELS - 2);
01681 int t = top.y;
01682 int r = top.x + (TILE_PIXELS - 2);
01683 int b = bot.y;
01684
01685 static const int OVERLAY_WIDTH = 4;
01686
01687
01688 MarkAllViewportsDirty(l - OVERLAY_WIDTH, t - OVERLAY_WIDTH - TILE_HEIGHT, r + OVERLAY_WIDTH, b + OVERLAY_WIDTH);
01689
01690
01691 if (top_x != x_start) {
01692 top_x -= TILE_SIZE;
01693 } else {
01694 top_y += TILE_SIZE;
01695 }
01696
01697
01698 if (bot_y != y_end) {
01699 bot_y += TILE_SIZE;
01700 } else {
01701 bot_x -= TILE_SIZE;
01702 }
01703 } while (bot_x >= top_x);
01704 }
01705
01706
01707 void SetSelectionRed(bool b)
01708 {
01709 _thd.make_square_red = b;
01710 SetSelectionTilesDirty();
01711 }
01712
01721 static bool CheckClickOnViewportSign(const ViewPort *vp, int x, int y, const ViewportSign *sign)
01722 {
01723 bool small = (vp->zoom >= ZOOM_LVL_OUT_4X);
01724 int sign_half_width = ScaleByZoom((small ? sign->width_small : sign->width_normal) / 2, vp->zoom);
01725 int sign_height = ScaleByZoom(VPSM_TOP + (small ? FONT_HEIGHT_SMALL : FONT_HEIGHT_NORMAL) + VPSM_BOTTOM, vp->zoom);
01726
01727 x = ScaleByZoom(x - vp->left, vp->zoom) + vp->virtual_left;
01728 y = ScaleByZoom(y - vp->top, vp->zoom) + vp->virtual_top;
01729
01730 return
01731 y >= sign->top &&
01732 y < sign->top + sign_height &&
01733 x >= sign->center - sign_half_width &&
01734 x < sign->center + sign_half_width;
01735 }
01736
01737 static bool CheckClickOnTown(const ViewPort *vp, int x, int y)
01738 {
01739 if (!HasBit(_display_opt, DO_SHOW_TOWN_NAMES)) return false;
01740
01741 const Town *t;
01742 FOR_ALL_TOWNS(t) {
01743 if (CheckClickOnViewportSign(vp, x, y, &t->sign)) {
01744 ShowTownViewWindow(t->index);
01745 return true;
01746 }
01747 }
01748
01749 return false;
01750 }
01751
01752 static bool CheckClickOnStation(const ViewPort *vp, int x, int y)
01753 {
01754 if (!(HasBit(_display_opt, DO_SHOW_STATION_NAMES) || HasBit(_display_opt, DO_SHOW_WAYPOINT_NAMES)) || IsInvisibilitySet(TO_SIGNS)) return false;
01755
01756 const BaseStation *st;
01757 FOR_ALL_BASE_STATIONS(st) {
01758
01759 bool is_station = Station::IsExpected(st);
01760
01761
01762 if (!HasBit(_display_opt, is_station ? DO_SHOW_STATION_NAMES : DO_SHOW_WAYPOINT_NAMES)) continue;
01763
01764 if (CheckClickOnViewportSign(vp, x, y, &st->sign)) {
01765 if (is_station) {
01766 ShowStationViewWindow(st->index);
01767 } else {
01768 ShowWaypointWindow(Waypoint::From(st));
01769 }
01770 return true;
01771 }
01772 }
01773
01774 return false;
01775 }
01776
01777
01778 static bool CheckClickOnSign(const ViewPort *vp, int x, int y)
01779 {
01780
01781 if (!HasBit(_display_opt, DO_SHOW_SIGNS) || IsInvisibilitySet(TO_SIGNS) || _current_company == COMPANY_SPECTATOR) return false;
01782
01783 const Sign *si;
01784 FOR_ALL_SIGNS(si) {
01785 if (CheckClickOnViewportSign(vp, x, y, &si->sign)) {
01786 HandleClickOnSign(si);
01787 return true;
01788 }
01789 }
01790
01791 return false;
01792 }
01793
01794
01795 static bool CheckClickOnLandscape(const ViewPort *vp, int x, int y)
01796 {
01797 Point pt = TranslateXYToTileCoord(vp, x, y);
01798
01799 if (pt.x != -1) return ClickTile(TileVirtXY(pt.x, pt.y));
01800 return true;
01801 }
01802
01803
01804 bool HandleViewportClicked(const ViewPort *vp, int x, int y)
01805 {
01806 const Vehicle *v;
01807
01808 if (CheckClickOnTown(vp, x, y)) return true;
01809 if (CheckClickOnStation(vp, x, y)) return true;
01810 if (CheckClickOnSign(vp, x, y)) return true;
01811 CheckClickOnLandscape(vp, x, y);
01812
01813 v = CheckClickOnVehicle(vp, x, y);
01814 if (v != NULL) {
01815 DEBUG(misc, 2, "Vehicle %d (index %d) at %p", v->unitnumber, v->index, v);
01816 if (IsCompanyBuildableVehicleType(v)) ShowVehicleViewWindow(v->First());
01817 return true;
01818 }
01819 return CheckClickOnLandscape(vp, x, y);
01820 }
01821
01822 Vehicle *CheckMouseOverVehicle()
01823 {
01824 const Window *w;
01825 const ViewPort *vp;
01826
01827 int x = _cursor.pos.x;
01828 int y = _cursor.pos.y;
01829
01830 w = FindWindowFromPt(x, y);
01831 if (w == NULL) return NULL;
01832
01833 vp = IsPtInWindowViewport(w, x, y);
01834 return (vp != NULL) ? CheckClickOnVehicle(vp, x, y) : NULL;
01835 }
01836
01837
01838
01839 void PlaceObject()
01840 {
01841 Point pt;
01842 Window *w;
01843
01844 pt = GetTileBelowCursor();
01845 if (pt.x == -1) return;
01846
01847 if (_thd.place_mode == HT_POINT) {
01848 pt.x += 8;
01849 pt.y += 8;
01850 }
01851
01852 _tile_fract_coords.x = pt.x & TILE_UNIT_MASK;
01853 _tile_fract_coords.y = pt.y & TILE_UNIT_MASK;
01854
01855 w = GetCallbackWnd();
01856 if (w != NULL) w->OnPlaceObject(pt, TileVirtXY(pt.x, pt.y));
01857 }
01858
01859
01868 bool ScrollWindowTo(int x, int y, int z, Window *w, bool instant)
01869 {
01870
01871 if (z == -1) z = GetSlopeZ(Clamp(x, 0, MapSizeX() * TILE_SIZE - 1), Clamp(y, 0, MapSizeY() * TILE_SIZE - 1));
01872
01873 Point pt = MapXYZToViewport(w->viewport, x, y, z);
01874 w->viewport->follow_vehicle = INVALID_VEHICLE;
01875
01876 if (w->viewport->dest_scrollpos_x == pt.x && w->viewport->dest_scrollpos_y == pt.y)
01877 return false;
01878
01879 if (instant) {
01880 w->viewport->scrollpos_x = pt.x;
01881 w->viewport->scrollpos_y = pt.y;
01882 }
01883
01884 w->viewport->dest_scrollpos_x = pt.x;
01885 w->viewport->dest_scrollpos_y = pt.y;
01886 return true;
01887 }
01888
01889 bool ScrollMainWindowToTile(TileIndex tile, bool instant)
01890 {
01891 return ScrollMainWindowTo(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2, -1, instant);
01892 }
01893
01894 void SetRedErrorSquare(TileIndex tile)
01895 {
01896 TileIndex old;
01897
01898 old = _thd.redsq;
01899 _thd.redsq = tile;
01900
01901 if (tile != old) {
01902 if (tile != INVALID_TILE) MarkTileDirtyByTile(tile);
01903 if (old != INVALID_TILE) MarkTileDirtyByTile(old);
01904 }
01905 }
01906
01911 void SetTileSelectSize(int w, int h)
01912 {
01913 _thd.new_size.x = w * TILE_SIZE;
01914 _thd.new_size.y = h * TILE_SIZE;
01915 _thd.new_outersize.x = 0;
01916 _thd.new_outersize.y = 0;
01917 }
01918
01919 void SetTileSelectBigSize(int ox, int oy, int sx, int sy)
01920 {
01921 _thd.offs.x = ox * TILE_SIZE;
01922 _thd.offs.y = oy * TILE_SIZE;
01923 _thd.new_outersize.x = sx * TILE_SIZE;
01924 _thd.new_outersize.y = sy * TILE_SIZE;
01925 }
01926
01928 static HighLightStyle GetAutorailHT(int x, int y)
01929 {
01930 return HT_RAIL | _autorail_piece[x & TILE_UNIT_MASK][y & TILE_UNIT_MASK];
01931 }
01932
01940 void UpdateTileSelection()
01941 {
01942 int x1;
01943 int y1;
01944
01945 _thd.new_drawstyle = HT_NONE;
01946
01947 if (_thd.place_mode == HT_SPECIAL) {
01948 x1 = _thd.selend.x;
01949 y1 = _thd.selend.y;
01950 if (x1 != -1) {
01951 int x2 = _thd.selstart.x & ~TILE_UNIT_MASK;
01952 int y2 = _thd.selstart.y & ~TILE_UNIT_MASK;
01953 x1 &= ~TILE_UNIT_MASK;
01954 y1 &= ~TILE_UNIT_MASK;
01955
01956 if (x1 >= x2) Swap(x1, x2);
01957 if (y1 >= y2) Swap(y1, y2);
01958 _thd.new_pos.x = x1;
01959 _thd.new_pos.y = y1;
01960 _thd.new_size.x = x2 - x1 + TILE_SIZE;
01961 _thd.new_size.y = y2 - y1 + TILE_SIZE;
01962 _thd.new_drawstyle = _thd.next_drawstyle;
01963 }
01964 } else if (_thd.place_mode != HT_NONE) {
01965 Point pt = GetTileBelowCursor();
01966 x1 = pt.x;
01967 y1 = pt.y;
01968 if (x1 != -1) {
01969 switch (_thd.place_mode & HT_DRAG_MASK) {
01970 case HT_RECT:
01971 _thd.new_drawstyle = HT_RECT;
01972 break;
01973 case HT_POINT:
01974 _thd.new_drawstyle = HT_POINT;
01975 x1 += TILE_SIZE / 2;
01976 y1 += TILE_SIZE / 2;
01977 break;
01978 case HT_RAIL:
01979
01980 _thd.new_drawstyle = GetAutorailHT(pt.x, pt.y);
01981 break;
01982 case HT_LINE:
01983 switch (_thd.place_mode & HT_DIR_MASK) {
01984 case HT_DIR_X: _thd.new_drawstyle = HT_LINE | HT_DIR_X; break;
01985 case HT_DIR_Y: _thd.new_drawstyle = HT_LINE | HT_DIR_Y; break;
01986
01987 case HT_DIR_HU:
01988 case HT_DIR_HL:
01989 _thd.new_drawstyle = (pt.x & TILE_UNIT_MASK) + (pt.y & TILE_UNIT_MASK) <= TILE_SIZE ? HT_LINE | HT_DIR_HU : HT_LINE | HT_DIR_HL;
01990 break;
01991
01992 case HT_DIR_VL:
01993 case HT_DIR_VR:
01994 _thd.new_drawstyle = (pt.x & TILE_UNIT_MASK) > (pt.y & TILE_UNIT_MASK) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
01995 break;
01996
01997 default: NOT_REACHED();
01998 }
01999 _thd.selstart.x = x1 & ~TILE_UNIT_MASK;
02000 _thd.selstart.y = y1 & ~TILE_UNIT_MASK;
02001 break;
02002 default:
02003 NOT_REACHED();
02004 break;
02005 }
02006 _thd.new_pos.x = x1 & ~TILE_UNIT_MASK;
02007 _thd.new_pos.y = y1 & ~TILE_UNIT_MASK;
02008 }
02009 }
02010
02011
02012 if (_thd.drawstyle != _thd.new_drawstyle ||
02013 _thd.pos.x != _thd.new_pos.x || _thd.pos.y != _thd.new_pos.y ||
02014 _thd.size.x != _thd.new_size.x || _thd.size.y != _thd.new_size.y ||
02015 _thd.outersize.x != _thd.new_outersize.x ||
02016 _thd.outersize.y != _thd.new_outersize.y) {
02017
02018 if (_thd.drawstyle) SetSelectionTilesDirty();
02019
02020 _thd.drawstyle = _thd.new_drawstyle;
02021 _thd.pos = _thd.new_pos;
02022 _thd.size = _thd.new_size;
02023 _thd.outersize = _thd.new_outersize;
02024 _thd.dirty = 0xff;
02025
02026
02027 if (_thd.new_drawstyle) SetSelectionTilesDirty();
02028 }
02029 }
02030
02036 static inline void ShowMeasurementTooltips(StringID str, uint paramcount, const uint64 params[])
02037 {
02038 if (!_settings_client.gui.measure_tooltip) return;
02039 GuiShowTooltips(str, paramcount, params, true);
02040 }
02041
02043 void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process)
02044 {
02045 _thd.select_method = method;
02046 _thd.select_proc = process;
02047 _thd.selend.x = TileX(tile) * TILE_SIZE;
02048 _thd.selstart.x = TileX(tile) * TILE_SIZE;
02049 _thd.selend.y = TileY(tile) * TILE_SIZE;
02050 _thd.selstart.y = TileY(tile) * TILE_SIZE;
02051
02052
02053
02054
02055 if (method == VPM_X_OR_Y || method == VPM_FIX_X || method == VPM_FIX_Y) {
02056 _thd.selend.x += TILE_SIZE / 2;
02057 _thd.selend.y += TILE_SIZE / 2;
02058 _thd.selstart.x += TILE_SIZE / 2;
02059 _thd.selstart.y += TILE_SIZE / 2;
02060 }
02061
02062 if (_thd.place_mode == HT_RECT) {
02063 _thd.place_mode = HT_SPECIAL;
02064 _thd.next_drawstyle = HT_RECT;
02065 } else if (_thd.place_mode & (HT_RAIL | HT_LINE)) {
02066 _thd.place_mode = HT_SPECIAL;
02067 _thd.next_drawstyle = _thd.drawstyle;
02068 } else {
02069 _thd.place_mode = HT_SPECIAL;
02070 _thd.next_drawstyle = HT_POINT;
02071 }
02072 _special_mouse_mode = WSM_SIZING;
02073 }
02074
02075 void VpSetPlaceSizingLimit(int limit)
02076 {
02077 _thd.sizelimit = limit;
02078 }
02079
02084 void VpSetPresizeRange(TileIndex from, TileIndex to)
02085 {
02086 uint64 distance = DistanceManhattan(from, to) + 1;
02087
02088 _thd.selend.x = TileX(to) * TILE_SIZE;
02089 _thd.selend.y = TileY(to) * TILE_SIZE;
02090 _thd.selstart.x = TileX(from) * TILE_SIZE;
02091 _thd.selstart.y = TileY(from) * TILE_SIZE;
02092 _thd.next_drawstyle = HT_RECT;
02093
02094
02095 if (distance > 1) ShowMeasurementTooltips(STR_MEASURE_LENGTH, 1, &distance);
02096 }
02097
02098 static void VpStartPreSizing()
02099 {
02100 _thd.selend.x = -1;
02101 _special_mouse_mode = WSM_PRESIZE;
02102 }
02103
02106 static HighLightStyle Check2x1AutoRail(int mode)
02107 {
02108 int fxpy = _tile_fract_coords.x + _tile_fract_coords.y;
02109 int sxpy = (_thd.selend.x & TILE_UNIT_MASK) + (_thd.selend.y & TILE_UNIT_MASK);
02110 int fxmy = _tile_fract_coords.x - _tile_fract_coords.y;
02111 int sxmy = (_thd.selend.x & TILE_UNIT_MASK) - (_thd.selend.y & TILE_UNIT_MASK);
02112
02113 switch (mode) {
02114 default: NOT_REACHED();
02115 case 0:
02116 if (fxpy >= 20 && sxpy <= 12) return HT_DIR_HL;
02117 if (fxmy < -3 && sxmy > 3) return HT_DIR_VR;
02118 return HT_DIR_Y;
02119
02120 case 1:
02121 if (fxmy > 3 && sxmy < -3) return HT_DIR_VL;
02122 if (fxpy <= 12 && sxpy >= 20) return HT_DIR_HU;
02123 return HT_DIR_Y;
02124
02125 case 2:
02126 if (fxmy > 3 && sxmy < -3) return HT_DIR_VL;
02127 if (fxpy >= 20 && sxpy <= 12) return HT_DIR_HL;
02128 return HT_DIR_X;
02129
02130 case 3:
02131 if (fxmy < -3 && sxmy > 3) return HT_DIR_VR;
02132 if (fxpy <= 12 && sxpy >= 20) return HT_DIR_HU;
02133 return HT_DIR_X;
02134 }
02135 }
02136
02148 static bool SwapDirection(HighLightStyle style, TileIndex start_tile, TileIndex end_tile)
02149 {
02150 uint start_x = TileX(start_tile);
02151 uint start_y = TileY(start_tile);
02152 uint end_x = TileX(end_tile);
02153 uint end_y = TileY(end_tile);
02154
02155 switch (style & HT_DRAG_MASK) {
02156 case HT_RAIL:
02157 case HT_LINE: return (end_x > start_x || (end_x == start_x && end_y > start_y));
02158
02159 case HT_RECT:
02160 case HT_POINT: return (end_x != start_x && end_y < start_y);
02161 default: NOT_REACHED();
02162 }
02163
02164 return false;
02165 }
02166
02181 static int CalcHeightdiff(HighLightStyle style, uint distance, TileIndex start_tile, TileIndex end_tile)
02182 {
02183 bool swap = SwapDirection(style, start_tile, end_tile);
02184 byte style_t;
02185 uint h0, h1, ht;
02186
02187 if (start_tile == end_tile) return 0;
02188 if (swap) Swap(start_tile, end_tile);
02189
02190 switch (style & HT_DRAG_MASK) {
02191 case HT_RECT: {
02192 static const TileIndexDiffC heightdiff_area_by_dir[] = {
02193 {1, 0}, {0, 0},
02194 {0, 1}, {1, 1}
02195 };
02196
02197
02198
02199 style_t = (byte)(TileX(end_tile) > TileX(start_tile));
02200 start_tile = TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_area_by_dir[style_t]));
02201 end_tile = TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_area_by_dir[2 + style_t]));
02202 }
02203
02204 case HT_POINT:
02205 h0 = TileHeight(start_tile);
02206 h1 = TileHeight(end_tile);
02207 break;
02208 default: {
02209 static const HighLightStyle flip_style_direction[] = {
02210 HT_DIR_X, HT_DIR_Y, HT_DIR_HL, HT_DIR_HU, HT_DIR_VR, HT_DIR_VL
02211 };
02212 static const TileIndexDiffC heightdiff_line_by_dir[] = {
02213 {1, 0}, {1, 1}, {0, 1}, {1, 1},
02214 {1, 0}, {0, 0}, {1, 0}, {1, 1},
02215 {1, 0}, {1, 1}, {0, 1}, {1, 1},
02216
02217 {0, 1}, {0, 0}, {1, 0}, {0, 0},
02218 {0, 1}, {0, 0}, {1, 1}, {0, 1},
02219 {1, 0}, {0, 0}, {0, 0}, {0, 1},
02220 };
02221
02222 distance %= 2;
02223 style &= HT_DIR_MASK;
02224
02225
02226
02227
02228
02229 if (swap && distance == 0) style = flip_style_direction[style];
02230
02231
02232 style_t = style * 2;
02233 assert(style_t < lengthof(heightdiff_line_by_dir) - 13);
02234 h0 = TileHeight(TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_line_by_dir[style_t])));
02235 ht = TileHeight(TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_line_by_dir[style_t + 1])));
02236 h0 = max(h0, ht);
02237
02238
02239
02240 if (distance == 0) style_t = flip_style_direction[style] * 2;
02241 assert(style_t < lengthof(heightdiff_line_by_dir) - 13);
02242 h1 = TileHeight(TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_line_by_dir[12 + style_t])));
02243 ht = TileHeight(TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_line_by_dir[12 + style_t + 1])));
02244 h1 = max(h1, ht);
02245 } break;
02246 }
02247
02248 if (swap) Swap(h0, h1);
02249
02250 return (int)(h1 - h0) * 50;
02251 }
02252
02253 static const StringID measure_strings_length[] = {STR_NULL, STR_MEASURE_LENGTH, STR_MEASURE_LENGTH_HEIGHTDIFF};
02254
02261 static void CheckUnderflow(int &test, int &other, int mult)
02262 {
02263 if (test >= 0) return;
02264
02265 other += mult * test;
02266 test = 0;
02267 }
02268
02276 static void CheckOverflow(int &test, int &other, int max, int mult)
02277 {
02278 if (test <= max) return;
02279
02280 other += mult * (test - max);
02281 test = max;
02282 }
02283
02285 static void CalcRaildirsDrawstyle(TileHighlightData *thd, int x, int y, int method)
02286 {
02287 HighLightStyle b;
02288
02289 int dx = thd->selstart.x - (thd->selend.x & ~TILE_UNIT_MASK);
02290 int dy = thd->selstart.y - (thd->selend.y & ~TILE_UNIT_MASK);
02291 uint w = abs(dx) + TILE_SIZE;
02292 uint h = abs(dy) + TILE_SIZE;
02293
02294 if (method & ~(VPM_RAILDIRS | VPM_SIGNALDIRS)) {
02295
02296 method &= ~(VPM_RAILDIRS | VPM_SIGNALDIRS);
02297 int raw_dx = thd->selstart.x - thd->selend.x;
02298 int raw_dy = thd->selstart.y - thd->selend.y;
02299 switch (method) {
02300 case VPM_FIX_X:
02301 b = HT_LINE | HT_DIR_Y;
02302 x = thd->selstart.x;
02303 break;
02304
02305 case VPM_FIX_Y:
02306 b = HT_LINE | HT_DIR_X;
02307 y = thd->selstart.y;
02308 break;
02309
02310 case VPM_FIX_HORIZONTAL:
02311 if (dx == -dy) {
02312
02313
02314 b = (x & TILE_UNIT_MASK) + (y & TILE_UNIT_MASK) >= TILE_SIZE ? HT_LINE | HT_DIR_HL : HT_LINE | HT_DIR_HU;
02315 } else {
02316
02317
02318 b = dx + dy >= TILE_SIZE ? HT_LINE | HT_DIR_HU : HT_LINE | HT_DIR_HL;
02319
02320
02321
02322
02323 int offset = (raw_dx - raw_dy) / 2;
02324 x = thd->selstart.x - (offset & ~TILE_UNIT_MASK);
02325 y = thd->selstart.y + (offset & ~TILE_UNIT_MASK);
02326
02327
02328 if ((offset & TILE_UNIT_MASK) > (TILE_SIZE / 2)) {
02329 if (dx + dy >= TILE_SIZE) {
02330 x += (dx + dy < 0) ? TILE_SIZE : -TILE_SIZE;
02331 } else {
02332 y += (dx + dy < 0) ? TILE_SIZE : -TILE_SIZE;
02333 }
02334 }
02335
02336
02337 CheckUnderflow(x, y, 1);
02338 CheckUnderflow(y, x, 1);
02339 CheckOverflow(x, y, (MapMaxX() - 1) * TILE_SIZE, 1);
02340 CheckOverflow(y, x, (MapMaxY() - 1) * TILE_SIZE, 1);
02341 assert(x >= 0 && y >= 0 && x <= (int)MapMaxX() * TILE_SIZE && y <= (int)MapMaxY() * TILE_SIZE);
02342 }
02343 break;
02344
02345 case VPM_FIX_VERTICAL:
02346 if (dx == dy) {
02347
02348
02349 b = (x & TILE_UNIT_MASK) > (y & TILE_UNIT_MASK) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
02350 } else {
02351
02352
02353 b = dx < dy ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
02354
02355
02356
02357
02358 int offset = (raw_dx + raw_dy + TILE_SIZE) / 2;
02359 x = thd->selstart.x - (offset & ~TILE_UNIT_MASK);
02360 y = thd->selstart.y - (offset & ~TILE_UNIT_MASK);
02361
02362
02363 if ((offset & TILE_UNIT_MASK) > (TILE_SIZE / 2)) {
02364 if (dx - dy < 0) {
02365 y += (dx > dy) ? TILE_SIZE : -TILE_SIZE;
02366 } else {
02367 x += (dx < dy) ? TILE_SIZE : -TILE_SIZE;
02368 }
02369 }
02370
02371
02372 CheckUnderflow(x, y, -1);
02373 CheckUnderflow(y, x, -1);
02374 CheckOverflow(x, y, (MapMaxX() - 1) * TILE_SIZE, -1);
02375 CheckOverflow(y, x, (MapMaxY() - 1) * TILE_SIZE, -1);
02376 assert(x >= 0 && y >= 0 && x <= (int)MapMaxX() * TILE_SIZE && y <= (int)MapMaxY() * TILE_SIZE);
02377 }
02378 break;
02379
02380 default:
02381 NOT_REACHED();
02382 }
02383 } else if (TileVirtXY(thd->selstart.x, thd->selstart.y) == TileVirtXY(x, y)) {
02384 if (method & VPM_RAILDIRS) {
02385 b = GetAutorailHT(x, y);
02386 } else {
02387 b = HT_RECT;
02388 }
02389 } else if (h == TILE_SIZE) {
02390 if (dx == TILE_SIZE) {
02391 b = (Check2x1AutoRail(3)) | HT_LINE;
02392 } else if (dx == -TILE_SIZE) {
02393 b = (Check2x1AutoRail(2)) | HT_LINE;
02394 } else {
02395 b = HT_LINE | HT_DIR_X;
02396 }
02397 y = thd->selstart.y;
02398 } else if (w == TILE_SIZE) {
02399 if (dy == TILE_SIZE) {
02400 b = (Check2x1AutoRail(1)) | HT_LINE;
02401 } else if (dy == -TILE_SIZE) {
02402 b = (Check2x1AutoRail(0)) | HT_LINE;
02403 } else {
02404 b = HT_LINE | HT_DIR_Y;
02405 }
02406 x = thd->selstart.x;
02407 } else if (w > h * 2) {
02408 b = HT_LINE | HT_DIR_X;
02409 y = thd->selstart.y;
02410 } else if (h > w * 2) {
02411 b = HT_LINE | HT_DIR_Y;
02412 x = thd->selstart.x;
02413 } else {
02414 int d = w - h;
02415 thd->selend.x = thd->selend.x & ~TILE_UNIT_MASK;
02416 thd->selend.y = thd->selend.y & ~TILE_UNIT_MASK;
02417
02418
02419 if (x > thd->selstart.x) {
02420 if (y > thd->selstart.y) {
02421
02422 if (d == 0) {
02423 b = (x & TILE_UNIT_MASK) > (y & TILE_UNIT_MASK) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
02424 } else if (d >= 0) {
02425 x = thd->selstart.x + h;
02426 b = HT_LINE | HT_DIR_VL;
02427 } else {
02428 y = thd->selstart.y + w;
02429 b = HT_LINE | HT_DIR_VR;
02430 }
02431 } else {
02432
02433 if (d == 0) {
02434 b = (x & TILE_UNIT_MASK) + (y & TILE_UNIT_MASK) >= TILE_SIZE ? HT_LINE | HT_DIR_HL : HT_LINE | HT_DIR_HU;
02435 } else if (d >= 0) {
02436 x = thd->selstart.x + h;
02437 b = HT_LINE | HT_DIR_HL;
02438 } else {
02439 y = thd->selstart.y - w;
02440 b = HT_LINE | HT_DIR_HU;
02441 }
02442 }
02443 } else {
02444 if (y > thd->selstart.y) {
02445
02446 if (d == 0) {
02447 b = (x & TILE_UNIT_MASK) + (y & TILE_UNIT_MASK) >= TILE_SIZE ? HT_LINE | HT_DIR_HL : HT_LINE | HT_DIR_HU;
02448 } else if (d >= 0) {
02449 x = thd->selstart.x - h;
02450 b = HT_LINE | HT_DIR_HU;
02451 } else {
02452 y = thd->selstart.y + w;
02453 b = HT_LINE | HT_DIR_HL;
02454 }
02455 } else {
02456
02457 if (d == 0) {
02458 b = (x & TILE_UNIT_MASK) > (y & TILE_UNIT_MASK) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
02459 } else if (d >= 0) {
02460 x = thd->selstart.x - h;
02461 b = HT_LINE | HT_DIR_VR;
02462 } else {
02463 y = thd->selstart.y - w;
02464 b = HT_LINE | HT_DIR_VL;
02465 }
02466 }
02467 }
02468 }
02469
02470 if (_settings_client.gui.measure_tooltip) {
02471 TileIndex t0 = TileVirtXY(thd->selstart.x, thd->selstart.y);
02472 TileIndex t1 = TileVirtXY(x, y);
02473 uint distance = DistanceManhattan(t0, t1) + 1;
02474 byte index = 0;
02475 uint64 params[2];
02476
02477 if (distance != 1) {
02478 int heightdiff = CalcHeightdiff(b, distance, t0, t1);
02479
02480
02481
02482 if ((b & HT_DIR_MASK) != HT_DIR_X && (b & HT_DIR_MASK) != HT_DIR_Y) {
02483 distance = (distance + 1) / 2;
02484 }
02485
02486 params[index++] = distance;
02487 if (heightdiff != 0) params[index++] = heightdiff;
02488 }
02489
02490 ShowMeasurementTooltips(measure_strings_length[index], index, params);
02491 }
02492
02493 thd->selend.x = x;
02494 thd->selend.y = y;
02495 thd->next_drawstyle = b;
02496 }
02497
02504 void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method)
02505 {
02506 int sx, sy;
02507 HighLightStyle style;
02508
02509 if (x == -1) {
02510 _thd.selend.x = -1;
02511 return;
02512 }
02513
02514
02515 if (method & (VPM_RAILDIRS | VPM_SIGNALDIRS)) {
02516 _thd.selend.x = x;
02517 _thd.selend.y = y;
02518 CalcRaildirsDrawstyle(&_thd, x, y, method);
02519 return;
02520 }
02521
02522
02523 if (_thd.next_drawstyle == HT_POINT) {
02524 x += TILE_SIZE / 2;
02525 y += TILE_SIZE / 2;
02526 }
02527
02528 sx = _thd.selstart.x;
02529 sy = _thd.selstart.y;
02530
02531 switch (method) {
02532 case VPM_X_OR_Y:
02533 if (abs(sy - y) < abs(sx - x)) {
02534 y = sy;
02535 style = HT_DIR_X;
02536 } else {
02537 x = sx;
02538 style = HT_DIR_Y;
02539 }
02540 goto calc_heightdiff_single_direction;
02541 case VPM_FIX_X:
02542 x = sx;
02543 style = HT_DIR_Y;
02544 goto calc_heightdiff_single_direction;
02545 case VPM_FIX_Y:
02546 y = sy;
02547 style = HT_DIR_X;
02548
02549 calc_heightdiff_single_direction:;
02550 if (_settings_client.gui.measure_tooltip) {
02551 TileIndex t0 = TileVirtXY(sx, sy);
02552 TileIndex t1 = TileVirtXY(x, y);
02553 uint distance = DistanceManhattan(t0, t1) + 1;
02554 byte index = 0;
02555 uint64 params[2];
02556
02557 if (distance != 1) {
02558
02559
02560
02561
02562
02563 int heightdiff = CalcHeightdiff(HT_LINE | style, 0, t0, t1);
02564
02565 params[index++] = distance;
02566 if (heightdiff != 0) params[index++] = heightdiff;
02567 }
02568
02569 ShowMeasurementTooltips(measure_strings_length[index], index, params);
02570 } break;
02571
02572 case VPM_X_AND_Y_LIMITED: {
02573 int limit = (_thd.sizelimit - 1) * TILE_SIZE;
02574 x = sx + Clamp(x - sx, -limit, limit);
02575 y = sy + Clamp(y - sy, -limit, limit);
02576 }
02577 case VPM_X_AND_Y: {
02578 if (_settings_client.gui.measure_tooltip) {
02579 static const StringID measure_strings_area[] = {
02580 STR_NULL, STR_NULL, STR_MEASURE_AREA, STR_MEASURE_AREA_HEIGHTDIFF
02581 };
02582
02583 TileIndex t0 = TileVirtXY(sx, sy);
02584 TileIndex t1 = TileVirtXY(x, y);
02585 uint dx = Delta(TileX(t0), TileX(t1)) + 1;
02586 uint dy = Delta(TileY(t0), TileY(t1)) + 1;
02587 byte index = 0;
02588 uint64 params[3];
02589
02590
02591
02592 style = (HighLightStyle)_thd.next_drawstyle;
02593 if (style & HT_RECT) {
02594 if (dx == 1) {
02595 style = HT_LINE | HT_DIR_Y;
02596 } else if (dy == 1) {
02597 style = HT_LINE | HT_DIR_X;
02598 }
02599 }
02600
02601 if (dx != 1 || dy != 1) {
02602 int heightdiff = CalcHeightdiff(style, 0, t0, t1);
02603
02604 params[index++] = dx;
02605 params[index++] = dy;
02606 if (heightdiff != 0) params[index++] = heightdiff;
02607 }
02608
02609 ShowMeasurementTooltips(measure_strings_area[index], index, params);
02610 }
02611 break;
02612
02613 }
02614 default: NOT_REACHED();
02615 }
02616
02617 _thd.selend.x = x;
02618 _thd.selend.y = y;
02619 }
02620
02625 bool VpHandlePlaceSizingDrag()
02626 {
02627 if (_special_mouse_mode != WSM_SIZING) return true;
02628
02629
02630 Window *w = FindWindowById(_thd.window_class, _thd.window_number);
02631 if (w == NULL) {
02632 ResetObjectToPlace();
02633 return false;
02634 }
02635
02636
02637 if (_left_button_down) {
02638 w->OnPlaceDrag(_thd.select_method, _thd.select_proc, GetTileBelowCursor());
02639 return false;
02640 }
02641
02642
02643
02644 _special_mouse_mode = WSM_NONE;
02645 if (_thd.next_drawstyle == HT_RECT) {
02646 _thd.place_mode = HT_RECT;
02647 } else if (_thd.select_method & VPM_SIGNALDIRS) {
02648 _thd.place_mode = HT_RECT;
02649 } else if (_thd.select_method & VPM_RAILDIRS) {
02650 _thd.place_mode = (_thd.select_method & ~VPM_RAILDIRS) ? _thd.next_drawstyle : HT_RAIL;
02651 } else {
02652 _thd.place_mode = HT_POINT;
02653 }
02654 SetTileSelectSize(1, 1);
02655
02656 w->OnPlaceMouseUp(_thd.select_method, _thd.select_proc, _thd.selend, TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y));
02657
02658 return false;
02659 }
02660
02661 void SetObjectToPlaceWnd(CursorID icon, SpriteID pal, HighLightStyle mode, Window *w)
02662 {
02663 SetObjectToPlace(icon, pal, mode, w->window_class, w->window_number);
02664 }
02665
02666 #include "table/animcursors.h"
02667
02668 void SetObjectToPlace(CursorID icon, SpriteID pal, HighLightStyle mode, WindowClass window_class, WindowNumber window_num)
02669 {
02670
02671 if (_thd.place_mode != HT_NONE || _special_mouse_mode == WSM_DRAGDROP) {
02672 Window *w = FindWindowById(_thd.window_class, _thd.window_number);
02673 if (w != NULL) {
02674
02675
02676
02677
02678
02679 _thd.window_class = WC_INVALID;
02680 w->OnPlaceObjectAbort();
02681 }
02682 }
02683
02684 SetTileSelectSize(1, 1);
02685
02686 _thd.make_square_red = false;
02687
02688 if (mode == HT_DRAG) {
02689 mode = HT_NONE;
02690 _special_mouse_mode = WSM_DRAGDROP;
02691 } else {
02692 _special_mouse_mode = WSM_NONE;
02693 }
02694
02695 _thd.place_mode = mode;
02696 _thd.window_class = window_class;
02697 _thd.window_number = window_num;
02698
02699 if (mode == HT_SPECIAL)
02700 VpStartPreSizing();
02701
02702 if ((int)icon < 0) {
02703 SetAnimatedMouseCursor(_animcursors[~icon]);
02704 } else {
02705 SetMouseCursor(icon, pal);
02706 }
02707
02708 }
02709
02710 void ResetObjectToPlace()
02711 {
02712 SetObjectToPlace(SPR_CURSOR_MOUSE, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0);
02713 }