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