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
00482 void DrawGroundSpriteAt(SpriteID image, SpriteID pal, int32 x, int32 y, byte z, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
00483 {
00484 assert((image & SPRITE_MASK) < MAX_SPRITES);
00485
00486 TileSpriteToDraw *ts = _vd.tile_sprites_to_draw.Append();
00487 ts->image = image;
00488 ts->pal = pal;
00489 ts->sub = sub;
00490 Point pt = RemapCoords(x, y, z);
00491 ts->x = pt.x + extra_offs_x;
00492 ts->y = pt.y + extra_offs_y;
00493 }
00494
00507 static void AddChildSpriteToFoundation(SpriteID image, SpriteID pal, const SubSprite *sub, FoundationPart foundation_part, int extra_offs_x, int extra_offs_y)
00508 {
00509 assert(IsInsideMM(foundation_part, 0, FOUNDATION_PART_END));
00510 assert(_vd.foundation[foundation_part] != -1);
00511 Point offs = _vd.foundation_offset[foundation_part];
00512
00513
00514 int *old_child = _vd.last_child;
00515 _vd.last_child = _vd.last_foundation_child[foundation_part];
00516
00517 AddChildSpriteScreen(image, pal, offs.x + extra_offs_x, offs.y + extra_offs_y, false, sub);
00518
00519
00520 _vd.last_child = old_child;
00521 }
00522
00533 void DrawGroundSprite(SpriteID image, SpriteID pal, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
00534 {
00535
00536 if (_vd.foundation_part == FOUNDATION_PART_NONE) _vd.foundation_part = FOUNDATION_PART_NORMAL;
00537
00538 if (_vd.foundation[_vd.foundation_part] != -1) {
00539 AddChildSpriteToFoundation(image, pal, sub, _vd.foundation_part, extra_offs_x, extra_offs_y);
00540 } else {
00541 DrawGroundSpriteAt(image, pal, _cur_ti->x, _cur_ti->y, _cur_ti->z, sub, extra_offs_x, extra_offs_y);
00542 }
00543 }
00544
00545
00553 void OffsetGroundSprite(int x, int y)
00554 {
00555
00556 switch (_vd.foundation_part) {
00557 case FOUNDATION_PART_NONE:
00558 _vd.foundation_part = FOUNDATION_PART_NORMAL;
00559 break;
00560 case FOUNDATION_PART_NORMAL:
00561 _vd.foundation_part = FOUNDATION_PART_HALFTILE;
00562 break;
00563 default: NOT_REACHED();
00564 }
00565
00566
00567 if (_vd.last_child != NULL) _vd.foundation[_vd.foundation_part] = _vd.parent_sprites_to_draw.Length() - 1;
00568
00569 _vd.foundation_offset[_vd.foundation_part].x = x;
00570 _vd.foundation_offset[_vd.foundation_part].y = y;
00571 _vd.last_foundation_child[_vd.foundation_part] = _vd.last_child;
00572 }
00573
00585 static void AddCombinedSprite(SpriteID image, SpriteID pal, int x, int y, byte z, const SubSprite *sub)
00586 {
00587 Point pt = RemapCoords(x, y, z);
00588 const Sprite *spr = GetSprite(image & SPRITE_MASK, ST_NORMAL);
00589
00590 if (pt.x + spr->x_offs >= _vd.dpi.left + _vd.dpi.width ||
00591 pt.x + spr->x_offs + spr->width <= _vd.dpi.left ||
00592 pt.y + spr->y_offs >= _vd.dpi.top + _vd.dpi.height ||
00593 pt.y + spr->y_offs + spr->height <= _vd.dpi.top)
00594 return;
00595
00596 const ParentSpriteToDraw *pstd = _vd.parent_sprites_to_draw.End() - 1;
00597 AddChildSpriteScreen(image, pal, pt.x - pstd->left, pt.y - pstd->top, false, sub);
00598 }
00599
00624 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)
00625 {
00626 int32 left, right, top, bottom;
00627
00628 assert((image & SPRITE_MASK) < MAX_SPRITES);
00629
00630
00631 if (transparent) {
00632 SetBit(image, PALETTE_MODIFIER_TRANSPARENT);
00633 pal = PALETTE_TO_TRANSPARENT;
00634 }
00635
00636 if (_vd.combine_sprites == SPRITE_COMBINE_ACTIVE) {
00637 AddCombinedSprite(image, pal, x, y, z, sub);
00638 return;
00639 }
00640
00641 _vd.last_child = NULL;
00642
00643 Point pt = RemapCoords(x, y, z);
00644 int tmp_left, tmp_top, tmp_x = pt.x, tmp_y = pt.y;
00645
00646
00647 if (image == SPR_EMPTY_BOUNDING_BOX) {
00648 left = tmp_left = RemapCoords(x + w , y + bb_offset_y, z + bb_offset_z).x;
00649 right = RemapCoords(x + bb_offset_x, y + h , z + bb_offset_z).x + 1;
00650 top = tmp_top = RemapCoords(x + bb_offset_x, y + bb_offset_y, z + dz ).y;
00651 bottom = RemapCoords(x + w , y + h , z + bb_offset_z).y + 1;
00652 } else {
00653 const Sprite *spr = GetSprite(image & SPRITE_MASK, ST_NORMAL);
00654 left = tmp_left = (pt.x += spr->x_offs);
00655 right = (pt.x + spr->width );
00656 top = tmp_top = (pt.y += spr->y_offs);
00657 bottom = (pt.y + spr->height);
00658 }
00659
00660 if (_draw_bounding_boxes && (image != SPR_EMPTY_BOUNDING_BOX)) {
00661
00662 left = min(left , RemapCoords(x + w , y + bb_offset_y, z + bb_offset_z).x);
00663 right = max(right , RemapCoords(x + bb_offset_x, y + h , z + bb_offset_z).x + 1);
00664 top = min(top , RemapCoords(x + bb_offset_x, y + bb_offset_y, z + dz ).y);
00665 bottom = max(bottom, RemapCoords(x + w , y + h , z + bb_offset_z).y + 1);
00666 }
00667
00668
00669 if (left >= _vd.dpi.left + _vd.dpi.width ||
00670 right <= _vd.dpi.left ||
00671 top >= _vd.dpi.top + _vd.dpi.height ||
00672 bottom <= _vd.dpi.top) {
00673 return;
00674 }
00675
00676 ParentSpriteToDraw *ps = _vd.parent_sprites_to_draw.Append();
00677 ps->x = tmp_x;
00678 ps->y = tmp_y;
00679
00680 ps->left = tmp_left;
00681 ps->top = tmp_top;
00682
00683 ps->image = image;
00684 ps->pal = pal;
00685 ps->sub = sub;
00686 ps->xmin = x + bb_offset_x;
00687 ps->xmax = x + max(bb_offset_x, w) - 1;
00688
00689 ps->ymin = y + bb_offset_y;
00690 ps->ymax = y + max(bb_offset_y, h) - 1;
00691
00692 ps->zmin = z + bb_offset_z;
00693 ps->zmax = z + max(bb_offset_z, dz) - 1;
00694
00695 ps->comparison_done = false;
00696 ps->first_child = -1;
00697
00698 _vd.last_child = &ps->first_child;
00699
00700 if (_vd.combine_sprites == SPRITE_COMBINE_PENDING) _vd.combine_sprites = SPRITE_COMBINE_ACTIVE;
00701 }
00702
00721 void StartSpriteCombine()
00722 {
00723 assert(_vd.combine_sprites == SPRITE_COMBINE_NONE);
00724 _vd.combine_sprites = SPRITE_COMBINE_PENDING;
00725 }
00726
00731 void EndSpriteCombine()
00732 {
00733 assert(_vd.combine_sprites != SPRITE_COMBINE_NONE);
00734 _vd.combine_sprites = SPRITE_COMBINE_NONE;
00735 }
00736
00747 void AddChildSpriteScreen(SpriteID image, SpriteID pal, int x, int y, bool transparent, const SubSprite *sub)
00748 {
00749 assert((image & SPRITE_MASK) < MAX_SPRITES);
00750
00751
00752 if (_vd.last_child == NULL) return;
00753
00754
00755 if (transparent) {
00756 SetBit(image, PALETTE_MODIFIER_TRANSPARENT);
00757 pal = PALETTE_TO_TRANSPARENT;
00758 }
00759
00760 *_vd.last_child = _vd.child_screen_sprites_to_draw.Length();
00761
00762 ChildScreenSpriteToDraw *cs = _vd.child_screen_sprites_to_draw.Append();
00763 cs->image = image;
00764 cs->pal = pal;
00765 cs->sub = sub;
00766 cs->x = x;
00767 cs->y = y;
00768 cs->next = -1;
00769
00770
00771
00772
00773 if (_vd.last_foundation_child[0] == _vd.last_child) _vd.last_foundation_child[0] = &cs->next;
00774 if (_vd.last_foundation_child[1] == _vd.last_child) _vd.last_foundation_child[1] = &cs->next;
00775 _vd.last_child = &cs->next;
00776 }
00777
00778 static void AddStringToDraw(int x, int y, StringID string, uint64 params_1, uint64 params_2, Colours colour, uint16 width)
00779 {
00780 assert(width != 0);
00781 StringSpriteToDraw *ss = _vd.string_sprites_to_draw.Append();
00782 ss->string = string;
00783 ss->x = x;
00784 ss->y = y;
00785 ss->params[0] = params_1;
00786 ss->params[1] = params_2;
00787 ss->width = width;
00788 ss->colour = colour;
00789 }
00790
00791
00803 static void DrawSelectionSprite(SpriteID image, SpriteID pal, const TileInfo *ti, int z_offset, FoundationPart foundation_part)
00804 {
00805
00806 if (_vd.foundation[foundation_part] == -1) {
00807
00808 DrawGroundSpriteAt(image, pal, ti->x, ti->y, ti->z + z_offset);
00809 } else {
00810
00811 AddChildSpriteToFoundation(image, pal, NULL, foundation_part, 0, -z_offset);
00812 }
00813 }
00814
00821 static void DrawTileSelectionRect(const TileInfo *ti, SpriteID pal)
00822 {
00823 if (!IsValidTile(ti->tile)) return;
00824
00825 SpriteID sel;
00826 if (IsHalftileSlope(ti->tileh)) {
00827 Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
00828 SpriteID sel2 = SPR_HALFTILE_SELECTION_FLAT + halftile_corner;
00829 DrawSelectionSprite(sel2, pal, ti, 7 + TILE_HEIGHT, FOUNDATION_PART_HALFTILE);
00830
00831 Corner opposite_corner = OppositeCorner(halftile_corner);
00832 if (IsSteepSlope(ti->tileh)) {
00833 sel = SPR_HALFTILE_SELECTION_DOWN;
00834 } else {
00835 sel = ((ti->tileh & SlopeWithOneCornerRaised(opposite_corner)) != 0 ? SPR_HALFTILE_SELECTION_UP : SPR_HALFTILE_SELECTION_FLAT);
00836 }
00837 sel += opposite_corner;
00838 } else {
00839 sel = SPR_SELECT_TILE + _tileh_to_sprite[ti->tileh];
00840 }
00841 DrawSelectionSprite(sel, pal, ti, 7, FOUNDATION_PART_NORMAL);
00842 }
00843
00844 static bool IsPartOfAutoLine(int px, int py)
00845 {
00846 px -= _thd.selstart.x;
00847 py -= _thd.selstart.y;
00848
00849 if ((_thd.drawstyle & ~HT_DIR_MASK) != HT_LINE) return false;
00850
00851 switch (_thd.drawstyle & HT_DIR_MASK) {
00852 case HT_DIR_X: return py == 0;
00853 case HT_DIR_Y: return px == 0;
00854 case HT_DIR_HU: return px == -py || px == -py - 16;
00855 case HT_DIR_HL: return px == -py || px == -py + 16;
00856 case HT_DIR_VL: return px == py || px == py + 16;
00857 case HT_DIR_VR: return px == py || px == py - 16;
00858 default:
00859 NOT_REACHED();
00860 }
00861 }
00862
00863
00864 static const HighLightStyle _autorail_type[6][2] = {
00865 { HT_DIR_X, HT_DIR_X },
00866 { HT_DIR_Y, HT_DIR_Y },
00867 { HT_DIR_HU, HT_DIR_HL },
00868 { HT_DIR_HL, HT_DIR_HU },
00869 { HT_DIR_VL, HT_DIR_VR },
00870 { HT_DIR_VR, HT_DIR_VL }
00871 };
00872
00873 #include "table/autorail.h"
00874
00881 static void DrawAutorailSelection(const TileInfo *ti, uint autorail_type)
00882 {
00883 SpriteID image;
00884 SpriteID pal;
00885 int offset;
00886
00887 FoundationPart foundation_part = FOUNDATION_PART_NORMAL;
00888 Slope autorail_tileh = RemoveHalftileSlope(ti->tileh);
00889 if (IsHalftileSlope(ti->tileh)) {
00890 static const uint _lower_rail[4] = { 5U, 2U, 4U, 3U };
00891 Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
00892 if (autorail_type != _lower_rail[halftile_corner]) {
00893 foundation_part = FOUNDATION_PART_HALFTILE;
00894
00895 autorail_tileh = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
00896 }
00897 }
00898
00899 offset = _AutorailTilehSprite[autorail_tileh][autorail_type];
00900 if (offset >= 0) {
00901 image = SPR_AUTORAIL_BASE + offset;
00902 pal = PAL_NONE;
00903 } else {
00904 image = SPR_AUTORAIL_BASE - offset;
00905 pal = PALETTE_SEL_TILE_RED;
00906 }
00907
00908 DrawSelectionSprite(image, _thd.make_square_red ? PALETTE_SEL_TILE_RED : pal, ti, 7, foundation_part);
00909 }
00910
00915 static void DrawTileSelection(const TileInfo *ti)
00916 {
00917
00918 bool is_redsq = _thd.redsq == ti->tile;
00919 if (is_redsq) DrawTileSelectionRect(ti, PALETTE_TILE_RED_PULSATING);
00920
00921
00922 if (_thd.drawstyle == 0) return;
00923
00924
00925 if (IsInsideBS(ti->x, _thd.pos.x, _thd.size.x) &&
00926 IsInsideBS(ti->y, _thd.pos.y, _thd.size.y)) {
00927 if (_thd.drawstyle & HT_RECT) {
00928 if (!is_redsq) DrawTileSelectionRect(ti, _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE);
00929 } else if (_thd.drawstyle & HT_POINT) {
00930
00931 byte z = 0;
00932 FoundationPart foundation_part = FOUNDATION_PART_NORMAL;
00933 if (ti->tileh & SLOPE_N) {
00934 z += TILE_HEIGHT;
00935 if (RemoveHalftileSlope(ti->tileh) == SLOPE_STEEP_N) z += TILE_HEIGHT;
00936 }
00937 if (IsHalftileSlope(ti->tileh)) {
00938 Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
00939 if ((halftile_corner == CORNER_W) || (halftile_corner == CORNER_E)) z += TILE_HEIGHT;
00940 if (halftile_corner != CORNER_S) {
00941 foundation_part = FOUNDATION_PART_HALFTILE;
00942 if (IsSteepSlope(ti->tileh)) z -= TILE_HEIGHT;
00943 }
00944 }
00945 DrawSelectionSprite(_cur_dpi->zoom <= ZOOM_LVL_DETAIL ? SPR_DOT : SPR_DOT_SMALL, PAL_NONE, ti, z, foundation_part);
00946 } else if (_thd.drawstyle & HT_RAIL) {
00947
00948 HighLightStyle type = _thd.drawstyle & HT_DIR_MASK;
00949 assert(type < HT_DIR_END);
00950 DrawAutorailSelection(ti, _autorail_type[type][0]);
00951 } else if (IsPartOfAutoLine(ti->x, ti->y)) {
00952
00953 HighLightStyle dir = _thd.drawstyle & HT_DIR_MASK;
00954 uint side;
00955
00956 if (dir == HT_DIR_X || dir == HT_DIR_Y) {
00957 side = 0;
00958 } else {
00959 TileIndex start = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
00960 side = Delta(Delta(TileX(start), TileX(ti->tile)), Delta(TileY(start), TileY(ti->tile)));
00961 }
00962
00963 DrawAutorailSelection(ti, _autorail_type[dir][side]);
00964 }
00965 return;
00966 }
00967
00968
00969 if (!is_redsq && _thd.outersize.x &&
00970 _thd.size.x < _thd.size.x + _thd.outersize.x &&
00971 IsInsideBS(ti->x, _thd.pos.x + _thd.offs.x, _thd.size.x + _thd.outersize.x) &&
00972 IsInsideBS(ti->y, _thd.pos.y + _thd.offs.y, _thd.size.y + _thd.outersize.y)) {
00973
00974 DrawTileSelectionRect(ti, PALETTE_SEL_TILE_BLUE);
00975 return;
00976 }
00977 }
00978
00979 static void ViewportAddLandscape()
00980 {
00981 int x, y, width, height;
00982 TileInfo ti;
00983 bool direction;
00984
00985 _cur_ti = &ti;
00986
00987
00988 x = ((_vd.dpi.top >> 1) - (_vd.dpi.left >> 2)) & ~TILE_UNIT_MASK;
00989 y = ((_vd.dpi.top >> 1) + (_vd.dpi.left >> 2) - TILE_SIZE) & ~TILE_UNIT_MASK;
00990
00991
00992 {
00993 Point pt = RemapCoords(x, y, 241);
00994 width = (_vd.dpi.left + _vd.dpi.width - pt.x + 95) >> 6;
00995 height = (_vd.dpi.top + _vd.dpi.height - pt.y) >> 5 << 1;
00996 }
00997
00998 assert(width > 0);
00999 assert(height > 0);
01000
01001 direction = false;
01002
01003 do {
01004 int width_cur = width;
01005 int x_cur = x;
01006 int y_cur = y;
01007
01008 do {
01009 TileType tt = MP_VOID;
01010
01011 ti.x = x_cur;
01012 ti.y = y_cur;
01013
01014 ti.z = 0;
01015
01016 ti.tileh = SLOPE_FLAT;
01017 ti.tile = INVALID_TILE;
01018
01019 if (0 <= x_cur && x_cur < (int)MapMaxX() * TILE_SIZE &&
01020 0 <= y_cur && y_cur < (int)MapMaxY() * TILE_SIZE) {
01021 TileIndex tile = TileVirtXY(x_cur, y_cur);
01022
01023 if (!_settings_game.construction.freeform_edges || (TileX(tile) != 0 && TileY(tile) != 0)) {
01024 if (x_cur == ((int)MapMaxX() - 1) * TILE_SIZE || y_cur == ((int)MapMaxY() - 1) * TILE_SIZE) {
01025 uint maxh = max<uint>(TileHeight(tile), 1);
01026 for (uint h = 0; h < maxh; h++) {
01027 DrawGroundSpriteAt(SPR_SHADOW_CELL, PAL_NONE, ti.x, ti.y, h * TILE_HEIGHT);
01028 }
01029 }
01030
01031 ti.tile = tile;
01032 ti.tileh = GetTileSlope(tile, &ti.z);
01033 tt = GetTileType(tile);
01034 }
01035 }
01036
01037 _vd.foundation_part = FOUNDATION_PART_NONE;
01038 _vd.foundation[0] = -1;
01039 _vd.foundation[1] = -1;
01040 _vd.last_foundation_child[0] = NULL;
01041 _vd.last_foundation_child[1] = NULL;
01042
01043 _tile_type_procs[tt]->draw_tile_proc(&ti);
01044
01045 if ((x_cur == (int)MapMaxX() * TILE_SIZE && IsInsideMM(y_cur, 0, MapMaxY() * TILE_SIZE + 1)) ||
01046 (y_cur == (int)MapMaxY() * TILE_SIZE && IsInsideMM(x_cur, 0, MapMaxX() * TILE_SIZE + 1))) {
01047 TileIndex tile = TileVirtXY(x_cur, y_cur);
01048 ti.tile = tile;
01049 ti.tileh = GetTileSlope(tile, &ti.z);
01050 tt = GetTileType(tile);
01051 }
01052 if (ti.tile != INVALID_TILE) DrawTileSelection(&ti);
01053
01054 y_cur += 0x10;
01055 x_cur -= 0x10;
01056 } while (--width_cur);
01057
01058 if ((direction ^= 1) != 0) {
01059 y += 0x10;
01060 } else {
01061 x += 0x10;
01062 }
01063 } while (--height);
01064 }
01065
01076 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)
01077 {
01078 bool small = dpi->zoom >= small_from;
01079
01080 int left = dpi->left;
01081 int top = dpi->top;
01082 int right = left + dpi->width;
01083 int bottom = top + dpi->height;
01084
01085 int sign_height = ScaleByZoom(VPSM_TOP + FONT_HEIGHT_NORMAL + VPSM_BOTTOM, dpi->zoom);
01086 int sign_half_width = ScaleByZoom((small ? sign->width_small : sign->width_normal) / 2, dpi->zoom);
01087
01088 if (bottom < sign->top ||
01089 top > sign->top + sign_height ||
01090 right < sign->center - sign_half_width ||
01091 left > sign->center + sign_half_width) {
01092 return;
01093 }
01094
01095 if (!small) {
01096 AddStringToDraw(sign->center - sign_half_width, sign->top, string_normal, params_1, params_2, colour, sign->width_normal);
01097 } else {
01098 int shadow_offset = 0;
01099 if (string_small_shadow != STR_NULL) {
01100 shadow_offset = 4;
01101 AddStringToDraw(sign->center - sign_half_width + shadow_offset, sign->top, string_small_shadow, params_1, params_2, INVALID_COLOUR, sign->width_small);
01102 }
01103 AddStringToDraw(sign->center - sign_half_width, sign->top - shadow_offset, string_small, params_1, params_2,
01104 colour, sign->width_small | 0x8000);
01105 }
01106 }
01107
01108 static void ViewportAddTownNames(DrawPixelInfo *dpi)
01109 {
01110 if (!HasBit(_display_opt, DO_SHOW_TOWN_NAMES) || _game_mode == GM_MENU) return;
01111
01112 const Town *t;
01113 FOR_ALL_TOWNS(t) {
01114 ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &t->sign,
01115 _settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_VIEWPORT_TOWN,
01116 STR_VIEWPORT_TOWN_TINY_WHITE, STR_VIEWPORT_TOWN_TINY_BLACK,
01117 t->index, t->population);
01118 }
01119 }
01120
01121
01122 static void ViewportAddStationNames(DrawPixelInfo *dpi)
01123 {
01124 if (!(HasBit(_display_opt, DO_SHOW_STATION_NAMES) || HasBit(_display_opt, DO_SHOW_WAYPOINT_NAMES)) || _game_mode == GM_MENU) return;
01125
01126 const BaseStation *st;
01127 FOR_ALL_BASE_STATIONS(st) {
01128
01129 bool is_station = Station::IsExpected(st);
01130
01131
01132 if (!HasBit(_display_opt, is_station ? DO_SHOW_STATION_NAMES : DO_SHOW_WAYPOINT_NAMES)) continue;
01133
01134 ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &st->sign,
01135 is_station ? STR_VIEWPORT_STATION : STR_VIEWPORT_WAYPOINT,
01136 (is_station ? STR_VIEWPORT_STATION : STR_VIEWPORT_WAYPOINT) + 1, STR_NULL,
01137 st->index, st->facilities, (st->owner == OWNER_NONE || !st->IsInUse()) ? COLOUR_GREY : _company_colours[st->owner]);
01138 }
01139 }
01140
01141
01142 static void ViewportAddSigns(DrawPixelInfo *dpi)
01143 {
01144
01145 if (!HasBit(_display_opt, DO_SHOW_SIGNS) || IsInvisibilitySet(TO_SIGNS)) return;
01146
01147 const Sign *si;
01148 FOR_ALL_SIGNS(si) {
01149 ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &si->sign,
01150 STR_WHITE_SIGN,
01151 IsTransparencySet(TO_SIGNS) ? STR_VIEWPORT_SIGN_SMALL_WHITE : STR_VIEWPORT_SIGN_SMALL_BLACK, STR_NULL,
01152 si->index, 0, (si->owner == OWNER_NONE) ? COLOUR_GREY : _company_colours[si->owner]);
01153 }
01154 }
01155
01162 void ViewportSign::UpdatePosition(int center, int top, StringID str)
01163 {
01164 if (this->width_normal != 0) this->MarkDirty();
01165
01166 this->top = top;
01167
01168 char buffer[DRAW_STRING_BUFFER];
01169
01170 GetString(buffer, str, lastof(buffer));
01171 this->width_normal = VPSM_LEFT + Align(GetStringBoundingBox(buffer).width, 2) + VPSM_RIGHT;
01172 this->center = center;
01173
01174
01175 _cur_fontsize = FS_SMALL;
01176 this->width_small = VPSM_LEFT + Align(GetStringBoundingBox(buffer).width, 2) + VPSM_RIGHT;
01177 _cur_fontsize = FS_NORMAL;
01178
01179 this->MarkDirty();
01180 }
01181
01187 void ViewportSign::MarkDirty() const
01188 {
01189
01190
01191
01192
01193 MarkAllViewportsDirty(
01194 this->center - ScaleByZoom(this->width_normal / 2 + 1, ZOOM_LVL_MAX),
01195 this->top - ScaleByZoom(1, ZOOM_LVL_MAX),
01196 this->center + ScaleByZoom(this->width_normal / 2 + 1, ZOOM_LVL_MAX),
01197 this->top + ScaleByZoom(VPSM_TOP + FONT_HEIGHT_NORMAL + VPSM_BOTTOM + 1, ZOOM_LVL_MAX));
01198 }
01199
01200 static void ViewportDrawTileSprites(const TileSpriteToDrawVector *tstdv)
01201 {
01202 const TileSpriteToDraw *tsend = tstdv->End();
01203 for (const TileSpriteToDraw *ts = tstdv->Begin(); ts != tsend; ++ts) {
01204 DrawSprite(ts->image, ts->pal, ts->x, ts->y, ts->sub);
01205 }
01206 }
01207
01209 static void ViewportSortParentSprites(ParentSpriteToSortVector *psdv)
01210 {
01211 ParentSpriteToDraw **psdvend = psdv->End();
01212 ParentSpriteToDraw **psd = psdv->Begin();
01213 while (psd != psdvend) {
01214 ParentSpriteToDraw *ps = *psd;
01215
01216 if (ps->comparison_done) {
01217 psd++;
01218 continue;
01219 }
01220
01221 ps->comparison_done = true;
01222
01223 for (ParentSpriteToDraw **psd2 = psd + 1; psd2 != psdvend; psd2++) {
01224 ParentSpriteToDraw *ps2 = *psd2;
01225
01226 if (ps2->comparison_done) continue;
01227
01228
01229
01230
01231 if (ps->xmax >= ps2->xmin && ps->xmin <= ps2->xmax &&
01232 ps->ymax >= ps2->ymin && ps->ymin <= ps2->ymax &&
01233 ps->zmax >= ps2->zmin && ps->zmin <= ps2->zmax) {
01234
01235
01236
01237
01238
01239
01240 if (ps->xmin + ps->xmax + ps->ymin + ps->ymax + ps->zmin + ps->zmax <=
01241 ps2->xmin + ps2->xmax + ps2->ymin + ps2->ymax + ps2->zmin + ps2->zmax) {
01242 continue;
01243 }
01244 } else {
01245
01246
01247
01248
01249 if (ps->xmax < ps2->xmin ||
01250 ps->ymax < ps2->ymin ||
01251 ps->zmax < ps2->zmin) {
01252 continue;
01253 }
01254 }
01255
01256
01257 ParentSpriteToDraw *temp = ps2;
01258 for (ParentSpriteToDraw **psd3 = psd2; psd3 > psd; psd3--) {
01259 *psd3 = *(psd3 - 1);
01260 }
01261 *psd = temp;
01262 }
01263 }
01264 }
01265
01266 static void ViewportDrawParentSprites(const ParentSpriteToSortVector *psd, const ChildScreenSpriteToDrawVector *csstdv)
01267 {
01268 const ParentSpriteToDraw * const *psd_end = psd->End();
01269 for (const ParentSpriteToDraw * const *it = psd->Begin(); it != psd_end; it++) {
01270 const ParentSpriteToDraw *ps = *it;
01271 if (ps->image != SPR_EMPTY_BOUNDING_BOX) DrawSprite(ps->image, ps->pal, ps->x, ps->y, ps->sub);
01272
01273 int child_idx = ps->first_child;
01274 while (child_idx >= 0) {
01275 const ChildScreenSpriteToDraw *cs = csstdv->Get(child_idx);
01276 child_idx = cs->next;
01277 DrawSprite(cs->image, cs->pal, ps->left + cs->x, ps->top + cs->y, cs->sub);
01278 }
01279 }
01280 }
01281
01286 static void ViewportDrawBoundingBoxes(const ParentSpriteToSortVector *psd)
01287 {
01288 const ParentSpriteToDraw * const *psd_end = psd->End();
01289 for (const ParentSpriteToDraw * const *it = psd->Begin(); it != psd_end; it++) {
01290 const ParentSpriteToDraw *ps = *it;
01291 Point pt1 = RemapCoords(ps->xmax + 1, ps->ymax + 1, ps->zmax + 1);
01292 Point pt2 = RemapCoords(ps->xmin , ps->ymax + 1, ps->zmax + 1);
01293 Point pt3 = RemapCoords(ps->xmax + 1, ps->ymin , ps->zmax + 1);
01294 Point pt4 = RemapCoords(ps->xmax + 1, ps->ymax + 1, ps->zmin );
01295
01296 DrawBox( pt1.x, pt1.y,
01297 pt2.x - pt1.x, pt2.y - pt1.y,
01298 pt3.x - pt1.x, pt3.y - pt1.y,
01299 pt4.x - pt1.x, pt4.y - pt1.y);
01300 }
01301 }
01302
01303 static void ViewportDrawStrings(DrawPixelInfo *dpi, const StringSpriteToDrawVector *sstdv)
01304 {
01305 DrawPixelInfo dp;
01306 ZoomLevel zoom;
01307
01308 _cur_dpi = &dp;
01309 dp = *dpi;
01310
01311 zoom = dp.zoom;
01312 dp.zoom = ZOOM_LVL_NORMAL;
01313
01314 dp.left = UnScaleByZoom(dp.left, zoom);
01315 dp.top = UnScaleByZoom(dp.top, zoom);
01316 dp.width = UnScaleByZoom(dp.width, zoom);
01317 dp.height = UnScaleByZoom(dp.height, zoom);
01318
01319 const StringSpriteToDraw *ssend = sstdv->End();
01320 for (const StringSpriteToDraw *ss = sstdv->Begin(); ss != ssend; ++ss) {
01321 TextColour colour = TC_BLACK;
01322 bool small = HasBit(ss->width, 15);
01323 int w = GB(ss->width, 0, 15);
01324 int x = UnScaleByZoom(ss->x, zoom);
01325 int y = UnScaleByZoom(ss->y, zoom);
01326 int h = VPSM_TOP + (small ? FONT_HEIGHT_SMALL : FONT_HEIGHT_NORMAL) + VPSM_BOTTOM;
01327
01328 SetDParam(0, ss->params[0]);
01329 SetDParam(1, ss->params[1]);
01330
01331 if (ss->colour != INVALID_COLOUR) {
01332
01333 if (IsInvisibilitySet(TO_SIGNS) && ss->string != STR_WHITE_SIGN) continue;
01334
01335
01336
01337 if (IsTransparencySet(TO_SIGNS) && ss->string != STR_WHITE_SIGN) {
01338
01339
01340 colour = (TextColour)_colour_gradient[ss->colour][6] | IS_PALETTE_COLOUR;
01341 }
01342
01343
01344
01345 if (!IsTransparencySet(TO_SIGNS) || ss->string == STR_WHITE_SIGN) {
01346 DrawFrameRect(
01347 x, y, x + w, y + h, ss->colour,
01348 IsTransparencySet(TO_SIGNS) ? FR_TRANSPARENT : FR_NONE
01349 );
01350 }
01351 }
01352
01353 DrawString(x + VPSM_LEFT, x + w - 1 - VPSM_RIGHT, y + VPSM_TOP, ss->string, colour, SA_CENTER);
01354 }
01355 }
01356
01357 void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom)
01358 {
01359 DrawPixelInfo *old_dpi = _cur_dpi;
01360 _cur_dpi = &_vd.dpi;
01361
01362 _vd.dpi.zoom = vp->zoom;
01363 int mask = ScaleByZoom(-1, vp->zoom);
01364
01365 _vd.combine_sprites = SPRITE_COMBINE_NONE;
01366
01367 _vd.dpi.width = (right - left) & mask;
01368 _vd.dpi.height = (bottom - top) & mask;
01369 _vd.dpi.left = left & mask;
01370 _vd.dpi.top = top & mask;
01371 _vd.dpi.pitch = old_dpi->pitch;
01372 _vd.last_child = NULL;
01373
01374 int x = UnScaleByZoom(_vd.dpi.left - (vp->virtual_left & mask), vp->zoom) + vp->left;
01375 int y = UnScaleByZoom(_vd.dpi.top - (vp->virtual_top & mask), vp->zoom) + vp->top;
01376
01377 _vd.dpi.dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(old_dpi->dst_ptr, x - old_dpi->left, y - old_dpi->top);
01378
01379 ViewportAddLandscape();
01380 ViewportAddVehicles(&_vd.dpi);
01381
01382 ViewportAddTownNames(&_vd.dpi);
01383 ViewportAddStationNames(&_vd.dpi);
01384 ViewportAddSigns(&_vd.dpi);
01385
01386 DrawTextEffects(&_vd.dpi);
01387
01388 if (_vd.tile_sprites_to_draw.Length() != 0) ViewportDrawTileSprites(&_vd.tile_sprites_to_draw);
01389
01390 ParentSpriteToDraw *psd_end = _vd.parent_sprites_to_draw.End();
01391 for (ParentSpriteToDraw *it = _vd.parent_sprites_to_draw.Begin(); it != psd_end; it++) {
01392 *_vd.parent_sprites_to_sort.Append() = it;
01393 }
01394
01395 ViewportSortParentSprites(&_vd.parent_sprites_to_sort);
01396 ViewportDrawParentSprites(&_vd.parent_sprites_to_sort, &_vd.child_screen_sprites_to_draw);
01397
01398 if (_draw_bounding_boxes) ViewportDrawBoundingBoxes(&_vd.parent_sprites_to_sort);
01399
01400 if (_vd.string_sprites_to_draw.Length() != 0) ViewportDrawStrings(&_vd.dpi, &_vd.string_sprites_to_draw);
01401
01402 _cur_dpi = old_dpi;
01403
01404 _vd.string_sprites_to_draw.Clear();
01405 _vd.tile_sprites_to_draw.Clear();
01406 _vd.parent_sprites_to_draw.Clear();
01407 _vd.parent_sprites_to_sort.Clear();
01408 _vd.child_screen_sprites_to_draw.Clear();
01409 }
01410
01413 static void ViewportDrawChk(const ViewPort *vp, int left, int top, int right, int bottom)
01414 {
01415 if (ScaleByZoom(bottom - top, vp->zoom) * ScaleByZoom(right - left, vp->zoom) > 180000) {
01416 if ((bottom - top) > (right - left)) {
01417 int t = (top + bottom) >> 1;
01418 ViewportDrawChk(vp, left, top, right, t);
01419 ViewportDrawChk(vp, left, t, right, bottom);
01420 } else {
01421 int t = (left + right) >> 1;
01422 ViewportDrawChk(vp, left, top, t, bottom);
01423 ViewportDrawChk(vp, t, top, right, bottom);
01424 }
01425 } else {
01426 ViewportDoDraw(vp,
01427 ScaleByZoom(left - vp->left, vp->zoom) + vp->virtual_left,
01428 ScaleByZoom(top - vp->top, vp->zoom) + vp->virtual_top,
01429 ScaleByZoom(right - vp->left, vp->zoom) + vp->virtual_left,
01430 ScaleByZoom(bottom - vp->top, vp->zoom) + vp->virtual_top
01431 );
01432 }
01433 }
01434
01435 static inline void ViewportDraw(const ViewPort *vp, int left, int top, int right, int bottom)
01436 {
01437 if (right <= vp->left || bottom <= vp->top) return;
01438
01439 if (left >= vp->left + vp->width) return;
01440
01441 if (left < vp->left) left = vp->left;
01442 if (right > vp->left + vp->width) right = vp->left + vp->width;
01443
01444 if (top >= vp->top + vp->height) return;
01445
01446 if (top < vp->top) top = vp->top;
01447 if (bottom > vp->top + vp->height) bottom = vp->top + vp->height;
01448
01449 ViewportDrawChk(vp, left, top, right, bottom);
01450 }
01451
01455 void Window::DrawViewport() const
01456 {
01457 DrawPixelInfo *dpi = _cur_dpi;
01458
01459 dpi->left += this->left;
01460 dpi->top += this->top;
01461
01462 ViewportDraw(this->viewport, dpi->left, dpi->top, dpi->left + dpi->width, dpi->top + dpi->height);
01463
01464 dpi->left -= this->left;
01465 dpi->top -= this->top;
01466 }
01467
01468 static inline void ClampViewportToMap(const ViewPort *vp, int &x, int &y)
01469 {
01470
01471 x += vp->virtual_width / 2;
01472 y += vp->virtual_height / 2;
01473
01474
01475
01476 int vx = -x + y * 2;
01477 int vy = x + y * 2;
01478
01479
01480 vx = Clamp(vx, 0, MapMaxX() * TILE_SIZE * 4);
01481 vy = Clamp(vy, 0, MapMaxY() * TILE_SIZE * 4);
01482
01483
01484 x = (-vx + vy) / 2;
01485 y = ( vx + vy) / 4;
01486
01487
01488 x -= vp->virtual_width / 2;
01489 y -= vp->virtual_height / 2;
01490 }
01491
01496 void UpdateViewportPosition(Window *w)
01497 {
01498 const ViewPort *vp = w->viewport;
01499
01500 if (w->viewport->follow_vehicle != INVALID_VEHICLE) {
01501 const Vehicle *veh = Vehicle::Get(w->viewport->follow_vehicle);
01502 Point pt = MapXYZToViewport(vp, veh->x_pos, veh->y_pos, veh->z_pos);
01503
01504 w->viewport->scrollpos_x = pt.x;
01505 w->viewport->scrollpos_y = pt.y;
01506 SetViewportPosition(w, pt.x, pt.y);
01507 } else {
01508
01509 ClampViewportToMap(vp, w->viewport->dest_scrollpos_x, w->viewport->dest_scrollpos_y);
01510
01511 int delta_x = w->viewport->dest_scrollpos_x - w->viewport->scrollpos_x;
01512 int delta_y = w->viewport->dest_scrollpos_y - w->viewport->scrollpos_y;
01513
01514 if (delta_x != 0 || delta_y != 0) {
01515 if (_settings_client.gui.smooth_scroll) {
01516 int max_scroll = ScaleByMapSize1D(512);
01517
01518 w->viewport->scrollpos_x += Clamp(delta_x / 4, -max_scroll, max_scroll);
01519 w->viewport->scrollpos_y += Clamp(delta_y / 4, -max_scroll, max_scroll);
01520 } else {
01521 w->viewport->scrollpos_x = w->viewport->dest_scrollpos_x;
01522 w->viewport->scrollpos_y = w->viewport->dest_scrollpos_y;
01523 }
01524 }
01525
01526 ClampViewportToMap(vp, w->viewport->scrollpos_x, w->viewport->scrollpos_y);
01527
01528 SetViewportPosition(w, w->viewport->scrollpos_x, w->viewport->scrollpos_y);
01529 }
01530 }
01531
01541 static void MarkViewportDirty(const ViewPort *vp, int left, int top, int right, int bottom)
01542 {
01543 right -= vp->virtual_left;
01544 if (right <= 0) return;
01545
01546 bottom -= vp->virtual_top;
01547 if (bottom <= 0) return;
01548
01549 left = max(0, left - vp->virtual_left);
01550
01551 if (left >= vp->virtual_width) return;
01552
01553 top = max(0, top - vp->virtual_top);
01554
01555 if (top >= vp->virtual_height) return;
01556
01557 SetDirtyBlocks(
01558 UnScaleByZoomLower(left, vp->zoom) + vp->left,
01559 UnScaleByZoomLower(top, vp->zoom) + vp->top,
01560 UnScaleByZoom(right, vp->zoom) + vp->left + 1,
01561 UnScaleByZoom(bottom, vp->zoom) + vp->top + 1
01562 );
01563 }
01564
01573 void MarkAllViewportsDirty(int left, int top, int right, int bottom)
01574 {
01575 Window *w;
01576 FOR_ALL_WINDOWS_FROM_BACK(w) {
01577 ViewPort *vp = w->viewport;
01578 if (vp != NULL) {
01579 assert(vp->width != 0);
01580 MarkViewportDirty(vp, left, top, right, bottom);
01581 }
01582 }
01583 }
01584
01585 void MarkTileDirtyByTile(TileIndex tile)
01586 {
01587 Point pt = RemapCoords(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE, GetTileZ(tile));
01588 MarkAllViewportsDirty(
01589 pt.x - 31,
01590 pt.y - 122,
01591 pt.x - 31 + 67,
01592 pt.y - 122 + 154
01593 );
01594 }
01595
01603 static void SetSelectionTilesDirty()
01604 {
01605 int x_start = _thd.pos.x;
01606 int y_start = _thd.pos.y;
01607
01608 int x_size = _thd.size.x;
01609 int y_size = _thd.size.y;
01610
01611 if (_thd.outersize.x != 0) {
01612 x_size += _thd.outersize.x;
01613 x_start += _thd.offs.x;
01614 y_size += _thd.outersize.y;
01615 y_start += _thd.offs.y;
01616 }
01617
01618 x_size -= TILE_SIZE;
01619 y_size -= TILE_SIZE;
01620
01621 assert(x_size >= 0);
01622 assert(y_size >= 0);
01623
01624 int x_end = Clamp(x_start + x_size, 0, MapSizeX() * TILE_SIZE - TILE_SIZE);
01625 int y_end = Clamp(y_start + y_size, 0, MapSizeY() * TILE_SIZE - TILE_SIZE);
01626
01627 x_start = Clamp(x_start, 0, MapSizeX() * TILE_SIZE - TILE_SIZE);
01628 y_start = Clamp(y_start, 0, MapSizeY() * TILE_SIZE - TILE_SIZE);
01629
01630
01631 assert((x_end | y_end | x_start | y_start) % TILE_SIZE == 0);
01632
01633
01634
01635
01636
01637
01638
01639
01640
01641
01642
01643
01644
01645
01646
01647
01648
01649
01650
01651 int top_x = x_end;
01652 int top_y = y_start;
01653 int bot_x = top_x;
01654 int bot_y = top_y;
01655
01656 do {
01657 Point top = RemapCoords2(top_x, top_y);
01658 Point bot = RemapCoords2(bot_x + TILE_SIZE - 1, bot_y + TILE_SIZE - 1);
01659
01660
01661
01662
01663 int l = top.x - (TILE_PIXELS - 2);
01664 int t = top.y;
01665 int r = top.x + (TILE_PIXELS - 2);
01666 int b = bot.y;
01667
01668 static const int OVERLAY_WIDTH = 4;
01669
01670
01671 MarkAllViewportsDirty(l - OVERLAY_WIDTH, t - OVERLAY_WIDTH - TILE_HEIGHT, r + OVERLAY_WIDTH, b + OVERLAY_WIDTH);
01672
01673
01674 if (top_x != x_start) {
01675 top_x -= TILE_SIZE;
01676 } else {
01677 top_y += TILE_SIZE;
01678 }
01679
01680
01681 if (bot_y != y_end) {
01682 bot_y += TILE_SIZE;
01683 } else {
01684 bot_x -= TILE_SIZE;
01685 }
01686 } while (bot_x >= top_x);
01687 }
01688
01689
01690 void SetSelectionRed(bool b)
01691 {
01692 _thd.make_square_red = b;
01693 SetSelectionTilesDirty();
01694 }
01695
01704 static bool CheckClickOnViewportSign(const ViewPort *vp, int x, int y, const ViewportSign *sign)
01705 {
01706 bool small = (vp->zoom >= ZOOM_LVL_OUT_4X);
01707 int sign_half_width = ScaleByZoom((small ? sign->width_small : sign->width_normal) / 2, vp->zoom);
01708 int sign_height = ScaleByZoom(VPSM_TOP + (small ? FONT_HEIGHT_SMALL : FONT_HEIGHT_NORMAL) + VPSM_BOTTOM, vp->zoom);
01709
01710 x = ScaleByZoom(x - vp->left, vp->zoom) + vp->virtual_left;
01711 y = ScaleByZoom(y - vp->top, vp->zoom) + vp->virtual_top;
01712
01713 return
01714 y >= sign->top &&
01715 y < sign->top + sign_height &&
01716 x >= sign->center - sign_half_width &&
01717 x < sign->center + sign_half_width;
01718 }
01719
01720 static bool CheckClickOnTown(const ViewPort *vp, int x, int y)
01721 {
01722 if (!HasBit(_display_opt, DO_SHOW_TOWN_NAMES)) return false;
01723
01724 const Town *t;
01725 FOR_ALL_TOWNS(t) {
01726 if (CheckClickOnViewportSign(vp, x, y, &t->sign)) {
01727 ShowTownViewWindow(t->index);
01728 return true;
01729 }
01730 }
01731
01732 return false;
01733 }
01734
01735 static bool CheckClickOnStation(const ViewPort *vp, int x, int y)
01736 {
01737 if (!(HasBit(_display_opt, DO_SHOW_STATION_NAMES) || HasBit(_display_opt, DO_SHOW_WAYPOINT_NAMES)) || IsInvisibilitySet(TO_SIGNS)) return false;
01738
01739 const BaseStation *st;
01740 FOR_ALL_BASE_STATIONS(st) {
01741
01742 bool is_station = Station::IsExpected(st);
01743
01744
01745 if (!HasBit(_display_opt, is_station ? DO_SHOW_STATION_NAMES : DO_SHOW_WAYPOINT_NAMES)) continue;
01746
01747 if (CheckClickOnViewportSign(vp, x, y, &st->sign)) {
01748 if (is_station) {
01749 ShowStationViewWindow(st->index);
01750 } else {
01751 ShowWaypointWindow(Waypoint::From(st));
01752 }
01753 return true;
01754 }
01755 }
01756
01757 return false;
01758 }
01759
01760
01761 static bool CheckClickOnSign(const ViewPort *vp, int x, int y)
01762 {
01763
01764 if (!HasBit(_display_opt, DO_SHOW_SIGNS) || IsInvisibilitySet(TO_SIGNS) || _current_company == COMPANY_SPECTATOR) return false;
01765
01766 const Sign *si;
01767 FOR_ALL_SIGNS(si) {
01768 if (CheckClickOnViewportSign(vp, x, y, &si->sign)) {
01769 HandleClickOnSign(si);
01770 return true;
01771 }
01772 }
01773
01774 return false;
01775 }
01776
01777
01778 static bool CheckClickOnLandscape(const ViewPort *vp, int x, int y)
01779 {
01780 Point pt = TranslateXYToTileCoord(vp, x, y);
01781
01782 if (pt.x != -1) return ClickTile(TileVirtXY(pt.x, pt.y));
01783 return true;
01784 }
01785
01786
01787 bool HandleViewportClicked(const ViewPort *vp, int x, int y)
01788 {
01789 const Vehicle *v;
01790
01791 if (CheckClickOnTown(vp, x, y)) return true;
01792 if (CheckClickOnStation(vp, x, y)) return true;
01793 if (CheckClickOnSign(vp, x, y)) return true;
01794 CheckClickOnLandscape(vp, x, y);
01795
01796 v = CheckClickOnVehicle(vp, x, y);
01797 if (v != NULL) {
01798 DEBUG(misc, 2, "Vehicle %d (index %d) at %p", v->unitnumber, v->index, v);
01799 if (IsCompanyBuildableVehicleType(v)) ShowVehicleViewWindow(v->First());
01800 return true;
01801 }
01802 return CheckClickOnLandscape(vp, x, y);
01803 }
01804
01805 Vehicle *CheckMouseOverVehicle()
01806 {
01807 const Window *w;
01808 const ViewPort *vp;
01809
01810 int x = _cursor.pos.x;
01811 int y = _cursor.pos.y;
01812
01813 w = FindWindowFromPt(x, y);
01814 if (w == NULL) return NULL;
01815
01816 vp = IsPtInWindowViewport(w, x, y);
01817 return (vp != NULL) ? CheckClickOnVehicle(vp, x, y) : NULL;
01818 }
01819
01820
01821
01822 void PlaceObject()
01823 {
01824 Point pt;
01825 Window *w;
01826
01827 pt = GetTileBelowCursor();
01828 if (pt.x == -1) return;
01829
01830 if (_thd.place_mode == HT_POINT) {
01831 pt.x += 8;
01832 pt.y += 8;
01833 }
01834
01835 _tile_fract_coords.x = pt.x & TILE_UNIT_MASK;
01836 _tile_fract_coords.y = pt.y & TILE_UNIT_MASK;
01837
01838 w = GetCallbackWnd();
01839 if (w != NULL) w->OnPlaceObject(pt, TileVirtXY(pt.x, pt.y));
01840 }
01841
01842
01851 bool ScrollWindowTo(int x, int y, int z, Window *w, bool instant)
01852 {
01853
01854 if (z == -1) z = GetSlopeZ(Clamp(x, 0, MapSizeX() * TILE_SIZE - 1), Clamp(y, 0, MapSizeY() * TILE_SIZE - 1));
01855
01856 Point pt = MapXYZToViewport(w->viewport, x, y, z);
01857 w->viewport->follow_vehicle = INVALID_VEHICLE;
01858
01859 if (w->viewport->dest_scrollpos_x == pt.x && w->viewport->dest_scrollpos_y == pt.y)
01860 return false;
01861
01862 if (instant) {
01863 w->viewport->scrollpos_x = pt.x;
01864 w->viewport->scrollpos_y = pt.y;
01865 }
01866
01867 w->viewport->dest_scrollpos_x = pt.x;
01868 w->viewport->dest_scrollpos_y = pt.y;
01869 return true;
01870 }
01871
01872 bool ScrollMainWindowToTile(TileIndex tile, bool instant)
01873 {
01874 return ScrollMainWindowTo(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2, -1, instant);
01875 }
01876
01877 void SetRedErrorSquare(TileIndex tile)
01878 {
01879 TileIndex old;
01880
01881 old = _thd.redsq;
01882 _thd.redsq = tile;
01883
01884 if (tile != old) {
01885 if (tile != INVALID_TILE) MarkTileDirtyByTile(tile);
01886 if (old != INVALID_TILE) MarkTileDirtyByTile(old);
01887 }
01888 }
01889
01894 void SetTileSelectSize(int w, int h)
01895 {
01896 _thd.new_size.x = w * TILE_SIZE;
01897 _thd.new_size.y = h * TILE_SIZE;
01898 _thd.new_outersize.x = 0;
01899 _thd.new_outersize.y = 0;
01900 }
01901
01902 void SetTileSelectBigSize(int ox, int oy, int sx, int sy)
01903 {
01904 _thd.offs.x = ox * TILE_SIZE;
01905 _thd.offs.y = oy * TILE_SIZE;
01906 _thd.new_outersize.x = sx * TILE_SIZE;
01907 _thd.new_outersize.y = sy * TILE_SIZE;
01908 }
01909
01911 static HighLightStyle GetAutorailHT(int x, int y)
01912 {
01913 return HT_RAIL | _autorail_piece[x & TILE_UNIT_MASK][y & TILE_UNIT_MASK];
01914 }
01915
01923 void UpdateTileSelection()
01924 {
01925 int x1;
01926 int y1;
01927
01928 _thd.new_drawstyle = HT_NONE;
01929
01930 if (_thd.place_mode == HT_SPECIAL) {
01931 x1 = _thd.selend.x;
01932 y1 = _thd.selend.y;
01933 if (x1 != -1) {
01934 int x2 = _thd.selstart.x & ~TILE_UNIT_MASK;
01935 int y2 = _thd.selstart.y & ~TILE_UNIT_MASK;
01936 x1 &= ~TILE_UNIT_MASK;
01937 y1 &= ~TILE_UNIT_MASK;
01938
01939 if (x1 >= x2) Swap(x1, x2);
01940 if (y1 >= y2) Swap(y1, y2);
01941 _thd.new_pos.x = x1;
01942 _thd.new_pos.y = y1;
01943 _thd.new_size.x = x2 - x1 + TILE_SIZE;
01944 _thd.new_size.y = y2 - y1 + TILE_SIZE;
01945 _thd.new_drawstyle = _thd.next_drawstyle;
01946 }
01947 } else if (_thd.place_mode != HT_NONE) {
01948 Point pt = GetTileBelowCursor();
01949 x1 = pt.x;
01950 y1 = pt.y;
01951 if (x1 != -1) {
01952 switch (_thd.place_mode & HT_DRAG_MASK) {
01953 case HT_RECT:
01954 _thd.new_drawstyle = HT_RECT;
01955 break;
01956 case HT_POINT:
01957 _thd.new_drawstyle = HT_POINT;
01958 x1 += TILE_SIZE / 2;
01959 y1 += TILE_SIZE / 2;
01960 break;
01961 case HT_RAIL:
01962
01963 _thd.new_drawstyle = GetAutorailHT(pt.x, pt.y);
01964 break;
01965 case HT_LINE:
01966 switch (_thd.place_mode & HT_DIR_MASK) {
01967 case HT_DIR_X: _thd.new_drawstyle = HT_LINE | HT_DIR_X; break;
01968 case HT_DIR_Y: _thd.new_drawstyle = HT_LINE | HT_DIR_Y; break;
01969
01970 case HT_DIR_HU:
01971 case HT_DIR_HL:
01972 _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;
01973 break;
01974
01975 case HT_DIR_VL:
01976 case HT_DIR_VR:
01977 _thd.new_drawstyle = (pt.x & TILE_UNIT_MASK) > (pt.y & TILE_UNIT_MASK) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
01978 break;
01979
01980 default: NOT_REACHED();
01981 }
01982 _thd.selstart.x = x1 & ~TILE_UNIT_MASK;
01983 _thd.selstart.y = y1 & ~TILE_UNIT_MASK;
01984 break;
01985 default:
01986 NOT_REACHED();
01987 break;
01988 }
01989 _thd.new_pos.x = x1 & ~TILE_UNIT_MASK;
01990 _thd.new_pos.y = y1 & ~TILE_UNIT_MASK;
01991 }
01992 }
01993
01994
01995 if (_thd.drawstyle != _thd.new_drawstyle ||
01996 _thd.pos.x != _thd.new_pos.x || _thd.pos.y != _thd.new_pos.y ||
01997 _thd.size.x != _thd.new_size.x || _thd.size.y != _thd.new_size.y ||
01998 _thd.outersize.x != _thd.new_outersize.x ||
01999 _thd.outersize.y != _thd.new_outersize.y) {
02000
02001 if (_thd.drawstyle) SetSelectionTilesDirty();
02002
02003 _thd.drawstyle = _thd.new_drawstyle;
02004 _thd.pos = _thd.new_pos;
02005 _thd.size = _thd.new_size;
02006 _thd.outersize = _thd.new_outersize;
02007 _thd.dirty = 0xff;
02008
02009
02010 if (_thd.new_drawstyle) SetSelectionTilesDirty();
02011 }
02012 }
02013
02019 static inline void ShowMeasurementTooltips(StringID str, uint paramcount, const uint64 params[])
02020 {
02021 if (!_settings_client.gui.measure_tooltip) return;
02022 GuiShowTooltips(str, paramcount, params, true);
02023 }
02024
02026 void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process)
02027 {
02028 _thd.select_method = method;
02029 _thd.select_proc = process;
02030 _thd.selend.x = TileX(tile) * TILE_SIZE;
02031 _thd.selstart.x = TileX(tile) * TILE_SIZE;
02032 _thd.selend.y = TileY(tile) * TILE_SIZE;
02033 _thd.selstart.y = TileY(tile) * TILE_SIZE;
02034
02035
02036
02037
02038 if (method == VPM_X_OR_Y || method == VPM_FIX_X || method == VPM_FIX_Y) {
02039 _thd.selend.x += TILE_SIZE / 2;
02040 _thd.selend.y += TILE_SIZE / 2;
02041 _thd.selstart.x += TILE_SIZE / 2;
02042 _thd.selstart.y += TILE_SIZE / 2;
02043 }
02044
02045 if (_thd.place_mode == HT_RECT) {
02046 _thd.place_mode = HT_SPECIAL;
02047 _thd.next_drawstyle = HT_RECT;
02048 } else if (_thd.place_mode & (HT_RAIL | HT_LINE)) {
02049 _thd.place_mode = HT_SPECIAL;
02050 _thd.next_drawstyle = _thd.drawstyle;
02051 } else {
02052 _thd.place_mode = HT_SPECIAL;
02053 _thd.next_drawstyle = HT_POINT;
02054 }
02055 _special_mouse_mode = WSM_SIZING;
02056 }
02057
02058 void VpSetPlaceSizingLimit(int limit)
02059 {
02060 _thd.sizelimit = limit;
02061 }
02062
02067 void VpSetPresizeRange(TileIndex from, TileIndex to)
02068 {
02069 uint64 distance = DistanceManhattan(from, to) + 1;
02070
02071 _thd.selend.x = TileX(to) * TILE_SIZE;
02072 _thd.selend.y = TileY(to) * TILE_SIZE;
02073 _thd.selstart.x = TileX(from) * TILE_SIZE;
02074 _thd.selstart.y = TileY(from) * TILE_SIZE;
02075 _thd.next_drawstyle = HT_RECT;
02076
02077
02078 if (distance > 1) ShowMeasurementTooltips(STR_MEASURE_LENGTH, 1, &distance);
02079 }
02080
02081 static void VpStartPreSizing()
02082 {
02083 _thd.selend.x = -1;
02084 _special_mouse_mode = WSM_PRESIZE;
02085 }
02086
02089 static HighLightStyle Check2x1AutoRail(int mode)
02090 {
02091 int fxpy = _tile_fract_coords.x + _tile_fract_coords.y;
02092 int sxpy = (_thd.selend.x & TILE_UNIT_MASK) + (_thd.selend.y & TILE_UNIT_MASK);
02093 int fxmy = _tile_fract_coords.x - _tile_fract_coords.y;
02094 int sxmy = (_thd.selend.x & TILE_UNIT_MASK) - (_thd.selend.y & TILE_UNIT_MASK);
02095
02096 switch (mode) {
02097 default: NOT_REACHED();
02098 case 0:
02099 if (fxpy >= 20 && sxpy <= 12) return HT_DIR_HL;
02100 if (fxmy < -3 && sxmy > 3) return HT_DIR_VR;
02101 return HT_DIR_Y;
02102
02103 case 1:
02104 if (fxmy > 3 && sxmy < -3) return HT_DIR_VL;
02105 if (fxpy <= 12 && sxpy >= 20) return HT_DIR_HU;
02106 return HT_DIR_Y;
02107
02108 case 2:
02109 if (fxmy > 3 && sxmy < -3) return HT_DIR_VL;
02110 if (fxpy >= 20 && sxpy <= 12) return HT_DIR_HL;
02111 return HT_DIR_X;
02112
02113 case 3:
02114 if (fxmy < -3 && sxmy > 3) return HT_DIR_VR;
02115 if (fxpy <= 12 && sxpy >= 20) return HT_DIR_HU;
02116 return HT_DIR_X;
02117 }
02118 }
02119
02131 static bool SwapDirection(HighLightStyle style, TileIndex start_tile, TileIndex end_tile)
02132 {
02133 uint start_x = TileX(start_tile);
02134 uint start_y = TileY(start_tile);
02135 uint end_x = TileX(end_tile);
02136 uint end_y = TileY(end_tile);
02137
02138 switch (style & HT_DRAG_MASK) {
02139 case HT_RAIL:
02140 case HT_LINE: return (end_x > start_x || (end_x == start_x && end_y > start_y));
02141
02142 case HT_RECT:
02143 case HT_POINT: return (end_x != start_x && end_y < start_y);
02144 default: NOT_REACHED();
02145 }
02146
02147 return false;
02148 }
02149
02164 static int CalcHeightdiff(HighLightStyle style, uint distance, TileIndex start_tile, TileIndex end_tile)
02165 {
02166 bool swap = SwapDirection(style, start_tile, end_tile);
02167 byte style_t;
02168 uint h0, h1, ht;
02169
02170 if (start_tile == end_tile) return 0;
02171 if (swap) Swap(start_tile, end_tile);
02172
02173 switch (style & HT_DRAG_MASK) {
02174 case HT_RECT: {
02175 static const TileIndexDiffC heightdiff_area_by_dir[] = {
02176 {1, 0}, {0, 0},
02177 {0, 1}, {1, 1}
02178 };
02179
02180
02181
02182 style_t = (byte)(TileX(end_tile) > TileX(start_tile));
02183 start_tile = TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_area_by_dir[style_t]));
02184 end_tile = TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_area_by_dir[2 + style_t]));
02185 }
02186
02187 case HT_POINT:
02188 h0 = TileHeight(start_tile);
02189 h1 = TileHeight(end_tile);
02190 break;
02191 default: {
02192 static const HighLightStyle flip_style_direction[] = {
02193 HT_DIR_X, HT_DIR_Y, HT_DIR_HL, HT_DIR_HU, HT_DIR_VR, HT_DIR_VL
02194 };
02195 static const TileIndexDiffC heightdiff_line_by_dir[] = {
02196 {1, 0}, {1, 1}, {0, 1}, {1, 1},
02197 {1, 0}, {0, 0}, {1, 0}, {1, 1},
02198 {1, 0}, {1, 1}, {0, 1}, {1, 1},
02199
02200 {0, 1}, {0, 0}, {1, 0}, {0, 0},
02201 {0, 1}, {0, 0}, {1, 1}, {0, 1},
02202 {1, 0}, {0, 0}, {0, 0}, {0, 1},
02203 };
02204
02205 distance %= 2;
02206 style &= HT_DIR_MASK;
02207
02208
02209
02210
02211
02212 if (swap && distance == 0) style = flip_style_direction[style];
02213
02214
02215 style_t = style * 2;
02216 assert(style_t < lengthof(heightdiff_line_by_dir) - 13);
02217 h0 = TileHeight(TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_line_by_dir[style_t])));
02218 ht = TileHeight(TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_line_by_dir[style_t + 1])));
02219 h0 = max(h0, ht);
02220
02221
02222
02223 if (distance == 0) style_t = flip_style_direction[style] * 2;
02224 assert(style_t < lengthof(heightdiff_line_by_dir) - 13);
02225 h1 = TileHeight(TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_line_by_dir[12 + style_t])));
02226 ht = TileHeight(TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_line_by_dir[12 + style_t + 1])));
02227 h1 = max(h1, ht);
02228 } break;
02229 }
02230
02231 if (swap) Swap(h0, h1);
02232
02233 return (int)(h1 - h0) * 50;
02234 }
02235
02236 static const StringID measure_strings_length[] = {STR_NULL, STR_MEASURE_LENGTH, STR_MEASURE_LENGTH_HEIGHTDIFF};
02237
02244 static void CheckUnderflow(int &test, int &other, int mult)
02245 {
02246 if (test >= 0) return;
02247
02248 other += mult * test;
02249 test = 0;
02250 }
02251
02259 static void CheckOverflow(int &test, int &other, int max, int mult)
02260 {
02261 if (test <= max) return;
02262
02263 other += mult * (test - max);
02264 test = max;
02265 }
02266
02268 static void CalcRaildirsDrawstyle(TileHighlightData *thd, int x, int y, int method)
02269 {
02270 HighLightStyle b;
02271
02272 int dx = thd->selstart.x - (thd->selend.x & ~TILE_UNIT_MASK);
02273 int dy = thd->selstart.y - (thd->selend.y & ~TILE_UNIT_MASK);
02274 uint w = abs(dx) + TILE_SIZE;
02275 uint h = abs(dy) + TILE_SIZE;
02276
02277 if (method & ~(VPM_RAILDIRS | VPM_SIGNALDIRS)) {
02278
02279 method &= ~(VPM_RAILDIRS | VPM_SIGNALDIRS);
02280 int raw_dx = thd->selstart.x - thd->selend.x;
02281 int raw_dy = thd->selstart.y - thd->selend.y;
02282 switch (method) {
02283 case VPM_FIX_X:
02284 b = HT_LINE | HT_DIR_Y;
02285 x = thd->selstart.x;
02286 break;
02287
02288 case VPM_FIX_Y:
02289 b = HT_LINE | HT_DIR_X;
02290 y = thd->selstart.y;
02291 break;
02292
02293 case VPM_FIX_HORIZONTAL:
02294 if (dx == -dy) {
02295
02296
02297 b = (x & TILE_UNIT_MASK) + (y & TILE_UNIT_MASK) >= TILE_SIZE ? HT_LINE | HT_DIR_HL : HT_LINE | HT_DIR_HU;
02298 } else {
02299
02300
02301 b = dx + dy >= TILE_SIZE ? HT_LINE | HT_DIR_HU : HT_LINE | HT_DIR_HL;
02302
02303
02304
02305
02306 int offset = (raw_dx - raw_dy) / 2;
02307 x = thd->selstart.x - (offset & ~TILE_UNIT_MASK);
02308 y = thd->selstart.y + (offset & ~TILE_UNIT_MASK);
02309
02310
02311 if ((offset & TILE_UNIT_MASK) > (TILE_SIZE / 2)) {
02312 if (dx + dy >= TILE_SIZE) {
02313 x += (dx + dy < 0) ? TILE_SIZE : -TILE_SIZE;
02314 } else {
02315 y += (dx + dy < 0) ? TILE_SIZE : -TILE_SIZE;
02316 }
02317 }
02318
02319
02320 CheckUnderflow(x, y, 1);
02321 CheckUnderflow(y, x, 1);
02322 CheckOverflow(x, y, (MapMaxX() - 1) * TILE_SIZE, 1);
02323 CheckOverflow(y, x, (MapMaxY() - 1) * TILE_SIZE, 1);
02324 assert(x >= 0 && y >= 0 && x <= (int)MapMaxX() * TILE_SIZE && y <= (int)MapMaxY() * TILE_SIZE);
02325 }
02326 break;
02327
02328 case VPM_FIX_VERTICAL:
02329 if (dx == dy) {
02330
02331
02332 b = (x & TILE_UNIT_MASK) > (y & TILE_UNIT_MASK) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
02333 } else {
02334
02335
02336 b = dx < dy ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
02337
02338
02339
02340
02341 int offset = (raw_dx + raw_dy + TILE_SIZE) / 2;
02342 x = thd->selstart.x - (offset & ~TILE_UNIT_MASK);
02343 y = thd->selstart.y - (offset & ~TILE_UNIT_MASK);
02344
02345
02346 if ((offset & TILE_UNIT_MASK) > (TILE_SIZE / 2)) {
02347 if (dx - dy < 0) {
02348 y += (dx > dy) ? TILE_SIZE : -TILE_SIZE;
02349 } else {
02350 x += (dx < dy) ? TILE_SIZE : -TILE_SIZE;
02351 }
02352 }
02353
02354
02355 CheckUnderflow(x, y, -1);
02356 CheckUnderflow(y, x, -1);
02357 CheckOverflow(x, y, (MapMaxX() - 1) * TILE_SIZE, -1);
02358 CheckOverflow(y, x, (MapMaxY() - 1) * TILE_SIZE, -1);
02359 assert(x >= 0 && y >= 0 && x <= (int)MapMaxX() * TILE_SIZE && y <= (int)MapMaxY() * TILE_SIZE);
02360 }
02361 break;
02362
02363 default:
02364 NOT_REACHED();
02365 }
02366 } else if (TileVirtXY(thd->selstart.x, thd->selstart.y) == TileVirtXY(x, y)) {
02367 if (method & VPM_RAILDIRS) {
02368 b = GetAutorailHT(x, y);
02369 } else {
02370 b = HT_RECT;
02371 }
02372 } else if (h == TILE_SIZE) {
02373 if (dx == TILE_SIZE) {
02374 b = (Check2x1AutoRail(3)) | HT_LINE;
02375 } else if (dx == -TILE_SIZE) {
02376 b = (Check2x1AutoRail(2)) | HT_LINE;
02377 } else {
02378 b = HT_LINE | HT_DIR_X;
02379 }
02380 y = thd->selstart.y;
02381 } else if (w == TILE_SIZE) {
02382 if (dy == TILE_SIZE) {
02383 b = (Check2x1AutoRail(1)) | HT_LINE;
02384 } else if (dy == -TILE_SIZE) {
02385 b = (Check2x1AutoRail(0)) | HT_LINE;
02386 } else {
02387 b = HT_LINE | HT_DIR_Y;
02388 }
02389 x = thd->selstart.x;
02390 } else if (w > h * 2) {
02391 b = HT_LINE | HT_DIR_X;
02392 y = thd->selstart.y;
02393 } else if (h > w * 2) {
02394 b = HT_LINE | HT_DIR_Y;
02395 x = thd->selstart.x;
02396 } else {
02397 int d = w - h;
02398 thd->selend.x = thd->selend.x & ~TILE_UNIT_MASK;
02399 thd->selend.y = thd->selend.y & ~TILE_UNIT_MASK;
02400
02401
02402 if (x > thd->selstart.x) {
02403 if (y > thd->selstart.y) {
02404
02405 if (d == 0) {
02406 b = (x & TILE_UNIT_MASK) > (y & TILE_UNIT_MASK) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
02407 } else if (d >= 0) {
02408 x = thd->selstart.x + h;
02409 b = HT_LINE | HT_DIR_VL;
02410 } else {
02411 y = thd->selstart.y + w;
02412 b = HT_LINE | HT_DIR_VR;
02413 }
02414 } else {
02415
02416 if (d == 0) {
02417 b = (x & TILE_UNIT_MASK) + (y & TILE_UNIT_MASK) >= TILE_SIZE ? HT_LINE | HT_DIR_HL : HT_LINE | HT_DIR_HU;
02418 } else if (d >= 0) {
02419 x = thd->selstart.x + h;
02420 b = HT_LINE | HT_DIR_HL;
02421 } else {
02422 y = thd->selstart.y - w;
02423 b = HT_LINE | HT_DIR_HU;
02424 }
02425 }
02426 } else {
02427 if (y > thd->selstart.y) {
02428
02429 if (d == 0) {
02430 b = (x & TILE_UNIT_MASK) + (y & TILE_UNIT_MASK) >= TILE_SIZE ? HT_LINE | HT_DIR_HL : HT_LINE | HT_DIR_HU;
02431 } else if (d >= 0) {
02432 x = thd->selstart.x - h;
02433 b = HT_LINE | HT_DIR_HU;
02434 } else {
02435 y = thd->selstart.y + w;
02436 b = HT_LINE | HT_DIR_HL;
02437 }
02438 } else {
02439
02440 if (d == 0) {
02441 b = (x & TILE_UNIT_MASK) > (y & TILE_UNIT_MASK) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
02442 } else if (d >= 0) {
02443 x = thd->selstart.x - h;
02444 b = HT_LINE | HT_DIR_VR;
02445 } else {
02446 y = thd->selstart.y - w;
02447 b = HT_LINE | HT_DIR_VL;
02448 }
02449 }
02450 }
02451 }
02452
02453 if (_settings_client.gui.measure_tooltip) {
02454 TileIndex t0 = TileVirtXY(thd->selstart.x, thd->selstart.y);
02455 TileIndex t1 = TileVirtXY(x, y);
02456 uint distance = DistanceManhattan(t0, t1) + 1;
02457 byte index = 0;
02458 uint64 params[2];
02459
02460 if (distance != 1) {
02461 int heightdiff = CalcHeightdiff(b, distance, t0, t1);
02462
02463
02464
02465 if ((b & HT_DIR_MASK) != HT_DIR_X && (b & HT_DIR_MASK) != HT_DIR_Y) {
02466 distance = (distance + 1) / 2;
02467 }
02468
02469 params[index++] = distance;
02470 if (heightdiff != 0) params[index++] = heightdiff;
02471 }
02472
02473 ShowMeasurementTooltips(measure_strings_length[index], index, params);
02474 }
02475
02476 thd->selend.x = x;
02477 thd->selend.y = y;
02478 thd->next_drawstyle = b;
02479 }
02480
02487 void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method)
02488 {
02489 int sx, sy;
02490 HighLightStyle style;
02491
02492 if (x == -1) {
02493 _thd.selend.x = -1;
02494 return;
02495 }
02496
02497
02498 if (method & (VPM_RAILDIRS | VPM_SIGNALDIRS)) {
02499 _thd.selend.x = x;
02500 _thd.selend.y = y;
02501 CalcRaildirsDrawstyle(&_thd, x, y, method);
02502 return;
02503 }
02504
02505
02506 if (_thd.next_drawstyle == HT_POINT) {
02507 x += TILE_SIZE / 2;
02508 y += TILE_SIZE / 2;
02509 }
02510
02511 sx = _thd.selstart.x;
02512 sy = _thd.selstart.y;
02513
02514 switch (method) {
02515 case VPM_X_OR_Y:
02516 if (abs(sy - y) < abs(sx - x)) {
02517 y = sy;
02518 style = HT_DIR_X;
02519 } else {
02520 x = sx;
02521 style = HT_DIR_Y;
02522 }
02523 goto calc_heightdiff_single_direction;
02524 case VPM_FIX_X:
02525 x = sx;
02526 style = HT_DIR_Y;
02527 goto calc_heightdiff_single_direction;
02528 case VPM_FIX_Y:
02529 y = sy;
02530 style = HT_DIR_X;
02531
02532 calc_heightdiff_single_direction:;
02533 if (_settings_client.gui.measure_tooltip) {
02534 TileIndex t0 = TileVirtXY(sx, sy);
02535 TileIndex t1 = TileVirtXY(x, y);
02536 uint distance = DistanceManhattan(t0, t1) + 1;
02537 byte index = 0;
02538 uint64 params[2];
02539
02540 if (distance != 1) {
02541
02542
02543
02544
02545
02546 int heightdiff = CalcHeightdiff(HT_LINE | style, 0, t0, t1);
02547
02548 params[index++] = distance;
02549 if (heightdiff != 0) params[index++] = heightdiff;
02550 }
02551
02552 ShowMeasurementTooltips(measure_strings_length[index], index, params);
02553 } break;
02554
02555 case VPM_X_AND_Y_LIMITED: {
02556 int limit = (_thd.sizelimit - 1) * TILE_SIZE;
02557 x = sx + Clamp(x - sx, -limit, limit);
02558 y = sy + Clamp(y - sy, -limit, limit);
02559 }
02560 case VPM_X_AND_Y: {
02561 if (_settings_client.gui.measure_tooltip) {
02562 static const StringID measure_strings_area[] = {
02563 STR_NULL, STR_NULL, STR_MEASURE_AREA, STR_MEASURE_AREA_HEIGHTDIFF
02564 };
02565
02566 TileIndex t0 = TileVirtXY(sx, sy);
02567 TileIndex t1 = TileVirtXY(x, y);
02568 uint dx = Delta(TileX(t0), TileX(t1)) + 1;
02569 uint dy = Delta(TileY(t0), TileY(t1)) + 1;
02570 byte index = 0;
02571 uint64 params[3];
02572
02573
02574
02575 style = (HighLightStyle)_thd.next_drawstyle;
02576 if (style & HT_RECT) {
02577 if (dx == 1) {
02578 style = HT_LINE | HT_DIR_Y;
02579 } else if (dy == 1) {
02580 style = HT_LINE | HT_DIR_X;
02581 }
02582 }
02583
02584 if (dx != 1 || dy != 1) {
02585 int heightdiff = CalcHeightdiff(style, 0, t0, t1);
02586
02587 params[index++] = dx;
02588 params[index++] = dy;
02589 if (heightdiff != 0) params[index++] = heightdiff;
02590 }
02591
02592 ShowMeasurementTooltips(measure_strings_area[index], index, params);
02593 }
02594 break;
02595
02596 }
02597 default: NOT_REACHED();
02598 }
02599
02600 _thd.selend.x = x;
02601 _thd.selend.y = y;
02602 }
02603
02608 bool VpHandlePlaceSizingDrag()
02609 {
02610 if (_special_mouse_mode != WSM_SIZING) return true;
02611
02612
02613 Window *w = FindWindowById(_thd.window_class, _thd.window_number);
02614 if (w == NULL) {
02615 ResetObjectToPlace();
02616 return false;
02617 }
02618
02619
02620 if (_left_button_down) {
02621 w->OnPlaceDrag(_thd.select_method, _thd.select_proc, GetTileBelowCursor());
02622 return false;
02623 }
02624
02625
02626
02627 _special_mouse_mode = WSM_NONE;
02628 if (_thd.next_drawstyle == HT_RECT) {
02629 _thd.place_mode = HT_RECT;
02630 } else if (_thd.select_method & VPM_SIGNALDIRS) {
02631 _thd.place_mode = HT_RECT;
02632 } else if (_thd.select_method & VPM_RAILDIRS) {
02633 _thd.place_mode = (_thd.select_method & ~VPM_RAILDIRS) ? _thd.next_drawstyle : HT_RAIL;
02634 } else {
02635 _thd.place_mode = HT_POINT;
02636 }
02637 SetTileSelectSize(1, 1);
02638
02639 w->OnPlaceMouseUp(_thd.select_method, _thd.select_proc, _thd.selend, TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y));
02640
02641 return false;
02642 }
02643
02644 void SetObjectToPlaceWnd(CursorID icon, SpriteID pal, HighLightStyle mode, Window *w)
02645 {
02646 SetObjectToPlace(icon, pal, mode, w->window_class, w->window_number);
02647 }
02648
02649 #include "table/animcursors.h"
02650
02651 void SetObjectToPlace(CursorID icon, SpriteID pal, HighLightStyle mode, WindowClass window_class, WindowNumber window_num)
02652 {
02653
02654 if (_thd.place_mode != HT_NONE || _special_mouse_mode == WSM_DRAGDROP) {
02655 Window *w = FindWindowById(_thd.window_class, _thd.window_number);
02656 if (w != NULL) {
02657
02658
02659
02660
02661
02662 _thd.window_class = WC_INVALID;
02663 w->OnPlaceObjectAbort();
02664 }
02665 }
02666
02667 SetTileSelectSize(1, 1);
02668
02669 _thd.make_square_red = false;
02670
02671 if (mode == HT_DRAG) {
02672 mode = HT_NONE;
02673 _special_mouse_mode = WSM_DRAGDROP;
02674 } else {
02675 _special_mouse_mode = WSM_NONE;
02676 }
02677
02678 _thd.place_mode = mode;
02679 _thd.window_class = window_class;
02680 _thd.window_number = window_num;
02681
02682 if (mode == HT_SPECIAL)
02683 VpStartPreSizing();
02684
02685 if ((int)icon < 0) {
02686 SetAnimatedMouseCursor(_animcursors[~icon]);
02687 } else {
02688 SetMouseCursor(icon, pal);
02689 }
02690
02691 }
02692
02693 void ResetObjectToPlace()
02694 {
02695 SetObjectToPlace(SPR_CURSOR_MOUSE, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0);
02696 }