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 IsTileForestIndustry(tile) ? 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 || o == OWNER_WATER) {
00546 if (t == MP_WATER) return MKCOLOUR(0xCACACACA);
00547 const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00548 return _smallmap_show_heightmap ? cs->height_colours[TileHeight(tile)] : cs->default_colour;
00549 } else if (o == OWNER_TOWN) {
00550 return MKCOLOUR(0xB4B4B4B4);
00551 }
00552
00553 return _legend_land_owners[_company_to_list_pos[o]].colour * 0x01010101;
00554 }
00555
00557 static const byte _vehicle_type_colours[6] = {
00558 184, 191, 152, 15, 215, 184
00559 };
00560
00561
00563 class SmallMapWindow : public Window {
00565 enum SmallMapType {
00566 SMT_CONTOUR,
00567 SMT_VEHICLES,
00568 SMT_INDUSTRY,
00569 SMT_ROUTES,
00570 SMT_VEGETATION,
00571 SMT_OWNER,
00572 };
00573
00575 enum ZoomLevelChange {
00576 ZLC_INITIALIZE,
00577 ZLC_ZOOM_OUT,
00578 ZLC_ZOOM_IN,
00579 };
00580
00581 static SmallMapType map_type;
00582 static bool show_towns;
00583
00584 static const uint LEGEND_BLOB_WIDTH = 8;
00585 static const uint INDUSTRY_MIN_NUMBER_OF_COLUMNS = 2;
00586 uint min_number_of_fixed_rows;
00587 uint column_width;
00588
00589 int32 scroll_x;
00590 int32 scroll_y;
00591 int32 subscroll;
00592 int zoom;
00593
00594 static const uint8 FORCE_REFRESH_PERIOD = 0x1F;
00595 uint8 refresh;
00596
00603 FORCEINLINE Point RemapTile(int tile_x, int tile_y) const
00604 {
00605 int x_offset = tile_x - this->scroll_x / (int)TILE_SIZE;
00606 int y_offset = tile_y - this->scroll_y / (int)TILE_SIZE;
00607
00608 if (this->zoom == 1) return RemapCoords(x_offset, y_offset, 0);
00609
00610
00611 if (x_offset < 0) x_offset -= this->zoom - 1;
00612 if (y_offset < 0) y_offset -= this->zoom - 1;
00613
00614 return RemapCoords(x_offset / this->zoom, y_offset / this->zoom, 0);
00615 }
00616
00627 FORCEINLINE Point PixelToTile(int px, int py, int *sub, bool add_sub = true) const
00628 {
00629 if (add_sub) px += this->subscroll;
00630
00631
00632
00633 Point pt = {((py >> 1) - (px >> 2)) * this->zoom, ((py >> 1) + (px >> 2)) * this->zoom};
00634 px &= 3;
00635
00636 if (py & 1) {
00637 if (px < 2) {
00638 pt.x += this->zoom;
00639 px += 2;
00640 } else {
00641 pt.y += this->zoom;
00642 px -= 2;
00643 }
00644 }
00645
00646 *sub = px;
00647 return pt;
00648 }
00649
00659 Point ComputeScroll(int tx, int ty, int x, int y, int *sub)
00660 {
00661 assert(x >= 0 && y >= 0);
00662
00663 int new_sub;
00664 Point tile_xy = PixelToTile(x, y, &new_sub, false);
00665 tx -= tile_xy.x;
00666 ty -= tile_xy.y;
00667
00668 Point scroll;
00669 if (new_sub == 0) {
00670 *sub = 0;
00671 scroll.x = (tx + this->zoom) * TILE_SIZE;
00672 scroll.y = (ty - this->zoom) * TILE_SIZE;
00673 } else {
00674 *sub = 4 - new_sub;
00675 scroll.x = (tx + 2 * this->zoom) * TILE_SIZE;
00676 scroll.y = (ty - 2 * this->zoom) * TILE_SIZE;
00677 }
00678 return scroll;
00679 }
00680
00687 void SetZoomLevel(ZoomLevelChange change, const Point *zoom_pt)
00688 {
00689 static const int zoomlevels[] = {1, 2, 4, 6, 8};
00690 static const int MIN_ZOOM_INDEX = 0;
00691 static const int MAX_ZOOM_INDEX = lengthof(zoomlevels) - 1;
00692
00693 int new_index, cur_index, sub;
00694 Point tile;
00695 switch (change) {
00696 case ZLC_INITIALIZE:
00697 cur_index = - 1;
00698 new_index = MIN_ZOOM_INDEX;
00699 break;
00700
00701 case ZLC_ZOOM_IN:
00702 case ZLC_ZOOM_OUT:
00703 for (cur_index = MIN_ZOOM_INDEX; cur_index <= MAX_ZOOM_INDEX; cur_index++) {
00704 if (this->zoom == zoomlevels[cur_index]) break;
00705 }
00706 assert(cur_index <= MAX_ZOOM_INDEX);
00707
00708 tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
00709 new_index = Clamp(cur_index + ((change == ZLC_ZOOM_IN) ? -1 : 1), MIN_ZOOM_INDEX, MAX_ZOOM_INDEX);
00710 break;
00711
00712 default: NOT_REACHED();
00713 }
00714
00715 if (new_index != cur_index) {
00716 this->zoom = zoomlevels[new_index];
00717 if (cur_index >= 0) {
00718 Point new_tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
00719 this->SetNewScroll(this->scroll_x + (tile.x - new_tile.x) * TILE_SIZE,
00720 this->scroll_y + (tile.y - new_tile.y) * TILE_SIZE, sub);
00721 }
00722 this->SetWidgetDisabledState(SM_WIDGET_ZOOM_IN, this->zoom == zoomlevels[MIN_ZOOM_INDEX]);
00723 this->SetWidgetDisabledState(SM_WIDGET_ZOOM_OUT, this->zoom == zoomlevels[MAX_ZOOM_INDEX]);
00724 this->SetDirty();
00725 }
00726 }
00727
00733 inline uint32 GetTileColours(const TileArea &ta) const
00734 {
00735 int importance = 0;
00736 TileIndex tile = INVALID_TILE;
00737 TileType et = MP_VOID;
00738
00739 TILE_AREA_LOOP(ti, ta) {
00740 TileType ttype = GetEffectiveTileType(ti);
00741 if (_tiletype_importance[ttype] > importance) {
00742 importance = _tiletype_importance[ttype];
00743 tile = ti;
00744 et = ttype;
00745 }
00746 }
00747
00748 switch (this->map_type) {
00749 case SMT_CONTOUR:
00750 return GetSmallMapContoursPixels(tile, et);
00751
00752 case SMT_VEHICLES:
00753 return GetSmallMapVehiclesPixels(tile, et);
00754
00755 case SMT_INDUSTRY:
00756 return GetSmallMapIndustriesPixels(tile, et);
00757
00758 case SMT_ROUTES:
00759 return GetSmallMapRoutesPixels(tile, et);
00760
00761 case SMT_VEGETATION:
00762 return GetSmallMapVegetationPixels(tile, et);
00763
00764 case SMT_OWNER:
00765 return GetSmallMapOwnerPixels(tile, et);
00766
00767 default: NOT_REACHED();
00768 }
00769 }
00770
00785 void DrawSmallMapColumn(void *dst, uint xc, uint yc, int pitch, int reps, int start_pos, int end_pos, Blitter *blitter) const
00786 {
00787 void *dst_ptr_abs_end = blitter->MoveTo(_screen.dst_ptr, 0, _screen.height);
00788 uint min_xy = _settings_game.construction.freeform_edges ? 1 : 0;
00789
00790 do {
00791
00792 if (xc >= MapMaxX() || yc >= MapMaxY()) continue;
00793
00794
00795 if (dst < _screen.dst_ptr) continue;
00796 if (dst >= dst_ptr_abs_end) continue;
00797
00798
00799 TileArea ta;
00800 if (min_xy == 1 && (xc == 0 || yc == 0)) {
00801 if (this->zoom == 1) continue;
00802
00803 ta = TileArea(TileXY(max(min_xy, xc), max(min_xy, yc)), this->zoom - (xc == 0), this->zoom - (yc == 0));
00804 } else {
00805 ta = TileArea(TileXY(xc, yc), this->zoom, this->zoom);
00806 }
00807 ta.ClampToMap();
00808
00809 uint32 val = this->GetTileColours(ta);
00810 uint8 *val8 = (uint8 *)&val;
00811 int idx = max(0, -start_pos);
00812 for (int pos = max(0, start_pos); pos < end_pos; pos++) {
00813 blitter->SetPixel(dst, idx, 0, val8[idx]);
00814 idx++;
00815 }
00816
00817 } while (xc += this->zoom, yc += this->zoom, dst = blitter->MoveTo(dst, pitch, 0), --reps != 0);
00818 }
00819
00825 void DrawVehicles(const DrawPixelInfo *dpi, Blitter *blitter) const
00826 {
00827 const Vehicle *v;
00828 FOR_ALL_VEHICLES(v) {
00829 if (v->type == VEH_EFFECT) continue;
00830 if (v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) continue;
00831
00832
00833 Point pt = this->RemapTile(v->x_pos / TILE_SIZE, v->y_pos / TILE_SIZE);
00834
00835 int y = pt.y - dpi->top;
00836 if (!IsInsideMM(y, 0, dpi->height)) continue;
00837
00838 bool skip = false;
00839 int x = pt.x - this->subscroll - 3 - dpi->left;
00840 if (x < 0) {
00841
00842
00843 if (++x != 0) continue;
00844 skip = true;
00845 } else if (x >= dpi->width - 1) {
00846
00847 if (x != dpi->width - 1) continue;
00848 skip = true;
00849 }
00850
00851
00852 byte colour = (this->map_type == SMT_VEHICLES) ? _vehicle_type_colours[v->type] : 0xF;
00853
00854
00855 blitter->SetPixel(dpi->dst_ptr, x, y, colour);
00856 if (!skip) blitter->SetPixel(dpi->dst_ptr, x + 1, y, colour);
00857 }
00858 }
00859
00864 void DrawTowns(const DrawPixelInfo *dpi) const
00865 {
00866 const Town *t;
00867 FOR_ALL_TOWNS(t) {
00868
00869 Point pt = this->RemapTile(TileX(t->xy), TileY(t->xy));
00870 int x = pt.x - this->subscroll - (t->sign.width_small >> 1);
00871 int y = pt.y;
00872
00873
00874 if (x + t->sign.width_small > dpi->left &&
00875 x < dpi->left + dpi->width &&
00876 y + FONT_HEIGHT_SMALL > dpi->top &&
00877 y < dpi->top + dpi->height) {
00878
00879 SetDParam(0, t->index);
00880 DrawString(x, x + t->sign.width_small, y, STR_SMALLMAP_TOWN);
00881 }
00882 }
00883 }
00884
00891 static inline void DrawVertMapIndicator(int x, int y, int y2)
00892 {
00893 GfxFillRect(x, y, x, y + 3, 69);
00894 GfxFillRect(x, y2 - 3, x, y2, 69);
00895 }
00896
00903 static inline void DrawHorizMapIndicator(int x, int x2, int y)
00904 {
00905 GfxFillRect(x, y, x + 3, y, 69);
00906 GfxFillRect(x2 - 3, y, x2, y, 69);
00907 }
00908
00912 void DrawMapIndicators() const
00913 {
00914
00915 const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
00916
00917 Point tile = InverseRemapCoords(vp->virtual_left, vp->virtual_top);
00918 Point tl = this->RemapTile(tile.x >> 4, tile.y >> 4);
00919 tl.x -= this->subscroll;
00920
00921 tile = InverseRemapCoords(vp->virtual_left + vp->virtual_width, vp->virtual_top + vp->virtual_height);
00922 Point br = this->RemapTile(tile.x >> 4, tile.y >> 4);
00923 br.x -= this->subscroll;
00924
00925 SmallMapWindow::DrawVertMapIndicator(tl.x, tl.y, br.y);
00926 SmallMapWindow::DrawVertMapIndicator(br.x, tl.y, br.y);
00927
00928 SmallMapWindow::DrawHorizMapIndicator(tl.x, br.x, tl.y);
00929 SmallMapWindow::DrawHorizMapIndicator(tl.x, br.x, br.y);
00930 }
00931
00943 void DrawSmallMap(DrawPixelInfo *dpi) const
00944 {
00945 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00946 DrawPixelInfo *old_dpi;
00947
00948 old_dpi = _cur_dpi;
00949 _cur_dpi = dpi;
00950
00951
00952 GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, 0);
00953
00954
00955 int dx;
00956 Point tile = this->PixelToTile(dpi->left, dpi->top, &dx);
00957 int tile_x = this->scroll_x / (int)TILE_SIZE + tile.x;
00958 int tile_y = this->scroll_y / (int)TILE_SIZE + tile.y;
00959
00960 void *ptr = blitter->MoveTo(dpi->dst_ptr, -dx - 4, 0);
00961 int x = - dx - 4;
00962 int y = 0;
00963
00964 for (;;) {
00965
00966 if (x >= -3) {
00967 if (x >= dpi->width) break;
00968
00969 int end_pos = min(dpi->width, x + 4);
00970 int reps = (dpi->height - y + 1) / 2;
00971 if (reps > 0) {
00972 this->DrawSmallMapColumn(ptr, tile_x, tile_y, dpi->pitch * 2, reps, x, end_pos, blitter);
00973 }
00974 }
00975
00976 if (y == 0) {
00977 tile_y += this->zoom;
00978 y++;
00979 ptr = blitter->MoveTo(ptr, 0, 1);
00980 } else {
00981 tile_x -= this->zoom;
00982 y--;
00983 ptr = blitter->MoveTo(ptr, 0, -1);
00984 }
00985 ptr = blitter->MoveTo(ptr, 2, 0);
00986 x += 2;
00987 }
00988
00989
00990 if (this->map_type == SMT_CONTOUR || this->map_type == SMT_VEHICLES) this->DrawVehicles(dpi, blitter);
00991
00992
00993 if (this->show_towns) this->DrawTowns(dpi);
00994
00995
00996 this->DrawMapIndicators();
00997
00998 _cur_dpi = old_dpi;
00999 }
01000
01004 void SetupWidgetData()
01005 {
01006 StringID legend_tooltip;
01007 StringID enable_all_tooltip;
01008 StringID disable_all_tooltip;
01009 int plane;
01010 switch (this->map_type) {
01011 case SMT_INDUSTRY:
01012 legend_tooltip = STR_SMALLMAP_TOOLTIP_INDUSTRY_SELECTION;
01013 enable_all_tooltip = STR_SMALLMAP_TOOLTIP_ENABLE_ALL_INDUSTRIES;
01014 disable_all_tooltip = STR_SMALLMAP_TOOLTIP_DISABLE_ALL_INDUSTRIES;
01015 plane = 0;
01016 break;
01017
01018 case SMT_OWNER:
01019 legend_tooltip = STR_SMALLMAP_TOOLTIP_COMPANY_SELECTION;
01020 enable_all_tooltip = STR_SMALLMAP_TOOLTIP_ENABLE_ALL_COMPANIES;
01021 disable_all_tooltip = STR_SMALLMAP_TOOLTIP_DISABLE_ALL_COMPANIES;
01022 plane = 0;
01023 break;
01024
01025 default:
01026 legend_tooltip = STR_NULL;
01027 enable_all_tooltip = STR_NULL;
01028 disable_all_tooltip = STR_NULL;
01029 plane = 1;
01030 break;
01031 }
01032
01033 this->GetWidget<NWidgetCore>(SM_WIDGET_LEGEND)->SetDataTip(STR_NULL, legend_tooltip);
01034 this->GetWidget<NWidgetCore>(SM_WIDGET_ENABLE_ALL)->SetDataTip(STR_SMALLMAP_ENABLE_ALL, enable_all_tooltip);
01035 this->GetWidget<NWidgetCore>(SM_WIDGET_DISABLE_ALL)->SetDataTip(STR_SMALLMAP_DISABLE_ALL, disable_all_tooltip);
01036 this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECT_BUTTONS)->SetDisplayedPlane(plane);
01037 }
01038
01039 public:
01040 uint min_number_of_columns;
01041
01042 SmallMapWindow(const WindowDesc *desc, int window_number) : Window(), refresh(FORCE_REFRESH_PERIOD)
01043 {
01044 this->InitNested(desc, window_number);
01045 this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
01046
01047 BuildLandLegend();
01048 this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_show_heightmap);
01049
01050 this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
01051
01052 this->SetupWidgetData();
01053
01054 this->SetZoomLevel(ZLC_INITIALIZE, NULL);
01055 this->SmallMapCenterOnCurrentPos();
01056 }
01057
01062 inline uint GetMinLegendWidth() const
01063 {
01064 return WD_FRAMERECT_LEFT + this->min_number_of_columns * this->column_width;
01065 }
01066
01071 inline uint GetNumberColumnsLegend(uint width) const
01072 {
01073 return width / this->column_width;
01074 }
01075
01081 uint GetLegendHeight(uint num_columns) const
01082 {
01083 uint num_rows = max(this->min_number_of_fixed_rows, CeilDiv(max(_smallmap_company_count, _smallmap_industry_count), num_columns));
01084 return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + num_rows * FONT_HEIGHT_SMALL;
01085 }
01086
01087 virtual void SetStringParameters(int widget) const
01088 {
01089 switch (widget) {
01090 case SM_WIDGET_CAPTION:
01091 SetDParam(0, STR_SMALLMAP_TYPE_CONTOURS + this->map_type);
01092 break;
01093 }
01094 }
01095
01096 virtual void OnInit()
01097 {
01098 uint min_width = 0;
01099 this->min_number_of_columns = INDUSTRY_MIN_NUMBER_OF_COLUMNS;
01100 this->min_number_of_fixed_rows = 0;
01101 for (uint i = 0; i < lengthof(_legend_table); i++) {
01102 uint height = 0;
01103 uint num_columns = 1;
01104 for (const LegendAndColour *tbl = _legend_table[i]; !tbl->end; ++tbl) {
01105 StringID str;
01106 if (i == SMT_INDUSTRY) {
01107 SetDParam(0, tbl->legend);
01108 SetDParam(1, IndustryPool::MAX_SIZE);
01109 str = STR_SMALLMAP_INDUSTRY;
01110 } else if (i == SMT_OWNER) {
01111 if (tbl->company != INVALID_COMPANY) {
01112 if (!Company::IsValidID(tbl->company)) {
01113
01114 BuildOwnerLegend();
01115 this->OnInit();
01116 return;
01117 }
01118
01119 SetDParam(0, tbl->company);
01120 str = STR_SMALLMAP_COMPANY;
01121 } else {
01122 str = tbl->legend;
01123 }
01124 } else {
01125 if (tbl->col_break) {
01126 this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
01127 height = 0;
01128 num_columns++;
01129 }
01130 height++;
01131 str = tbl->legend;
01132 }
01133 min_width = max(GetStringBoundingBox(str).width, min_width);
01134 }
01135 this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
01136 this->min_number_of_columns = max(this->min_number_of_columns, num_columns);
01137 }
01138
01139
01140 this->column_width = min_width + LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
01141 }
01142
01143 virtual void OnPaint()
01144 {
01145 if (this->map_type == SMT_OWNER) {
01146 for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
01147 if (tbl->company != INVALID_COMPANY && !Company::IsValidID(tbl->company)) {
01148
01149 BuildOwnerLegend();
01150 this->InvalidateData(1);
01151 break;
01152 }
01153 }
01154 }
01155
01156 this->DrawWidgets();
01157 }
01158
01159 virtual void DrawWidget(const Rect &r, int widget) const
01160 {
01161 switch (widget) {
01162 case SM_WIDGET_MAP: {
01163 DrawPixelInfo new_dpi;
01164 if (!FillDrawPixelInfo(&new_dpi, r.left + 1, r.top + 1, r.right - r.left - 1, r.bottom - r.top - 1)) return;
01165 this->DrawSmallMap(&new_dpi);
01166 break;
01167 }
01168
01169 case SM_WIDGET_LEGEND: {
01170 uint columns = this->GetNumberColumnsLegend(r.right - r.left + 1);
01171 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);
01172 bool rtl = _current_text_dir == TD_RTL;
01173 uint y_org = r.top + WD_FRAMERECT_TOP;
01174 uint x = rtl ? r.right - this->column_width - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT;
01175 uint y = y_org;
01176 uint i = 0;
01177 uint row_height = FONT_HEIGHT_SMALL;
01178
01179 uint text_left = rtl ? 0 : LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT;
01180 uint text_right = this->column_width - 1 - (rtl ? LEGEND_BLOB_WIDTH + WD_FRAMERECT_RIGHT : 0);
01181 uint blob_left = rtl ? this->column_width - 1 - LEGEND_BLOB_WIDTH : 0;
01182 uint blob_right = rtl ? this->column_width - 1 : LEGEND_BLOB_WIDTH;
01183
01184 for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
01185 if (tbl->col_break || ((this->map_type == SMT_INDUSTRY || this->map_type == SMT_OWNER) && i++ >= number_of_rows)) {
01186
01187
01188 x += rtl ? -(int)this->column_width : this->column_width;
01189 y = y_org;
01190 i = 1;
01191 }
01192
01193 if (this->map_type == SMT_INDUSTRY) {
01194
01195
01196 SetDParam(0, tbl->legend);
01197 SetDParam(1, Industry::GetIndustryTypeCount(tbl->type));
01198 if (!tbl->show_on_map) {
01199
01200
01201 DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_GREY);
01202 } else {
01203 DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_BLACK);
01204 GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0);
01205 }
01206 } else if (this->map_type == SMT_OWNER && tbl->company != INVALID_COMPANY) {
01207 SetDParam(0, tbl->company);
01208 if (!tbl->show_on_map) {
01209
01210
01211 DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_COMPANY, TC_GREY);
01212 } else {
01213 DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_COMPANY, TC_BLACK);
01214 GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0);
01215 }
01216 } else {
01217 if (this->map_type == SMT_CONTOUR) SetDParam(0, tbl->height * TILE_HEIGHT_STEP);
01218
01219
01220 GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0);
01221 DrawString(x + text_left, x + text_right, y, tbl->legend);
01222 }
01223 GfxFillRect(x + blob_left + 1, y + 2, x + blob_right - 1, y + row_height - 2, tbl->colour);
01224
01225 y += row_height;
01226 }
01227 }
01228 }
01229 }
01230
01235 void SwitchMapType(SmallMapType map_type)
01236 {
01237 this->RaiseWidget(this->map_type + SM_WIDGET_CONTOUR);
01238 this->map_type = map_type;
01239 this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
01240
01241 this->SetupWidgetData();
01242
01243 this->SetDirty();
01244 }
01245
01246 virtual void OnClick(Point pt, int widget, int click_count)
01247 {
01248
01249 InvalidateWindowClassesData(WC_INDUSTRY_CARGOES, NUM_INDUSTRYTYPES);
01250
01251 switch (widget) {
01252 case SM_WIDGET_MAP: {
01253
01254
01255
01256
01257
01258
01259
01260
01261 _left_button_clicked = false;
01262
01263 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01264 Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
01265 int sub;
01266 pt = this->PixelToTile(pt.x - wid->pos_x, pt.y - wid->pos_y, &sub);
01267 pt = RemapCoords(this->scroll_x + pt.x * TILE_SIZE + this->zoom * (TILE_SIZE - sub * TILE_SIZE / 4),
01268 this->scroll_y + pt.y * TILE_SIZE + sub * this->zoom * TILE_SIZE / 4, 0);
01269
01270 w->viewport->follow_vehicle = INVALID_VEHICLE;
01271 w->viewport->dest_scrollpos_x = pt.x - (w->viewport->virtual_width >> 1);
01272 w->viewport->dest_scrollpos_y = pt.y - (w->viewport->virtual_height >> 1);
01273
01274 this->SetDirty();
01275 break;
01276 }
01277
01278 case SM_WIDGET_ZOOM_IN:
01279 case SM_WIDGET_ZOOM_OUT: {
01280 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01281 Point pt = {wid->current_x / 2, wid->current_y / 2};
01282 this->SetZoomLevel((widget == SM_WIDGET_ZOOM_IN) ? ZLC_ZOOM_IN : ZLC_ZOOM_OUT, &pt);
01283 SndPlayFx(SND_15_BEEP);
01284 break;
01285 }
01286
01287 case SM_WIDGET_CONTOUR:
01288 case SM_WIDGET_VEHICLES:
01289 case SM_WIDGET_INDUSTRIES:
01290 case SM_WIDGET_ROUTES:
01291 case SM_WIDGET_VEGETATION:
01292 case SM_WIDGET_OWNERS:
01293 this->SwitchMapType((SmallMapType)(widget - SM_WIDGET_CONTOUR));
01294 SndPlayFx(SND_15_BEEP);
01295 break;
01296
01297 case SM_WIDGET_CENTERMAP:
01298 this->SmallMapCenterOnCurrentPos();
01299 this->HandleButtonClick(SM_WIDGET_CENTERMAP);
01300 SndPlayFx(SND_15_BEEP);
01301 break;
01302
01303 case SM_WIDGET_TOGGLETOWNNAME:
01304 this->show_towns = !this->show_towns;
01305 this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
01306
01307 this->SetDirty();
01308 SndPlayFx(SND_15_BEEP);
01309 break;
01310
01311 case SM_WIDGET_LEGEND:
01312
01313 if (this->map_type == SMT_INDUSTRY) {
01314
01315 const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_LEGEND);
01316 uint line = (pt.y - wi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_SMALL;
01317 uint columns = this->GetNumberColumnsLegend(wi->current_x);
01318 uint number_of_rows = max(CeilDiv(max(_smallmap_company_count, _smallmap_industry_count), columns), this->min_number_of_fixed_rows);
01319 if (line >= number_of_rows) break;
01320
01321 bool rtl = _current_text_dir == TD_RTL;
01322 int x = pt.x - wi->pos_x;
01323 if (rtl) x = wi->current_x - x;
01324 uint column = (x - WD_FRAMERECT_LEFT) / this->column_width;
01325
01326
01327 int industry_pos = (column * number_of_rows) + line;
01328 if (industry_pos < _smallmap_industry_count) {
01329 if (_ctrl_pressed) {
01330
01331 bool changes = false;
01332 for (int i = 0; i != _smallmap_industry_count; i++) {
01333 bool new_state = i == industry_pos;
01334 if (_legend_from_industries[i].show_on_map != new_state) {
01335 changes = true;
01336 _legend_from_industries[i].show_on_map = new_state;
01337 }
01338 }
01339 if (!changes) {
01340
01341 for (int i = 0; i != _smallmap_industry_count; i++) {
01342 _legend_from_industries[i].show_on_map = true;
01343 }
01344 }
01345 } else {
01346 _legend_from_industries[industry_pos].show_on_map = !_legend_from_industries[industry_pos].show_on_map;
01347 }
01348 }
01349 this->SetDirty();
01350 } else if (this->map_type == SMT_OWNER) {
01351
01352 const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_LEGEND);
01353 uint line = (pt.y - wi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_SMALL;
01354 uint columns = this->GetNumberColumnsLegend(wi->current_x);
01355 uint number_of_rows = max(CeilDiv(max(_smallmap_company_count, _smallmap_industry_count), columns), this->min_number_of_fixed_rows);
01356 if (line >= number_of_rows) break;
01357
01358 bool rtl = _current_text_dir == TD_RTL;
01359 int x = pt.x - wi->pos_x;
01360 if (rtl) x = wi->current_x - x;
01361 uint column = (x - WD_FRAMERECT_LEFT) / this->column_width;
01362
01363
01364 int company_pos = (column * number_of_rows) + line;
01365 if (company_pos < NUM_NO_COMPANY_ENTRIES) break;
01366 if (company_pos < _smallmap_company_count) {
01367 if (_ctrl_pressed) {
01368
01369 bool changes = false;
01370 for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
01371 bool new_state = i == company_pos;
01372 if (_legend_land_owners[i].show_on_map != new_state) {
01373 changes = true;
01374 _legend_land_owners[i].show_on_map = new_state;
01375 }
01376 }
01377 if (!changes) {
01378
01379 for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
01380 _legend_land_owners[i].show_on_map = true;
01381 }
01382 }
01383 } else {
01384 _legend_land_owners[company_pos].show_on_map = !_legend_land_owners[company_pos].show_on_map;
01385 }
01386 }
01387 this->SetDirty();
01388 }
01389 break;
01390
01391 case SM_WIDGET_ENABLE_ALL:
01392 if (this->map_type == SMT_INDUSTRY) {
01393 for (int i = 0; i != _smallmap_industry_count; i++) {
01394 _legend_from_industries[i].show_on_map = true;
01395 }
01396 } else if (this->map_type == SMT_OWNER) {
01397 for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
01398 _legend_land_owners[i].show_on_map = true;
01399 }
01400 }
01401 this->SetDirty();
01402 break;
01403
01404 case SM_WIDGET_DISABLE_ALL:
01405 if (this->map_type == SMT_INDUSTRY) {
01406 for (int i = 0; i != _smallmap_industry_count; i++) {
01407 _legend_from_industries[i].show_on_map = false;
01408 }
01409 } else {
01410 for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
01411 _legend_land_owners[i].show_on_map = false;
01412 }
01413 }
01414 this->SetDirty();
01415 break;
01416
01417 case SM_WIDGET_SHOW_HEIGHT:
01418 _smallmap_show_heightmap = !_smallmap_show_heightmap;
01419 this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_show_heightmap);
01420 this->SetDirty();
01421 break;
01422 }
01423 }
01424
01432 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
01433 {
01434 if (!gui_scope) return;
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].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 if (_settings_client.gui.scrollwheel_scrolling == 0) {
01467 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01468 int cursor_x = _cursor.pos.x - this->left - wid->pos_x;
01469 int cursor_y = _cursor.pos.y - this->top - wid->pos_y;
01470 if (IsInsideMM(cursor_x, 0, wid->current_x) && IsInsideMM(cursor_y, 0, wid->current_y)) {
01471 Point pt = {cursor_x, cursor_y};
01472 this->SetZoomLevel((wheel < 0) ? ZLC_ZOOM_IN : ZLC_ZOOM_OUT, &pt);
01473 }
01474 }
01475 }
01476
01477 virtual void OnTick()
01478 {
01479
01480 if (--this->refresh != 0) return;
01481
01482 this->refresh = FORCE_REFRESH_PERIOD;
01483 this->SetDirty();
01484 }
01485
01493 void SetNewScroll(int sx, int sy, int sub)
01494 {
01495 const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01496 Point hv = InverseRemapCoords(wi->current_x * TILE_SIZE / 2, wi->current_y * TILE_SIZE / 2);
01497 hv.x *= this->zoom;
01498 hv.y *= this->zoom;
01499
01500 if (sx < -hv.x) {
01501 sx = -hv.x;
01502 sub = 0;
01503 }
01504 if (sx > (int)(MapMaxX() * TILE_SIZE) - hv.x) {
01505 sx = MapMaxX() * TILE_SIZE - hv.x;
01506 sub = 0;
01507 }
01508 if (sy < -hv.y) {
01509 sy = -hv.y;
01510 sub = 0;
01511 }
01512 if (sy > (int)(MapMaxY() * TILE_SIZE) - hv.y) {
01513 sy = MapMaxY() * TILE_SIZE - hv.y;
01514 sub = 0;
01515 }
01516
01517 this->scroll_x = sx;
01518 this->scroll_y = sy;
01519 this->subscroll = sub;
01520 }
01521
01522 virtual void OnScroll(Point delta)
01523 {
01524 _cursor.fix_at = true;
01525
01526
01527 int sub;
01528 Point pt = this->PixelToTile(delta.x, delta.y, &sub);
01529 this->SetNewScroll(this->scroll_x + pt.x * TILE_SIZE, this->scroll_y + pt.y * TILE_SIZE, sub);
01530
01531 this->SetDirty();
01532 }
01533
01534 void SmallMapCenterOnCurrentPos()
01535 {
01536 const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
01537 Point pt = InverseRemapCoords(vp->virtual_left + vp->virtual_width / 2, vp->virtual_top + vp->virtual_height / 2);
01538
01539 int sub;
01540 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01541 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);
01542 this->SetNewScroll(sxy.x, sxy.y, sub);
01543 this->SetDirty();
01544 }
01545 };
01546
01547 SmallMapWindow::SmallMapType SmallMapWindow::map_type = SMT_CONTOUR;
01548 bool SmallMapWindow::show_towns = true;
01549
01558 class NWidgetSmallmapDisplay : public NWidgetContainer {
01559 const SmallMapWindow *smallmap_window;
01560 public:
01561 NWidgetSmallmapDisplay() : NWidgetContainer(NWID_VERTICAL)
01562 {
01563 this->smallmap_window = NULL;
01564 }
01565
01566 virtual void SetupSmallestSize(Window *w, bool init_array)
01567 {
01568 NWidgetBase *display = this->head;
01569 NWidgetBase *bar = display->next;
01570
01571 display->SetupSmallestSize(w, init_array);
01572 bar->SetupSmallestSize(w, init_array);
01573
01574 this->smallmap_window = dynamic_cast<SmallMapWindow *>(w);
01575 this->smallest_x = max(display->smallest_x, bar->smallest_x + smallmap_window->GetMinLegendWidth());
01576 this->smallest_y = display->smallest_y + max(bar->smallest_y, smallmap_window->GetLegendHeight(smallmap_window->min_number_of_columns));
01577 this->fill_x = max(display->fill_x, bar->fill_x);
01578 this->fill_y = (display->fill_y == 0 && bar->fill_y == 0) ? 0 : min(display->fill_y, bar->fill_y);
01579 this->resize_x = max(display->resize_x, bar->resize_x);
01580 this->resize_y = min(display->resize_y, bar->resize_y);
01581 }
01582
01583 virtual void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
01584 {
01585 this->pos_x = x;
01586 this->pos_y = y;
01587 this->current_x = given_width;
01588 this->current_y = given_height;
01589
01590 NWidgetBase *display = this->head;
01591 NWidgetBase *bar = display->next;
01592
01593 if (sizing == ST_SMALLEST) {
01594 this->smallest_x = given_width;
01595 this->smallest_y = given_height;
01596
01597 display->AssignSizePosition(ST_SMALLEST, x, y, display->smallest_x, display->smallest_y, rtl);
01598 bar->AssignSizePosition(ST_SMALLEST, x, y + display->smallest_y, bar->smallest_x, bar->smallest_y, rtl);
01599 }
01600
01601 uint bar_height = max(bar->smallest_y, this->smallmap_window->GetLegendHeight(this->smallmap_window->GetNumberColumnsLegend(given_width - bar->smallest_x)));
01602 uint display_height = given_height - bar_height;
01603 display->AssignSizePosition(ST_RESIZE, x, y, given_width, display_height, rtl);
01604 bar->AssignSizePosition(ST_RESIZE, x, y + display_height, given_width, bar_height, rtl);
01605 }
01606
01607 virtual NWidgetCore *GetWidgetFromPos(int x, int y)
01608 {
01609 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
01610 for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01611 NWidgetCore *widget = child_wid->GetWidgetFromPos(x, y);
01612 if (widget != NULL) return widget;
01613 }
01614 return NULL;
01615 }
01616
01617 virtual void Draw(const Window *w)
01618 {
01619 for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) child_wid->Draw(w);
01620 }
01621 };
01622
01624 static const NWidgetPart _nested_smallmap_display[] = {
01625 NWidget(WWT_PANEL, COLOUR_BROWN, SM_WIDGET_MAP_BORDER),
01626 NWidget(WWT_INSET, COLOUR_BROWN, SM_WIDGET_MAP), SetMinimalSize(346, 140), SetResize(1, 1), SetPadding(2, 2, 2, 2), EndContainer(),
01627 EndContainer(),
01628 };
01629
01631 static const NWidgetPart _nested_smallmap_bar[] = {
01632 NWidget(WWT_PANEL, COLOUR_BROWN),
01633 NWidget(NWID_HORIZONTAL),
01634 NWidget(WWT_EMPTY, INVALID_COLOUR, SM_WIDGET_LEGEND), SetResize(1, 1),
01635 NWidget(NWID_VERTICAL),
01636
01637 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01638 NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_IN),
01639 SetDataTip(SPR_IMG_ZOOMIN, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN), SetFill(1, 1),
01640 NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_CENTERMAP),
01641 SetDataTip(SPR_IMG_SMALLMAP, STR_SMALLMAP_CENTER), SetFill(1, 1),
01642 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_CONTOUR),
01643 SetDataTip(SPR_IMG_SHOW_COUNTOURS, STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP), SetFill(1, 1),
01644 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEHICLES),
01645 SetDataTip(SPR_IMG_SHOW_VEHICLES, STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP), SetFill(1, 1),
01646 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_INDUSTRIES),
01647 SetDataTip(SPR_IMG_INDUSTRY, STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRIES_ON_MAP), SetFill(1, 1),
01648 EndContainer(),
01649
01650 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01651 NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_OUT),
01652 SetDataTip(SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT), SetFill(1, 1),
01653 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_TOGGLETOWNNAME),
01654 SetDataTip(SPR_IMG_TOWN, STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF), SetFill(1, 1),
01655 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_ROUTES),
01656 SetDataTip(SPR_IMG_SHOW_ROUTES, STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON), SetFill(1, 1),
01657 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEGETATION),
01658 SetDataTip(SPR_IMG_PLANTTREES, STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP), SetFill(1, 1),
01659 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_OWNERS),
01660 SetDataTip(SPR_IMG_COMPANY_GENERAL, STR_SMALLMAP_TOOLTIP_SHOW_LAND_OWNERS_ON_MAP), SetFill(1, 1),
01661 EndContainer(),
01662 NWidget(NWID_SPACER), SetResize(0, 1),
01663 EndContainer(),
01664 EndContainer(),
01665 EndContainer(),
01666 };
01667
01668 static NWidgetBase *SmallMapDisplay(int *biggest_index)
01669 {
01670 NWidgetContainer *map_display = new NWidgetSmallmapDisplay;
01671
01672 MakeNWidgets(_nested_smallmap_display, lengthof(_nested_smallmap_display), biggest_index, map_display);
01673 MakeNWidgets(_nested_smallmap_bar, lengthof(_nested_smallmap_bar), biggest_index, map_display);
01674 return map_display;
01675 }
01676
01677
01678 static const NWidgetPart _nested_smallmap_widgets[] = {
01679 NWidget(NWID_HORIZONTAL),
01680 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
01681 NWidget(WWT_CAPTION, COLOUR_BROWN, SM_WIDGET_CAPTION), SetDataTip(STR_SMALLMAP_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01682 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
01683 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
01684 EndContainer(),
01685 NWidgetFunction(SmallMapDisplay),
01686
01687 NWidget(NWID_HORIZONTAL),
01688 NWidget(WWT_PANEL, COLOUR_BROWN),
01689 NWidget(NWID_HORIZONTAL),
01690 NWidget(NWID_SELECTION, INVALID_COLOUR, SM_WIDGET_SELECT_BUTTONS),
01691 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01692 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, SM_WIDGET_ENABLE_ALL), SetDataTip(STR_SMALLMAP_ENABLE_ALL, STR_NULL),
01693 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, SM_WIDGET_DISABLE_ALL), SetDataTip(STR_SMALLMAP_DISABLE_ALL, STR_NULL),
01694 NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_SHOW_HEIGHT), SetDataTip(STR_SMALLMAP_SHOW_HEIGHT, STR_SMALLMAP_TOOLTIP_SHOW_HEIGHT),
01695 EndContainer(),
01696 NWidget(NWID_SPACER), SetFill(1, 1),
01697 EndContainer(),
01698 NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
01699 EndContainer(),
01700 EndContainer(),
01701 NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
01702 EndContainer(),
01703 };
01704
01705 static const WindowDesc _smallmap_desc(
01706 WDP_AUTO, 446, 314,
01707 WC_SMALLMAP, WC_NONE,
01708 WDF_UNCLICK_BUTTONS,
01709 _nested_smallmap_widgets, lengthof(_nested_smallmap_widgets)
01710 );
01711
01712 void ShowSmallMap()
01713 {
01714 AllocateWindowDescFront<SmallMapWindow>(&_smallmap_desc, 0);
01715 }
01716
01725 bool ScrollMainWindowTo(int x, int y, int z, bool instant)
01726 {
01727 bool res = ScrollWindowTo(x, y, z, FindWindowById(WC_MAIN_WINDOW, 0), instant);
01728
01729
01730
01731
01732
01733 if (res) return res;
01734
01735 SmallMapWindow *w = dynamic_cast<SmallMapWindow*>(FindWindowById(WC_SMALLMAP, 0));
01736 if (w != NULL) w->SmallMapCenterOnCurrentPos();
01737
01738 return res;
01739 }