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