00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "clear_map.h"
00014 #include "industry.h"
00015 #include "station_map.h"
00016 #include "landscape.h"
00017 #include "window_gui.h"
00018 #include "tree_map.h"
00019 #include "viewport_func.h"
00020 #include "town.h"
00021 #include "blitter/factory.hpp"
00022 #include "tunnelbridge_map.h"
00023 #include "strings_func.h"
00024 #include "core/endian_func.hpp"
00025 #include "vehicle_base.h"
00026 #include "sound_func.h"
00027 #include "window_func.h"
00028 #include "company_base.h"
00029
00030 #include "table/strings.h"
00031
00033 enum SmallMapWindowWidgets {
00034 SM_WIDGET_CAPTION,
00035 SM_WIDGET_MAP_BORDER,
00036 SM_WIDGET_MAP,
00037 SM_WIDGET_LEGEND,
00038 SM_WIDGET_ZOOM_IN,
00039 SM_WIDGET_ZOOM_OUT,
00040 SM_WIDGET_CONTOUR,
00041 SM_WIDGET_VEHICLES,
00042 SM_WIDGET_INDUSTRIES,
00043 SM_WIDGET_ROUTES,
00044 SM_WIDGET_VEGETATION,
00045 SM_WIDGET_OWNERS,
00046 SM_WIDGET_CENTERMAP,
00047 SM_WIDGET_TOGGLETOWNNAME,
00048 SM_WIDGET_SELECT_BUTTONS,
00049 SM_WIDGET_ENABLE_ALL,
00050 SM_WIDGET_DISABLE_ALL,
00051 SM_WIDGET_SHOW_HEIGHT,
00052 };
00053
00054 static int _smallmap_industry_count;
00055 static int _smallmap_company_count;
00056
00057 static const int NUM_NO_COMPANY_ENTRIES = 4;
00058
00060 #define MK(a, b) {a, b, {INVALID_INDUSTRYTYPE}, true, false, false}
00061
00063 #define MC(height) {0, STR_TINY_BLACK_HEIGHT, {height}, true, false, false}
00064
00066 #define MO(a, b) {a, b, {INVALID_COMPANY}, true, false, false}
00067
00069 #define MOEND() {0, 0, {OWNER_NONE}, true, true, false}
00070
00072 #define MKEND() {0, STR_NULL, {INVALID_INDUSTRYTYPE}, true, true, false}
00073
00078 #define MS(a, b) {a, b, {INVALID_INDUSTRYTYPE}, true, false, true}
00079
00081 struct LegendAndColour {
00082 uint8 colour;
00083 StringID legend;
00084 union {
00085 IndustryType type;
00086 uint8 height;
00087 CompanyID company;
00088 } u;
00089 bool show_on_map;
00090 bool end;
00091 bool col_break;
00092 };
00093
00095 static LegendAndColour _legend_land_contours[] = {
00096
00097 MC(0),
00098 MC(4),
00099 MC(8),
00100 MC(12),
00101 MC(14),
00102
00103 MS(0xD7, STR_SMALLMAP_LEGENDA_ROADS),
00104 MK(0x0A, STR_SMALLMAP_LEGENDA_RAILROADS),
00105 MK(0x98, STR_SMALLMAP_LEGENDA_STATIONS_AIRPORTS_DOCKS),
00106 MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
00107 MK(0x0F, STR_SMALLMAP_LEGENDA_VEHICLES),
00108 MKEND()
00109 };
00110
00111 static const LegendAndColour _legend_vehicles[] = {
00112 MK(0xB8, STR_SMALLMAP_LEGENDA_TRAINS),
00113 MK(0xBF, STR_SMALLMAP_LEGENDA_ROAD_VEHICLES),
00114 MK(0x98, STR_SMALLMAP_LEGENDA_SHIPS),
00115 MK(0x0F, STR_SMALLMAP_LEGENDA_AIRCRAFT),
00116
00117 MS(0xD7, STR_SMALLMAP_LEGENDA_TRANSPORT_ROUTES),
00118 MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
00119 MKEND()
00120 };
00121
00122 static const LegendAndColour _legend_routes[] = {
00123 MK(0xD7, STR_SMALLMAP_LEGENDA_ROADS),
00124 MK(0x0A, STR_SMALLMAP_LEGENDA_RAILROADS),
00125 MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
00126
00127 MS(0x56, STR_SMALLMAP_LEGENDA_RAILROAD_STATION),
00128 MK(0xC2, STR_SMALLMAP_LEGENDA_TRUCK_LOADING_BAY),
00129 MK(0xBF, STR_SMALLMAP_LEGENDA_BUS_STATION),
00130 MK(0xB8, STR_SMALLMAP_LEGENDA_AIRPORT_HELIPORT),
00131 MK(0x98, STR_SMALLMAP_LEGENDA_DOCK),
00132 MKEND()
00133 };
00134
00135 static const LegendAndColour _legend_vegetation[] = {
00136 MK(0x52, STR_SMALLMAP_LEGENDA_ROUGH_LAND),
00137 MK(0x54, STR_SMALLMAP_LEGENDA_GRASS_LAND),
00138 MK(0x37, STR_SMALLMAP_LEGENDA_BARE_LAND),
00139 MK(0x25, STR_SMALLMAP_LEGENDA_FIELDS),
00140 MK(0x57, STR_SMALLMAP_LEGENDA_TREES),
00141 MK(0xD0, STR_SMALLMAP_LEGENDA_FOREST),
00142
00143 MS(0x0A, STR_SMALLMAP_LEGENDA_ROCKS),
00144 MK(0xC2, STR_SMALLMAP_LEGENDA_DESERT),
00145 MK(0x98, STR_SMALLMAP_LEGENDA_SNOW),
00146 MK(0xD7, STR_SMALLMAP_LEGENDA_TRANSPORT_ROUTES),
00147 MK(0xB5, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
00148 MKEND()
00149 };
00150
00151 static LegendAndColour _legend_land_owners[NUM_NO_COMPANY_ENTRIES + MAX_COMPANIES + 1] = {
00152 MO(0xCA, STR_SMALLMAP_LEGENDA_WATER),
00153 MO(0x00, STR_SMALLMAP_LEGENDA_NO_OWNER),
00154 MO(0xB4, STR_SMALLMAP_LEGENDA_TOWNS),
00155 MO(0x20, STR_SMALLMAP_LEGENDA_INDUSTRIES),
00156
00157 MOEND(),
00158 };
00159
00160 #undef MK
00161 #undef MC
00162 #undef MS
00163 #undef MO
00164 #undef MOEND
00165 #undef MKEND
00166
00171 static LegendAndColour _legend_from_industries[NUM_INDUSTRYTYPES + 1];
00173 static uint _industry_to_list_pos[NUM_INDUSTRYTYPES];
00175 static bool _smallmap_show_heightmap = false;
00177 static uint _company_to_list_pos[MAX_COMPANIES];
00178
00182 void BuildIndustriesLegend()
00183 {
00184 uint j = 0;
00185
00186
00187 for (uint8 i = 0; i < NUM_INDUSTRYTYPES; i++) {
00188 IndustryType ind = _sorted_industry_types[i];
00189 const IndustrySpec *indsp = GetIndustrySpec(ind);
00190 if (indsp->enabled) {
00191 _legend_from_industries[j].legend = indsp->name;
00192 _legend_from_industries[j].colour = indsp->map_colour;
00193 _legend_from_industries[j].u.type = ind;
00194 _legend_from_industries[j].show_on_map = true;
00195 _legend_from_industries[j].col_break = false;
00196 _legend_from_industries[j].end = false;
00197
00198
00199 _industry_to_list_pos[ind] = j;
00200 j++;
00201 }
00202 }
00203
00204 _legend_from_industries[j].end = true;
00205
00206
00207 _smallmap_industry_count = j;
00208 }
00209
00210 static const LegendAndColour * const _legend_table[] = {
00211 _legend_land_contours,
00212 _legend_vehicles,
00213 _legend_from_industries,
00214 _legend_routes,
00215 _legend_vegetation,
00216 _legend_land_owners,
00217 };
00218
00219 #define MKCOLOUR(x) TO_LE32X(x)
00220
00222 static const uint32 _green_map_heights[] = {
00223 MKCOLOUR(0x5A5A5A5A),
00224 MKCOLOUR(0x5A5B5A5B),
00225 MKCOLOUR(0x5B5B5B5B),
00226 MKCOLOUR(0x5B5C5B5C),
00227 MKCOLOUR(0x5C5C5C5C),
00228 MKCOLOUR(0x5C5D5C5D),
00229 MKCOLOUR(0x5D5D5D5D),
00230 MKCOLOUR(0x5D5E5D5E),
00231 MKCOLOUR(0x5E5E5E5E),
00232 MKCOLOUR(0x5E5F5E5F),
00233 MKCOLOUR(0x5F5F5F5F),
00234 MKCOLOUR(0x5F1F5F1F),
00235 MKCOLOUR(0x1F1F1F1F),
00236 MKCOLOUR(0x1F271F27),
00237 MKCOLOUR(0x27272727),
00238 MKCOLOUR(0x27272727),
00239 };
00240 assert_compile(lengthof(_green_map_heights) == MAX_TILE_HEIGHT + 1);
00241
00243 static const uint32 _dark_green_map_heights[] = {
00244 MKCOLOUR(0x60606060),
00245 MKCOLOUR(0x60616061),
00246 MKCOLOUR(0x61616161),
00247 MKCOLOUR(0x61626162),
00248 MKCOLOUR(0x62626262),
00249 MKCOLOUR(0x62636263),
00250 MKCOLOUR(0x63636363),
00251 MKCOLOUR(0x63646364),
00252 MKCOLOUR(0x64646464),
00253 MKCOLOUR(0x64656465),
00254 MKCOLOUR(0x65656565),
00255 MKCOLOUR(0x65666566),
00256 MKCOLOUR(0x66666666),
00257 MKCOLOUR(0x66676667),
00258 MKCOLOUR(0x67676767),
00259 MKCOLOUR(0x67676767),
00260 };
00261 assert_compile(lengthof(_dark_green_map_heights) == MAX_TILE_HEIGHT + 1);
00262
00264 static const uint32 _violet_map_heights[] = {
00265 MKCOLOUR(0x80808080),
00266 MKCOLOUR(0x80818081),
00267 MKCOLOUR(0x81818181),
00268 MKCOLOUR(0x81828182),
00269 MKCOLOUR(0x82828282),
00270 MKCOLOUR(0x82838283),
00271 MKCOLOUR(0x83838383),
00272 MKCOLOUR(0x83848384),
00273 MKCOLOUR(0x84848484),
00274 MKCOLOUR(0x84858485),
00275 MKCOLOUR(0x85858585),
00276 MKCOLOUR(0x85868586),
00277 MKCOLOUR(0x86868686),
00278 MKCOLOUR(0x86878687),
00279 MKCOLOUR(0x87878787),
00280 MKCOLOUR(0x87878787),
00281 };
00282 assert_compile(lengthof(_violet_map_heights) == MAX_TILE_HEIGHT + 1);
00283
00285 struct SmallMapColourScheme {
00286 const uint32 *height_colours;
00287 uint32 default_colour;
00288 };
00289
00291 static const SmallMapColourScheme _heightmap_schemes[] = {
00292 {_green_map_heights, MKCOLOUR(0x54545454)},
00293 {_dark_green_map_heights, MKCOLOUR(0x62626262)},
00294 {_violet_map_heights, MKCOLOUR(0x82828282)},
00295 };
00296
00297 void BuildLandLegend()
00298 {
00299 for (LegendAndColour *lc = _legend_land_contours; lc->legend == STR_TINY_BLACK_HEIGHT; lc++) {
00300 lc->colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[lc->u.height];
00301 }
00302 }
00303
00307 void BuildOwnerLegend()
00308 {
00309 _legend_land_owners[1].colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].default_colour;
00310
00311 int i = NUM_NO_COMPANY_ENTRIES;
00312 const Company *c;
00313 FOR_ALL_COMPANIES(c) {
00314 _legend_land_owners[i].colour = _colour_gradient[c->colour][5];
00315 _legend_land_owners[i].u.company = c->index;
00316 _legend_land_owners[i].show_on_map = true;
00317 _legend_land_owners[i].col_break = false;
00318 _legend_land_owners[i].end = false;
00319 _company_to_list_pos[c->index] = i;
00320 i++;
00321 }
00322
00323
00324 _legend_land_owners[i].end = true;
00325
00326
00327 _smallmap_company_count = i;
00328 }
00329
00330 struct AndOr {
00331 uint32 mor;
00332 uint32 mand;
00333 };
00334
00335 static inline uint32 ApplyMask(uint32 colour, const AndOr *mask)
00336 {
00337 return (colour & mask->mand) | mask->mor;
00338 }
00339
00340
00342 static const AndOr _smallmap_contours_andor[] = {
00343 {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
00344 {MKCOLOUR(0x000A0A00), MKCOLOUR(0xFF0000FF)},
00345 {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
00346 {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)},
00347 {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
00348 {MKCOLOUR(0x98989898), MKCOLOUR(0x00000000)},
00349 {MKCOLOUR(0xCACACACA), MKCOLOUR(0x00000000)},
00350 {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
00351 {MKCOLOUR(0xB5B5B5B5), MKCOLOUR(0x00000000)},
00352 {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
00353 {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)},
00354 {MKCOLOUR(0x000A0A00), MKCOLOUR(0xFF0000FF)},
00355 };
00356
00358 static const AndOr _smallmap_vehicles_andor[] = {
00359 {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
00360 {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
00361 {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
00362 {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)},
00363 {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
00364 {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
00365 {MKCOLOUR(0xCACACACA), MKCOLOUR(0x00000000)},
00366 {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
00367 {MKCOLOUR(0xB5B5B5B5), MKCOLOUR(0x00000000)},
00368 {MKCOLOUR(0x00000000), MKCOLOUR(0xFFFFFFFF)},
00369 {MKCOLOUR(0x00B5B500), MKCOLOUR(0xFF0000FF)},
00370 {MKCOLOUR(0x00D7D700), MKCOLOUR(0xFF0000FF)},
00371 };
00372
00374 static const byte _tiletype_importance[] = {
00375 2,
00376 8,
00377 7,
00378 5,
00379 2,
00380 9,
00381 2,
00382 1,
00383 6,
00384 8,
00385 2,
00386 0,
00387 };
00388
00389
00390 static inline TileType GetEffectiveTileType(TileIndex tile)
00391 {
00392 TileType t = GetTileType(tile);
00393
00394 if (t == MP_TUNNELBRIDGE) {
00395 TransportType tt = GetTunnelBridgeTransportType(tile);
00396
00397 switch (tt) {
00398 case TRANSPORT_RAIL: t = MP_RAILWAY; break;
00399 case TRANSPORT_ROAD: t = MP_ROAD; break;
00400 default: t = MP_WATER; break;
00401 }
00402 }
00403 return t;
00404 }
00405
00412 static inline uint32 GetSmallMapContoursPixels(TileIndex tile, TileType t)
00413 {
00414 const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00415 return ApplyMask(cs->height_colours[TileHeight(tile)], &_smallmap_contours_andor[t]);
00416 }
00417
00425 static inline uint32 GetSmallMapVehiclesPixels(TileIndex tile, TileType t)
00426 {
00427 const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00428 return ApplyMask(cs->default_colour, &_smallmap_vehicles_andor[t]);
00429 }
00430
00438 static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile, TileType t)
00439 {
00440 if (t == MP_INDUSTRY) {
00441
00442 if (_legend_from_industries[_industry_to_list_pos[Industry::GetByTile(tile)->type]].show_on_map) {
00443 return GetIndustrySpec(Industry::GetByTile(tile)->type)->map_colour * 0x01010101;
00444 } else {
00445
00446 t = (IsTileOnWater(tile) ? MP_WATER : MP_CLEAR);
00447 }
00448 }
00449
00450 const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00451 return ApplyMask(_smallmap_show_heightmap ? cs->height_colours[TileHeight(tile)] : cs->default_colour, &_smallmap_vehicles_andor[t]);
00452 }
00453
00461 static inline uint32 GetSmallMapRoutesPixels(TileIndex tile, TileType t)
00462 {
00463 if (t == MP_STATION) {
00464 switch (GetStationType(tile)) {
00465 case STATION_RAIL: return MKCOLOUR(0x56565656);
00466 case STATION_AIRPORT: return MKCOLOUR(0xB8B8B8B8);
00467 case STATION_TRUCK: return MKCOLOUR(0xC2C2C2C2);
00468 case STATION_BUS: return MKCOLOUR(0xBFBFBFBF);
00469 case STATION_DOCK: return MKCOLOUR(0x98989898);
00470 default: return MKCOLOUR(0xFFFFFFFF);
00471 }
00472 } else if (t == MP_RAILWAY) {
00473 AndOr andor = {
00474 GetRailTypeInfo(GetRailType(tile))->map_colour * MKCOLOUR(0x00010100),
00475 _smallmap_contours_andor[t].mand
00476 };
00477
00478 const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00479 return ApplyMask(cs->default_colour, &andor);
00480 }
00481
00482
00483 const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00484 return ApplyMask(cs->default_colour, &_smallmap_contours_andor[t]);
00485 }
00486
00487
00488 static const uint32 _vegetation_clear_bits[] = {
00489 MKCOLOUR(0x54545454),
00490 MKCOLOUR(0x52525252),
00491 MKCOLOUR(0x0A0A0A0A),
00492 MKCOLOUR(0x25252525),
00493 MKCOLOUR(0x98989898),
00494 MKCOLOUR(0xC2C2C2C2),
00495 MKCOLOUR(0x54545454),
00496 MKCOLOUR(0x54545454),
00497 };
00498
00506 static inline uint32 GetSmallMapVegetationPixels(TileIndex tile, TileType t)
00507 {
00508 switch (t) {
00509 case MP_CLEAR:
00510 return (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) < 3) ? MKCOLOUR(0x37373737) : _vegetation_clear_bits[GetClearGround(tile)];
00511
00512 case MP_INDUSTRY:
00513 return GetIndustrySpec(Industry::GetByTile(tile)->type)->check_proc == CHECK_FOREST ? MKCOLOUR(0xD0D0D0D0) : MKCOLOUR(0xB5B5B5B5);
00514
00515 case MP_TREES:
00516 if (GetTreeGround(tile) == TREE_GROUND_SNOW_DESERT || GetTreeGround(tile) == TREE_GROUND_ROUGH_SNOW) {
00517 return (_settings_game.game_creation.landscape == LT_ARCTIC) ? MKCOLOUR(0x98575798) : MKCOLOUR(0xC25757C2);
00518 }
00519 return MKCOLOUR(0x54575754);
00520
00521 default:
00522 return ApplyMask(MKCOLOUR(0x54545454), &_smallmap_vehicles_andor[t]);
00523 }
00524 }
00525
00533 static inline uint32 GetSmallMapOwnerPixels(TileIndex tile, TileType t)
00534 {
00535 Owner o;
00536
00537 switch (t) {
00538 case MP_INDUSTRY: return MKCOLOUR(0x20202020);
00539 case MP_HOUSE: return MKCOLOUR(0xB4B4B4B4);
00540 default: o = GetTileOwner(tile); break;
00541
00542
00543
00544
00545 }
00546
00547 if ((o <= MAX_COMPANIES && !_legend_land_owners[_company_to_list_pos[o]].show_on_map) || o == OWNER_NONE) {
00548 const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00549 return _smallmap_show_heightmap ? cs->height_colours[TileHeight(tile)] : cs->default_colour;
00550 } else if (o == OWNER_WATER) {
00551 return MKCOLOUR(0xCACACACA);
00552 } else if (o == OWNER_TOWN) {
00553 return MKCOLOUR(0xB4B4B4B4);
00554 }
00555
00556 return _legend_land_owners[_company_to_list_pos[o]].colour * 0x01010101;
00557 }
00558
00560 static const byte _vehicle_type_colours[6] = {
00561 184, 191, 152, 15, 215, 184
00562 };
00563
00564
00566 class SmallMapWindow : public Window {
00568 enum SmallMapType {
00569 SMT_CONTOUR,
00570 SMT_VEHICLES,
00571 SMT_INDUSTRY,
00572 SMT_ROUTES,
00573 SMT_VEGETATION,
00574 SMT_OWNER,
00575 };
00576
00578 enum ZoomLevelChange {
00579 ZLC_INITIALIZE,
00580 ZLC_ZOOM_OUT,
00581 ZLC_ZOOM_IN,
00582 };
00583
00584 static SmallMapType map_type;
00585 static bool show_towns;
00586
00587 static const uint LEGEND_BLOB_WIDTH = 8;
00588 static const uint INDUSTRY_MIN_NUMBER_OF_COLUMNS = 2;
00589 uint min_number_of_fixed_rows;
00590 uint column_width;
00591
00592 int32 scroll_x;
00593 int32 scroll_y;
00594 int32 subscroll;
00595 int zoom;
00596
00597 static const uint8 FORCE_REFRESH_PERIOD = 0x1F;
00598 uint8 refresh;
00599
00606 FORCEINLINE Point RemapTile(int tile_x, int tile_y) const
00607 {
00608 int x_offset = tile_x - this->scroll_x / (int)TILE_SIZE;
00609 int y_offset = tile_y - this->scroll_y / (int)TILE_SIZE;
00610
00611 if (this->zoom == 1) return RemapCoords(x_offset, y_offset, 0);
00612
00613
00614 if (x_offset < 0) x_offset -= this->zoom - 1;
00615 if (y_offset < 0) y_offset -= this->zoom - 1;
00616
00617 return RemapCoords(x_offset / this->zoom, y_offset / this->zoom, 0);
00618 }
00619
00630 FORCEINLINE Point PixelToTile(int px, int py, int *sub, bool add_sub = true) const
00631 {
00632 if (add_sub) px += this->subscroll;
00633
00634
00635
00636 Point pt = {((py >> 1) - (px >> 2)) * this->zoom, ((py >> 1) + (px >> 2)) * this->zoom};
00637 px &= 3;
00638
00639 if (py & 1) {
00640 if (px < 2) {
00641 pt.x += this->zoom;
00642 px += 2;
00643 } else {
00644 pt.y += this->zoom;
00645 px -= 2;
00646 }
00647 }
00648
00649 *sub = px;
00650 return pt;
00651 }
00652
00662 Point ComputeScroll(int tx, int ty, int x, int y, int *sub)
00663 {
00664 assert(x >= 0 && y >= 0);
00665
00666 int new_sub;
00667 Point tile_xy = PixelToTile(x, y, &new_sub, false);
00668 tx -= tile_xy.x;
00669 ty -= tile_xy.y;
00670
00671 Point scroll;
00672 if (new_sub == 0) {
00673 *sub = 0;
00674 scroll.x = (tx + this->zoom) * TILE_SIZE;
00675 scroll.y = (ty - this->zoom) * TILE_SIZE;
00676 } else {
00677 *sub = 4 - new_sub;
00678 scroll.x = (tx + 2 * this->zoom) * TILE_SIZE;
00679 scroll.y = (ty - 2 * this->zoom) * TILE_SIZE;
00680 }
00681 return scroll;
00682 }
00683
00690 void SetZoomLevel(ZoomLevelChange change, const Point *zoom_pt)
00691 {
00692 static const int zoomlevels[] = {1, 2, 4, 6, 8};
00693 static const int MIN_ZOOM_INDEX = 0;
00694 static const int MAX_ZOOM_INDEX = lengthof(zoomlevels) - 1;
00695
00696 int new_index, cur_index, sub;
00697 Point tile;
00698 switch (change) {
00699 case ZLC_INITIALIZE:
00700 cur_index = - 1;
00701 new_index = MIN_ZOOM_INDEX;
00702 break;
00703
00704 case ZLC_ZOOM_IN:
00705 case ZLC_ZOOM_OUT:
00706 for (cur_index = MIN_ZOOM_INDEX; cur_index <= MAX_ZOOM_INDEX; cur_index++) {
00707 if (this->zoom == zoomlevels[cur_index]) break;
00708 }
00709 assert(cur_index <= MAX_ZOOM_INDEX);
00710
00711 tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
00712 new_index = Clamp(cur_index + ((change == ZLC_ZOOM_IN) ? -1 : 1), MIN_ZOOM_INDEX, MAX_ZOOM_INDEX);
00713 break;
00714
00715 default: NOT_REACHED();
00716 }
00717
00718 if (new_index != cur_index) {
00719 this->zoom = zoomlevels[new_index];
00720 if (cur_index >= 0) {
00721 Point new_tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
00722 this->SetNewScroll(this->scroll_x + (tile.x - new_tile.x) * TILE_SIZE,
00723 this->scroll_y + (tile.y - new_tile.y) * TILE_SIZE, sub);
00724 }
00725 this->SetWidgetDisabledState(SM_WIDGET_ZOOM_IN, this->zoom == zoomlevels[MIN_ZOOM_INDEX]);
00726 this->SetWidgetDisabledState(SM_WIDGET_ZOOM_OUT, this->zoom == zoomlevels[MAX_ZOOM_INDEX]);
00727 this->SetDirty();
00728 }
00729 }
00730
00736 inline uint32 GetTileColours(const TileArea &ta) const
00737 {
00738 int importance = 0;
00739 TileIndex tile = INVALID_TILE;
00740 TileType et = MP_VOID;
00741
00742 TILE_AREA_LOOP(ti, ta) {
00743 TileType ttype = GetEffectiveTileType(ti);
00744 if (_tiletype_importance[ttype] > importance) {
00745 importance = _tiletype_importance[ttype];
00746 tile = ti;
00747 et = ttype;
00748 }
00749 }
00750
00751 switch (this->map_type) {
00752 case SMT_CONTOUR:
00753 return GetSmallMapContoursPixels(tile, et);
00754
00755 case SMT_VEHICLES:
00756 return GetSmallMapVehiclesPixels(tile, et);
00757
00758 case SMT_INDUSTRY:
00759 return GetSmallMapIndustriesPixels(tile, et);
00760
00761 case SMT_ROUTES:
00762 return GetSmallMapRoutesPixels(tile, et);
00763
00764 case SMT_VEGETATION:
00765 return GetSmallMapVegetationPixels(tile, et);
00766
00767 case SMT_OWNER:
00768 return GetSmallMapOwnerPixels(tile, et);
00769
00770 default: NOT_REACHED();
00771 }
00772 }
00773
00788 void DrawSmallMapColumn(void *dst, uint xc, uint yc, int pitch, int reps, int start_pos, int end_pos, Blitter *blitter) const
00789 {
00790 void *dst_ptr_abs_end = blitter->MoveTo(_screen.dst_ptr, 0, _screen.height);
00791 uint min_xy = _settings_game.construction.freeform_edges ? 1 : 0;
00792
00793 do {
00794
00795 if (xc >= MapMaxX() || yc >= MapMaxY()) continue;
00796
00797
00798 if (dst < _screen.dst_ptr) continue;
00799 if (dst >= dst_ptr_abs_end) continue;
00800
00801
00802 TileArea ta;
00803 if (min_xy == 1 && (xc == 0 || yc == 0)) {
00804 if (this->zoom == 1) continue;
00805
00806 ta = TileArea(TileXY(max(min_xy, xc), max(min_xy, yc)), this->zoom - (xc == 0), this->zoom - (yc == 0));
00807 } else {
00808 ta = TileArea(TileXY(xc, yc), this->zoom, this->zoom);
00809 }
00810 ta.ClampToMap();
00811
00812 uint32 val = this->GetTileColours(ta);
00813 uint8 *val8 = (uint8 *)&val;
00814 int idx = max(0, -start_pos);
00815 for (int pos = max(0, start_pos); pos < end_pos; pos++) {
00816 blitter->SetPixel(dst, idx, 0, val8[idx]);
00817 idx++;
00818 }
00819
00820 } while (xc += this->zoom, yc += this->zoom, dst = blitter->MoveTo(dst, pitch, 0), --reps != 0);
00821 }
00822
00828 void DrawVehicles(const DrawPixelInfo *dpi, Blitter *blitter) const
00829 {
00830 const Vehicle *v;
00831 FOR_ALL_VEHICLES(v) {
00832 if (v->type == VEH_EFFECT) continue;
00833 if (v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) continue;
00834
00835
00836 Point pt = this->RemapTile(v->x_pos / TILE_SIZE, v->y_pos / TILE_SIZE);
00837
00838 int y = pt.y - dpi->top;
00839 if (!IsInsideMM(y, 0, dpi->height)) continue;
00840
00841 bool skip = false;
00842 int x = pt.x - this->subscroll - 3 - dpi->left;
00843 if (x < 0) {
00844
00845
00846 if (++x != 0) continue;
00847 skip = true;
00848 } else if (x >= dpi->width - 1) {
00849
00850 if (x != dpi->width - 1) continue;
00851 skip = true;
00852 }
00853
00854
00855 byte colour = (this->map_type == SMT_VEHICLES) ? _vehicle_type_colours[v->type] : 0xF;
00856
00857
00858 blitter->SetPixel(dpi->dst_ptr, x, y, colour);
00859 if (!skip) blitter->SetPixel(dpi->dst_ptr, x + 1, y, colour);
00860 }
00861 }
00862
00867 void DrawTowns(const DrawPixelInfo *dpi) const
00868 {
00869 const Town *t;
00870 FOR_ALL_TOWNS(t) {
00871
00872 Point pt = this->RemapTile(TileX(t->xy), TileY(t->xy));
00873 int x = pt.x - this->subscroll - (t->sign.width_small >> 1);
00874 int y = pt.y;
00875
00876
00877 if (x + t->sign.width_small > dpi->left &&
00878 x < dpi->left + dpi->width &&
00879 y + FONT_HEIGHT_SMALL > dpi->top &&
00880 y < dpi->top + dpi->height) {
00881
00882 SetDParam(0, t->index);
00883 DrawString(x, x + t->sign.width_small, y, STR_SMALLMAP_TOWN);
00884 }
00885 }
00886 }
00887
00894 static inline void DrawVertMapIndicator(int x, int y, int y2)
00895 {
00896 GfxFillRect(x, y, x, y + 3, 69);
00897 GfxFillRect(x, y2 - 3, x, y2, 69);
00898 }
00899
00906 static inline void DrawHorizMapIndicator(int x, int x2, int y)
00907 {
00908 GfxFillRect(x, y, x + 3, y, 69);
00909 GfxFillRect(x2 - 3, y, x2, y, 69);
00910 }
00911
00915 void DrawMapIndicators() const
00916 {
00917
00918 const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
00919
00920 Point tile = InverseRemapCoords(vp->virtual_left, vp->virtual_top);
00921 Point tl = this->RemapTile(tile.x >> 4, tile.y >> 4);
00922 tl.x -= this->subscroll;
00923
00924 tile = InverseRemapCoords(vp->virtual_left + vp->virtual_width, vp->virtual_top + vp->virtual_height);
00925 Point br = this->RemapTile(tile.x >> 4, tile.y >> 4);
00926 br.x -= this->subscroll;
00927
00928 SmallMapWindow::DrawVertMapIndicator(tl.x, tl.y, br.y);
00929 SmallMapWindow::DrawVertMapIndicator(br.x, tl.y, br.y);
00930
00931 SmallMapWindow::DrawHorizMapIndicator(tl.x, br.x, tl.y);
00932 SmallMapWindow::DrawHorizMapIndicator(tl.x, br.x, br.y);
00933 }
00934
00946 void DrawSmallMap(DrawPixelInfo *dpi) const
00947 {
00948 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00949 DrawPixelInfo *old_dpi;
00950
00951 old_dpi = _cur_dpi;
00952 _cur_dpi = dpi;
00953
00954
00955 GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, 0);
00956
00957
00958 int dx;
00959 Point tile = this->PixelToTile(dpi->left, dpi->top, &dx);
00960 int tile_x = this->scroll_x / (int)TILE_SIZE + tile.x;
00961 int tile_y = this->scroll_y / (int)TILE_SIZE + tile.y;
00962
00963 void *ptr = blitter->MoveTo(dpi->dst_ptr, -dx - 4, 0);
00964 int x = - dx - 4;
00965 int y = 0;
00966
00967 for (;;) {
00968
00969 if (x >= -3) {
00970 if (x >= dpi->width) break;
00971
00972 int end_pos = min(dpi->width, x + 4);
00973 int reps = (dpi->height - y + 1) / 2;
00974 if (reps > 0) {
00975 this->DrawSmallMapColumn(ptr, tile_x, tile_y, dpi->pitch * 2, reps, x, end_pos, blitter);
00976 }
00977 }
00978
00979 if (y == 0) {
00980 tile_y += this->zoom;
00981 y++;
00982 ptr = blitter->MoveTo(ptr, 0, 1);
00983 } else {
00984 tile_x -= this->zoom;
00985 y--;
00986 ptr = blitter->MoveTo(ptr, 0, -1);
00987 }
00988 ptr = blitter->MoveTo(ptr, 2, 0);
00989 x += 2;
00990 }
00991
00992
00993 if (this->map_type == SMT_CONTOUR || this->map_type == SMT_VEHICLES) this->DrawVehicles(dpi, blitter);
00994
00995
00996 if (this->show_towns) this->DrawTowns(dpi);
00997
00998
00999 this->DrawMapIndicators();
01000
01001 _cur_dpi = old_dpi;
01002 }
01003
01007 void SetupWidgetData()
01008 {
01009 StringID legend_tooltip;
01010 StringID enable_all_tooltip;
01011 StringID disable_all_tooltip;
01012 int plane;
01013 switch (this->map_type) {
01014 case SMT_INDUSTRY:
01015 legend_tooltip = STR_SMALLMAP_TOOLTIP_INDUSTRY_SELECTION;
01016 enable_all_tooltip = STR_SMALLMAP_TOOLTIP_ENABLE_ALL_INDUSTRIES;
01017 disable_all_tooltip = STR_SMALLMAP_TOOLTIP_DISABLE_ALL_INDUSTRIES;
01018 plane = 0;
01019 break;
01020
01021 case SMT_OWNER:
01022 legend_tooltip = STR_SMALLMAP_TOOLTIP_COMPANY_SELECTION;
01023 enable_all_tooltip = STR_SMALLMAP_TOOLTIP_ENABLE_ALL_COMPANIES;
01024 disable_all_tooltip = STR_SMALLMAP_TOOLTIP_DISABLE_ALL_COMPANIES;
01025 plane = 0;
01026 break;
01027
01028 default:
01029 legend_tooltip = STR_NULL;
01030 enable_all_tooltip = STR_NULL;
01031 disable_all_tooltip = STR_NULL;
01032 plane = 1;
01033 break;
01034 }
01035
01036 this->GetWidget<NWidgetCore>(SM_WIDGET_LEGEND)->SetDataTip(STR_NULL, legend_tooltip);
01037 this->GetWidget<NWidgetCore>(SM_WIDGET_ENABLE_ALL)->SetDataTip(STR_SMALLMAP_ENABLE_ALL, enable_all_tooltip);
01038 this->GetWidget<NWidgetCore>(SM_WIDGET_DISABLE_ALL)->SetDataTip(STR_SMALLMAP_DISABLE_ALL, disable_all_tooltip);
01039 this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECT_BUTTONS)->SetDisplayedPlane(plane);
01040 }
01041
01042 public:
01043 uint min_number_of_columns;
01044
01045 SmallMapWindow(const WindowDesc *desc, int window_number) : Window(), refresh(FORCE_REFRESH_PERIOD)
01046 {
01047 this->InitNested(desc, window_number);
01048 this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
01049
01050 BuildLandLegend();
01051 this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_show_heightmap);
01052
01053 this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
01054
01055 this->SetupWidgetData();
01056
01057 this->SetZoomLevel(ZLC_INITIALIZE, NULL);
01058 this->SmallMapCenterOnCurrentPos();
01059 }
01060
01065 inline uint GetMinLegendWidth() const
01066 {
01067 return WD_FRAMERECT_LEFT + this->min_number_of_columns * this->column_width;
01068 }
01069
01074 inline uint GetNumberColumnsLegend(uint width) const
01075 {
01076 return width / this->column_width;
01077 }
01078
01084 uint GetLegendHeight(uint num_columns) const
01085 {
01086 uint num_rows = max(this->min_number_of_fixed_rows, CeilDiv(max(_smallmap_company_count, _smallmap_industry_count), num_columns));
01087 return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + num_rows * FONT_HEIGHT_SMALL;
01088 }
01089
01090 virtual void SetStringParameters(int widget) const
01091 {
01092 switch (widget) {
01093 case SM_WIDGET_CAPTION:
01094 SetDParam(0, STR_SMALLMAP_TYPE_CONTOURS + this->map_type);
01095 break;
01096 }
01097 }
01098
01099 virtual void OnInit()
01100 {
01101 uint min_width = 0;
01102 this->min_number_of_columns = INDUSTRY_MIN_NUMBER_OF_COLUMNS;
01103 this->min_number_of_fixed_rows = 0;
01104 for (uint i = 0; i < lengthof(_legend_table); i++) {
01105 uint height = 0;
01106 uint num_columns = 1;
01107 for (const LegendAndColour *tbl = _legend_table[i]; !tbl->end; ++tbl) {
01108 StringID str;
01109 if (i == SMT_INDUSTRY) {
01110 SetDParam(0, tbl->legend);
01111 SetDParam(1, IndustryPool::MAX_SIZE);
01112 str = STR_SMALLMAP_INDUSTRY;
01113 } else if (i == SMT_OWNER) {
01114 if (tbl->u.company != INVALID_COMPANY) {
01115 if (!Company::IsValidID(tbl->u.company)) {
01116
01117 BuildOwnerLegend();
01118 this->OnInit();
01119 return;
01120 }
01121
01122 SetDParam(0, tbl->u.company);
01123 str = STR_SMALLMAP_COMPANY;
01124 } else {
01125 str = tbl->legend;
01126 }
01127 } else {
01128 if (tbl->col_break) {
01129 this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
01130 height = 0;
01131 num_columns++;
01132 }
01133 height++;
01134 str = tbl->legend;
01135 }
01136 min_width = max(GetStringBoundingBox(str).width, min_width);
01137 }
01138 this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
01139 this->min_number_of_columns = max(this->min_number_of_columns, num_columns);
01140 }
01141
01142
01143 this->column_width = min_width + LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
01144 }
01145
01146 virtual void OnPaint()
01147 {
01148 if (this->map_type == SMT_OWNER) {
01149 for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
01150 if (tbl->u.company != INVALID_COMPANY && !Company::IsValidID(tbl->u.company)) {
01151
01152 BuildOwnerLegend();
01153 this->InvalidateData(1);
01154 break;
01155 }
01156 }
01157 }
01158
01159 this->DrawWidgets();
01160 }
01161
01162 virtual void DrawWidget(const Rect &r, int widget) const
01163 {
01164 switch (widget) {
01165 case SM_WIDGET_MAP: {
01166 DrawPixelInfo new_dpi;
01167 if (!FillDrawPixelInfo(&new_dpi, r.left + 1, r.top + 1, r.right - r.left - 1, r.bottom - r.top - 1)) return;
01168 this->DrawSmallMap(&new_dpi);
01169 break;
01170 }
01171
01172 case SM_WIDGET_LEGEND: {
01173 uint columns = this->GetNumberColumnsLegend(r.right - r.left + 1);
01174 uint number_of_rows = max((this->map_type == SMT_INDUSTRY || this->map_type == SMT_OWNER) ? CeilDiv(max(_smallmap_company_count, _smallmap_industry_count), columns) : 0, this->min_number_of_fixed_rows);
01175 bool rtl = _current_text_dir == TD_RTL;
01176 uint y_org = r.top + WD_FRAMERECT_TOP;
01177 uint x = rtl ? r.right - this->column_width - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT;
01178 uint y = y_org;
01179 uint i = 0;
01180 uint row_height = FONT_HEIGHT_SMALL;
01181
01182 uint text_left = rtl ? 0 : LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT;
01183 uint text_right = this->column_width - 1 - (rtl ? LEGEND_BLOB_WIDTH + WD_FRAMERECT_RIGHT : 0);
01184 uint blob_left = rtl ? this->column_width - 1 - LEGEND_BLOB_WIDTH : 0;
01185 uint blob_right = rtl ? this->column_width - 1 : LEGEND_BLOB_WIDTH;
01186
01187 for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
01188 if (tbl->col_break || ((this->map_type == SMT_INDUSTRY || this->map_type == SMT_OWNER) && i++ >= number_of_rows)) {
01189
01190
01191 x += rtl ? -(int)this->column_width : this->column_width;
01192 y = y_org;
01193 i = 1;
01194 }
01195
01196 if (this->map_type == SMT_INDUSTRY) {
01197
01198
01199 SetDParam(0, tbl->legend);
01200 SetDParam(1, Industry::GetIndustryTypeCount(tbl->u.type));
01201 if (!tbl->show_on_map) {
01202
01203
01204 DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_GREY);
01205 } else {
01206 DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_BLACK);
01207 GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0);
01208 }
01209 } else if (this->map_type == SMT_OWNER && tbl->u.company != INVALID_COMPANY) {
01210 SetDParam(0, tbl->u.company);
01211 if (!tbl->show_on_map) {
01212
01213
01214 DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_COMPANY, TC_GREY);
01215 } else {
01216 DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_COMPANY, TC_BLACK);
01217 GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0);
01218 }
01219 } else {
01220 if (this->map_type == SMT_CONTOUR) SetDParam(0, tbl->u.height * TILE_HEIGHT_STEP);
01221
01222
01223 GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0);
01224 DrawString(x + text_left, x + text_right, y, tbl->legend);
01225 }
01226 GfxFillRect(x + blob_left + 1, y + 2, x + blob_right - 1, y + row_height - 2, tbl->colour);
01227
01228 y += row_height;
01229 }
01230 }
01231 }
01232 }
01233
01238 void SwitchMapType(SmallMapType map_type)
01239 {
01240 this->RaiseWidget(this->map_type + SM_WIDGET_CONTOUR);
01241 this->map_type = map_type;
01242 this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
01243
01244 this->SetupWidgetData();
01245
01246 this->SetDirty();
01247 }
01248
01249 virtual void OnClick(Point pt, int widget, int click_count)
01250 {
01251
01252 InvalidateWindowClassesData(WC_INDUSTRY_CARGOES, NUM_INDUSTRYTYPES);
01253
01254 switch (widget) {
01255 case SM_WIDGET_MAP: {
01256
01257
01258
01259
01260
01261
01262
01263
01264 _left_button_clicked = false;
01265
01266 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01267 Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
01268 int sub;
01269 pt = this->PixelToTile(pt.x - wid->pos_x, pt.y - wid->pos_y, &sub);
01270 pt = RemapCoords(this->scroll_x + pt.x * TILE_SIZE + this->zoom * (TILE_SIZE - sub * TILE_SIZE / 4),
01271 this->scroll_y + pt.y * TILE_SIZE + sub * this->zoom * TILE_SIZE / 4, 0);
01272
01273 w->viewport->follow_vehicle = INVALID_VEHICLE;
01274 w->viewport->dest_scrollpos_x = pt.x - (w->viewport->virtual_width >> 1);
01275 w->viewport->dest_scrollpos_y = pt.y - (w->viewport->virtual_height >> 1);
01276
01277 this->SetDirty();
01278 break;
01279 }
01280
01281 case SM_WIDGET_ZOOM_IN:
01282 case SM_WIDGET_ZOOM_OUT: {
01283 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01284 Point pt = {wid->current_x / 2, wid->current_y / 2};
01285 this->SetZoomLevel((widget == SM_WIDGET_ZOOM_IN) ? ZLC_ZOOM_IN : ZLC_ZOOM_OUT, &pt);
01286 SndPlayFx(SND_15_BEEP);
01287 break;
01288 }
01289
01290 case SM_WIDGET_CONTOUR:
01291 case SM_WIDGET_VEHICLES:
01292 case SM_WIDGET_INDUSTRIES:
01293 case SM_WIDGET_ROUTES:
01294 case SM_WIDGET_VEGETATION:
01295 case SM_WIDGET_OWNERS:
01296 this->SwitchMapType((SmallMapType)(widget - SM_WIDGET_CONTOUR));
01297 SndPlayFx(SND_15_BEEP);
01298 break;
01299
01300 case SM_WIDGET_CENTERMAP:
01301 this->SmallMapCenterOnCurrentPos();
01302 this->HandleButtonClick(SM_WIDGET_CENTERMAP);
01303 SndPlayFx(SND_15_BEEP);
01304 break;
01305
01306 case SM_WIDGET_TOGGLETOWNNAME:
01307 this->show_towns = !this->show_towns;
01308 this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
01309
01310 this->SetDirty();
01311 SndPlayFx(SND_15_BEEP);
01312 break;
01313
01314 case SM_WIDGET_LEGEND:
01315
01316 if (this->map_type == SMT_INDUSTRY) {
01317
01318 const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_LEGEND);
01319 uint line = (pt.y - wi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_SMALL;
01320 uint columns = this->GetNumberColumnsLegend(wi->current_x);
01321 uint number_of_rows = max(CeilDiv(max(_smallmap_company_count, _smallmap_industry_count), columns), this->min_number_of_fixed_rows);
01322 if (line >= number_of_rows) break;
01323
01324 bool rtl = _current_text_dir == TD_RTL;
01325 int x = pt.x - wi->pos_x;
01326 if (rtl) x = wi->current_x - x;
01327 uint column = (x - WD_FRAMERECT_LEFT) / this->column_width;
01328
01329
01330 int industry_pos = (column * number_of_rows) + line;
01331 if (industry_pos < _smallmap_industry_count) {
01332 if (_ctrl_pressed) {
01333
01334 bool changes = false;
01335 for (int i = 0; i != _smallmap_industry_count; i++) {
01336 bool new_state = i == industry_pos;
01337 if (_legend_from_industries[i].show_on_map != new_state) {
01338 changes = true;
01339 _legend_from_industries[i].show_on_map = new_state;
01340 }
01341 }
01342 if (!changes) {
01343
01344 for (int i = 0; i != _smallmap_industry_count; i++) {
01345 _legend_from_industries[i].show_on_map = true;
01346 }
01347 }
01348 } else {
01349 _legend_from_industries[industry_pos].show_on_map = !_legend_from_industries[industry_pos].show_on_map;
01350 }
01351 }
01352 this->SetDirty();
01353 } else if (this->map_type == SMT_OWNER) {
01354
01355 const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_LEGEND);
01356 uint line = (pt.y - wi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_SMALL;
01357 uint columns = this->GetNumberColumnsLegend(wi->current_x);
01358 uint number_of_rows = max(CeilDiv(max(_smallmap_company_count, _smallmap_industry_count), columns), this->min_number_of_fixed_rows);
01359 if (line >= number_of_rows) break;
01360
01361 bool rtl = _current_text_dir == TD_RTL;
01362 int x = pt.x - wi->pos_x;
01363 if (rtl) x = wi->current_x - x;
01364 uint column = (x - WD_FRAMERECT_LEFT) / this->column_width;
01365
01366
01367 int company_pos = (column * number_of_rows) + line;
01368 if (company_pos < NUM_NO_COMPANY_ENTRIES) break;
01369 if (company_pos < _smallmap_company_count) {
01370 if (_ctrl_pressed) {
01371
01372 bool changes = false;
01373 for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
01374 bool new_state = i == company_pos;
01375 if (_legend_land_owners[i].show_on_map != new_state) {
01376 changes = true;
01377 _legend_land_owners[i].show_on_map = new_state;
01378 }
01379 }
01380 if (!changes) {
01381
01382 for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
01383 _legend_land_owners[i].show_on_map = true;
01384 }
01385 }
01386 } else {
01387 _legend_land_owners[company_pos].show_on_map = !_legend_land_owners[company_pos].show_on_map;
01388 }
01389 }
01390 this->SetDirty();
01391 }
01392 break;
01393
01394 case SM_WIDGET_ENABLE_ALL:
01395 if (this->map_type == SMT_INDUSTRY) {
01396 for (int i = 0; i != _smallmap_industry_count; i++) {
01397 _legend_from_industries[i].show_on_map = true;
01398 }
01399 } else if (this->map_type == SMT_OWNER) {
01400 for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
01401 _legend_land_owners[i].show_on_map = true;
01402 }
01403 }
01404 this->SetDirty();
01405 break;
01406
01407 case SM_WIDGET_DISABLE_ALL:
01408 if (this->map_type == SMT_INDUSTRY) {
01409 for (int i = 0; i != _smallmap_industry_count; i++) {
01410 _legend_from_industries[i].show_on_map = false;
01411 }
01412 } else {
01413 for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
01414 _legend_land_owners[i].show_on_map = false;
01415 }
01416 }
01417 this->SetDirty();
01418 break;
01419
01420 case SM_WIDGET_SHOW_HEIGHT:
01421 _smallmap_show_heightmap = !_smallmap_show_heightmap;
01422 this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_show_heightmap);
01423 this->SetDirty();
01424 break;
01425 }
01426 }
01427
01433 virtual void OnInvalidateData(int data)
01434 {
01435 switch (data) {
01436 case 1:
01437
01438 this->ReInit();
01439 break;
01440
01441 case 0: {
01442 extern uint64 _displayed_industries;
01443 if (this->map_type != SMT_INDUSTRY) this->SwitchMapType(SMT_INDUSTRY);
01444
01445 for (int i = 0; i != _smallmap_industry_count; i++) {
01446 _legend_from_industries[i].show_on_map = HasBit(_displayed_industries, _legend_from_industries[i].u.type);
01447 }
01448 break;
01449 }
01450
01451 default: NOT_REACHED();
01452 }
01453 this->SetDirty();
01454 }
01455
01456 virtual bool OnRightClick(Point pt, int widget)
01457 {
01458 if (widget != SM_WIDGET_MAP || _scrolling_viewport) return false;
01459
01460 _scrolling_viewport = true;
01461 return true;
01462 }
01463
01464 virtual void OnMouseWheel(int wheel)
01465 {
01466 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01467 int cursor_x = _cursor.pos.x - this->left - wid->pos_x;
01468 int cursor_y = _cursor.pos.y - this->top - wid->pos_y;
01469 if (IsInsideMM(cursor_x, 0, wid->current_x) && IsInsideMM(cursor_y, 0, wid->current_y)) {
01470 Point pt = {cursor_x, cursor_y};
01471 this->SetZoomLevel((wheel < 0) ? ZLC_ZOOM_IN : ZLC_ZOOM_OUT, &pt);
01472 }
01473 }
01474
01475 virtual void OnTick()
01476 {
01477
01478 if (--this->refresh != 0) return;
01479
01480 this->refresh = FORCE_REFRESH_PERIOD;
01481 this->SetDirty();
01482 }
01483
01491 void SetNewScroll(int sx, int sy, int sub)
01492 {
01493 const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01494 Point hv = InverseRemapCoords(wi->current_x * TILE_SIZE / 2, wi->current_y * TILE_SIZE / 2);
01495 hv.x *= this->zoom;
01496 hv.y *= this->zoom;
01497
01498 if (sx < -hv.x) {
01499 sx = -hv.x;
01500 sub = 0;
01501 }
01502 if (sx > (int)(MapMaxX() * TILE_SIZE) - hv.x) {
01503 sx = MapMaxX() * TILE_SIZE - hv.x;
01504 sub = 0;
01505 }
01506 if (sy < -hv.y) {
01507 sy = -hv.y;
01508 sub = 0;
01509 }
01510 if (sy > (int)(MapMaxY() * TILE_SIZE) - hv.y) {
01511 sy = MapMaxY() * TILE_SIZE - hv.y;
01512 sub = 0;
01513 }
01514
01515 this->scroll_x = sx;
01516 this->scroll_y = sy;
01517 this->subscroll = sub;
01518 }
01519
01520 virtual void OnScroll(Point delta)
01521 {
01522 _cursor.fix_at = true;
01523
01524
01525 int sub;
01526 Point pt = this->PixelToTile(delta.x, delta.y, &sub);
01527 this->SetNewScroll(this->scroll_x + pt.x * TILE_SIZE, this->scroll_y + pt.y * TILE_SIZE, sub);
01528
01529 this->SetDirty();
01530 }
01531
01532 void SmallMapCenterOnCurrentPos()
01533 {
01534 const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
01535 Point pt = InverseRemapCoords(vp->virtual_left + vp->virtual_width / 2, vp->virtual_top + vp->virtual_height / 2);
01536
01537 int sub;
01538 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01539 Point sxy = this->ComputeScroll(pt.x / TILE_SIZE, pt.y / TILE_SIZE, max(0, (int)wid->current_x / 2 - 2), wid->current_y / 2, &sub);
01540 this->SetNewScroll(sxy.x, sxy.y, sub);
01541 this->SetDirty();
01542 }
01543 };
01544
01545 SmallMapWindow::SmallMapType SmallMapWindow::map_type = SMT_CONTOUR;
01546 bool SmallMapWindow::show_towns = true;
01547
01556 class NWidgetSmallmapDisplay : public NWidgetContainer {
01557 const SmallMapWindow *smallmap_window;
01558 public:
01559 NWidgetSmallmapDisplay() : NWidgetContainer(NWID_VERTICAL)
01560 {
01561 this->smallmap_window = NULL;
01562 }
01563
01564 virtual void SetupSmallestSize(Window *w, bool init_array)
01565 {
01566 NWidgetBase *display = this->head;
01567 NWidgetBase *bar = display->next;
01568
01569 display->SetupSmallestSize(w, init_array);
01570 bar->SetupSmallestSize(w, init_array);
01571
01572 this->smallmap_window = dynamic_cast<SmallMapWindow *>(w);
01573 this->smallest_x = max(display->smallest_x, bar->smallest_x + smallmap_window->GetMinLegendWidth());
01574 this->smallest_y = display->smallest_y + max(bar->smallest_y, smallmap_window->GetLegendHeight(smallmap_window->min_number_of_columns));
01575 this->fill_x = max(display->fill_x, bar->fill_x);
01576 this->fill_y = (display->fill_y == 0 && bar->fill_y == 0) ? 0 : min(display->fill_y, bar->fill_y);
01577 this->resize_x = max(display->resize_x, bar->resize_x);
01578 this->resize_y = min(display->resize_y, bar->resize_y);
01579 }
01580
01581 virtual void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
01582 {
01583 this->pos_x = x;
01584 this->pos_y = y;
01585 this->current_x = given_width;
01586 this->current_y = given_height;
01587
01588 NWidgetBase *display = this->head;
01589 NWidgetBase *bar = display->next;
01590
01591 if (sizing == ST_SMALLEST) {
01592 this->smallest_x = given_width;
01593 this->smallest_y = given_height;
01594
01595 display->AssignSizePosition(ST_SMALLEST, x, y, display->smallest_x, display->smallest_y, rtl);
01596 bar->AssignSizePosition(ST_SMALLEST, x, y + display->smallest_y, bar->smallest_x, bar->smallest_y, rtl);
01597 }
01598
01599 uint bar_height = max(bar->smallest_y, this->smallmap_window->GetLegendHeight(this->smallmap_window->GetNumberColumnsLegend(given_width - bar->smallest_x)));
01600 uint display_height = given_height - bar_height;
01601 display->AssignSizePosition(ST_RESIZE, x, y, given_width, display_height, rtl);
01602 bar->AssignSizePosition(ST_RESIZE, x, y + display_height, given_width, bar_height, rtl);
01603 }
01604
01605 virtual NWidgetCore *GetWidgetFromPos(int x, int y)
01606 {
01607 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
01608 for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01609 NWidgetCore *widget = child_wid->GetWidgetFromPos(x, y);
01610 if (widget != NULL) return widget;
01611 }
01612 return NULL;
01613 }
01614
01615 virtual void Draw(const Window *w)
01616 {
01617 for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) child_wid->Draw(w);
01618 }
01619 };
01620
01622 static const NWidgetPart _nested_smallmap_display[] = {
01623 NWidget(WWT_PANEL, COLOUR_BROWN, SM_WIDGET_MAP_BORDER),
01624 NWidget(WWT_INSET, COLOUR_BROWN, SM_WIDGET_MAP), SetMinimalSize(346, 140), SetResize(1, 1), SetPadding(2, 2, 2, 2), EndContainer(),
01625 EndContainer(),
01626 };
01627
01629 static const NWidgetPart _nested_smallmap_bar[] = {
01630 NWidget(WWT_PANEL, COLOUR_BROWN),
01631 NWidget(NWID_HORIZONTAL),
01632 NWidget(WWT_EMPTY, INVALID_COLOUR, SM_WIDGET_LEGEND), SetResize(1, 1),
01633 NWidget(NWID_VERTICAL),
01634
01635 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01636 NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_IN),
01637 SetDataTip(SPR_IMG_ZOOMIN, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN), SetFill(1, 1),
01638 NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_CENTERMAP),
01639 SetDataTip(SPR_IMG_SMALLMAP, STR_SMALLMAP_CENTER), SetFill(1, 1),
01640 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_CONTOUR),
01641 SetDataTip(SPR_IMG_SHOW_COUNTOURS, STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP), SetFill(1, 1),
01642 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEHICLES),
01643 SetDataTip(SPR_IMG_SHOW_VEHICLES, STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP), SetFill(1, 1),
01644 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_INDUSTRIES),
01645 SetDataTip(SPR_IMG_INDUSTRY, STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRIES_ON_MAP), SetFill(1, 1),
01646 EndContainer(),
01647
01648 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01649 NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_OUT),
01650 SetDataTip(SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT), SetFill(1, 1),
01651 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_TOGGLETOWNNAME),
01652 SetDataTip(SPR_IMG_TOWN, STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF), SetFill(1, 1),
01653 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_ROUTES),
01654 SetDataTip(SPR_IMG_SHOW_ROUTES, STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON), SetFill(1, 1),
01655 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEGETATION),
01656 SetDataTip(SPR_IMG_PLANTTREES, STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP), SetFill(1, 1),
01657 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_OWNERS),
01658 SetDataTip(SPR_IMG_COMPANY_GENERAL, STR_SMALLMAP_TOOLTIP_SHOW_LAND_OWNERS_ON_MAP), SetFill(1, 1),
01659 EndContainer(),
01660 NWidget(NWID_SPACER), SetResize(0, 1),
01661 EndContainer(),
01662 EndContainer(),
01663 EndContainer(),
01664 };
01665
01666 static NWidgetBase *SmallMapDisplay(int *biggest_index)
01667 {
01668 NWidgetContainer *map_display = new NWidgetSmallmapDisplay;
01669
01670 MakeNWidgets(_nested_smallmap_display, lengthof(_nested_smallmap_display), biggest_index, map_display);
01671 MakeNWidgets(_nested_smallmap_bar, lengthof(_nested_smallmap_bar), biggest_index, map_display);
01672 return map_display;
01673 }
01674
01675
01676 static const NWidgetPart _nested_smallmap_widgets[] = {
01677 NWidget(NWID_HORIZONTAL),
01678 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
01679 NWidget(WWT_CAPTION, COLOUR_BROWN, SM_WIDGET_CAPTION), SetDataTip(STR_SMALLMAP_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01680 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
01681 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
01682 EndContainer(),
01683 NWidgetFunction(SmallMapDisplay),
01684
01685 NWidget(NWID_HORIZONTAL),
01686 NWidget(WWT_PANEL, COLOUR_BROWN),
01687 NWidget(NWID_HORIZONTAL),
01688 NWidget(NWID_SELECTION, INVALID_COLOUR, SM_WIDGET_SELECT_BUTTONS),
01689 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01690 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, SM_WIDGET_ENABLE_ALL), SetDataTip(STR_SMALLMAP_ENABLE_ALL, STR_NULL),
01691 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, SM_WIDGET_DISABLE_ALL), SetDataTip(STR_SMALLMAP_DISABLE_ALL, STR_NULL),
01692 NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_SHOW_HEIGHT), SetDataTip(STR_SMALLMAP_SHOW_HEIGHT, STR_SMALLMAP_TOOLTIP_SHOW_HEIGHT),
01693 EndContainer(),
01694 NWidget(NWID_SPACER), SetFill(1, 1),
01695 EndContainer(),
01696 NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
01697 EndContainer(),
01698 EndContainer(),
01699 NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
01700 EndContainer(),
01701 };
01702
01703 static const WindowDesc _smallmap_desc(
01704 WDP_AUTO, 446, 314,
01705 WC_SMALLMAP, WC_NONE,
01706 WDF_UNCLICK_BUTTONS,
01707 _nested_smallmap_widgets, lengthof(_nested_smallmap_widgets)
01708 );
01709
01710 void ShowSmallMap()
01711 {
01712 AllocateWindowDescFront<SmallMapWindow>(&_smallmap_desc, 0);
01713 }
01714
01723 bool ScrollMainWindowTo(int x, int y, int z, bool instant)
01724 {
01725 bool res = ScrollWindowTo(x, y, z, FindWindowById(WC_MAIN_WINDOW, 0), instant);
01726
01727
01728
01729
01730
01731 if (res) return res;
01732
01733 SmallMapWindow *w = dynamic_cast<SmallMapWindow*>(FindWindowById(WC_SMALLMAP, 0));
01734 if (w != NULL) w->SmallMapCenterOnCurrentPos();
01735
01736 return res;
01737 }