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 "widgets/smallmap_widget.h"
00031
00032 #include "table/strings.h"
00033
00034 static int _smallmap_industry_count;
00035 static int _smallmap_company_count;
00036
00037 static const int NUM_NO_COMPANY_ENTRIES = 4;
00038
00039 static const uint8 PC_ROUGH_LAND = 0x52;
00040 static const uint8 PC_GRASS_LAND = 0x54;
00041 static const uint8 PC_BARE_LAND = 0x37;
00042 static const uint8 PC_FIELDS = 0x25;
00043 static const uint8 PC_TREES = 0x57;
00044 static const uint8 PC_WATER = 0xCA;
00045
00047 #define MK(a, b) {a, b, INVALID_INDUSTRYTYPE, 0, INVALID_COMPANY, true, false, false}
00048
00050 #define MC(height) {0, STR_TINY_BLACK_HEIGHT, INVALID_INDUSTRYTYPE, height, INVALID_COMPANY, true, false, false}
00051
00053 #define MO(a, b) {a, b, INVALID_INDUSTRYTYPE, 0, INVALID_COMPANY, true, false, false}
00054
00056 #define MOEND() {0, 0, INVALID_INDUSTRYTYPE, 0, OWNER_NONE, true, true, false}
00057
00059 #define MKEND() {0, STR_NULL, INVALID_INDUSTRYTYPE, 0, INVALID_COMPANY, true, true, false}
00060
00065 #define MS(a, b) {a, b, INVALID_INDUSTRYTYPE, 0, INVALID_COMPANY, true, false, true}
00066
00068 struct LegendAndColour {
00069 uint8 colour;
00070 StringID legend;
00071 IndustryType type;
00072 uint8 height;
00073 CompanyID company;
00074 bool show_on_map;
00075 bool end;
00076 bool col_break;
00077 };
00078
00080 static LegendAndColour _legend_land_contours[] = {
00081
00082 MC(0),
00083 MC(4),
00084 MC(8),
00085 MC(12),
00086 MC(14),
00087
00088 MS(PC_BLACK, STR_SMALLMAP_LEGENDA_ROADS),
00089 MK(PC_GREY, STR_SMALLMAP_LEGENDA_RAILROADS),
00090 MK(PC_LIGHT_BLUE, STR_SMALLMAP_LEGENDA_STATIONS_AIRPORTS_DOCKS),
00091 MK(PC_DARK_RED, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
00092 MK(PC_WHITE, STR_SMALLMAP_LEGENDA_VEHICLES),
00093 MKEND()
00094 };
00095
00096 static const LegendAndColour _legend_vehicles[] = {
00097 MK(PC_RED, STR_SMALLMAP_LEGENDA_TRAINS),
00098 MK(PC_YELLOW, STR_SMALLMAP_LEGENDA_ROAD_VEHICLES),
00099 MK(PC_LIGHT_BLUE, STR_SMALLMAP_LEGENDA_SHIPS),
00100 MK(PC_WHITE, STR_SMALLMAP_LEGENDA_AIRCRAFT),
00101
00102 MS(PC_BLACK, STR_SMALLMAP_LEGENDA_TRANSPORT_ROUTES),
00103 MK(PC_DARK_RED, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
00104 MKEND()
00105 };
00106
00107 static const LegendAndColour _legend_routes[] = {
00108 MK(PC_BLACK, STR_SMALLMAP_LEGENDA_ROADS),
00109 MK(PC_GREY, STR_SMALLMAP_LEGENDA_RAILROADS),
00110 MK(PC_DARK_RED, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
00111
00112 MS(PC_VERY_DARK_BROWN, STR_SMALLMAP_LEGENDA_RAILROAD_STATION),
00113 MK(PC_ORANGE, STR_SMALLMAP_LEGENDA_TRUCK_LOADING_BAY),
00114 MK(PC_YELLOW, STR_SMALLMAP_LEGENDA_BUS_STATION),
00115 MK(PC_RED, STR_SMALLMAP_LEGENDA_AIRPORT_HELIPORT),
00116 MK(PC_LIGHT_BLUE, STR_SMALLMAP_LEGENDA_DOCK),
00117 MKEND()
00118 };
00119
00120 static const LegendAndColour _legend_vegetation[] = {
00121 MK(PC_ROUGH_LAND, STR_SMALLMAP_LEGENDA_ROUGH_LAND),
00122 MK(PC_GRASS_LAND, STR_SMALLMAP_LEGENDA_GRASS_LAND),
00123 MK(PC_BARE_LAND, STR_SMALLMAP_LEGENDA_BARE_LAND),
00124 MK(PC_FIELDS, STR_SMALLMAP_LEGENDA_FIELDS),
00125 MK(PC_TREES, STR_SMALLMAP_LEGENDA_TREES),
00126 MK(PC_GREEN, STR_SMALLMAP_LEGENDA_FOREST),
00127
00128 MS(PC_GREY, STR_SMALLMAP_LEGENDA_ROCKS),
00129 MK(PC_ORANGE, STR_SMALLMAP_LEGENDA_DESERT),
00130 MK(PC_LIGHT_BLUE, STR_SMALLMAP_LEGENDA_SNOW),
00131 MK(PC_BLACK, STR_SMALLMAP_LEGENDA_TRANSPORT_ROUTES),
00132 MK(PC_DARK_RED, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
00133 MKEND()
00134 };
00135
00136 static LegendAndColour _legend_land_owners[NUM_NO_COMPANY_ENTRIES + MAX_COMPANIES + 1] = {
00137 MO(PC_WATER, STR_SMALLMAP_LEGENDA_WATER),
00138 MO(0x00, STR_SMALLMAP_LEGENDA_NO_OWNER),
00139 MO(PC_DARK_RED, STR_SMALLMAP_LEGENDA_TOWNS),
00140 MO(PC_DARK_GREY, STR_SMALLMAP_LEGENDA_INDUSTRIES),
00141
00142 MOEND(),
00143 };
00144
00145 #undef MK
00146 #undef MC
00147 #undef MS
00148 #undef MO
00149 #undef MOEND
00150 #undef MKEND
00151
00156 static LegendAndColour _legend_from_industries[NUM_INDUSTRYTYPES + 1];
00158 static uint _industry_to_list_pos[NUM_INDUSTRYTYPES];
00160 static bool _smallmap_show_heightmap = false;
00162 static uint _company_to_list_pos[MAX_COMPANIES];
00163
00167 void BuildIndustriesLegend()
00168 {
00169 uint j = 0;
00170
00171
00172 for (uint8 i = 0; i < NUM_INDUSTRYTYPES; i++) {
00173 IndustryType ind = _sorted_industry_types[i];
00174 const IndustrySpec *indsp = GetIndustrySpec(ind);
00175 if (indsp->enabled) {
00176 _legend_from_industries[j].legend = indsp->name;
00177 _legend_from_industries[j].colour = indsp->map_colour;
00178 _legend_from_industries[j].type = ind;
00179 _legend_from_industries[j].show_on_map = true;
00180 _legend_from_industries[j].col_break = false;
00181 _legend_from_industries[j].end = false;
00182
00183
00184 _industry_to_list_pos[ind] = j;
00185 j++;
00186 }
00187 }
00188
00189 _legend_from_industries[j].end = true;
00190
00191
00192 _smallmap_industry_count = j;
00193 }
00194
00195 static const LegendAndColour * const _legend_table[] = {
00196 _legend_land_contours,
00197 _legend_vehicles,
00198 _legend_from_industries,
00199 _legend_routes,
00200 _legend_vegetation,
00201 _legend_land_owners,
00202 };
00203
00204 #define MKCOLOUR(x) TO_LE32X(x)
00205
00206 #define MKCOLOUR_XXXX(x) (MKCOLOUR(0x01010101) * (uint)(x))
00207 #define MKCOLOUR_X0X0(x) (MKCOLOUR(0x01000100) * (uint)(x))
00208 #define MKCOLOUR_0X0X(x) (MKCOLOUR(0x00010001) * (uint)(x))
00209 #define MKCOLOUR_0XX0(x) (MKCOLOUR(0x00010100) * (uint)(x))
00210 #define MKCOLOUR_X00X(x) (MKCOLOUR(0x01000001) * (uint)(x))
00211
00212 #define MKCOLOUR_XYXY(x, y) (MKCOLOUR_X0X0(x) | MKCOLOUR_0X0X(y))
00213 #define MKCOLOUR_XYYX(x, y) (MKCOLOUR_X00X(x) | MKCOLOUR_0XX0(y))
00214
00215 #define MKCOLOUR_0000 MKCOLOUR_XXXX(0x00)
00216 #define MKCOLOUR_0FF0 MKCOLOUR_0XX0(0xFF)
00217 #define MKCOLOUR_F00F MKCOLOUR_X00X(0xFF)
00218 #define MKCOLOUR_FFFF MKCOLOUR_XXXX(0xFF)
00219
00221 static const uint32 _green_map_heights[] = {
00222 MKCOLOUR_XXXX(0x5A),
00223 MKCOLOUR_XYXY(0x5A, 0x5B),
00224 MKCOLOUR_XXXX(0x5B),
00225 MKCOLOUR_XYXY(0x5B, 0x5C),
00226 MKCOLOUR_XXXX(0x5C),
00227 MKCOLOUR_XYXY(0x5C, 0x5D),
00228 MKCOLOUR_XXXX(0x5D),
00229 MKCOLOUR_XYXY(0x5D, 0x5E),
00230 MKCOLOUR_XXXX(0x5E),
00231 MKCOLOUR_XYXY(0x5E, 0x5F),
00232 MKCOLOUR_XXXX(0x5F),
00233 MKCOLOUR_XYXY(0x5F, 0x1F),
00234 MKCOLOUR_XXXX(0x1F),
00235 MKCOLOUR_XYXY(0x1F, 0x27),
00236 MKCOLOUR_XXXX(0x27),
00237 MKCOLOUR_XXXX(0x27),
00238 };
00239 assert_compile(lengthof(_green_map_heights) == MAX_TILE_HEIGHT + 1);
00240
00242 static const uint32 _dark_green_map_heights[] = {
00243 MKCOLOUR_XXXX(0x60),
00244 MKCOLOUR_XYXY(0x60, 0x61),
00245 MKCOLOUR_XXXX(0x61),
00246 MKCOLOUR_XYXY(0x61, 0x62),
00247 MKCOLOUR_XXXX(0x62),
00248 MKCOLOUR_XYXY(0x62, 0x63),
00249 MKCOLOUR_XXXX(0x63),
00250 MKCOLOUR_XYXY(0x63, 0x64),
00251 MKCOLOUR_XXXX(0x64),
00252 MKCOLOUR_XYXY(0x64, 0x65),
00253 MKCOLOUR_XXXX(0x65),
00254 MKCOLOUR_XYXY(0x65, 0x66),
00255 MKCOLOUR_XXXX(0x66),
00256 MKCOLOUR_XYXY(0x66, 0x67),
00257 MKCOLOUR_XXXX(0x67),
00258 MKCOLOUR_XXXX(0x67),
00259 };
00260 assert_compile(lengthof(_dark_green_map_heights) == MAX_TILE_HEIGHT + 1);
00261
00263 static const uint32 _violet_map_heights[] = {
00264 MKCOLOUR_XXXX(0x80),
00265 MKCOLOUR_XYXY(0x80, 0x81),
00266 MKCOLOUR_XXXX(0x81),
00267 MKCOLOUR_XYXY(0x81, 0x82),
00268 MKCOLOUR_XXXX(0x82),
00269 MKCOLOUR_XYXY(0x82, 0x83),
00270 MKCOLOUR_XXXX(0x83),
00271 MKCOLOUR_XYXY(0x83, 0x84),
00272 MKCOLOUR_XXXX(0x84),
00273 MKCOLOUR_XYXY(0x84, 0x85),
00274 MKCOLOUR_XXXX(0x85),
00275 MKCOLOUR_XYXY(0x85, 0x86),
00276 MKCOLOUR_XXXX(0x86),
00277 MKCOLOUR_XYXY(0x86, 0x87),
00278 MKCOLOUR_XXXX(0x87),
00279 MKCOLOUR_XXXX(0x87),
00280 };
00281 assert_compile(lengthof(_violet_map_heights) == MAX_TILE_HEIGHT + 1);
00282
00284 struct SmallMapColourScheme {
00285 const uint32 *height_colours;
00286 uint32 default_colour;
00287 };
00288
00290 static const SmallMapColourScheme _heightmap_schemes[] = {
00291 {_green_map_heights, MKCOLOUR_XXXX(0x54)},
00292 {_dark_green_map_heights, MKCOLOUR_XXXX(0x62)},
00293 {_violet_map_heights, MKCOLOUR_XXXX(0x82)},
00294 };
00295
00299 void BuildLandLegend()
00300 {
00301 for (LegendAndColour *lc = _legend_land_contours; lc->legend == STR_TINY_BLACK_HEIGHT; lc++) {
00302 lc->colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[lc->height];
00303 }
00304 }
00305
00309 void BuildOwnerLegend()
00310 {
00311 _legend_land_owners[1].colour = _heightmap_schemes[_settings_client.gui.smallmap_land_colour].default_colour;
00312
00313 int i = NUM_NO_COMPANY_ENTRIES;
00314 const Company *c;
00315 FOR_ALL_COMPANIES(c) {
00316 _legend_land_owners[i].colour = _colour_gradient[c->colour][5];
00317 _legend_land_owners[i].company = c->index;
00318 _legend_land_owners[i].show_on_map = true;
00319 _legend_land_owners[i].col_break = false;
00320 _legend_land_owners[i].end = false;
00321 _company_to_list_pos[c->index] = i;
00322 i++;
00323 }
00324
00325
00326 _legend_land_owners[i].end = true;
00327
00328
00329 _smallmap_company_count = i;
00330 }
00331
00332 struct AndOr {
00333 uint32 mor;
00334 uint32 mand;
00335 };
00336
00337 static inline uint32 ApplyMask(uint32 colour, const AndOr *mask)
00338 {
00339 return (colour & mask->mand) | mask->mor;
00340 }
00341
00342
00344 static const AndOr _smallmap_contours_andor[] = {
00345 {MKCOLOUR_0000 , MKCOLOUR_FFFF},
00346 {MKCOLOUR_0XX0(PC_GREY ), MKCOLOUR_F00F},
00347 {MKCOLOUR_0XX0(PC_BLACK ), MKCOLOUR_F00F},
00348 {MKCOLOUR_0XX0(PC_DARK_RED ), MKCOLOUR_F00F},
00349 {MKCOLOUR_0000 , MKCOLOUR_FFFF},
00350 {MKCOLOUR_XXXX(PC_LIGHT_BLUE), MKCOLOUR_0000},
00351 {MKCOLOUR_XXXX(PC_WATER ), MKCOLOUR_0000},
00352 {MKCOLOUR_0000 , MKCOLOUR_FFFF},
00353 {MKCOLOUR_XXXX(PC_DARK_RED ), MKCOLOUR_0000},
00354 {MKCOLOUR_0000 , MKCOLOUR_FFFF},
00355 {MKCOLOUR_0XX0(PC_DARK_RED ), MKCOLOUR_F00F},
00356 {MKCOLOUR_0XX0(PC_GREY ), MKCOLOUR_F00F},
00357 };
00358
00360 static const AndOr _smallmap_vehicles_andor[] = {
00361 {MKCOLOUR_0000 , MKCOLOUR_FFFF},
00362 {MKCOLOUR_0XX0(PC_BLACK ), MKCOLOUR_F00F},
00363 {MKCOLOUR_0XX0(PC_BLACK ), MKCOLOUR_F00F},
00364 {MKCOLOUR_0XX0(PC_DARK_RED ), MKCOLOUR_F00F},
00365 {MKCOLOUR_0000 , MKCOLOUR_FFFF},
00366 {MKCOLOUR_0XX0(PC_BLACK ), MKCOLOUR_F00F},
00367 {MKCOLOUR_XXXX(PC_WATER ), MKCOLOUR_0000},
00368 {MKCOLOUR_0000 , MKCOLOUR_FFFF},
00369 {MKCOLOUR_XXXX(PC_DARK_RED ), MKCOLOUR_0000},
00370 {MKCOLOUR_0000 , MKCOLOUR_FFFF},
00371 {MKCOLOUR_0XX0(PC_DARK_RED ), MKCOLOUR_F00F},
00372 {MKCOLOUR_0XX0(PC_BLACK ), MKCOLOUR_F00F},
00373 };
00374
00376 static const byte _tiletype_importance[] = {
00377 2,
00378 8,
00379 7,
00380 5,
00381 2,
00382 9,
00383 2,
00384 1,
00385 6,
00386 8,
00387 2,
00388 0,
00389 };
00390
00391
00392 static inline TileType GetEffectiveTileType(TileIndex tile)
00393 {
00394 TileType t = GetTileType(tile);
00395
00396 if (t == MP_TUNNELBRIDGE) {
00397 TransportType tt = GetTunnelBridgeTransportType(tile);
00398
00399 switch (tt) {
00400 case TRANSPORT_RAIL: t = MP_RAILWAY; break;
00401 case TRANSPORT_ROAD: t = MP_ROAD; break;
00402 default: t = MP_WATER; break;
00403 }
00404 }
00405 return t;
00406 }
00407
00414 static inline uint32 GetSmallMapContoursPixels(TileIndex tile, TileType t)
00415 {
00416 const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00417 return ApplyMask(cs->height_colours[TileHeight(tile)], &_smallmap_contours_andor[t]);
00418 }
00419
00427 static inline uint32 GetSmallMapVehiclesPixels(TileIndex tile, TileType t)
00428 {
00429 const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00430 return ApplyMask(cs->default_colour, &_smallmap_vehicles_andor[t]);
00431 }
00432
00440 static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile, TileType t)
00441 {
00442 if (t == MP_INDUSTRY) {
00443
00444 if (_legend_from_industries[_industry_to_list_pos[Industry::GetByTile(tile)->type]].show_on_map) {
00445 return GetIndustrySpec(Industry::GetByTile(tile)->type)->map_colour * 0x01010101;
00446 } else {
00447
00448 t = (IsTileOnWater(tile) ? MP_WATER : MP_CLEAR);
00449 }
00450 }
00451
00452 const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00453 return ApplyMask(_smallmap_show_heightmap ? cs->height_colours[TileHeight(tile)] : cs->default_colour, &_smallmap_vehicles_andor[t]);
00454 }
00455
00463 static inline uint32 GetSmallMapRoutesPixels(TileIndex tile, TileType t)
00464 {
00465 if (t == MP_STATION) {
00466 switch (GetStationType(tile)) {
00467 case STATION_RAIL: return MKCOLOUR_XXXX(PC_VERY_DARK_BROWN);
00468 case STATION_AIRPORT: return MKCOLOUR_XXXX(PC_RED);
00469 case STATION_TRUCK: return MKCOLOUR_XXXX(PC_ORANGE);
00470 case STATION_BUS: return MKCOLOUR_XXXX(PC_YELLOW);
00471 case STATION_DOCK: return MKCOLOUR_XXXX(PC_LIGHT_BLUE);
00472 default: return MKCOLOUR_FFFF;
00473 }
00474 } else if (t == MP_RAILWAY) {
00475 AndOr andor = {
00476 MKCOLOUR_0XX0(GetRailTypeInfo(GetRailType(tile))->map_colour),
00477 _smallmap_contours_andor[t].mand
00478 };
00479
00480 const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00481 return ApplyMask(cs->default_colour, &andor);
00482 }
00483
00484
00485 const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00486 return ApplyMask(cs->default_colour, &_smallmap_contours_andor[t]);
00487 }
00488
00489
00490 static const uint32 _vegetation_clear_bits[] = {
00491 MKCOLOUR_XXXX(PC_GRASS_LAND),
00492 MKCOLOUR_XXXX(PC_ROUGH_LAND),
00493 MKCOLOUR_XXXX(PC_GREY),
00494 MKCOLOUR_XXXX(PC_FIELDS),
00495 MKCOLOUR_XXXX(PC_LIGHT_BLUE),
00496 MKCOLOUR_XXXX(PC_ORANGE),
00497 MKCOLOUR_XXXX(PC_GRASS_LAND),
00498 MKCOLOUR_XXXX(PC_GRASS_LAND),
00499 };
00500
00508 static inline uint32 GetSmallMapVegetationPixels(TileIndex tile, TileType t)
00509 {
00510 switch (t) {
00511 case MP_CLEAR:
00512 return (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) < 3) ? MKCOLOUR_XXXX(PC_BARE_LAND) : _vegetation_clear_bits[GetClearGround(tile)];
00513
00514 case MP_INDUSTRY:
00515 return IsTileForestIndustry(tile) ? MKCOLOUR_XXXX(PC_GREEN) : MKCOLOUR_XXXX(PC_DARK_RED);
00516
00517 case MP_TREES:
00518 if (GetTreeGround(tile) == TREE_GROUND_SNOW_DESERT || GetTreeGround(tile) == TREE_GROUND_ROUGH_SNOW) {
00519 return (_settings_game.game_creation.landscape == LT_ARCTIC) ? MKCOLOUR_XYYX(PC_LIGHT_BLUE, PC_TREES) : MKCOLOUR_XYYX(PC_ORANGE, PC_TREES);
00520 }
00521 return MKCOLOUR_XYYX(PC_GRASS_LAND, PC_TREES);
00522
00523 default:
00524 return ApplyMask(MKCOLOUR_XXXX(PC_GRASS_LAND), &_smallmap_vehicles_andor[t]);
00525 }
00526 }
00527
00535 static inline uint32 GetSmallMapOwnerPixels(TileIndex tile, TileType t)
00536 {
00537 Owner o;
00538
00539 switch (t) {
00540 case MP_INDUSTRY: return MKCOLOUR_XXXX(PC_DARK_GREY);
00541 case MP_HOUSE: return MKCOLOUR_XXXX(PC_DARK_RED);
00542 default: o = GetTileOwner(tile); break;
00543
00544
00545
00546
00547 }
00548
00549 if ((o < MAX_COMPANIES && !_legend_land_owners[_company_to_list_pos[o]].show_on_map) || o == OWNER_NONE || o == OWNER_WATER) {
00550 if (t == MP_WATER) return MKCOLOUR_XXXX(PC_WATER);
00551 const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
00552 return _smallmap_show_heightmap ? cs->height_colours[TileHeight(tile)] : cs->default_colour;
00553 } else if (o == OWNER_TOWN) {
00554 return MKCOLOUR_XXXX(PC_DARK_RED);
00555 }
00556
00557 return MKCOLOUR_XXXX(_legend_land_owners[_company_to_list_pos[o]].colour);
00558 }
00559
00561 static const byte _vehicle_type_colours[6] = {
00562 PC_RED, PC_YELLOW, PC_LIGHT_BLUE, PC_WHITE, PC_BLACK, PC_RED
00563 };
00564
00565
00567 class SmallMapWindow : public Window {
00569 enum SmallMapType {
00570 SMT_CONTOUR,
00571 SMT_VEHICLES,
00572 SMT_INDUSTRY,
00573 SMT_ROUTES,
00574 SMT_VEGETATION,
00575 SMT_OWNER,
00576 };
00577
00579 enum ZoomLevelChange {
00580 ZLC_INITIALIZE,
00581 ZLC_ZOOM_OUT,
00582 ZLC_ZOOM_IN,
00583 };
00584
00585 static SmallMapType map_type;
00586 static bool show_towns;
00587
00588 static const uint LEGEND_BLOB_WIDTH = 8;
00589 static const uint INDUSTRY_MIN_NUMBER_OF_COLUMNS = 2;
00590 uint min_number_of_fixed_rows;
00591 uint column_width;
00592
00593 int32 scroll_x;
00594 int32 scroll_y;
00595 int32 subscroll;
00596 int zoom;
00597
00598 static const uint8 FORCE_REFRESH_PERIOD = 0x1F;
00599 uint8 refresh;
00600
00601 inline Point SmallmapRemapCoords(int x, int y) const
00602 {
00603 Point pt;
00604 pt.x = (y - x) * 2;
00605 pt.y = y + x;
00606 return pt;
00607 }
00608
00615 inline Point RemapTile(int tile_x, int tile_y) const
00616 {
00617 int x_offset = tile_x - this->scroll_x / (int)TILE_SIZE;
00618 int y_offset = tile_y - this->scroll_y / (int)TILE_SIZE;
00619
00620 if (this->zoom == 1) return SmallmapRemapCoords(x_offset, y_offset);
00621
00622
00623 if (x_offset < 0) x_offset -= this->zoom - 1;
00624 if (y_offset < 0) y_offset -= this->zoom - 1;
00625
00626 return SmallmapRemapCoords(x_offset / this->zoom, y_offset / this->zoom);
00627 }
00628
00639 inline Point PixelToTile(int px, int py, int *sub, bool add_sub = true) const
00640 {
00641 if (add_sub) px += this->subscroll;
00642
00643
00644
00645 Point pt = {((py >> 1) - (px >> 2)) * this->zoom, ((py >> 1) + (px >> 2)) * this->zoom};
00646 px &= 3;
00647
00648 if (py & 1) {
00649 if (px < 2) {
00650 pt.x += this->zoom;
00651 px += 2;
00652 } else {
00653 pt.y += this->zoom;
00654 px -= 2;
00655 }
00656 }
00657
00658 *sub = px;
00659 return pt;
00660 }
00661
00671 Point ComputeScroll(int tx, int ty, int x, int y, int *sub)
00672 {
00673 assert(x >= 0 && y >= 0);
00674
00675 int new_sub;
00676 Point tile_xy = PixelToTile(x, y, &new_sub, false);
00677 tx -= tile_xy.x;
00678 ty -= tile_xy.y;
00679
00680 Point scroll;
00681 if (new_sub == 0) {
00682 *sub = 0;
00683 scroll.x = (tx + this->zoom) * TILE_SIZE;
00684 scroll.y = (ty - this->zoom) * TILE_SIZE;
00685 } else {
00686 *sub = 4 - new_sub;
00687 scroll.x = (tx + 2 * this->zoom) * TILE_SIZE;
00688 scroll.y = (ty - 2 * this->zoom) * TILE_SIZE;
00689 }
00690 return scroll;
00691 }
00692
00699 void SetZoomLevel(ZoomLevelChange change, const Point *zoom_pt)
00700 {
00701 static const int zoomlevels[] = {1, 2, 4, 6, 8};
00702 static const int MIN_ZOOM_INDEX = 0;
00703 static const int MAX_ZOOM_INDEX = lengthof(zoomlevels) - 1;
00704
00705 int new_index, cur_index, sub;
00706 Point tile;
00707 switch (change) {
00708 case ZLC_INITIALIZE:
00709 cur_index = - 1;
00710 new_index = MIN_ZOOM_INDEX;
00711 break;
00712
00713 case ZLC_ZOOM_IN:
00714 case ZLC_ZOOM_OUT:
00715 for (cur_index = MIN_ZOOM_INDEX; cur_index <= MAX_ZOOM_INDEX; cur_index++) {
00716 if (this->zoom == zoomlevels[cur_index]) break;
00717 }
00718 assert(cur_index <= MAX_ZOOM_INDEX);
00719
00720 tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
00721 new_index = Clamp(cur_index + ((change == ZLC_ZOOM_IN) ? -1 : 1), MIN_ZOOM_INDEX, MAX_ZOOM_INDEX);
00722 break;
00723
00724 default: NOT_REACHED();
00725 }
00726
00727 if (new_index != cur_index) {
00728 this->zoom = zoomlevels[new_index];
00729 if (cur_index >= 0) {
00730 Point new_tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
00731 this->SetNewScroll(this->scroll_x + (tile.x - new_tile.x) * TILE_SIZE,
00732 this->scroll_y + (tile.y - new_tile.y) * TILE_SIZE, sub);
00733 }
00734 this->SetWidgetDisabledState(WID_SM_ZOOM_IN, this->zoom == zoomlevels[MIN_ZOOM_INDEX]);
00735 this->SetWidgetDisabledState(WID_SM_ZOOM_OUT, this->zoom == zoomlevels[MAX_ZOOM_INDEX]);
00736 this->SetDirty();
00737 }
00738 }
00739
00745 inline uint32 GetTileColours(const TileArea &ta) const
00746 {
00747 int importance = 0;
00748 TileIndex tile = INVALID_TILE;
00749 TileType et = MP_VOID;
00750
00751 TILE_AREA_LOOP(ti, ta) {
00752 TileType ttype = GetEffectiveTileType(ti);
00753 if (_tiletype_importance[ttype] > importance) {
00754 importance = _tiletype_importance[ttype];
00755 tile = ti;
00756 et = ttype;
00757 }
00758 }
00759
00760 switch (this->map_type) {
00761 case SMT_CONTOUR:
00762 return GetSmallMapContoursPixels(tile, et);
00763
00764 case SMT_VEHICLES:
00765 return GetSmallMapVehiclesPixels(tile, et);
00766
00767 case SMT_INDUSTRY:
00768 return GetSmallMapIndustriesPixels(tile, et);
00769
00770 case SMT_ROUTES:
00771 return GetSmallMapRoutesPixels(tile, et);
00772
00773 case SMT_VEGETATION:
00774 return GetSmallMapVegetationPixels(tile, et);
00775
00776 case SMT_OWNER:
00777 return GetSmallMapOwnerPixels(tile, et);
00778
00779 default: NOT_REACHED();
00780 }
00781 }
00782
00797 void DrawSmallMapColumn(void *dst, uint xc, uint yc, int pitch, int reps, int start_pos, int end_pos, Blitter *blitter) const
00798 {
00799 void *dst_ptr_abs_end = blitter->MoveTo(_screen.dst_ptr, 0, _screen.height);
00800 uint min_xy = _settings_game.construction.freeform_edges ? 1 : 0;
00801
00802 do {
00803
00804 if (xc >= MapMaxX() || yc >= MapMaxY()) continue;
00805
00806
00807 if (dst < _screen.dst_ptr) continue;
00808 if (dst >= dst_ptr_abs_end) continue;
00809
00810
00811 TileArea ta;
00812 if (min_xy == 1 && (xc == 0 || yc == 0)) {
00813 if (this->zoom == 1) continue;
00814
00815 ta = TileArea(TileXY(max(min_xy, xc), max(min_xy, yc)), this->zoom - (xc == 0), this->zoom - (yc == 0));
00816 } else {
00817 ta = TileArea(TileXY(xc, yc), this->zoom, this->zoom);
00818 }
00819 ta.ClampToMap();
00820
00821 uint32 val = this->GetTileColours(ta);
00822 uint8 *val8 = (uint8 *)&val;
00823 int idx = max(0, -start_pos);
00824 for (int pos = max(0, start_pos); pos < end_pos; pos++) {
00825 blitter->SetPixel(dst, idx, 0, val8[idx]);
00826 idx++;
00827 }
00828
00829 } while (xc += this->zoom, yc += this->zoom, dst = blitter->MoveTo(dst, pitch, 0), --reps != 0);
00830 }
00831
00837 void DrawVehicles(const DrawPixelInfo *dpi, Blitter *blitter) const
00838 {
00839 const Vehicle *v;
00840 FOR_ALL_VEHICLES(v) {
00841 if (v->type == VEH_EFFECT) continue;
00842 if (v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) continue;
00843
00844
00845 Point pt = this->RemapTile(v->x_pos / TILE_SIZE, v->y_pos / TILE_SIZE);
00846
00847 int y = pt.y - dpi->top;
00848 if (!IsInsideMM(y, 0, dpi->height)) continue;
00849
00850 bool skip = false;
00851 int x = pt.x - this->subscroll - 3 - dpi->left;
00852 if (x < 0) {
00853
00854
00855 if (++x != 0) continue;
00856 skip = true;
00857 } else if (x >= dpi->width - 1) {
00858
00859 if (x != dpi->width - 1) continue;
00860 skip = true;
00861 }
00862
00863
00864 byte colour = (this->map_type == SMT_VEHICLES) ? _vehicle_type_colours[v->type] : PC_WHITE;
00865
00866
00867 blitter->SetPixel(dpi->dst_ptr, x, y, colour);
00868 if (!skip) blitter->SetPixel(dpi->dst_ptr, x + 1, y, colour);
00869 }
00870 }
00871
00876 void DrawTowns(const DrawPixelInfo *dpi) const
00877 {
00878 const Town *t;
00879 FOR_ALL_TOWNS(t) {
00880
00881 Point pt = this->RemapTile(TileX(t->xy), TileY(t->xy));
00882 int x = pt.x - this->subscroll - (t->cache.sign.width_small >> 1);
00883 int y = pt.y;
00884
00885
00886 if (x + t->cache.sign.width_small > dpi->left &&
00887 x < dpi->left + dpi->width &&
00888 y + FONT_HEIGHT_SMALL > dpi->top &&
00889 y < dpi->top + dpi->height) {
00890
00891 SetDParam(0, t->index);
00892 DrawString(x, x + t->cache.sign.width_small, y, STR_SMALLMAP_TOWN);
00893 }
00894 }
00895 }
00896
00903 static inline void DrawVertMapIndicator(int x, int y, int y2)
00904 {
00905 GfxFillRect(x, y, x, y + 3, PC_VERY_LIGHT_YELLOW);
00906 GfxFillRect(x, y2 - 3, x, y2, PC_VERY_LIGHT_YELLOW);
00907 }
00908
00915 static inline void DrawHorizMapIndicator(int x, int x2, int y)
00916 {
00917 GfxFillRect(x, y, x + 3, y, PC_VERY_LIGHT_YELLOW);
00918 GfxFillRect(x2 - 3, y, x2, y, PC_VERY_LIGHT_YELLOW);
00919 }
00920
00924 void DrawMapIndicators() const
00925 {
00926
00927 const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
00928
00929 Point tile = InverseRemapCoords(vp->virtual_left, vp->virtual_top);
00930 Point tl = this->RemapTile(tile.x >> 4, tile.y >> 4);
00931 tl.x -= this->subscroll;
00932
00933 tile = InverseRemapCoords(vp->virtual_left + vp->virtual_width, vp->virtual_top + vp->virtual_height);
00934 Point br = this->RemapTile(tile.x >> 4, tile.y >> 4);
00935 br.x -= this->subscroll;
00936
00937 SmallMapWindow::DrawVertMapIndicator(tl.x, tl.y, br.y);
00938 SmallMapWindow::DrawVertMapIndicator(br.x, tl.y, br.y);
00939
00940 SmallMapWindow::DrawHorizMapIndicator(tl.x, br.x, tl.y);
00941 SmallMapWindow::DrawHorizMapIndicator(tl.x, br.x, br.y);
00942 }
00943
00955 void DrawSmallMap(DrawPixelInfo *dpi) const
00956 {
00957 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00958 DrawPixelInfo *old_dpi;
00959
00960 old_dpi = _cur_dpi;
00961 _cur_dpi = dpi;
00962
00963
00964 GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, PC_BLACK);
00965
00966
00967 int dx;
00968 Point tile = this->PixelToTile(dpi->left, dpi->top, &dx);
00969 int tile_x = this->scroll_x / (int)TILE_SIZE + tile.x;
00970 int tile_y = this->scroll_y / (int)TILE_SIZE + tile.y;
00971
00972 void *ptr = blitter->MoveTo(dpi->dst_ptr, -dx - 4, 0);
00973 int x = - dx - 4;
00974 int y = 0;
00975
00976 for (;;) {
00977
00978 if (x >= -3) {
00979 if (x >= dpi->width) break;
00980
00981 int end_pos = min(dpi->width, x + 4);
00982 int reps = (dpi->height - y + 1) / 2;
00983 if (reps > 0) {
00984 this->DrawSmallMapColumn(ptr, tile_x, tile_y, dpi->pitch * 2, reps, x, end_pos, blitter);
00985 }
00986 }
00987
00988 if (y == 0) {
00989 tile_y += this->zoom;
00990 y++;
00991 ptr = blitter->MoveTo(ptr, 0, 1);
00992 } else {
00993 tile_x -= this->zoom;
00994 y--;
00995 ptr = blitter->MoveTo(ptr, 0, -1);
00996 }
00997 ptr = blitter->MoveTo(ptr, 2, 0);
00998 x += 2;
00999 }
01000
01001
01002 if (this->map_type == SMT_CONTOUR || this->map_type == SMT_VEHICLES) this->DrawVehicles(dpi, blitter);
01003
01004
01005 if (this->show_towns) this->DrawTowns(dpi);
01006
01007
01008 this->DrawMapIndicators();
01009
01010 _cur_dpi = old_dpi;
01011 }
01012
01016 void SetupWidgetData()
01017 {
01018 StringID legend_tooltip;
01019 StringID enable_all_tooltip;
01020 StringID disable_all_tooltip;
01021 int plane;
01022 switch (this->map_type) {
01023 case SMT_INDUSTRY:
01024 legend_tooltip = STR_SMALLMAP_TOOLTIP_INDUSTRY_SELECTION;
01025 enable_all_tooltip = STR_SMALLMAP_TOOLTIP_ENABLE_ALL_INDUSTRIES;
01026 disable_all_tooltip = STR_SMALLMAP_TOOLTIP_DISABLE_ALL_INDUSTRIES;
01027 plane = 0;
01028 break;
01029
01030 case SMT_OWNER:
01031 legend_tooltip = STR_SMALLMAP_TOOLTIP_COMPANY_SELECTION;
01032 enable_all_tooltip = STR_SMALLMAP_TOOLTIP_ENABLE_ALL_COMPANIES;
01033 disable_all_tooltip = STR_SMALLMAP_TOOLTIP_DISABLE_ALL_COMPANIES;
01034 plane = 0;
01035 break;
01036
01037 default:
01038 legend_tooltip = STR_NULL;
01039 enable_all_tooltip = STR_NULL;
01040 disable_all_tooltip = STR_NULL;
01041 plane = 1;
01042 break;
01043 }
01044
01045 this->GetWidget<NWidgetCore>(WID_SM_LEGEND)->SetDataTip(STR_NULL, legend_tooltip);
01046 this->GetWidget<NWidgetCore>(WID_SM_ENABLE_ALL)->SetDataTip(STR_SMALLMAP_ENABLE_ALL, enable_all_tooltip);
01047 this->GetWidget<NWidgetCore>(WID_SM_DISABLE_ALL)->SetDataTip(STR_SMALLMAP_DISABLE_ALL, disable_all_tooltip);
01048 this->GetWidget<NWidgetStacked>(WID_SM_SELECT_BUTTONS)->SetDisplayedPlane(plane);
01049 }
01050
01051 public:
01052 uint min_number_of_columns;
01053
01054 SmallMapWindow(const WindowDesc *desc, int window_number) : Window(), refresh(FORCE_REFRESH_PERIOD)
01055 {
01056 this->InitNested(desc, window_number);
01057 this->LowerWidget(this->map_type + WID_SM_CONTOUR);
01058
01059 BuildLandLegend();
01060 this->SetWidgetLoweredState(WID_SM_SHOW_HEIGHT, _smallmap_show_heightmap);
01061
01062 this->SetWidgetLoweredState(WID_SM_TOGGLETOWNNAME, this->show_towns);
01063
01064 this->SetupWidgetData();
01065
01066 this->SetZoomLevel(ZLC_INITIALIZE, NULL);
01067 this->SmallMapCenterOnCurrentPos();
01068 }
01069
01074 inline uint GetMinLegendWidth() const
01075 {
01076 return WD_FRAMERECT_LEFT + this->min_number_of_columns * this->column_width;
01077 }
01078
01083 inline uint GetNumberColumnsLegend(uint width) const
01084 {
01085 return width / this->column_width;
01086 }
01087
01093 uint GetLegendHeight(uint num_columns) const
01094 {
01095 uint num_rows = max(this->min_number_of_fixed_rows, CeilDiv(max(_smallmap_company_count, _smallmap_industry_count), num_columns));
01096 return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + num_rows * FONT_HEIGHT_SMALL;
01097 }
01098
01099 virtual void SetStringParameters(int widget) const
01100 {
01101 switch (widget) {
01102 case WID_SM_CAPTION:
01103 SetDParam(0, STR_SMALLMAP_TYPE_CONTOURS + this->map_type);
01104 break;
01105 }
01106 }
01107
01108 virtual void OnInit()
01109 {
01110 uint min_width = 0;
01111 this->min_number_of_columns = INDUSTRY_MIN_NUMBER_OF_COLUMNS;
01112 this->min_number_of_fixed_rows = 0;
01113 for (uint i = 0; i < lengthof(_legend_table); i++) {
01114 uint height = 0;
01115 uint num_columns = 1;
01116 for (const LegendAndColour *tbl = _legend_table[i]; !tbl->end; ++tbl) {
01117 StringID str;
01118 if (i == SMT_INDUSTRY) {
01119 SetDParam(0, tbl->legend);
01120 SetDParam(1, IndustryPool::MAX_SIZE);
01121 str = STR_SMALLMAP_INDUSTRY;
01122 } else if (i == SMT_OWNER) {
01123 if (tbl->company != INVALID_COMPANY) {
01124 if (!Company::IsValidID(tbl->company)) {
01125
01126 BuildOwnerLegend();
01127 this->OnInit();
01128 return;
01129 }
01130
01131 SetDParam(0, tbl->company);
01132 str = STR_SMALLMAP_COMPANY;
01133 } else {
01134 str = tbl->legend;
01135 }
01136 } else {
01137 if (tbl->col_break) {
01138 this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
01139 height = 0;
01140 num_columns++;
01141 }
01142 height++;
01143 str = tbl->legend;
01144 }
01145 min_width = max(GetStringBoundingBox(str).width, min_width);
01146 }
01147 this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
01148 this->min_number_of_columns = max(this->min_number_of_columns, num_columns);
01149 }
01150
01151
01152 this->column_width = min_width + LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
01153 }
01154
01155 virtual void OnPaint()
01156 {
01157 if (this->map_type == SMT_OWNER) {
01158 for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
01159 if (tbl->company != INVALID_COMPANY && !Company::IsValidID(tbl->company)) {
01160
01161 BuildOwnerLegend();
01162 this->InvalidateData(1);
01163 break;
01164 }
01165 }
01166 }
01167
01168 this->DrawWidgets();
01169 }
01170
01171 virtual void DrawWidget(const Rect &r, int widget) const
01172 {
01173 switch (widget) {
01174 case WID_SM_MAP: {
01175 DrawPixelInfo new_dpi;
01176 if (!FillDrawPixelInfo(&new_dpi, r.left + 1, r.top + 1, r.right - r.left - 1, r.bottom - r.top - 1)) return;
01177 this->DrawSmallMap(&new_dpi);
01178 break;
01179 }
01180
01181 case WID_SM_LEGEND: {
01182 uint columns = this->GetNumberColumnsLegend(r.right - r.left + 1);
01183 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);
01184 bool rtl = _current_text_dir == TD_RTL;
01185 uint y_org = r.top + WD_FRAMERECT_TOP;
01186 uint x = rtl ? r.right - this->column_width - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT;
01187 uint y = y_org;
01188 uint i = 0;
01189 uint row_height = FONT_HEIGHT_SMALL;
01190
01191 uint text_left = rtl ? 0 : LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT;
01192 uint text_right = this->column_width - 1 - (rtl ? LEGEND_BLOB_WIDTH + WD_FRAMERECT_RIGHT : 0);
01193 uint blob_left = rtl ? this->column_width - 1 - LEGEND_BLOB_WIDTH : 0;
01194 uint blob_right = rtl ? this->column_width - 1 : LEGEND_BLOB_WIDTH;
01195
01196 for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
01197 if (tbl->col_break || ((this->map_type == SMT_INDUSTRY || this->map_type == SMT_OWNER) && i++ >= number_of_rows)) {
01198
01199
01200 x += rtl ? -(int)this->column_width : this->column_width;
01201 y = y_org;
01202 i = 1;
01203 }
01204
01205 if (this->map_type == SMT_INDUSTRY) {
01206
01207
01208 SetDParam(0, tbl->legend);
01209 SetDParam(1, Industry::GetIndustryTypeCount(tbl->type));
01210 if (!tbl->show_on_map) {
01211
01212
01213 DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_GREY);
01214 } else {
01215 DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_BLACK);
01216 GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, PC_BLACK);
01217 }
01218 } else if (this->map_type == SMT_OWNER && tbl->company != INVALID_COMPANY) {
01219 SetDParam(0, tbl->company);
01220 if (!tbl->show_on_map) {
01221
01222
01223 DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_COMPANY, TC_GREY);
01224 } else {
01225 DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_COMPANY, TC_BLACK);
01226 GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, PC_BLACK);
01227 }
01228 } else {
01229 if (this->map_type == SMT_CONTOUR) SetDParam(0, tbl->height * TILE_HEIGHT_STEP);
01230
01231
01232 GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, PC_BLACK);
01233 DrawString(x + text_left, x + text_right, y, tbl->legend);
01234 }
01235 GfxFillRect(x + blob_left + 1, y + 2, x + blob_right - 1, y + row_height - 2, tbl->colour);
01236
01237 y += row_height;
01238 }
01239 }
01240 }
01241 }
01242
01247 void SwitchMapType(SmallMapType map_type)
01248 {
01249 this->RaiseWidget(this->map_type + WID_SM_CONTOUR);
01250 this->map_type = map_type;
01251 this->LowerWidget(this->map_type + WID_SM_CONTOUR);
01252
01253 this->SetupWidgetData();
01254
01255 this->SetDirty();
01256 }
01257
01258 virtual void OnClick(Point pt, int widget, int click_count)
01259 {
01260
01261 InvalidateWindowClassesData(WC_INDUSTRY_CARGOES, NUM_INDUSTRYTYPES);
01262
01263 switch (widget) {
01264 case WID_SM_MAP: {
01265
01266
01267
01268
01269
01270
01271
01272
01273 _left_button_clicked = false;
01274
01275 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(WID_SM_MAP);
01276 Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
01277 int sub;
01278 pt = this->PixelToTile(pt.x - wid->pos_x, pt.y - wid->pos_y, &sub);
01279 pt = RemapCoords(this->scroll_x + pt.x * TILE_SIZE + this->zoom * (TILE_SIZE - sub * TILE_SIZE / 4),
01280 this->scroll_y + pt.y * TILE_SIZE + sub * this->zoom * TILE_SIZE / 4, 0);
01281
01282 w->viewport->follow_vehicle = INVALID_VEHICLE;
01283 w->viewport->dest_scrollpos_x = pt.x - (w->viewport->virtual_width >> 1);
01284 w->viewport->dest_scrollpos_y = pt.y - (w->viewport->virtual_height >> 1);
01285
01286 this->SetDirty();
01287 break;
01288 }
01289
01290 case WID_SM_ZOOM_IN:
01291 case WID_SM_ZOOM_OUT: {
01292 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(WID_SM_MAP);
01293 Point pt = {wid->current_x / 2, wid->current_y / 2};
01294 this->SetZoomLevel((widget == WID_SM_ZOOM_IN) ? ZLC_ZOOM_IN : ZLC_ZOOM_OUT, &pt);
01295 SndPlayFx(SND_15_BEEP);
01296 break;
01297 }
01298
01299 case WID_SM_CONTOUR:
01300 case WID_SM_VEHICLES:
01301 case WID_SM_INDUSTRIES:
01302 case WID_SM_ROUTES:
01303 case WID_SM_VEGETATION:
01304 case WID_SM_OWNERS:
01305 this->SwitchMapType((SmallMapType)(widget - WID_SM_CONTOUR));
01306 SndPlayFx(SND_15_BEEP);
01307 break;
01308
01309 case WID_SM_CENTERMAP:
01310 this->SmallMapCenterOnCurrentPos();
01311 this->HandleButtonClick(WID_SM_CENTERMAP);
01312 SndPlayFx(SND_15_BEEP);
01313 break;
01314
01315 case WID_SM_TOGGLETOWNNAME:
01316 this->show_towns = !this->show_towns;
01317 this->SetWidgetLoweredState(WID_SM_TOGGLETOWNNAME, this->show_towns);
01318
01319 this->SetDirty();
01320 SndPlayFx(SND_15_BEEP);
01321 break;
01322
01323 case WID_SM_LEGEND:
01324 if (this->map_type == SMT_INDUSTRY || this->map_type == SMT_OWNER) {
01325 const NWidgetBase *wi = this->GetWidget<NWidgetBase>(WID_SM_LEGEND);
01326 uint line = (pt.y - wi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_SMALL;
01327 uint columns = this->GetNumberColumnsLegend(wi->current_x);
01328 uint number_of_rows = max(CeilDiv(max(_smallmap_company_count, _smallmap_industry_count), columns), this->min_number_of_fixed_rows);
01329 if (line >= number_of_rows) break;
01330
01331 bool rtl = _current_text_dir == TD_RTL;
01332 int x = pt.x - wi->pos_x;
01333 if (rtl) x = wi->current_x - x;
01334 uint column = (x - WD_FRAMERECT_LEFT) / this->column_width;
01335
01336
01337 if (this->map_type == SMT_INDUSTRY) {
01338
01339 int industry_pos = (column * number_of_rows) + line;
01340 if (industry_pos < _smallmap_industry_count) {
01341 if (_ctrl_pressed) {
01342
01343 bool changes = false;
01344 for (int i = 0; i != _smallmap_industry_count; i++) {
01345 bool new_state = i == industry_pos;
01346 if (_legend_from_industries[i].show_on_map != new_state) {
01347 changes = true;
01348 _legend_from_industries[i].show_on_map = new_state;
01349 }
01350 }
01351 if (!changes) {
01352
01353 for (int i = 0; i != _smallmap_industry_count; i++) {
01354 _legend_from_industries[i].show_on_map = true;
01355 }
01356 }
01357 } else {
01358 _legend_from_industries[industry_pos].show_on_map = !_legend_from_industries[industry_pos].show_on_map;
01359 }
01360 }
01361 } else if (this->map_type == SMT_OWNER) {
01362
01363 int company_pos = (column * number_of_rows) + line;
01364 if (company_pos < NUM_NO_COMPANY_ENTRIES) break;
01365 if (company_pos < _smallmap_company_count) {
01366 if (_ctrl_pressed) {
01367
01368 bool changes = false;
01369 for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
01370 bool new_state = i == company_pos;
01371 if (_legend_land_owners[i].show_on_map != new_state) {
01372 changes = true;
01373 _legend_land_owners[i].show_on_map = new_state;
01374 }
01375 }
01376 if (!changes) {
01377
01378 for (int i = NUM_NO_COMPANY_ENTRIES; i != _smallmap_company_count; i++) {
01379 _legend_land_owners[i].show_on_map = true;
01380 }
01381 }
01382 } else {
01383 _legend_land_owners[company_pos].show_on_map = !_legend_land_owners[company_pos].show_on_map;
01384 }
01385 }
01386 }
01387 this->SetDirty();
01388 }
01389 break;
01390
01391 case WID_SM_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 WID_SM_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 WID_SM_SHOW_HEIGHT:
01418 _smallmap_show_heightmap = !_smallmap_show_heightmap;
01419 this->SetWidgetLoweredState(WID_SM_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 != WID_SM_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>(WID_SM_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>(WID_SM_MAP);
01496 Point hv = InverseRemapCoords(wi->current_x * ZOOM_LVL_BASE * TILE_SIZE / 2, wi->current_y * ZOOM_LVL_BASE * 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>(WID_SM_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, WID_SM_MAP_BORDER),
01626 NWidget(WWT_INSET, COLOUR_BROWN, WID_SM_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, WID_SM_LEGEND), SetResize(1, 1),
01635 NWidget(NWID_VERTICAL),
01636
01637 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01638 NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, WID_SM_ZOOM_IN),
01639 SetDataTip(SPR_IMG_ZOOMIN, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN), SetFill(1, 1),
01640 NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, WID_SM_CENTERMAP),
01641 SetDataTip(SPR_IMG_SMALLMAP, STR_SMALLMAP_CENTER), SetFill(1, 1),
01642 NWidget(WWT_IMGBTN, COLOUR_BROWN, WID_SM_CONTOUR),
01643 SetDataTip(SPR_IMG_SHOW_COUNTOURS, STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP), SetFill(1, 1),
01644 NWidget(WWT_IMGBTN, COLOUR_BROWN, WID_SM_VEHICLES),
01645 SetDataTip(SPR_IMG_SHOW_VEHICLES, STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP), SetFill(1, 1),
01646 NWidget(WWT_IMGBTN, COLOUR_BROWN, WID_SM_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, WID_SM_ZOOM_OUT),
01652 SetDataTip(SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT), SetFill(1, 1),
01653 NWidget(WWT_IMGBTN, COLOUR_BROWN, WID_SM_TOGGLETOWNNAME),
01654 SetDataTip(SPR_IMG_TOWN, STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF), SetFill(1, 1),
01655 NWidget(WWT_IMGBTN, COLOUR_BROWN, WID_SM_ROUTES),
01656 SetDataTip(SPR_IMG_SHOW_ROUTES, STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON), SetFill(1, 1),
01657 NWidget(WWT_IMGBTN, COLOUR_BROWN, WID_SM_VEGETATION),
01658 SetDataTip(SPR_IMG_PLANTTREES, STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP), SetFill(1, 1),
01659 NWidget(WWT_IMGBTN, COLOUR_BROWN, WID_SM_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, WID_SM_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, WID_SM_SELECT_BUTTONS),
01691 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01692 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_SM_ENABLE_ALL), SetDataTip(STR_SMALLMAP_ENABLE_ALL, STR_NULL),
01693 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_SM_DISABLE_ALL), SetDataTip(STR_SMALLMAP_DISABLE_ALL, STR_NULL),
01694 NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_SM_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
01715 void ShowSmallMap()
01716 {
01717 AllocateWindowDescFront<SmallMapWindow>(&_smallmap_desc, 0);
01718 }
01719
01728 bool ScrollMainWindowTo(int x, int y, int z, bool instant)
01729 {
01730 bool res = ScrollWindowTo(x, y, z, FindWindowById(WC_MAIN_WINDOW, 0), instant);
01731
01732
01733
01734
01735
01736 if (res) return res;
01737
01738 SmallMapWindow *w = dynamic_cast<SmallMapWindow*>(FindWindowById(WC_SMALLMAP, 0));
01739 if (w != NULL) w->SmallMapCenterOnCurrentPos();
01740
01741 return res;
01742 }