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 || 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
01430 virtual void OnInvalidateData(int data)
01431 {
01432 switch (data) {
01433 case 1:
01434
01435 this->ReInit();
01436 break;
01437
01438 case 0: {
01439 extern uint64 _displayed_industries;
01440 if (this->map_type != SMT_INDUSTRY) this->SwitchMapType(SMT_INDUSTRY);
01441
01442 for (int i = 0; i != _smallmap_industry_count; i++) {
01443 _legend_from_industries[i].show_on_map = HasBit(_displayed_industries, _legend_from_industries[i].type);
01444 }
01445 break;
01446 }
01447
01448 default: NOT_REACHED();
01449 }
01450 this->SetDirty();
01451 }
01452
01453 virtual bool OnRightClick(Point pt, int widget)
01454 {
01455 if (widget != SM_WIDGET_MAP || _scrolling_viewport) return false;
01456
01457 _scrolling_viewport = true;
01458 return true;
01459 }
01460
01461 virtual void OnMouseWheel(int wheel)
01462 {
01463 if (_settings_client.gui.scrollwheel_scrolling == 0) {
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
01474 virtual void OnTick()
01475 {
01476
01477 if (--this->refresh != 0) return;
01478
01479 this->refresh = FORCE_REFRESH_PERIOD;
01480 this->SetDirty();
01481 }
01482
01490 void SetNewScroll(int sx, int sy, int sub)
01491 {
01492 const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01493 Point hv = InverseRemapCoords(wi->current_x * TILE_SIZE / 2, wi->current_y * TILE_SIZE / 2);
01494 hv.x *= this->zoom;
01495 hv.y *= this->zoom;
01496
01497 if (sx < -hv.x) {
01498 sx = -hv.x;
01499 sub = 0;
01500 }
01501 if (sx > (int)(MapMaxX() * TILE_SIZE) - hv.x) {
01502 sx = MapMaxX() * TILE_SIZE - hv.x;
01503 sub = 0;
01504 }
01505 if (sy < -hv.y) {
01506 sy = -hv.y;
01507 sub = 0;
01508 }
01509 if (sy > (int)(MapMaxY() * TILE_SIZE) - hv.y) {
01510 sy = MapMaxY() * TILE_SIZE - hv.y;
01511 sub = 0;
01512 }
01513
01514 this->scroll_x = sx;
01515 this->scroll_y = sy;
01516 this->subscroll = sub;
01517 }
01518
01519 virtual void OnScroll(Point delta)
01520 {
01521 _cursor.fix_at = true;
01522
01523
01524 int sub;
01525 Point pt = this->PixelToTile(delta.x, delta.y, &sub);
01526 this->SetNewScroll(this->scroll_x + pt.x * TILE_SIZE, this->scroll_y + pt.y * TILE_SIZE, sub);
01527
01528 this->SetDirty();
01529 }
01530
01531 void SmallMapCenterOnCurrentPos()
01532 {
01533 const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
01534 Point pt = InverseRemapCoords(vp->virtual_left + vp->virtual_width / 2, vp->virtual_top + vp->virtual_height / 2);
01535
01536 int sub;
01537 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
01538 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);
01539 this->SetNewScroll(sxy.x, sxy.y, sub);
01540 this->SetDirty();
01541 }
01542 };
01543
01544 SmallMapWindow::SmallMapType SmallMapWindow::map_type = SMT_CONTOUR;
01545 bool SmallMapWindow::show_towns = true;
01546
01555 class NWidgetSmallmapDisplay : public NWidgetContainer {
01556 const SmallMapWindow *smallmap_window;
01557 public:
01558 NWidgetSmallmapDisplay() : NWidgetContainer(NWID_VERTICAL)
01559 {
01560 this->smallmap_window = NULL;
01561 }
01562
01563 virtual void SetupSmallestSize(Window *w, bool init_array)
01564 {
01565 NWidgetBase *display = this->head;
01566 NWidgetBase *bar = display->next;
01567
01568 display->SetupSmallestSize(w, init_array);
01569 bar->SetupSmallestSize(w, init_array);
01570
01571 this->smallmap_window = dynamic_cast<SmallMapWindow *>(w);
01572 this->smallest_x = max(display->smallest_x, bar->smallest_x + smallmap_window->GetMinLegendWidth());
01573 this->smallest_y = display->smallest_y + max(bar->smallest_y, smallmap_window->GetLegendHeight(smallmap_window->min_number_of_columns));
01574 this->fill_x = max(display->fill_x, bar->fill_x);
01575 this->fill_y = (display->fill_y == 0 && bar->fill_y == 0) ? 0 : min(display->fill_y, bar->fill_y);
01576 this->resize_x = max(display->resize_x, bar->resize_x);
01577 this->resize_y = min(display->resize_y, bar->resize_y);
01578 }
01579
01580 virtual void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
01581 {
01582 this->pos_x = x;
01583 this->pos_y = y;
01584 this->current_x = given_width;
01585 this->current_y = given_height;
01586
01587 NWidgetBase *display = this->head;
01588 NWidgetBase *bar = display->next;
01589
01590 if (sizing == ST_SMALLEST) {
01591 this->smallest_x = given_width;
01592 this->smallest_y = given_height;
01593
01594 display->AssignSizePosition(ST_SMALLEST, x, y, display->smallest_x, display->smallest_y, rtl);
01595 bar->AssignSizePosition(ST_SMALLEST, x, y + display->smallest_y, bar->smallest_x, bar->smallest_y, rtl);
01596 }
01597
01598 uint bar_height = max(bar->smallest_y, this->smallmap_window->GetLegendHeight(this->smallmap_window->GetNumberColumnsLegend(given_width - bar->smallest_x)));
01599 uint display_height = given_height - bar_height;
01600 display->AssignSizePosition(ST_RESIZE, x, y, given_width, display_height, rtl);
01601 bar->AssignSizePosition(ST_RESIZE, x, y + display_height, given_width, bar_height, rtl);
01602 }
01603
01604 virtual NWidgetCore *GetWidgetFromPos(int x, int y)
01605 {
01606 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
01607 for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
01608 NWidgetCore *widget = child_wid->GetWidgetFromPos(x, y);
01609 if (widget != NULL) return widget;
01610 }
01611 return NULL;
01612 }
01613
01614 virtual void Draw(const Window *w)
01615 {
01616 for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) child_wid->Draw(w);
01617 }
01618 };
01619
01621 static const NWidgetPart _nested_smallmap_display[] = {
01622 NWidget(WWT_PANEL, COLOUR_BROWN, SM_WIDGET_MAP_BORDER),
01623 NWidget(WWT_INSET, COLOUR_BROWN, SM_WIDGET_MAP), SetMinimalSize(346, 140), SetResize(1, 1), SetPadding(2, 2, 2, 2), EndContainer(),
01624 EndContainer(),
01625 };
01626
01628 static const NWidgetPart _nested_smallmap_bar[] = {
01629 NWidget(WWT_PANEL, COLOUR_BROWN),
01630 NWidget(NWID_HORIZONTAL),
01631 NWidget(WWT_EMPTY, INVALID_COLOUR, SM_WIDGET_LEGEND), SetResize(1, 1),
01632 NWidget(NWID_VERTICAL),
01633
01634 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01635 NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_IN),
01636 SetDataTip(SPR_IMG_ZOOMIN, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN), SetFill(1, 1),
01637 NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_CENTERMAP),
01638 SetDataTip(SPR_IMG_SMALLMAP, STR_SMALLMAP_CENTER), SetFill(1, 1),
01639 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_CONTOUR),
01640 SetDataTip(SPR_IMG_SHOW_COUNTOURS, STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP), SetFill(1, 1),
01641 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEHICLES),
01642 SetDataTip(SPR_IMG_SHOW_VEHICLES, STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP), SetFill(1, 1),
01643 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_INDUSTRIES),
01644 SetDataTip(SPR_IMG_INDUSTRY, STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRIES_ON_MAP), SetFill(1, 1),
01645 EndContainer(),
01646
01647 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01648 NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_OUT),
01649 SetDataTip(SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT), SetFill(1, 1),
01650 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_TOGGLETOWNNAME),
01651 SetDataTip(SPR_IMG_TOWN, STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF), SetFill(1, 1),
01652 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_ROUTES),
01653 SetDataTip(SPR_IMG_SHOW_ROUTES, STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON), SetFill(1, 1),
01654 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEGETATION),
01655 SetDataTip(SPR_IMG_PLANTTREES, STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP), SetFill(1, 1),
01656 NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_OWNERS),
01657 SetDataTip(SPR_IMG_COMPANY_GENERAL, STR_SMALLMAP_TOOLTIP_SHOW_LAND_OWNERS_ON_MAP), SetFill(1, 1),
01658 EndContainer(),
01659 NWidget(NWID_SPACER), SetResize(0, 1),
01660 EndContainer(),
01661 EndContainer(),
01662 EndContainer(),
01663 };
01664
01665 static NWidgetBase *SmallMapDisplay(int *biggest_index)
01666 {
01667 NWidgetContainer *map_display = new NWidgetSmallmapDisplay;
01668
01669 MakeNWidgets(_nested_smallmap_display, lengthof(_nested_smallmap_display), biggest_index, map_display);
01670 MakeNWidgets(_nested_smallmap_bar, lengthof(_nested_smallmap_bar), biggest_index, map_display);
01671 return map_display;
01672 }
01673
01674
01675 static const NWidgetPart _nested_smallmap_widgets[] = {
01676 NWidget(NWID_HORIZONTAL),
01677 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
01678 NWidget(WWT_CAPTION, COLOUR_BROWN, SM_WIDGET_CAPTION), SetDataTip(STR_SMALLMAP_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01679 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
01680 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
01681 EndContainer(),
01682 NWidgetFunction(SmallMapDisplay),
01683
01684 NWidget(NWID_HORIZONTAL),
01685 NWidget(WWT_PANEL, COLOUR_BROWN),
01686 NWidget(NWID_HORIZONTAL),
01687 NWidget(NWID_SELECTION, INVALID_COLOUR, SM_WIDGET_SELECT_BUTTONS),
01688 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01689 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, SM_WIDGET_ENABLE_ALL), SetDataTip(STR_SMALLMAP_ENABLE_ALL, STR_NULL),
01690 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, SM_WIDGET_DISABLE_ALL), SetDataTip(STR_SMALLMAP_DISABLE_ALL, STR_NULL),
01691 NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_SHOW_HEIGHT), SetDataTip(STR_SMALLMAP_SHOW_HEIGHT, STR_SMALLMAP_TOOLTIP_SHOW_HEIGHT),
01692 EndContainer(),
01693 NWidget(NWID_SPACER), SetFill(1, 1),
01694 EndContainer(),
01695 NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
01696 EndContainer(),
01697 EndContainer(),
01698 NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
01699 EndContainer(),
01700 };
01701
01702 static const WindowDesc _smallmap_desc(
01703 WDP_AUTO, 446, 314,
01704 WC_SMALLMAP, WC_NONE,
01705 WDF_UNCLICK_BUTTONS,
01706 _nested_smallmap_widgets, lengthof(_nested_smallmap_widgets)
01707 );
01708
01709 void ShowSmallMap()
01710 {
01711 AllocateWindowDescFront<SmallMapWindow>(&_smallmap_desc, 0);
01712 }
01713
01722 bool ScrollMainWindowTo(int x, int y, int z, bool instant)
01723 {
01724 bool res = ScrollWindowTo(x, y, z, FindWindowById(WC_MAIN_WINDOW, 0), instant);
01725
01726
01727
01728
01729
01730 if (res) return res;
01731
01732 SmallMapWindow *w = dynamic_cast<SmallMapWindow*>(FindWindowById(WC_SMALLMAP, 0));
01733 if (w != NULL) w->SmallMapCenterOnCurrentPos();
01734
01735 return res;
01736 }