OpenTTD Source 20260731-master-g77ba2b244a
smallmap_gui.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include "core/backup_type.hpp"
12#include "clear_map.h"
13#include "industry.h"
14#include "station_map.h"
15#include "landscape.h"
16#include "tree_map.h"
17#include "viewport_func.h"
18#include "town.h"
19#include "tunnelbridge_map.h"
20#include "core/endian_func.hpp"
21#include "vehicle_base.h"
22#include "sound_func.h"
23#include "window_func.h"
24#include "company_base.h"
25#include "zoom_func.h"
26#include "strings_func.h"
27#include "blitter/factory.hpp"
29#include "timer/timer.h"
30#include "timer/timer_window.h"
31#include "smallmap_gui.h"
32#include "core/enum_type.hpp"
33
35
36#include "table/strings.h"
37
38#include <bitset>
39
40#include "safeguards.h"
41
45
50 IndustryType type;
51 uint8_t height;
52 CompanyID company;
54 bool end;
55 bool col_break;
56};
57
59static const uint8_t _linkstat_colours_in_legenda[] = {0, 1, 3, 5, 7, 9, 11};
60
61static const int NUM_NO_COMPANY_ENTRIES = 4;
62
64#define MK(a, b) {b, a, IT_INVALID, 0, CompanyID::Invalid(), true, false, false}
65
67#define MC(col_break) {STR_TINY_BLACK_HEIGHT, {}, IT_INVALID, 0, CompanyID::Invalid(), true, false, col_break}
68
70#define MO(a, b) {b, a, IT_INVALID, 0, CompanyID::Invalid(), true, false, false}
71
73#define MOEND() {STR_NULL, {}, IT_INVALID, 0, OWNER_NONE, true, true, false}
74
76#define MKEND() {STR_NULL, {}, IT_INVALID, 0, CompanyID::Invalid(), true, true, false}
77
82#define MS(a, b) {b, a, IT_INVALID, 0, CompanyID::Invalid(), true, false, true}
83
86 MK(PC_BLACK, STR_SMALLMAP_LEGENDA_ROADS),
87 MK(PC_GREY, STR_SMALLMAP_LEGENDA_RAILROADS),
88 MK(PC_LIGHT_BLUE, STR_SMALLMAP_LEGENDA_STATIONS_AIRPORTS_DOCKS),
89 MK(PC_DARK_RED, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
90 MK(PC_WHITE, STR_SMALLMAP_LEGENDA_VEHICLES),
91
92 /* Placeholders for the colours and heights of the legend.
93 * The following values are set at BuildLandLegend() based
94 * on each colour scheme and the maximum map height. */
95 MC(true),
96 MC(false),
97 MC(false),
98 MC(false),
99 MC(false),
100 MC(false),
101 MC(true),
102 MC(false),
103 MC(false),
104 MC(false),
105 MC(false),
106 MC(false),
107 MKEND()
108};
109
110static const LegendAndColour _legend_vehicles[] = {
111 MK(PC_RED, STR_SMALLMAP_LEGENDA_TRAINS),
112 MK(PC_YELLOW, STR_SMALLMAP_LEGENDA_ROAD_VEHICLES),
113 MK(PC_LIGHT_BLUE, STR_SMALLMAP_LEGENDA_SHIPS),
114 MK(PC_WHITE, STR_SMALLMAP_LEGENDA_AIRCRAFT),
115
116 MS(PC_BLACK, STR_SMALLMAP_LEGENDA_TRANSPORT_ROUTES),
117 MK(PC_DARK_RED, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
118 MKEND()
119};
120
121static const LegendAndColour _legend_routes[] = {
122 MK(PC_BLACK, STR_SMALLMAP_LEGENDA_ROADS),
123 MK(PC_GREY, STR_SMALLMAP_LEGENDA_RAILROADS),
124 MK(PC_DARK_RED, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
125
126 MS(PC_VERY_DARK_BROWN, STR_SMALLMAP_LEGENDA_RAILROAD_STATION),
127 MK(PC_ORANGE, STR_SMALLMAP_LEGENDA_TRUCK_LOADING_BAY),
128 MK(PC_YELLOW, STR_SMALLMAP_LEGENDA_BUS_STATION),
129 MK(PC_RED, STR_SMALLMAP_LEGENDA_AIRPORT_HELIPORT),
130 MK(PC_LIGHT_BLUE, STR_SMALLMAP_LEGENDA_DOCK),
131 MKEND()
132};
133
134static const LegendAndColour _legend_vegetation[] = {
135 MK(PC_ROUGH_LAND, STR_SMALLMAP_LEGENDA_ROUGH_LAND),
136 MK(PC_GRASS_LAND, STR_SMALLMAP_LEGENDA_GRASS_LAND),
137 MK(PC_BARE_LAND, STR_SMALLMAP_LEGENDA_BARE_LAND),
138 MK(PC_RAINFOREST, STR_SMALLMAP_LEGENDA_RAINFOREST),
139 MK(PC_FIELDS, STR_SMALLMAP_LEGENDA_FIELDS),
140 MK(PC_TREES, STR_SMALLMAP_LEGENDA_TREES),
141
142 MS(PC_GREEN, STR_SMALLMAP_LEGENDA_FOREST),
143 MK(PC_GREY, STR_SMALLMAP_LEGENDA_ROCKS),
144 MK(PC_ORANGE, STR_SMALLMAP_LEGENDA_DESERT),
145 MK(PC_LIGHT_BLUE, STR_SMALLMAP_LEGENDA_SNOW),
146 MK(PC_BLACK, STR_SMALLMAP_LEGENDA_TRANSPORT_ROUTES),
147 MK(PC_DARK_RED, STR_SMALLMAP_LEGENDA_BUILDINGS_INDUSTRIES),
148 MKEND()
149};
150
151static LegendAndColour _legend_land_owners[NUM_NO_COMPANY_ENTRIES + MAX_COMPANIES + 1] = {
152 MO(PC_WATER, STR_SMALLMAP_LEGENDA_WATER),
153 MO({}, STR_SMALLMAP_LEGENDA_NO_OWNER), // This colour will vary depending on settings.
154 MO(PC_DARK_RED, STR_SMALLMAP_LEGENDA_TOWNS),
155 MO(PC_DARK_GREY, STR_SMALLMAP_LEGENDA_INDUSTRIES),
156 /* The legend will be terminated the first time it is used. */
157 MOEND(),
158};
159
160#undef MK
161#undef MC
162#undef MS
163#undef MO
164#undef MOEND
165#undef MKEND
166
179static bool _smallmap_show_heightmap = false;
181static IndustryType _smallmap_industry_highlight = IT_INVALID;
186
191{
192 uint j = 0;
193
194 /* Add each name */
195 for (IndustryType ind : _sorted_industry_types) {
196 const IndustrySpec *indsp = GetIndustrySpec(ind);
197 if (indsp->enabled) {
198 _legend_from_industries[j].legend = indsp->name;
199 _legend_from_industries[j].colour = indsp->map_colour;
200 _legend_from_industries[j].type = ind;
201 _legend_from_industries[j].show_on_map = true;
202 _legend_from_industries[j].col_break = false;
203 _legend_from_industries[j].end = false;
204
205 /* Store widget number for this industry type. */
206 _industry_to_list_pos[ind] = j;
207 j++;
208 }
209 }
210 /* Terminate the list */
211 _legend_from_industries[j].end = true;
212
213 /* Store number of enabled industries */
215}
216
221{
222 /* Clear the legend */
223 std::fill(std::begin(_legend_linkstats), std::end(_legend_linkstats), LegendAndColour{});
224
225 uint i = 0;
226 for (; i < _sorted_cargo_specs.size(); ++i) {
227 const CargoSpec *cs = _sorted_cargo_specs[i];
228
229 _legend_linkstats[i].legend = cs->name;
230 _legend_linkstats[i].colour = cs->legend_colour;
231 _legend_linkstats[i].type = cs->Index();
232 _legend_linkstats[i].show_on_map = true;
233 }
234
235 _legend_linkstats[i].col_break = true;
237
239 _legend_linkstats[i].legend = STR_EMPTY;
241 _legend_linkstats[i].show_on_map = true;
242 }
243
244 _legend_linkstats[_smallmap_cargo_count].legend = STR_LINKGRAPH_LEGEND_UNUSED;
245 _legend_linkstats[i - 1].legend = STR_LINKGRAPH_LEGEND_OVERLOADED;
246 _legend_linkstats[(_smallmap_cargo_count + i - 1) / 2].legend = STR_LINKGRAPH_LEGEND_SATURATED;
247 _legend_linkstats[i].end = true;
248}
249
261
265 _legend_vehicles,
268 _legend_routes,
269 _legend_vegetation,
270 _legend_land_owners,
271};
272
273#define MKCOLOUR(x) TO_LE32(x)
274
275#define MKCOLOUR_XXXX(x) (MKCOLOUR(0x01010101) * (uint)(x.p))
276#define MKCOLOUR_0XX0(x) (MKCOLOUR(0x00010100) * (uint)(x.p))
277#define MKCOLOUR_X00X(x) (MKCOLOUR(0x01000001) * (uint)(x.p))
278
279#define MKCOLOUR_XYYX(x, y) (MKCOLOUR_X00X(x) | MKCOLOUR_0XX0(y))
280
281#define MKCOLOUR_0000 MKCOLOUR_XXXX(PixelColour{0x00})
282#define MKCOLOUR_F00F MKCOLOUR_X00X(PixelColour{0xFF})
283#define MKCOLOUR_FFFF MKCOLOUR_XXXX(PixelColour{0xFF})
284
286
289 std::vector<uint32_t> height_colours;
290 std::span<const uint32_t> height_colours_base;
291 uint32_t default_colour;
292};
293
296 {{}, _green_map_heights, MKCOLOUR_XXXX(PixelColour{0x54})},
297 {{}, _dark_green_map_heights, MKCOLOUR_XXXX(PixelColour{0x62})},
298 {{}, _violet_map_heights, MKCOLOUR_XXXX(PixelColour{0x81})},
299};
300
305{
306 /* The smallmap window has never been initialized, so no need to change the legend. */
307 if (_heightmap_schemes[0].height_colours.empty()) return;
308
309 /*
310 * The general idea of this function is to fill the legend with an appropriate evenly spaced
311 * selection of height levels. All entries with STR_TINY_BLACK_HEIGHT are reserved for this.
312 * At the moment there are twelve of these.
313 *
314 * The table below defines up to which height level a particular delta in the legend should be
315 * used. One could opt for just dividing the maximum height and use that as delta, but that
316 * creates many "ugly" legend labels, e.g. once every 950 meter. As a result, this table will
317 * reduce the number of deltas to 7: every 100m, 200m, 300m, 500m, 750m, 1000m and 1250m. The
318 * deltas are closer together at the lower numbers because going from 12 entries to just 4, as
319 * would happen when replacing 200m and 300m by 250m, would mean the legend would be short and
320 * that might not be considered appropriate.
321 *
322 * The current method yields at least 7 legend entries and at most 12. It can be increased to
323 * 8 by adding a 150m and 400m option, but especially 150m creates ugly heights.
324 *
325 * It tries to evenly space the legend items over the two columns that are there for the legend.
326 */
327
328 /* Table for delta; if max_height is less than the first column, use the second column as value. */
329 uint deltas[][2] = { { 24, 2 }, { 48, 4 }, { 72, 6 }, { 120, 10 }, { 180, 15 }, { 240, 20 }, { MAX_TILE_HEIGHT + 1, 25 }};
330 uint i = 0;
331 for (; _settings_game.construction.map_height_limit >= deltas[i][0]; i++) {
332 /* Nothing to do here. */
333 }
334 uint delta = deltas[i][1];
335
336 int total_entries = (_settings_game.construction.map_height_limit / delta) + 1;
337 int rows = CeilDiv(total_entries, 2);
338 int j = 0;
339
340 for (i = 0; i < lengthof(_legend_land_contours) - 1 && j < total_entries; i++) {
341 if (_legend_land_contours[i].legend != STR_TINY_BLACK_HEIGHT) continue;
342
343 _legend_land_contours[i].col_break = j % rows == 0;
344 _legend_land_contours[i].end = false;
345 _legend_land_contours[i].height = j * delta;
346 _legend_land_contours[i].colour = PixelColour{static_cast<uint8_t>(_heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[_legend_land_contours[i].height])};
347 j++;
348 }
349 _legend_land_contours[i].end = true;
350}
351
356{
357 _legend_land_owners[1].colour = PixelColour{static_cast<uint8_t>(_heightmap_schemes[_settings_client.gui.smallmap_land_colour].default_colour)};
358
360 for (const Company *c : Company::Iterate()) {
361 _legend_land_owners[i].colour = GetColourGradient(c->colour, Shade::Light);
362 _legend_land_owners[i].company = c->index;
363 _legend_land_owners[i].show_on_map = true;
364 _legend_land_owners[i].col_break = false;
365 _legend_land_owners[i].end = false;
366 _company_to_list_pos[c->index] = i;
367 i++;
368 }
369
370 /* Terminate the list */
371 _legend_land_owners[i].end = true;
372
373 /* Store maximum amount of owner legend entries. */
375}
376
377struct AndOr {
378 uint32_t mor;
379 uint32_t mand;
380};
381
388static inline uint32_t ApplyMask(uint32_t colour, const AndOr &mask)
389{
390 return (colour & mask.mand) | mask.mor;
391}
392
393
396 AndOr(MKCOLOUR_0000, MKCOLOUR_FFFF), // TileType::Clear
397 AndOr(MKCOLOUR_0XX0(PC_GREY), MKCOLOUR_F00F), // TileType::Railway
398 AndOr(MKCOLOUR_0XX0(PC_BLACK), MKCOLOUR_F00F), // TileType::Road
399 AndOr(MKCOLOUR_0XX0(PC_DARK_RED), MKCOLOUR_F00F), // TileType::House
400 AndOr(MKCOLOUR_0000, MKCOLOUR_FFFF), // TileType::Trees
401 AndOr(MKCOLOUR_XXXX(PC_LIGHT_BLUE), MKCOLOUR_0000), // TileType::Station
402 AndOr(MKCOLOUR_XXXX(PC_WATER), MKCOLOUR_0000), // TileType::Water
403 AndOr(MKCOLOUR_0000, MKCOLOUR_FFFF), // TileType::Void
404 AndOr(MKCOLOUR_XXXX(PC_DARK_RED), MKCOLOUR_0000), // TileType::Industry
405 AndOr(MKCOLOUR_0000, MKCOLOUR_FFFF), // TileType::TunnelBridge
406 AndOr(MKCOLOUR_0XX0(PC_DARK_RED), MKCOLOUR_F00F), // TileType::Object
407};
408
411 AndOr(MKCOLOUR_0000, MKCOLOUR_FFFF), // TileType::Clear
412 AndOr(MKCOLOUR_0XX0(PC_BLACK), MKCOLOUR_F00F), // TileType::Railway
413 AndOr(MKCOLOUR_0XX0(PC_BLACK), MKCOLOUR_F00F), // TileType::Road
414 AndOr(MKCOLOUR_0XX0(PC_DARK_RED), MKCOLOUR_F00F), // TileType::House
415 AndOr(MKCOLOUR_0000, MKCOLOUR_FFFF), // TileType::Trees
416 AndOr(MKCOLOUR_0XX0(PC_BLACK), MKCOLOUR_F00F), // TileType::Station
417 AndOr(MKCOLOUR_XXXX(PC_WATER), MKCOLOUR_0000), // TileType::Water
418 AndOr(MKCOLOUR_0000, MKCOLOUR_FFFF), // TileType::Void
419 AndOr(MKCOLOUR_XXXX(PC_DARK_RED), MKCOLOUR_0000), // TileType::Industry
420 AndOr(MKCOLOUR_0000, MKCOLOUR_FFFF), // TileType::TunnelBridge
421 AndOr(MKCOLOUR_0XX0(PC_DARK_RED), MKCOLOUR_F00F), // TileType::Object
422};
423
426 2, // TileType::Clear
427 8, // TileType::Railway
428 7, // TileType::Road
429 5, // TileType::House
430 2, // TileType::Trees
431 9, // TileType::Station
432 2, // TileType::Water
433 1, // TileType::Void
434 6, // TileType::Industry
435 8, // TileType::TunnelBridge
436 2, // TileType::Object
437};
438
444static bool IsDryCoast(TileIndex tile)
445{
447}
448
457{
458 if (IsDryCoast(tile)) return table[TileType::Clear];
459 return table[t];
460}
461
468static inline uint32_t GetSmallMapContoursPixels(TileIndex tile, TileType t)
469{
470 const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
472}
473
480static inline uint32_t GetSmallMapVehiclesPixels(TileIndex tile, TileType t)
481{
482 const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
484}
485
498
506static inline uint32_t GetSmallMapRoutesPixels(TileIndex tile, TileType t)
507{
508 switch (t) {
510 switch (GetStationType(tile)) {
511 case StationType::Rail: return MKCOLOUR_XXXX(PC_VERY_DARK_BROWN);
512 case StationType::Airport: return MKCOLOUR_XXXX(PC_RED);
513 case StationType::Truck: return MKCOLOUR_XXXX(PC_ORANGE);
514 case StationType::Bus: return MKCOLOUR_XXXX(PC_YELLOW);
515 case StationType::Dock: return MKCOLOUR_XXXX(PC_LIGHT_BLUE);
516 default: return MKCOLOUR_FFFF;
517 }
518
519 case TileType::Railway: {
520 AndOr andor = {
521 MKCOLOUR_0XX0(GetRailTypeInfo(GetRailType(tile))->map_colour),
523 };
524
525 const SmallMapColourScheme &cs = _heightmap_schemes[_settings_client.gui.smallmap_land_colour];
526 return ApplyMask(cs.default_colour, andor);
527 }
528
529 case TileType::Road: {
530 const RoadTypeInfo *rti = nullptr;
531 if (GetRoadTypeRoad(tile) != INVALID_ROADTYPE) {
532 rti = GetRoadTypeInfo(GetRoadTypeRoad(tile));
533 } else {
534 rti = GetRoadTypeInfo(GetRoadTypeTram(tile));
535 }
536 if (rti != nullptr) {
537 AndOr andor = {
538 MKCOLOUR_0XX0(rti->map_colour),
540 };
541
542 const SmallMapColourScheme &cs = _heightmap_schemes[_settings_client.gui.smallmap_land_colour];
543 return ApplyMask(cs.default_colour, andor);
544 }
545 [[fallthrough]];
546 }
547
548 default:
549 /* Ground colour */
550 const SmallMapColourScheme &cs = _heightmap_schemes[_settings_client.gui.smallmap_land_colour];
552 }
553}
554
562static inline uint32_t GetSmallMapLinkStatsPixels(TileIndex tile, TileType t)
563{
565}
566
569 MKCOLOUR_XXXX(PC_GRASS_LAND),
570 MKCOLOUR_XXXX(PC_ROUGH_LAND),
571 MKCOLOUR_XXXX(PC_GREY),
572 MKCOLOUR_XXXX(PC_FIELDS),
573 MKCOLOUR_XXXX(PC_GRASS_LAND),
574 MKCOLOUR_XXXX(PC_ORANGE),
575 MKCOLOUR_XXXX(PC_GRASS_LAND),
576 MKCOLOUR_XXXX(PC_GRASS_LAND),
577};
578
586static inline uint32_t GetSmallMapVegetationPixels(TileIndex tile, TileType t)
587{
588 switch (t) {
589 case TileType::Clear:
590 if (IsSnowTile(tile)) return MKCOLOUR_XXXX(PC_LIGHT_BLUE);
592 if (GetClearDensity(tile) < 3) return MKCOLOUR_XXXX(PC_BARE_LAND);
593 if (GetTropicZone(tile) == TropicZone::Rainforest) return MKCOLOUR_XXXX(PC_RAINFOREST);
594 }
596
597 case TileType::Water:
598 if (IsDryCoast(tile)) return (GetTropicZone(tile) == TropicZone::Rainforest) ? MKCOLOUR_XXXX(PC_RAINFOREST) : MKCOLOUR_XXXX(PC_GRASS_LAND);
599 return ApplyMask(MKCOLOUR_XXXX(PC_GRASS_LAND), _smallmap_vehicles_andor[t]);
600
602 return IsTileForestIndustry(tile) ? MKCOLOUR_XXXX(PC_GREEN) : MKCOLOUR_XXXX(PC_DARK_RED);
603
604 case TileType::Trees:
606 return (_settings_game.game_creation.landscape == LandscapeType::Arctic) ? MKCOLOUR_XYYX(PC_LIGHT_BLUE, PC_TREES) : MKCOLOUR_XYYX(PC_ORANGE, PC_TREES);
607 }
608 return (GetTropicZone(tile) == TropicZone::Rainforest) ? MKCOLOUR_XYYX(PC_RAINFOREST, PC_TREES) : MKCOLOUR_XYYX(PC_GRASS_LAND, PC_TREES);
609
610 default:
612 }
613}
614
624uint32_t GetSmallMapOwnerPixels(TileIndex tile, TileType t, IncludeHeightmap include_heightmap)
625{
626 Owner o;
627
628 switch (t) {
629 case TileType::Void: return MKCOLOUR_XXXX(PC_BLACK);
630 case TileType::Industry: return MKCOLOUR_XXXX(PC_DARK_GREY);
631 case TileType::House: return MKCOLOUR_XXXX(PC_DARK_RED);
632 case TileType::Road:
634 break;
635
636 default:
637 o = GetTileOwner(tile);
638 break;
639 }
640
641 if ((o < MAX_COMPANIES && !_legend_land_owners[_company_to_list_pos[o]].show_on_map) || o == OWNER_NONE || o == OWNER_WATER) {
642 if (t == TileType::Water && !IsDryCoast(tile)) return MKCOLOUR_XXXX(PC_WATER);
643 const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
644 return ((include_heightmap == IncludeHeightmap::IfEnabled && _smallmap_show_heightmap) || include_heightmap == IncludeHeightmap::Always)
645 ? cs->height_colours[TileHeight(tile)] : cs->default_colour;
646 } else if (o == OWNER_TOWN) {
647 return MKCOLOUR_XXXX(PC_DARK_RED);
648 }
649
650 return MKCOLOUR_XXXX(_legend_land_owners[_company_to_list_pos[o]].colour);
651}
652
657
659class SmallMapWindow : public Window {
660protected:
662 enum class ZoomLevelChange : uint8_t {
666 };
667
669 static bool show_towns;
670 static bool show_ind_names;
671 static int map_height_limit;
672
673 static const uint INDUSTRY_MIN_NUMBER_OF_COLUMNS = 2;
674
677 uint column_width = 0;
678 uint legend_width = 0;
679
680 int32_t scroll_x = 0;
681 int32_t scroll_y = 0;
682 int32_t subscroll = 0;
683 int zoom = 0;
684
685 std::unique_ptr<LinkGraphOverlay> overlay{};
686
689 {
690 InvalidateWindowClassesData(WindowClass::IndustryCargoes, NUM_INDUSTRYTYPES);
691 }
692
693 static inline Point SmallmapRemapCoords(int x, int y)
694 {
695 Point pt;
696 pt.x = (y - x) * 2;
697 pt.y = y + x;
698 return pt;
699 }
700
707 static inline void DrawVertMapIndicator(int x, int y, int y2)
708 {
709 GfxFillRect(x, y, x, y + 3, PC_VERY_LIGHT_YELLOW);
710 GfxFillRect(x, y2 - 3, x, y2, PC_VERY_LIGHT_YELLOW);
711 }
712
719 static inline void DrawHorizMapIndicator(int x, int x2, int y)
720 {
721 GfxFillRect(x, y, x + 3, y, PC_VERY_LIGHT_YELLOW);
722 GfxFillRect(x2 - 3, y, x2, y, PC_VERY_LIGHT_YELLOW);
723 }
724
729 inline uint GetMinLegendWidth() const
730 {
731 return WidgetDimensions::scaled.framerect.left + this->min_number_of_columns * this->column_width;
732 }
733
739 inline uint GetNumberColumnsLegend(uint width) const
740 {
741 return width / this->column_width;
742 }
743
749 inline uint GetLegendHeight(uint num_columns) const
750 {
751 return WidgetDimensions::scaled.framerect.Vertical() +
753 }
754
764
769
771 const IntervalTimer<TimerWindow> refresh_interval = {std::chrono::milliseconds(930), [this](auto) {
772 ForceRefresh();
773 }};
774
779 {
780 /* Rebuild colour indices if necessary. */
781 if (SmallMapWindow::map_height_limit == _settings_game.construction.map_height_limit) return;
782
783 for (auto &heightmap_scheme : _heightmap_schemes) {
784 /* The heights go from 0 up to and including maximum. */
785 size_t heights = _settings_game.construction.map_height_limit + 1;
786 heightmap_scheme.height_colours.resize(heights);
787
788 for (size_t z = 0; z < heights; z++) {
789 size_t access_index = (heightmap_scheme.height_colours_base.size() * z) / heights;
790
791 /* Choose colour by mapping the range (0..max heightlevel) on the complete colour table. */
792 heightmap_scheme.height_colours[z] = heightmap_scheme.height_colours_base[access_index];
793 }
794 }
795
796 SmallMapWindow::map_height_limit = _settings_game.construction.map_height_limit;
798 }
799
808 uint GetNumberRowsLegend(uint columns) const
809 {
810 /* Reserve one column for link colours */
811 uint num_rows_linkstats = CeilDiv(_smallmap_cargo_count, columns - 1);
812 uint num_rows_others = CeilDiv(std::max(_smallmap_industry_count, _smallmap_company_count), columns);
813 return std::max({this->min_number_of_fixed_rows, num_rows_linkstats, num_rows_others});
814 }
815
827 void SelectLegendItem(int click_pos, LegendAndColour *legend, int end_legend_item, int begin_legend_item = 0)
828 {
829 if (_ctrl_pressed) {
830 /* Disable all, except the clicked one */
831 bool changes = false;
832 for (int i = begin_legend_item; i != end_legend_item; i++) {
833 bool new_state = (i == click_pos);
834 if (legend[i].show_on_map != new_state) {
835 changes = true;
836 legend[i].show_on_map = new_state;
837 }
838 }
839 if (!changes) {
840 /* Nothing changed? Then show all (again). */
841 for (int i = begin_legend_item; i != end_legend_item; i++) {
842 legend[i].show_on_map = true;
843 }
844 }
845 } else {
846 legend[click_pos].show_on_map = !legend[click_pos].show_on_map;
847 }
848
849 if (this->map_type == SmallMapType::Industries) this->BreakIndustryChainLink();
850 }
851
856 void SwitchMapType(SmallMapType map_type)
857 {
858 this->RaiseWidget(WID_SM_CONTOUR + to_underlying(this->map_type));
859 this->map_type = map_type;
860 this->LowerWidget(WID_SM_CONTOUR + to_underlying(this->map_type));
861
862 this->SetupWidgetData();
863
864 if (map_type == SmallMapType::LinkStats) this->overlay->SetDirty();
866 this->ReInit();
867 }
868
876 void SetNewScroll(int sx, int sy, int sub)
877 {
879 Point hv = InverseRemapCoords(wi->current_x * ZOOM_BASE * TILE_SIZE / 2, wi->current_y * ZOOM_BASE * TILE_SIZE / 2);
880 hv.x *= this->zoom;
881 hv.y *= this->zoom;
882
883 if (sx < -hv.x) {
884 sx = -hv.x;
885 sub = 0;
886 }
887 if (sx > (int)(Map::MaxX() * TILE_SIZE) - hv.x) {
888 sx = Map::MaxX() * TILE_SIZE - hv.x;
889 sub = 0;
890 }
891 if (sy < -hv.y) {
892 sy = -hv.y;
893 sub = 0;
894 }
895 if (sy > (int)(Map::MaxY() * TILE_SIZE) - hv.y) {
896 sy = Map::MaxY() * TILE_SIZE - hv.y;
897 sub = 0;
898 }
899
900 this->scroll_x = sx;
901 this->scroll_y = sy;
902 this->subscroll = sub;
903 if (this->map_type == SmallMapType::LinkStats) this->overlay->SetDirty();
904 }
905
909 void DrawMapIndicators() const
910 {
911 /* Find main viewport. */
912 const Viewport &vp = *GetMainWindow()->viewport;
913
914 Point upper_left_smallmap_coord = InverseRemapCoords2(vp.virtual_left, vp.virtual_top);
915 Point lower_right_smallmap_coord = InverseRemapCoords2(vp.virtual_left + vp.virtual_width - 1, vp.virtual_top + vp.virtual_height - 1);
916
917 Point upper_left = this->RemapTile(upper_left_smallmap_coord.x / (int)TILE_SIZE, upper_left_smallmap_coord.y / (int)TILE_SIZE);
918 upper_left.x -= this->subscroll;
919
920 Point lower_right = this->RemapTile(lower_right_smallmap_coord.x / (int)TILE_SIZE, lower_right_smallmap_coord.y / (int)TILE_SIZE);
921 lower_right.x -= this->subscroll;
922
923 SmallMapWindow::DrawVertMapIndicator(upper_left.x, upper_left.y, lower_right.y);
924 SmallMapWindow::DrawVertMapIndicator(lower_right.x, upper_left.y, lower_right.y);
925
926 SmallMapWindow::DrawHorizMapIndicator(upper_left.x, lower_right.x, upper_left.y);
927 SmallMapWindow::DrawHorizMapIndicator(upper_left.x, lower_right.x, lower_right.y);
928 }
929
943 void DrawSmallMapColumn(void *dst, uint xc, uint yc, int pitch, int reps, int start_pos, int end_pos, Blitter *blitter) const
944 {
945 void *dst_ptr_abs_end = blitter->MoveTo(_screen.dst_ptr, 0, _screen.height);
946 uint min_xy = _settings_game.construction.freeform_edges ? 1 : 0;
947
948 do {
949 /* Check if the tile (xc,yc) is within the map range */
950 if (xc >= Map::MaxX() || yc >= Map::MaxY()) continue;
951
952 /* Check if the dst pointer points to a pixel inside the screen buffer */
953 if (dst < _screen.dst_ptr) continue;
954 if (dst >= dst_ptr_abs_end) continue;
955
956 /* Construct tilearea covered by (xc, yc, xc + this->zoom, yc + this->zoom) such that it is within min_xy limits. */
957 TileArea ta;
958 if (min_xy == 1 && (xc == 0 || yc == 0)) {
959 if (this->zoom == 1) continue; // The tile area is empty, don't draw anything.
960
961 ta = TileArea(TileXY(std::max(min_xy, xc), std::max(min_xy, yc)), this->zoom - (xc == 0), this->zoom - (yc == 0));
962 } else {
963 ta = TileArea(TileXY(xc, yc), this->zoom, this->zoom);
964 }
965 ta.ClampToMap(); // Clamp to map boundaries (may contain TileType::Void tiles!).
966
967 uint32_t val = this->GetTileColours(ta);
968 uint8_t *val8 = (uint8_t *)&val;
969 int idx = std::max(0, -start_pos);
970 for (int pos = std::max(0, start_pos); pos < end_pos; pos++) {
971 blitter->SetPixel(dst, idx, 0, PixelColour{val8[idx]});
972 idx++;
973 }
974 /* Switch to next tile in the column */
975 } while (xc += this->zoom, yc += this->zoom, dst = blitter->MoveTo(dst, pitch, 0), --reps != 0);
976 }
977
983 void DrawVehicles(const DrawPixelInfo *dpi, Blitter *blitter) const
984 {
985 for (const Vehicle *v : Vehicle::Iterate()) {
986 if (v->type == VehicleType::Effect) continue;
987 if (v->vehstatus.Any({VehState::Hidden, VehState::Unclickable})) continue;
988
989 /* Remap into flat coordinates. */
990 Point pt = this->RemapTile(v->x_pos / (int)TILE_SIZE, v->y_pos / (int)TILE_SIZE);
991
992 int y = pt.y - dpi->top;
993 if (!IsInsideMM(y, 0, dpi->height)) continue; // y is out of bounds.
994
995 bool skip = false; // Default is to draw both pixels.
996 int x = pt.x - this->subscroll - 3 - dpi->left; // Offset X coordinate.
997 if (x < 0) {
998 /* if x+1 is 0, that means we're on the very left edge,
999 * and should thus only draw a single pixel */
1000 if (++x != 0) continue;
1001 skip = true;
1002 } else if (x >= dpi->width - 1) {
1003 /* Check if we're at the very right edge, and if so draw only a single pixel */
1004 if (x != dpi->width - 1) continue;
1005 skip = true;
1006 }
1007
1008 /* Calculate pointer to pixel and the colour */
1009 PixelColour colour = (this->map_type == SmallMapType::Vehicles) ? _vehicle_type_colours[v->type] : PC_WHITE;
1010
1011 /* And draw either one or two pixels depending on clipping */
1012 blitter->SetPixel(dpi->dst_ptr, x, y, colour);
1013 if (!skip) blitter->SetPixel(dpi->dst_ptr, x + 1, y, colour);
1014 }
1015 }
1016
1022 void DrawTowns(const DrawPixelInfo *dpi, const int vertical_padding) const
1023 {
1024 for (const Town *t : Town::Iterate()) {
1025 /* Remap the town coordinate */
1026 Point pt = this->RemapTile(TileX(t->xy), TileY(t->xy));
1027 int x = pt.x - this->subscroll - (t->cache.sign.width_small >> 1);
1028 int y = pt.y + vertical_padding;
1029
1030 /* Check if the town sign is within bounds */
1031 if (x + t->cache.sign.width_small > dpi->left &&
1032 x < dpi->left + dpi->width &&
1033 y + GetCharacterHeight(FontSize::Small) > dpi->top &&
1034 y < dpi->top + dpi->height) {
1035 /* And draw it. */
1036 DrawString(x, x + t->cache.sign.width_small, y, GetString(STR_SMALLMAP_TOWN, t->index));
1037 }
1038 }
1039 }
1040
1046 void DrawIndustryNames(const DrawPixelInfo *dpi, const int vertical_padding) const
1047 {
1048 if (this->map_type != SmallMapType::Industries) return;
1049
1050 for (const Industry *i : Industry::Iterate()) {
1052 if (!tbl.show_on_map) continue;
1053
1054 /* Industry names blink together with their blobs in the smallmap. */
1055 const bool is_blinking = i->type == _smallmap_industry_highlight && !_smallmap_industry_highlight_state;
1056 if (is_blinking) continue;
1057
1058 if (_industry_to_name_string_width[i->type] == 0) {
1060 }
1061 const uint16_t &legend_text_width = _industry_to_name_string_width[i->type];
1062
1063 /* Remap the industry coordinate */
1064 const TileIndex &tile = i->location.GetCenterTile();
1065 const Point pt = this->RemapTile(TileX(tile), TileY(tile));
1066 const int x = pt.x - this->subscroll - (legend_text_width / 2);
1067 const int y = pt.y + vertical_padding;
1068
1069 /* Check if the industry name is within bounds */
1070 if (x + legend_text_width > dpi->left &&
1071 x < dpi->left + dpi->width &&
1072 y + GetCharacterHeight(FontSize::Small) > dpi->top &&
1073 y < dpi->top + dpi->height) {
1074
1075 /* And draw it. */
1076 DrawString(x, x + legend_text_width, y, tbl.legend, TextColour::White, AlignmentH::Start, false, FontSize::Small);
1077 }
1078 }
1079 }
1080
1093 {
1095 AutoRestoreBackup dpi_backup(_cur_dpi, dpi);
1096
1097 /* If freeform edges are off, draw infinite water off the edges of the map. */
1098 const PixelColour map_clear_color = (_settings_game.construction.freeform_edges ? PC_BLACK : PC_WATER);
1099 GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, map_clear_color);
1100
1101 /* Which tile is displayed at (dpi->left, dpi->top)? */
1102 int dx;
1103 Point tile = this->PixelToTile(dpi->left, dpi->top, &dx);
1104 int tile_x = this->scroll_x / (int)TILE_SIZE + tile.x;
1105 int tile_y = this->scroll_y / (int)TILE_SIZE + tile.y;
1106
1107 void *ptr = blitter->MoveTo(dpi->dst_ptr, -dx - 4, 0);
1108 int x = - dx - 4;
1109 int y = 0;
1110
1111 for (;;) {
1112 /* Distance from left edge */
1113 if (x >= -3) {
1114 if (x >= dpi->width) break; // Exit the loop.
1115
1116 int end_pos = std::min(dpi->width, x + 4);
1117 int reps = (dpi->height - y + 1) / 2; // Number of lines.
1118 if (reps > 0) {
1119 this->DrawSmallMapColumn(ptr, tile_x, tile_y, dpi->pitch * 2, reps, x, end_pos, blitter);
1120 }
1121 }
1122
1123 if (y == 0) {
1124 tile_y += this->zoom;
1125 y++;
1126 ptr = blitter->MoveTo(ptr, 0, 1);
1127 } else {
1128 tile_x -= this->zoom;
1129 y--;
1130 ptr = blitter->MoveTo(ptr, 0, -1);
1131 }
1132 ptr = blitter->MoveTo(ptr, 2, 0);
1133 x += 2;
1134 }
1135
1136 /* Draw vehicles */
1137 if (this->map_type == SmallMapType::Contour || this->map_type == SmallMapType::Vehicles) this->DrawVehicles(dpi, blitter);
1138
1139 /* Draw link stat overlay */
1140 if (this->map_type == SmallMapType::LinkStats) this->overlay->Draw(dpi);
1141
1142 const int map_labels_vertical_padding = ScaleGUITrad(2);
1143
1144 /* Draw town names */
1145 if (this->show_towns) this->DrawTowns(dpi, map_labels_vertical_padding);
1146
1147 /* Draw industry names */
1148 if (this->show_ind_names) this->DrawIndustryNames(dpi, map_labels_vertical_padding);
1149
1150 /* Draw map indicators */
1151 this->DrawMapIndicators();
1152 }
1153
1160 Point RemapTile(int tile_x, int tile_y) const
1161 {
1162 int x_offset = tile_x - this->scroll_x / (int)TILE_SIZE;
1163 int y_offset = tile_y - this->scroll_y / (int)TILE_SIZE;
1164
1165 if (this->zoom == 1) return SmallmapRemapCoords(x_offset, y_offset);
1166
1167 /* For negative offsets, round towards -inf. */
1168 if (x_offset < 0) x_offset -= this->zoom - 1;
1169 if (y_offset < 0) y_offset -= this->zoom - 1;
1170
1171 return SmallmapRemapCoords(x_offset / this->zoom, y_offset / this->zoom);
1172 }
1173
1184 Point PixelToTile(int px, int py, int *sub, bool add_sub = true) const
1185 {
1186 if (add_sub) px += this->subscroll; // Total horizontal offset.
1187
1188 /* For each two rows down, add a x and a y tile, and
1189 * For each four pixels to the right, move a tile to the right. */
1190 Point pt = {((py >> 1) - (px >> 2)) * this->zoom, ((py >> 1) + (px >> 2)) * this->zoom};
1191 px &= 3;
1192
1193 if (py & 1) { // Odd number of rows, handle the 2 pixel shift.
1194 if (px < 2) {
1195 pt.x += this->zoom;
1196 px += 2;
1197 } else {
1198 pt.y += this->zoom;
1199 px -= 2;
1200 }
1201 }
1202
1203 *sub = px;
1204 return pt;
1205 }
1206
1216 Point ComputeScroll(int tx, int ty, int x, int y, int *sub) const
1217 {
1218 assert(x >= 0 && y >= 0);
1219
1220 int new_sub;
1221 Point tile_xy = PixelToTile(x, y, &new_sub, false);
1222 tx -= tile_xy.x;
1223 ty -= tile_xy.y;
1224
1225 Point scroll;
1226 if (new_sub == 0) {
1227 *sub = 0;
1228 scroll.x = (tx + this->zoom) * TILE_SIZE;
1229 scroll.y = (ty - this->zoom) * TILE_SIZE;
1230 } else {
1231 *sub = 4 - new_sub;
1232 scroll.x = (tx + 2 * this->zoom) * TILE_SIZE;
1233 scroll.y = (ty - 2 * this->zoom) * TILE_SIZE;
1234 }
1235 return scroll;
1236 }
1237
1244 void SetZoomLevel(ZoomLevelChange change, const Point *zoom_pt)
1245 {
1246 static const int zoomlevels[] = {1, 2, 4, 6, 8}; // Available zoom levels. Bigger number means more zoom-out (further away).
1247 static const int MIN_ZOOM_INDEX = 0;
1248 static const int MAX_ZOOM_INDEX = lengthof(zoomlevels) - 1;
1249
1250 int new_index, cur_index, sub;
1251 Point tile;
1252 switch (change) {
1254 cur_index = - 1; // Definitely different from new_index.
1255 new_index = MIN_ZOOM_INDEX;
1256 tile.x = tile.y = 0;
1257 break;
1258
1261 for (cur_index = MIN_ZOOM_INDEX; cur_index <= MAX_ZOOM_INDEX; cur_index++) {
1262 if (this->zoom == zoomlevels[cur_index]) break;
1263 }
1264 assert(cur_index <= MAX_ZOOM_INDEX);
1265
1266 tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
1267 new_index = Clamp(cur_index + ((change == ZoomLevelChange::ZoomIn) ? -1 : 1), MIN_ZOOM_INDEX, MAX_ZOOM_INDEX);
1268 break;
1269
1270 default: NOT_REACHED();
1271 }
1272
1273 if (new_index != cur_index) {
1274 this->zoom = zoomlevels[new_index];
1275 if (cur_index >= 0) {
1276 Point new_tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
1277 this->SetNewScroll(this->scroll_x + (tile.x - new_tile.x) * TILE_SIZE,
1278 this->scroll_y + (tile.y - new_tile.y) * TILE_SIZE, sub);
1279 } else if (this->map_type == SmallMapType::LinkStats) {
1280 this->overlay->SetDirty();
1281 }
1282 this->SetWidgetDisabledState(WID_SM_ZOOM_IN, this->zoom == zoomlevels[MIN_ZOOM_INDEX]);
1283 this->SetWidgetDisabledState(WID_SM_ZOOM_OUT, this->zoom == zoomlevels[MAX_ZOOM_INDEX]);
1284 this->SetDirty();
1285 }
1286 }
1287
1292 {
1293 CargoTypes cargo_mask{};
1294 for (int i = 0; i != _smallmap_cargo_count; ++i) {
1295 if (_legend_linkstats[i].show_on_map) cargo_mask.Set(static_cast<CargoType>(_legend_linkstats[i].type));
1296 }
1297 this->overlay->SetCargoMask(cargo_mask);
1298 }
1299
1304 {
1305 StringID legend_tooltip;
1306 StringID enable_all_tooltip;
1307 StringID disable_all_tooltip;
1308 int industry_names_select_plane;
1309 int select_buttons_plane;
1310 switch (this->map_type) {
1312 legend_tooltip = STR_SMALLMAP_TOOLTIP_INDUSTRY_SELECTION;
1313 enable_all_tooltip = STR_SMALLMAP_TOOLTIP_ENABLE_ALL_INDUSTRIES;
1314 disable_all_tooltip = STR_SMALLMAP_TOOLTIP_DISABLE_ALL_INDUSTRIES;
1315 industry_names_select_plane = 0;
1316 select_buttons_plane = 0;
1317 break;
1318
1320 legend_tooltip = STR_SMALLMAP_TOOLTIP_COMPANY_SELECTION;
1321 enable_all_tooltip = STR_SMALLMAP_TOOLTIP_ENABLE_ALL_COMPANIES;
1322 disable_all_tooltip = STR_SMALLMAP_TOOLTIP_DISABLE_ALL_COMPANIES;
1323 industry_names_select_plane = SZSP_NONE;
1324 select_buttons_plane = 0;
1325 break;
1326
1328 legend_tooltip = STR_SMALLMAP_TOOLTIP_CARGO_SELECTION;
1329 enable_all_tooltip = STR_SMALLMAP_TOOLTIP_ENABLE_ALL_CARGOS;
1330 disable_all_tooltip = STR_SMALLMAP_TOOLTIP_DISABLE_ALL_CARGOS;
1331 industry_names_select_plane = SZSP_NONE;
1332 select_buttons_plane = 0;
1333 break;
1334
1335 default:
1336 legend_tooltip = STR_NULL;
1337 enable_all_tooltip = STR_NULL;
1338 disable_all_tooltip = STR_NULL;
1339 industry_names_select_plane = SZSP_NONE;
1340 select_buttons_plane = 1;
1341 break;
1342 }
1343
1344 this->GetWidget<NWidgetCore>(WID_SM_LEGEND)->SetToolTip(legend_tooltip);
1345 this->GetWidget<NWidgetCore>(WID_SM_ENABLE_ALL)->SetStringTip(STR_SMALLMAP_ENABLE_ALL, enable_all_tooltip);
1346 this->GetWidget<NWidgetCore>(WID_SM_DISABLE_ALL)->SetStringTip(STR_SMALLMAP_DISABLE_ALL, disable_all_tooltip);
1347 this->GetWidget<NWidgetStacked>(WID_SM_SHOW_IND_NAMES_SEL)->SetDisplayedPlane(industry_names_select_plane);
1348 this->GetWidget<NWidgetStacked>(WID_SM_SELECT_BUTTONS)->SetDisplayedPlane(select_buttons_plane);
1349 }
1350
1356 uint32_t GetTileColours(const TileArea &ta) const
1357 {
1358 int importance = 0;
1359 TileIndex tile = INVALID_TILE; // Position of the most important tile.
1360 TileType et = TileType::Void; // Effective tile type at that position.
1361
1362 for (TileIndex ti : ta) {
1363 TileType ttype = GetTileType(ti);
1364
1365 switch (ttype) {
1368
1369 switch (tt) {
1370 case TransportType::Rail: ttype = TileType::Railway; break;
1371 case TransportType::Road: ttype = TileType::Road; break;
1372 default: ttype = TileType::Water; break;
1373 }
1374 break;
1375 }
1376
1377 case TileType::Industry:
1378 /* Special handling of industries while in "Industries" smallmap view. */
1379 if (this->map_type == SmallMapType::Industries) {
1380 /* If industry is allowed to be seen, use its colour on the map.
1381 * This has the highest priority above any value in _tiletype_importance. */
1382 IndustryType type = Industry::GetByTile(ti)->type;
1383 if (_legend_from_industries[_industry_to_list_pos[type]].show_on_map) {
1384 if (type == _smallmap_industry_highlight) {
1385 if (_smallmap_industry_highlight_state) return MKCOLOUR_XXXX(PC_WHITE);
1386 } else {
1387 return GetIndustrySpec(type)->map_colour.p * 0x01010101;
1388 }
1389 }
1390 /* Otherwise make it disappear */
1392 }
1393 break;
1394
1395 default:
1396 break;
1397 }
1398
1399 if (_tiletype_importance[ttype] > importance) {
1400 importance = _tiletype_importance[ttype];
1401 tile = ti;
1402 et = ttype;
1403 }
1404 }
1405
1406 switch (this->map_type) {
1408 return GetSmallMapContoursPixels(tile, et);
1409
1411 return GetSmallMapVehiclesPixels(tile, et);
1412
1414 return GetSmallMapIndustriesPixels(tile, et);
1415
1417 return GetSmallMapLinkStatsPixels(tile, et);
1418
1420 return GetSmallMapRoutesPixels(tile, et);
1421
1423 return GetSmallMapVegetationPixels(tile, et);
1424
1427
1428 default: NOT_REACHED();
1429 }
1430 }
1431
1438 {
1440 uint line = (pt.y - wi->pos_y - WidgetDimensions::scaled.framerect.top) / GetCharacterHeight(FontSize::Small);
1441 uint columns = this->GetNumberColumnsLegend(wi->current_x);
1442 uint number_of_rows = this->GetNumberRowsLegend(columns);
1443 if (line >= number_of_rows) return -1;
1444
1445 bool rtl = _current_text_dir == TD_RTL;
1446 int x = pt.x - wi->pos_x;
1447 if (rtl) x = wi->current_x - x;
1448 uint column = (x - WidgetDimensions::scaled.framerect.left) / this->column_width;
1449
1450 return (column * number_of_rows) + line;
1451 }
1452
1455 {
1456 if (this->map_type == SmallMapType::LinkStats) {
1457 CompanyMask company_mask = this->GetOverlayCompanyMask();
1458 if (this->overlay->GetCompanyMask() != company_mask) {
1459 this->overlay->SetCompanyMask(company_mask);
1460 } else {
1461 this->overlay->SetDirty();
1462 }
1463 }
1464 }
1465
1467 void Blink()
1468 {
1469 if (_smallmap_industry_highlight == IT_INVALID) return;
1470
1472
1473 this->UpdateLinks();
1474 this->SetDirty();
1475 }
1476
1479 {
1480 if (_smallmap_industry_highlight != IT_INVALID) return;
1481
1482 this->UpdateLinks();
1483 this->SetDirty();
1484 }
1485
1486public:
1487 friend class NWidgetSmallmapDisplay;
1488
1489 SmallMapWindow(WindowDesc &desc, int window_number) : Window(desc)
1490 {
1491 _smallmap_industry_highlight = IT_INVALID;
1492 this->overlay = std::make_unique<LinkGraphOverlay>(this, WID_SM_MAP, CargoTypes{}, this->GetOverlayCompanyMask(), 1);
1493 this->CreateNestedTree();
1495
1497
1499
1502
1503 this->SetupWidgetData();
1505
1506 this->SetZoomLevel(ZoomLevelChange::Init, nullptr);
1508 this->SetOverlayCargoMask();
1509 }
1510
1515 {
1516 const Viewport &vp = *GetMainWindow()->viewport;
1517 Point viewport_center = InverseRemapCoords2(vp.virtual_left + vp.virtual_width / 2, vp.virtual_top + vp.virtual_height / 2);
1518
1519 int sub;
1520 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(WID_SM_MAP);
1521 Point sxy = this->ComputeScroll(viewport_center.x / (int)TILE_SIZE, viewport_center.y / (int)TILE_SIZE,
1522 std::max(0, (int)wid->current_x / 2 - 2), wid->current_y / 2, &sub);
1523 this->SetNewScroll(sxy.x, sxy.y, sub);
1524 this->SetDirty();
1525 }
1526
1533 {
1534 int x = CentreBounds(st->rect.left, st->rect.right, 0);
1535 int y = CentreBounds(st->rect.top, st->rect.bottom, 0);
1536 Point ret = this->RemapTile(x, y);
1537
1538 /* Same magic 3 as in DrawVehicles; that's where I got it from.
1539 * No idea what it is, but without it the result looks bad.
1540 */
1541 ret.x -= 3 + this->subscroll;
1542 return ret;
1543 }
1544
1545 void Close([[maybe_unused]] int data) override
1546 {
1547 this->BreakIndustryChainLink();
1548 this->Window::Close();
1549 }
1550
1551 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
1552 {
1553 switch (widget) {
1554 case WID_SM_CAPTION:
1555 return GetString(STR_SMALLMAP_CAPTION, STR_SMALLMAP_TYPE_CONTOURS + to_underlying(this->map_type));
1556
1557 default:
1558 return this->Window::GetWidgetString(widget, stringid);
1559 }
1560 }
1561
1562 void OnInit() override
1563 {
1564 uint min_width = 0;
1565 this->min_number_of_columns = INDUSTRY_MIN_NUMBER_OF_COLUMNS;
1566 this->min_number_of_fixed_rows = lengthof(_linkstat_colours_in_legenda);
1568 uint height = 0;
1569 uint num_columns = 1;
1570 for (const LegendAndColour &tbl : _legend_table[i]) {
1571 if (tbl.end) break;
1572 std::string str;
1573 if (i == SmallMapType::Industries) {
1574 str = GetString(STR_SMALLMAP_INDUSTRY, tbl.legend, IndustryPool::MAX_SIZE);
1575 } else if (i == SmallMapType::LinkStats) {
1576 str = GetString(STR_SMALLMAP_LINKSTATS, tbl.legend);
1577 } else if (i == SmallMapType::Owners) {
1578 if (tbl.company != CompanyID::Invalid()) {
1579 if (!Company::IsValidID(tbl.company)) {
1580 /* Rebuild the owner legend. */
1582 this->OnInit();
1583 return;
1584 }
1585 /* Non-fixed legend entries for the owner view. */
1586 str = GetString(STR_SMALLMAP_COMPANY, tbl.company);
1587 } else {
1588 str = GetString(tbl.legend);
1589 }
1590 } else {
1591 if (tbl.col_break) {
1592 this->min_number_of_fixed_rows = std::max(this->min_number_of_fixed_rows, height);
1593 height = 0;
1594 num_columns++;
1595 }
1596 height++;
1597 if (i == SmallMapType::Contour) {
1598 str = GetString(tbl.legend, tbl.height * TILE_HEIGHT_STEP);
1599 } else {
1600 str = GetString(tbl.legend);
1601 }
1602 }
1603 min_width = std::max(GetStringBoundingBox(str).width, min_width);
1604 }
1605 this->min_number_of_fixed_rows = std::max(this->min_number_of_fixed_rows, height);
1606 this->min_number_of_columns = std::max(this->min_number_of_columns, num_columns);
1607 }
1608
1609 /* Width of the legend blob. */
1610 this->legend_width = GetCharacterHeight(FontSize::Small) * 9 / 6;
1611
1612 /* The width of a column is the minimum width of all texts + the size of the blob + some spacing */
1613 this->column_width = min_width + WidgetDimensions::scaled.hsep_normal + this->legend_width + WidgetDimensions::scaled.framerect.Horizontal();
1614
1615 /* Cached string widths of industry names in the smallmap. Calculation is deferred to DrawIndustryNames(). */
1616 std::fill(std::begin(_industry_to_name_string_width), std::end(_industry_to_name_string_width), 0);
1617 }
1618
1619 void OnPaint() override
1620 {
1621 if (this->map_type == SmallMapType::Owners) {
1622 for (const LegendAndColour &tbl : _legend_table[this->map_type]) {
1623 if (tbl.end) break;
1624 if (tbl.company != CompanyID::Invalid() && !Company::IsValidID(tbl.company)) {
1625 /* Rebuild the owner legend. */
1627 this->InvalidateData(1);
1628 break;
1629 }
1630 }
1631 }
1632
1633 this->DrawWidgets();
1634 }
1635
1643 void DrawLegend(const Rect &text, const Rect &icon, bool highlight, std::string_view string) const
1644 {
1645 if (!highlight) {
1646 /* Simply draw the string, not the black border of the legend colour.
1647 * This will enforce the idea of the disabled item */
1648 DrawString(text, string, TextColour::Grey);
1649 } else {
1650 DrawString(text, string, TextColour::Black);
1651 GfxFillRect(icon, PC_BLACK); // Outer border of the legend colour
1652 }
1653 }
1654
1655 void DrawWidget(const Rect &r, WidgetID widget) const override
1656 {
1657 switch (widget) {
1658 case WID_SM_MAP: {
1659 Rect ir = r.Shrink(WidgetDimensions::scaled.bevel);
1660 DrawPixelInfo new_dpi;
1661 if (!FillDrawPixelInfo(&new_dpi, ir)) return;
1662 this->DrawSmallMap(&new_dpi);
1663 break;
1664 }
1665
1666 case WID_SM_LEGEND: {
1667 uint columns = this->GetNumberColumnsLegend(r.Width());
1668 uint number_of_rows = this->GetNumberRowsLegend(columns);
1669 bool rtl = _current_text_dir == TD_RTL;
1670 uint i = 0; // Row counter for industry legend.
1671 uint row_height = GetCharacterHeight(FontSize::Small);
1672 int padding = ScaleGUITrad(1);
1673
1674 Rect origin = r.WithWidth(this->column_width, rtl).Shrink(WidgetDimensions::scaled.framerect).WithHeight(row_height);
1675 Rect text = origin.Indent(this->legend_width + WidgetDimensions::scaled.hsep_normal, rtl);
1676 Rect icon = origin.WithWidth(this->legend_width, rtl).Shrink(0, padding, 0, 0);
1677
1678 for (const LegendAndColour &tbl : _legend_table[this->map_type]) {
1679 if (tbl.end) break;
1680 if (tbl.col_break || ((this->map_type == SmallMapType::Industries || this->map_type == SmallMapType::Owners || this->map_type == SmallMapType::LinkStats) && i++ >= number_of_rows)) {
1681 /* Column break needed, continue at top, COLUMN_WIDTH pixels
1682 * (one "row") to the right. */
1683 int x = rtl ? -(int)this->column_width : this->column_width;
1684 int y = origin.top - text.top;
1685 text = text.Translate(x, y);
1686 icon = icon.Translate(x, y);
1687 i = 1;
1688 }
1689
1690 PixelColour legend_colour = tbl.colour;
1691
1692 switch (this->map_type) {
1694 /* Industry name must be formatted, since it's not in tiny font in the specs.
1695 * So, draw with a parameter and use the STR_SMALLMAP_INDUSTRY string, which is tiny font */
1696 if (tbl.show_on_map && tbl.type == _smallmap_industry_highlight) {
1698 }
1699 this->DrawLegend(text, icon, tbl.show_on_map, GetString(STR_SMALLMAP_INDUSTRY, tbl.legend, Industry::GetIndustryTypeCount(tbl.type)));
1700 break;
1701
1703 this->DrawLegend(text, icon, tbl.show_on_map, GetString(STR_SMALLMAP_LINKSTATS, tbl.legend));
1704 break;
1705
1707 if (tbl.company != CompanyID::Invalid()) {
1708 this->DrawLegend(text, icon, tbl.show_on_map, GetString(STR_SMALLMAP_COMPANY, tbl.company));
1709 break;
1710 }
1711 [[fallthrough]];
1712
1713 default:
1714 /* Anything that is not an industry or a company is using normal process */
1715 GfxFillRect(icon, PC_BLACK);
1716 if (this->map_type == SmallMapType::Contour) {
1717 DrawString(text, GetString(tbl.legend, tbl.height * TILE_HEIGHT_STEP));
1718 } else {
1719 DrawString(text, tbl.legend);
1720 }
1721 break;
1722 }
1723 GfxFillRect(icon.Shrink(WidgetDimensions::scaled.bevel), legend_colour); // Legend colour
1724
1725 text = text.Translate(0, row_height);
1726 icon = icon.Translate(0, row_height);
1727 }
1728 }
1729 }
1730 }
1731
1732 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1733 {
1734 switch (widget) {
1735 case WID_SM_MAP: { // Map window
1736 if (click_count > 0) this->mouse_capture_widget = widget;
1737
1738 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(WID_SM_MAP);
1739 Window *w = GetMainWindow();
1740 int sub;
1741 pt = this->PixelToTile(pt.x - wid->pos_x, pt.y - wid->pos_y, &sub);
1742 ScrollWindowTo(this->scroll_x + pt.x * TILE_SIZE, this->scroll_y + pt.y * TILE_SIZE, -1, w);
1743
1744 this->SetDirty();
1745 break;
1746 }
1747
1748 case WID_SM_ZOOM_IN:
1749 case WID_SM_ZOOM_OUT: {
1750 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(WID_SM_MAP);
1751 Point zoom_pt = { (int)wid->current_x / 2, (int)wid->current_y / 2};
1753 SndClickBeep();
1754 break;
1755 }
1756
1757 case WID_SM_CONTOUR: // Show land contours
1758 case WID_SM_VEHICLES: // Show vehicles
1759 case WID_SM_INDUSTRIES: // Show industries
1760 case WID_SM_LINKSTATS: // Show route map
1761 case WID_SM_ROUTES: // Show transport routes
1762 case WID_SM_VEGETATION: // Show vegetation
1763 case WID_SM_OWNERS: // Show land owners
1764 this->SwitchMapType(static_cast<SmallMapType>(widget - WID_SM_CONTOUR));
1765 SndClickBeep();
1766 break;
1767
1768 case WID_SM_CENTERMAP: // Center the smallmap again
1771 break;
1772
1773 case WID_SM_TOGGLETOWNNAME: // Toggle town names
1774 this->show_towns = !this->show_towns;
1775 this->SetWidgetLoweredState(WID_SM_TOGGLETOWNNAME, this->show_towns);
1776
1777 this->SetDirty();
1778 SndClickBeep();
1779 break;
1780
1781 case WID_SM_SHOW_IND_NAMES: // Toggle industry names
1782 this->show_ind_names = !this->show_ind_names;
1783 this->SetWidgetLoweredState(WID_SM_SHOW_IND_NAMES, this->show_ind_names);
1784
1785 this->SetDirty();
1786 SndClickBeep();
1787 break;
1788
1789 case WID_SM_LEGEND: // Legend
1790 if (this->map_type == SmallMapType::Industries || this->map_type == SmallMapType::LinkStats || this->map_type == SmallMapType::Owners) {
1791 int click_pos = this->GetPositionOnLegend(pt);
1792 if (click_pos < 0) break;
1793
1794 /* If industry type small map*/
1795 if (this->map_type == SmallMapType::Industries) {
1796 /* If click on industries label, find right industry type and enable/disable it. */
1797 if (click_pos < _smallmap_industry_count) {
1799 }
1800 } else if (this->map_type == SmallMapType::LinkStats) {
1801 if (click_pos < _smallmap_cargo_count) {
1803 this->SetOverlayCargoMask();
1804 }
1805 } else if (this->map_type == SmallMapType::Owners) {
1806 if (click_pos < _smallmap_company_count) {
1807 this->SelectLegendItem(click_pos, _legend_land_owners, _smallmap_company_count, NUM_NO_COMPANY_ENTRIES);
1808 }
1809 }
1810 this->SetDirty();
1811 }
1812 break;
1813
1814 case WID_SM_ENABLE_ALL:
1815 case WID_SM_DISABLE_ALL: {
1816 LegendAndColour *tbl = nullptr;
1817 switch (this->map_type) {
1820 this->BreakIndustryChainLink();
1821 break;
1823 tbl = &(_legend_land_owners[NUM_NO_COMPANY_ENTRIES]);
1824 break;
1826 tbl = _legend_linkstats;
1827 break;
1828 default:
1829 NOT_REACHED();
1830 }
1831 for (;!tbl->end && tbl->legend != STR_LINKGRAPH_LEGEND_UNUSED; ++tbl) {
1832 tbl->show_on_map = (widget == WID_SM_ENABLE_ALL);
1833 }
1834 if (this->map_type == SmallMapType::LinkStats) this->SetOverlayCargoMask();
1835 this->SetDirty();
1836 break;
1837 }
1838
1839 case WID_SM_SHOW_HEIGHT: // Enable/disable showing of heightmap.
1842 this->SetDirty();
1843 break;
1844 }
1845 }
1846
1855 void OnInvalidateData(int data = 0, bool gui_scope = true) override
1856 {
1857 if (!gui_scope) return;
1858
1859 switch (data) {
1860 case 1:
1861 /* The owner legend has already been rebuilt. */
1862 this->ReInit();
1863 break;
1864
1865 case 0: {
1866 extern std::bitset<NUM_INDUSTRYTYPES> _displayed_industries;
1868
1869 for (int i = 0; i != _smallmap_industry_count; i++) {
1871 }
1872 break;
1873 }
1874
1875 case 2:
1877 break;
1878
1879 default: NOT_REACHED();
1880 }
1881 this->SetDirty();
1882 }
1883
1884 bool OnRightClick(Point, WidgetID widget) override
1885 {
1886 if (widget != WID_SM_MAP || _scrolling_viewport) return false;
1887
1888 _scrolling_viewport = true;
1889 return true;
1890 }
1891
1892 void OnMouseWheel(int wheel, WidgetID widget) override
1893 {
1894 if (widget != WID_SM_MAP) return;
1895 if (_settings_client.gui.scrollwheel_scrolling != ScrollWheelScrolling::Off) {
1896 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(WID_SM_MAP);
1897 int cursor_x = _cursor.pos.x - this->left - wid->pos_x;
1898 int cursor_y = _cursor.pos.y - this->top - wid->pos_y;
1899 Point pt = {cursor_x, cursor_y};
1901 }
1902 }
1903
1904 void OnScroll(Point delta) override
1905 {
1906 if (_settings_client.gui.scroll_mode == ViewportScrollMode::ViewportRMBFixed || _settings_client.gui.scroll_mode == ViewportScrollMode::MapRMBFixed) _cursor.fix_at = true;
1907
1908 /* While tile is at (delta.x, delta.y)? */
1909 int sub;
1910 Point pt = this->PixelToTile(delta.x, delta.y, &sub);
1911 this->SetNewScroll(this->scroll_x + pt.x * TILE_SIZE, this->scroll_y + pt.y * TILE_SIZE, sub);
1912
1913 this->SetDirty();
1914 }
1915
1916 void OnMouseOver([[maybe_unused]] Point pt, WidgetID widget) override
1917 {
1918 IndustryType new_highlight = IT_INVALID;
1919 if (widget == WID_SM_LEGEND && this->map_type == SmallMapType::Industries) {
1920 int industry_pos = GetPositionOnLegend(pt);
1921 if (industry_pos >= 0 && industry_pos < _smallmap_industry_count) {
1922 new_highlight = _legend_from_industries[industry_pos].type;
1923 }
1924 }
1925 if (new_highlight != _smallmap_industry_highlight) {
1926 _smallmap_industry_highlight = new_highlight;
1928 this->SetDirty();
1929 }
1930 }
1931
1932};
1933
1935bool SmallMapWindow::show_towns = true;
1938
1943class NWidgetSmallmapButtons : public NWidgetContainer {
1945 static constexpr std::tuple<WidgetID, WidgetType, SpriteID, StringID> buttons[] = {
1946 {WID_SM_ZOOM_IN, WWT_PUSHIMGBTN, SPR_IMG_ZOOMIN, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN},
1947 {WID_SM_ZOOM_OUT, WWT_PUSHIMGBTN, SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT},
1948 {WID_SM_CENTERMAP, WWT_PUSHIMGBTN, SPR_IMG_SMALLMAP, STR_SMALLMAP_CENTER_TOOLTIP},
1949 {WID_SM_TOGGLETOWNNAME, WWT_IMGBTN, SPR_IMG_TOWN, STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF},
1950 {INVALID_WIDGET, WWT_PANEL, SPR_EMPTY, STR_NULL}, // Resizable spacer between top and bottom halves.
1951 {WID_SM_BLANK, WWT_IMGBTN, SPR_EMPTY, STR_NULL}, // Optional spacer in 2-column layout only.
1952 {WID_SM_LINKSTATS, WWT_IMGBTN, SPR_IMG_CARGOFLOW, STR_SMALLMAP_TOOLTIP_SHOW_LINK_STATS_ON_MAP},
1953 {WID_SM_ROUTES, WWT_IMGBTN, SPR_IMG_SHOW_ROUTES, STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON},
1954 {WID_SM_VEGETATION, WWT_IMGBTN, SPR_IMG_PLANTTREES, STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP},
1955 {WID_SM_OWNERS, WWT_IMGBTN, SPR_IMG_COMPANY_GENERAL, STR_SMALLMAP_TOOLTIP_SHOW_LAND_OWNERS_ON_MAP},
1956 {WID_SM_CONTOUR, WWT_IMGBTN, SPR_IMG_SHOW_COUNTOURS, STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP},
1957 {WID_SM_VEHICLES, WWT_IMGBTN, SPR_IMG_SHOW_VEHICLES, STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP},
1958 {WID_SM_INDUSTRIES, WWT_IMGBTN, SPR_IMG_INDUSTRY, STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRIES_ON_MAP},
1959 };
1960
1963
1964public:
1969 {
1970 for (const auto &[widget, tp, sprite, string] : buttons) {
1971 if (widget == INVALID_WIDGET) {
1972 auto leaf = std::make_unique<NWidgetBackground>(WWT_PANEL, Colours::Brown, widget);
1973 this->Add(std::move(leaf));
1974 } else {
1975 auto leaf = std::make_unique<NWidgetLeaf>(tp, Colours::Brown, widget, WidgetData{.sprite = sprite}, string);
1976 leaf->SetToolbarMinimalSize(1);
1977 this->Add(std::move(leaf));
1978 }
1979 }
1980 }
1981
1982 void SetupSmallestSize(Window *w) override
1983 {
1984 this->button.width = 0;
1985 this->button.height = 0;
1986
1987 this->num_buttons = 0;
1988 for (const auto &child_wid : this->children) {
1989 child_wid->SetupSmallestSize(w);
1990 this->button.width = std::max(this->button.width, child_wid->smallest_y + child_wid->padding.Vertical());
1991 this->button.height = std::max(this->button.height, child_wid->smallest_x + child_wid->padding.Horizontal());
1992 WidgetID index = child_wid->GetIndex();
1993 if (index != INVALID_WIDGET && index != WID_SM_BLANK) ++this->num_buttons;
1994 }
1995
1996 this->smallest_x = this->button.width * 2;
1997 this->smallest_y = this->button.height * CeilDiv(this->num_buttons, 2);
1998 this->fill_x = 0;
1999 this->fill_y = 1;
2000 this->resize_x = 0;
2001 this->resize_y = 1;
2002 }
2003
2004 std::pair<uint, uint> GetPreferredSizeForSize(uint given_width, uint given_height) override
2005 {
2006 if (given_height >= this->num_buttons * this->button.height) return {this->button.width, given_height};
2007 return {given_width, given_height};
2008 }
2009
2010 void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
2011 {
2012 assert(given_width >= this->button.width && given_height >= this->smallest_y);
2013
2014 this->pos_x = x;
2015 this->pos_y = y;
2016 this->current_x = given_width;
2017 this->current_y = given_height;
2018
2019 uint child_x = 0;
2020 uint child_y = 0;
2021 uint remaining = CeilDiv(this->num_buttons, this->smallest_x == this->current_x ? 2 : 1);
2022
2023 for (auto &child : this->children) {
2024 if (child->type == WWT_PANEL) {
2025 /* Spacer widget needs to fill the remaining space. */
2026 uint height = given_height - child_y - (remaining * this->button.height);
2027 child->AssignSizePosition(sizing, x, y + child_y, given_width, height, rtl);
2028 child_y += height;
2029 } else if (given_width == this->button.width && child->GetIndex() == WID_SM_BLANK) {
2030 /* Blank button only appears in 2-column layout. */
2031 child->AssignSizePosition(sizing, x + child_x, y + child_y, 0, 0, rtl);
2032 } else {
2033 child->AssignSizePosition(sizing, x + child_x, y + child_y, child->smallest_x, child->smallest_y, rtl);
2034 child_x += child->current_x;
2035 if (child_x >= given_width) {
2036 child_x = 0;
2037 child_y += child->current_y;
2038 --remaining;
2039 }
2040 }
2041 }
2042 }
2043
2048 static std::unique_ptr<NWidgetBase> Make()
2049 {
2050 return std::make_unique<NWidgetSmallmapButtons>();
2051 }
2052};
2053
2062class NWidgetSmallmapDisplay : public NWidgetContainer {
2064public:
2065 NWidgetSmallmapDisplay() : NWidgetContainer(NWID_CUSTOM) {}
2066
2067 void SetupSmallestSize(Window *w) override
2068 {
2069 assert(this->children.size() == 2);
2070 NWidgetBase *display = this->children.front().get();
2071 NWidgetBase *bar = this->children.back().get();
2072
2073 display->SetupSmallestSize(w);
2074 bar->SetupSmallestSize(w);
2075
2076 this->smallmap_window = dynamic_cast<SmallMapWindow *>(w);
2077 assert(this->smallmap_window != nullptr);
2078 this->smallest_x = std::max(display->smallest_x, bar->smallest_x + smallmap_window->GetMinLegendWidth());
2079 this->smallest_y = display->smallest_y + std::max(bar->smallest_y, smallmap_window->GetLegendHeight(smallmap_window->min_number_of_columns));
2080 this->fill_x = std::max(display->fill_x, bar->fill_x);
2081 this->fill_y = (display->fill_y == 0 && bar->fill_y == 0) ? 0 : std::min(display->fill_y, bar->fill_y);
2082 this->resize_x = std::max(display->resize_x, bar->resize_x);
2083 this->resize_y = std::min(display->resize_y, bar->resize_y);
2084 this->ApplyAspectRatio();
2085 }
2086
2087 void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
2088 {
2089 this->pos_x = x;
2090 this->pos_y = y;
2091 this->current_x = given_width;
2092 this->current_y = given_height;
2093
2094 assert(this->children.size() == 2);
2095 NWidgetBase *display = this->children.front().get();
2096 NWidgetBase *bar = this->children.back().get();
2097
2098 if (sizing == SizingType::Smallest) {
2099 this->smallest_x = given_width;
2100 this->smallest_y = given_height;
2101 /* Make display and bar exactly equal to their minimal size. */
2102 display->AssignSizePosition(SizingType::Smallest, x, y, display->smallest_x, display->smallest_y, rtl);
2103 bar->AssignSizePosition(SizingType::Smallest, x, y + display->smallest_y, bar->smallest_x, bar->smallest_y, rtl);
2104 }
2105
2106 uint bar_height = std::max(bar->smallest_y, this->smallmap_window->GetLegendHeight(this->smallmap_window->GetNumberColumnsLegend(given_width - bar->smallest_x)));
2107 uint display_height = given_height - bar_height;
2108 display->AssignSizePosition(SizingType::Resize, x, y, given_width, display_height, rtl);
2109 bar->AssignSizePosition(SizingType::Resize, x, y + display_height, given_width, bar_height, rtl);
2110 }
2111};
2112
2122
2124static constexpr std::initializer_list<NWidgetPart> _nested_smallmap_bar = {
2126 EndContainer(),
2127};
2128
2129static std::unique_ptr<NWidgetBase> SmallMapDisplay()
2130{
2131 std::unique_ptr<NWidgetBase> map_display = std::make_unique<NWidgetSmallmapDisplay>();
2132
2133 map_display = MakeNWidgets(_nested_smallmap_display, std::move(map_display));
2134 map_display = MakeNWidgets(_nested_smallmap_bar, std::move(map_display));
2135 return map_display;
2136}
2137
2138static constexpr std::initializer_list<NWidgetPart> _nested_smallmap_widgets = {
2145 EndContainer(),
2146 NWidgetFunction(SmallMapDisplay), // Smallmap display and legend bar + image buttons.
2147 /* Bottom button row and resize box. */
2153 NWidget(WWT_TEXTBTN, Colours::Brown, WID_SM_SHOW_HEIGHT), SetStringTip(STR_SMALLMAP_SHOW_HEIGHT, STR_SMALLMAP_TOOLTIP_SHOW_HEIGHT),
2154
2155 /* 'show industry names' button and container. Only shown for the industry map type. */
2157 NWidget(WWT_TEXTBTN, Colours::Brown, WID_SM_SHOW_IND_NAMES), SetStringTip(STR_SMALLMAP_SHOW_INDUSTRY_NAMES, STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRY_NAMES),
2158 EndContainer(),
2159
2161 EndContainer(),
2162 EndContainer(),
2164 EndContainer(),
2165 EndContainer(),
2167 EndContainer(),
2168};
2169
2172 WindowPosition::Automatic, "smallmap", 0, 0,
2173 WindowClass::SmallMap, WindowClass::None,
2174 {},
2175 _nested_smallmap_widgets
2176);
2177
2185
2194bool ScrollMainWindowTo(int x, int y, int z, bool instant)
2195{
2196 bool res = ScrollWindowTo(x, y, z, GetMainWindow(), instant);
2197
2198 /* If a user scrolls to a tile (via what way what so ever) and already is on
2199 * that tile (e.g.: pressed twice), move the smallmap to that location,
2200 * so you directly see where you are on the smallmap. */
2201
2202 if (res) return res;
2203
2204 SmallMapWindow *w = dynamic_cast<SmallMapWindow*>(FindWindowById(WindowClass::SmallMap, 0));
2205 if (w != nullptr) w->SmallMapCenterOnCurrentPos();
2206
2207 return res;
2208}
2209
2217{
2218 return static_cast<const SmallMapWindow *>(w)->GetStationMiddle(st);
2219}
Class for backupping variables and making sure they are restored later.
EnumBitSet< CargoType, uint64_t > CargoTypes
Bitset of CargoType elements.
Definition cargo_type.h:113
static constexpr CargoType NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:75
CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:22
std::vector< const CargoSpec * > _sorted_cargo_specs
Cargo specifications sorted alphabetically by name.
constexpr Timpl & Set()
Set all bits.
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
Definition factory.hpp:139
How all blitters should look like.
Definition base.hpp:29
virtual void * MoveTo(void *video, int x, int y)=0
Move the destination pointer the requested amount x and y, keeping in mind any pitch and bpp of the r...
virtual void SetPixel(void *video, int x, int y, PixelColour colour)=0
Draw a pixel with a given colour on the video-buffer.
Iterate a range of enum values.
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
static const PixelColour LINK_COLOURS[][12]
Colours for the various "load" states of links.
Baseclass for nested widgets.
uint resize_x
Horizontal resize step (0 means not resizable).
uint fill_x
Horizontal fill stepsize (from initial size, 0 means not resizable).
virtual void SetupSmallestSize(Window *w)=0
Compute smallest size needed by the widget.
uint smallest_x
Smallest horizontal size of the widget in a filled window.
uint current_x
Current horizontal size (after resizing).
int pos_y
Vertical position of top-left corner of the widget in the window.
int pos_x
Horizontal position of top-left corner of the widget in the window.
virtual void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)=0
Assign size and position to the widget.
uint smallest_y
Smallest vertical size of the widget in a filled window.
const WidgetID index
Index of the nested widget (INVALID_WIDGET means 'not used').
uint fill_y
Vertical fill stepsize (from initial size, 0 means not resizable).
uint resize_y
Vertical resize step (0 means not resizable).
uint current_y
Current vertical size (after resizing).
void Add(std::unique_ptr< NWidgetBase > &&wid)
Append widget wid to container.
Definition widget.cpp:1328
std::vector< std::unique_ptr< NWidgetBase > > children
Child widgets in container.
static std::unique_ptr< NWidgetBase > Make()
Create NWidgetSMallmapButtons widget.
uint num_buttons
The number of buttons excluding spacers.
static constexpr std::tuple< WidgetID, WidgetType, SpriteID, StringID > buttons[]
Smallmap button definitions.
std::pair< uint, uint > GetPreferredSizeForSize(uint given_width, uint given_height) override
Get a widget's preferred size for a given size.
void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
Assign size and position to the widget.
Dimension button
The size of a button.
NWidgetSmallmapButtons()
Construct a new NWidgetSmallmapButtons object.
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Custom container class for displaying smallmap with a vertically resizing legend panel.
void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
Assign size and position to the widget.
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
const SmallMapWindow * smallmap_window
Window manager instance.
PixelColour map_colour
Colour on mini-map.
Definition road.h:133
Class managing the smallmap window.
void SmallMapCenterOnCurrentPos()
Center the small map on the current center of the viewport.
void SetOverlayCargoMask()
Set the link graph overlay cargo mask from the legend.
void Close(int data) override
Hide the window and all its child windows, and mark them for a later deletion.
static SmallMapType map_type
Currently displayed legends.
void DrawLegend(const Rect &text, const Rect &icon, bool highlight, std::string_view string) const
Draw a string legend.
uint GetNumberColumnsLegend(uint width) const
Return number of columns that can be displayed in width pixels.
uint column_width
Width of a column in the WID_SM_LEGEND widget.
uint min_number_of_fixed_rows
Minimal number of rows in the legends for the fixed layouts only (all except SmallMapType::Industries...
void SelectLegendItem(int click_pos, LegendAndColour *legend, int end_legend_item, int begin_legend_item=0)
Select and toggle a legend item.
void SwitchMapType(SmallMapType map_type)
Select a new map type.
const IntervalTimer< TimerWindow > blink_interval
Blink the industries (if selected) on a regular interval.
void DrawSmallMap(DrawPixelInfo *dpi) const
Draws the small map.
Point ComputeScroll(int tx, int ty, int x, int y, int *sub) const
Compute base parameters of the smallmap such that tile (tx, ty) starts at pixel (x,...
Point GetStationMiddle(const Station *st) const
Get the center of the given station as point on the screen in the smallmap window.
void ForceRefresh()
Force a full refresh of the map.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
uint GetMinLegendWidth() const
Compute minimal required width of the legends.
static bool show_towns
Display town names in the smallmap.
Point PixelToTile(int px, int py, int *sub, bool add_sub=true) const
Determine the tile relative to the base tile of the smallmap, and the pixel position at that tile for...
void Blink()
Blink the industries (if hover over an industry).
int32_t scroll_y
Vertical world coordinate of the base tile left of the top-left corner of the smallmap display.
int32_t scroll_x
Horizontal world coordinate of the base tile left of the top-left corner of the smallmap display.
void DrawIndustryNames(const DrawPixelInfo *dpi, const int vertical_padding) const
Adds industry names to the smallmap.
static void DrawVertMapIndicator(int x, int y, int y2)
Draws vertical part of map indicator.
uint GetLegendHeight(uint num_columns) const
Compute height given a number of columns.
uint min_number_of_columns
Minimal number of columns in legends.
int32_t subscroll
Number of pixels (0..3) between the right end of the base tile and the pixel at the top-left corner o...
CompanyMask GetOverlayCompanyMask() const
Get a bitmask for company links to be displayed.
uint32_t GetTileColours(const TileArea &ta) const
Decide which colours to show to the user for a group of tiles.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
void DrawTowns(const DrawPixelInfo *dpi, const int vertical_padding) const
Adds town names to the smallmap.
void OnPaint() override
The window must be repainted.
void SetZoomLevel(ZoomLevelChange change, const Point *zoom_pt)
Initialize or change the zoom level.
int GetPositionOnLegend(Point pt)
Determines the mouse position on the legend.
void OnMouseWheel(int wheel, WidgetID widget) override
The mouse wheel has been turned.
void OnMouseOver(Point pt, WidgetID widget) override
The mouse is currently moving over the window or has just moved outside of the window.
ZoomLevelChange
Available kinds of zoomlevel changes.
@ Init
Initialize zoom level.
void SetupWidgetData()
Function to set up widgets depending on the information being shown on the smallmap.
static bool show_ind_names
Display industry names in the smallmap.
void RebuildColourIndexIfNecessary()
Rebuilds the colour indices used for fast access to the smallmap contour colours based on the heightl...
void DrawSmallMapColumn(void *dst, uint xc, uint yc, int pitch, int reps, int start_pos, int end_pos, Blitter *blitter) const
Draws one column of tiles of the small map in a certain mode onto the screen buffer,...
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
static int map_height_limit
Currently used/cached map height limit.
Point RemapTile(int tile_x, int tile_y) const
Remap tile to location on this smallmap.
static void DrawHorizMapIndicator(int x, int x2, int y)
Draws horizontal part of map indicator.
void OnInit() override
Notification that the nested widget tree gets initialized.
void UpdateLinks()
Update all the links on the map.
static const uint INDUSTRY_MIN_NUMBER_OF_COLUMNS
Minimal number of columns in the WID_SM_LEGEND widget for the SmallMapType::Industries legend.
void OnScroll(Point delta) override
Handle the request for (viewport) scrolling.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
uint legend_width
Width of legend 'blob'.
int zoom
Zoom level. Bigger number means more zoom-out (further away).
void DrawMapIndicators() const
Adds map indicators to the smallmap.
bool OnRightClick(Point, WidgetID widget) override
A click with the right mouse button has been made on the window.
void SetNewScroll(int sx, int sy, int sub)
Set new scroll_x, scroll_y, and subscroll values after limiting them such that the center of the smal...
uint GetNumberRowsLegend(uint columns) const
Get the number of rows in the legend from the number of columns.
const IntervalTimer< TimerWindow > refresh_interval
Update the whole map on a regular interval.
void DrawVehicles(const DrawPixelInfo *dpi, Blitter *blitter) const
Adds vehicles to the smallmap.
static void BreakIndustryChainLink()
Notify the industry chain window to stop sending newly selected industries.
A sort-of mixin that implements 'at(pos)' and 'operator[](pos)' only for a specific type.
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
Map accessors for 'clear' tiles.
bool IsClearGround(Tile t, ClearGround ct)
Set the type of clear tile.
Definition clear_map.h:65
@ Grass
Plain grass with dirt transition (0-3).
Definition clear_map.h:22
ClearGround GetClearGround(Tile t)
Get the type of clear tile.
Definition clear_map.h:52
bool IsSnowTile(Tile t)
Test if a tile is covered with snow.
Definition clear_map.h:40
uint GetClearDensity(Tile t)
Get the density of a non-field clear tile.
Definition clear_map.h:77
Definition of stuff that is very close to a company, like the company struct itself.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
static constexpr Owner OWNER_TOWN
A town owns the tile, or a town is expanding.
static constexpr Owner OWNER_NONE
The tile has no ownership.
static constexpr Owner OWNER_WATER
The tile/execution is done by "water".
Function to handling different endian machines.
Type (helpers) for enums.
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
EnumClassIndexContainer< std::array< T, to_underlying(N)>, Index > EnumIndexArray
A typedef for EnumClassIndexContainer using std::array as the backing container type.
Factory to 'query' all available blitters.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:88
@ Start
Align to the start, LTR/RTL aware.
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:899
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:39
void GfxFillRect(int left, int top, int right, int bottom, const std::variant< PixelColour, PaletteID > &colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:116
int DrawString(int left, int right, int top, std::string_view str, ExtendedTextColour colour, Alignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:668
bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
Set up a clipping area for only drawing into a certain area.
Definition gfx.cpp:1572
@ Small
Index of the small font in the font tables.
Definition gfx_type.h:250
@ Invalid
Invalid marker.
Definition gfx_type.h:302
@ Brown
Brown.
Definition gfx_type.h:298
@ White
White colour.
Definition gfx_type.h:330
@ Grey
Grey colour.
Definition gfx_type.h:332
@ Black
Black colour.
Definition gfx_type.h:334
constexpr NWidgetPart NWidgetFunction(NWidgetFunctionType *func_ptr)
Obtain a nested widget (sub)tree from an external source.
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
constexpr NWidgetPart SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Widget part function for setting additional space around a widget.
constexpr NWidgetPart SetStringTip(StringID string, StringID tip={})
Widget part function for setting the string and tooltip.
std::unique_ptr< NWidgetBase > MakeNWidgets(std::span< const NWidgetPart > nwid_parts, std::unique_ptr< NWidgetBase > &&container)
Construct a nested widget tree from an array of parts.
Definition widget.cpp:3431
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=INVALID_WIDGET)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
void SetDirty() const
Mark entire window as dirty (in need of re-paint).
Definition window.cpp:975
The colour tables for heightmaps.
static const uint32_t _violet_map_heights[]
Height map colours for the violet colour scheme, ordered by height.
static const uint32_t _dark_green_map_heights[]
Height map colours for the dark green colour scheme, ordered by height.
static const uint32_t _green_map_heights[]
Height map colours for the green colour scheme, ordered by height.
Base of all industries.
bool IsTileForestIndustry(TileIndex tile)
Check whether the tile is a forest.
const IndustrySpec * GetIndustrySpec(IndustryType thistype)
Accessor for array _industry_specs.
std::array< IndustryType, NUM_INDUSTRYTYPES > _sorted_industry_types
Industry types sorted by name.
std::bitset< NUM_INDUSTRYTYPES > _displayed_industries
Communication from the industry chain window to the smallmap window about what industries to display.
static const IndustryType NUM_INDUSTRYTYPES
total number of industry types, new and old; limited to 240 because we need some special ids like IT_...
Point InverseRemapCoords2(int x, int y, bool clamp_to_map, bool *clamped)
Map 2D viewport or smallmap coordinate to 3D world or tile coordinate.
Functions related to OTTD's landscape.
Point InverseRemapCoords(int x, int y)
Map 2D viewport or smallmap coordinate to 3D world or tile coordinate.
Definition landscape.h:111
@ Arctic
Landscape with snow levels.
Declaration of linkgraph overlay GUI.
#define Point
Macro that prevents name conflicts between included headers.
static TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:376
static uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:429
static uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:419
constexpr bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
PixelColour GetColourGradient(Colours colour, Shade shade)
Get colour gradient palette index.
Definition palette.cpp:393
static constexpr PixelColour PC_TREES
Green palette colour for trees.
@ Light
Light colour shade.
static constexpr PixelColour PC_VERY_LIGHT_YELLOW
Almost-white yellow palette colour.
static constexpr PixelColour PC_ROUGH_LAND
Dark green palette colour for rough land.
static constexpr PixelColour PC_YELLOW
Yellow palette colour.
static constexpr PixelColour PC_GREEN
Green palette colour.
static constexpr PixelColour PC_FIELDS
Light brown palette colour for fields.
static constexpr PixelColour PC_DARK_RED
Dark red palette colour.
static constexpr PixelColour PC_WATER
Dark blue palette colour for water.
static constexpr PixelColour PC_ORANGE
Orange palette colour.
static constexpr PixelColour PC_GRASS_LAND
Dark green palette colour for grass land.
static constexpr PixelColour PC_BARE_LAND
Brown palette colour for bare land.
static constexpr PixelColour PC_GREY
Grey palette colour.
static constexpr PixelColour PC_RED
Red palette colour.
static constexpr PixelColour PC_DARK_GREY
Dark grey palette colour.
static constexpr PixelColour PC_BLACK
Black palette colour.
static constexpr PixelColour PC_RAINFOREST
Pale green palette colour for rainforest.
static constexpr PixelColour PC_WHITE
White palette colour.
static constexpr PixelColour PC_VERY_DARK_BROWN
Almost-black brown palette colour.
static constexpr PixelColour PC_LIGHT_BLUE
Light blue palette colour.
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:303
RailType GetRailType(Tile t)
Gets the rail type of the given tile.
Definition rail_map.h:115
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition road.h:217
RoadType GetRoadTypeRoad(Tile t)
Get the road type for RoadTramType being RoadTramType::Road.
Definition road_map.h:152
RoadType GetRoadTypeTram(Tile t)
Get the road type for RoadTramType being RoadTramType::Tram.
Definition road_map.h:163
Owner GetRoadOwner(Tile t, RoadTramType rtt)
Get the owner of a specific road type.
Definition road_map.h:244
bool HasRoadTypeRoad(Tile t)
Check if a tile has a road type when RoadTramType is RoadTramType::Road.
Definition road_map.h:200
@ INVALID_ROADTYPE
flag for invalid roadtype
Definition road_type.h:29
@ Tram
Tram type.
Definition road_type.h:40
@ Road
Road type.
Definition road_type.h:39
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
@ MapRMBFixed
Map moves with mouse movement on holding right mouse button, cursor position is fixed.
@ ViewportRMBFixed
Viewport moves with mouse movement on holding right mouse button, cursor position is fixed.
@ Off
Scroll wheel has no effect.
bool IsSlopeWithOneCornerRaised(Slope s)
Tests if a specific slope has exactly one corner raised.
Definition slope_func.h:98
#define MK(a, b)
Macro for ordinary entry of LegendAndColour.
void ShowSmallMap()
Show the smallmap window.
static const EnumIndexArray< AndOr, TileType, TileType::MaxSize > _smallmap_contours_andor
Colour masks for "Contour" and "Routes" modes.
static uint32_t GetSmallMapLinkStatsPixels(TileIndex tile, TileType t)
Return the colour a tile would be displayed with in the small map in mode "link stats".
static uint32_t GetSmallMapContoursPixels(TileIndex tile, TileType t)
Return the colour a tile would be displayed with in the small map in mode "Contour".
void BuildLandLegend()
(Re)build the colour tables for the legends.
static uint32_t GetSmallMapVehiclesPixels(TileIndex tile, TileType t)
Return the colour a tile would be displayed with in the small map in mode "Vehicles".
static LegendAndColour _legend_from_industries[NUM_INDUSTRYTYPES+1]
Allow room for all industries, plus a terminator entry This is required in order to have the industry...
#define MS(a, b)
Macro for break marker in arrays of LegendAndColour.
#define MC(col_break)
Macro for a height legend entry with configurable colour.
static const EnumIndexArray< std::span< const LegendAndColour >, SmallMapType, SmallMapType::End > _legend_table
Legend to use for each SmallMapType.
static constexpr VehicleTypeIndexArray< PixelColour, VehicleType::End > _vehicle_type_colours
Vehicle colours in SmallMapType::Vehicles mode.
static const uint8_t _linkstat_colours_in_legenda[]
Link stat colours shown in legenda.
static uint _industry_to_list_pos[NUM_INDUSTRYTYPES]
For connecting industry type to position in industries list(small map legend).
static const EnumIndexArray< AndOr, TileType, TileType::MaxSize > _smallmap_vehicles_andor
Colour masks for "Vehicles", "Industry", and "Vegetation" modes.
static IndustryType _smallmap_industry_highlight
Highlight a specific industry type.
void BuildOwnerLegend()
Completes the array for the owned property legend.
static const int NUM_NO_COMPANY_ENTRIES
Number of entries in the owner legend that are not companies.
static int _smallmap_cargo_count
Number of cargos in the link stats legend.
uint32_t GetSmallMapOwnerPixels(TileIndex tile, TileType t, IncludeHeightmap include_heightmap)
Return the colour a tile would be displayed with in the small map in mode "Owner".
static bool IsDryCoast(TileIndex tile)
Test if a water tile is a dry coast tile, i.e.
static int _smallmap_company_count
Number of entries in the owner legend.
static SmallMapColourScheme _heightmap_schemes[]
Available colour schemes for height maps.
static constexpr std::initializer_list< NWidgetPart > _nested_smallmap_display
Widget parts of the smallmap display and image buttons.
static uint32_t GetSmallMapVegetationPixels(TileIndex tile, TileType t)
Return the colour a tile would be displayed with in the smallmap in mode "Vegetation".
static constexpr std::initializer_list< NWidgetPart > _nested_smallmap_bar
Widget parts of the smallmap legend bar.
static uint32_t GetSmallMapIndustriesPixels(TileIndex tile, TileType t)
Return the colour a tile would be displayed with in the small map in mode "Industries".
static LegendAndColour _legend_land_contours[]
Legend text giving the colours to look for on the minimap.
static bool _smallmap_show_heightmap
Show heightmap in industry and owner mode of smallmap window.
SmallMapType
Types of legends in the WID_SM_LEGEND widget.
@ Contour
Contour legend.
@ Vegetation
Vegetation legend.
@ LinkStats
LinkStats legend.
@ Owners
Owners legend.
@ Vehicles
Vehicles legend.
@ End
End marker.
@ Routes
Routes legend.
@ Industries
Industries legend.
static int _smallmap_industry_count
Number of used industries.
static bool _smallmap_industry_highlight_state
State of highlight blinking.
void BuildLinkStatsLegend()
Populate legend table for the link stat view.
static uint32_t GetSmallMapRoutesPixels(TileIndex tile, TileType t)
Return the colour a tile would be displayed with in the small map in mode "Routes".
static const AndOr & GetMaskPixelsForTileType(TileIndex tile, TileType t, const EnumIndexArray< AndOr, TileType, TileType::MaxSize > &table)
Get the mask pixel group for a tile from the given lookup table, taking account of coast tiles.
static uint16_t _industry_to_name_string_width[NUM_INDUSTRYTYPES]
The string bounding box width for each industry type in the smallmap.
#define MKEND()
Macro for end of list marker in arrays of LegendAndColour.
static constexpr EnumIndexArray< uint32_t, ClearGround, ClearGround::MaxSize > _vegetation_clear_bits
Lookup table of minimap colours to use for each ClearGround type.
static const EnumIndexArray< uint8_t, TileType, TileType::MaxSize > _tiletype_importance
Mapping of tile type to importance of the tile (higher number means more interesting to show).
bool ScrollMainWindowTo(int x, int y, int z, bool instant)
Scrolls the main window to given coordinates.
static TypedIndexContainer< std::array< uint32_t, MAX_COMPANIES >, CompanyID > _company_to_list_pos
For connecting company ID to position in owner list (small map legend).
void BuildIndustriesLegend()
Fills an array for the industries legends.
static WindowDesc _smallmap_desc(WindowPosition::Automatic, "smallmap", 0, 0, WindowClass::SmallMap, WindowClass::None, {}, _nested_smallmap_widgets)
Window definition for the smallmap window.
#define MOEND()
Macro used for forcing a rebuild of the owner legend the first time it is used.
static LegendAndColour _legend_linkstats[NUM_CARGO+lengthof(_linkstat_colours_in_legenda)+1]
Legend entries for the link stats view.
static uint32_t ApplyMask(uint32_t colour, const AndOr &mask)
Mix colours for a smallmap pixel group.
#define MO(a, b)
Macro for non-company owned property entry of LegendAndColour.
Point GetSmallMapStationMiddle(const Window *w, const Station *st)
Determine the middle of a station in the smallmap window.
Smallmap GUI functions.
IncludeHeightmap
Enum for how to include the heightmap pixels/colours in small map related functions.
@ Always
Always include the heightmap.
@ IfEnabled
Only include the heightmap if its enabled in the gui by the player.
Types related to the smallmap widgets.
@ WID_SM_OWNERS
Button to select the owners view.
@ WID_SM_VEHICLES
Button to select the vehicles view.
@ WID_SM_MAP_BORDER
Border around the smallmap.
@ WID_SM_CENTERMAP
Button to move smallmap center to main window center.
@ WID_SM_VEGETATION
Button to select the vegetation view.
@ WID_SM_TOGGLETOWNNAME
Toggle button to display town names.
@ WID_SM_CONTOUR
Button to select the contour view (height map).
@ WID_SM_ENABLE_ALL
Button to enable display of all legend entries.
@ WID_SM_SHOW_HEIGHT
Show heightmap toggle button.
@ WID_SM_ZOOM_OUT
Button to zoom out one step.
@ WID_SM_LEGEND
Bottom panel to display smallmap legends.
@ WID_SM_ZOOM_IN
Button to zoom in one step.
@ WID_SM_MAP
Panel containing the smallmap.
@ WID_SM_SHOW_IND_NAMES
Show industry names toggle button.
@ WID_SM_SELECT_BUTTONS
Selection widget for the buttons present in some smallmap modes.
@ WID_SM_BLANK
Empty button as placeholder.
@ WID_SM_LINKSTATS
Button to select the link stats view.
@ WID_SM_SHOW_IND_NAMES_SEL
Container for the 'show industry names' button, which can be hidden.
@ WID_SM_ROUTES
Button to select the routes view.
@ WID_SM_INDUSTRIES
Button to select the industries view.
@ WID_SM_CAPTION
Caption of the window.
@ WID_SM_DISABLE_ALL
Button to disable display of all legend entries.
void SndClickBeep()
Play a beep sound for a click event if enabled in settings.
Definition sound.cpp:254
Functions related to sound.
static const SpriteID SPR_IMG_PLANTTREES
Definition sprites.h:1259
static const SpriteID SPR_IMG_COMPANY_GENERAL
Definition sprites.h:1252
static const SpriteID SPR_IMG_INDUSTRY
Definition sprites.h:1258
static const SpriteID SPR_IMG_SHOW_ROUTES
Definition sprites.h:1257
static const SpriteID SPR_IMG_SHOW_VEHICLES
Definition sprites.h:1256
static const SpriteID SPR_IMG_ZOOMIN
Definition sprites.h:1264
static const SpriteID SPR_IMG_SHOW_COUNTOURS
Definition sprites.h:1255
static const SpriteID SPR_IMG_ZOOMOUT
Definition sprites.h:1265
static const SpriteID SPR_IMG_TOWN
Definition sprites.h:1248
static const SpriteID SPR_EMPTY
Empty (transparent blue) sprite.
Definition sprites.h:46
static const SpriteID SPR_IMG_SMALLMAP
Definition sprites.h:1247
Maps accessors for stations.
StationType GetStationType(Tile t)
Get the station type of this tile.
Definition station_map.h:44
@ Dock
Ship port.
@ Rail
Railways/train station.
@ Bus
Road stop for busses.
@ Truck
Road stop for trucks.
@ Airport
Airports and heliports, excluding the ones on oil rigs.
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:261
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
Functions related to OTTD's strings.
StrongType::Typedef< uint32_t, struct StringIDTag, StrongType::Compare, StrongType::Integer > StringID
Numeric value that represents a string, independent of the selected language.
@ TD_RTL
Text is written right-to-left by default.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
StationRect rect
NOSAVE: Station spread out rectangle maintained by StationRect::xxx() functions.
Specification of a cargo type.
Definition cargotype.h:77
CargoType Index() const
Determines index of this cargospec.
Definition cargotype.h:111
StringID name
Name of this type of cargo.
Definition cargotype.h:94
T y
Y coordinate.
T x
X coordinate.
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
Definition gfx_type.h:157
Defines the data structure for constructing industry.
StringID name
Displayed name of the industry.
bool enabled
entity still available (by default true).newgrf can disable it, though
PixelColour map_colour
colour used for the small map
Defines the internal data of a functional industry.
Definition industry.h:64
IndustryType type
type of industry.
Definition industry.h:117
static uint16_t GetIndustryTypeCount(IndustryType type)
Get the count of industries for this type.
Definition industry.h:267
static Industry * GetByTile(TileIndex tile)
Get the industry of the given tile.
Definition industry.h:253
Structure for holding relevant data for legends in small map.
PixelColour colour
Colour of the item on the map.
uint8_t height
Height in tiles. Only valid for height legend entries.
StringID legend
String corresponding to the coloured item.
bool show_on_map
For filtering industries, if true, industry is shown on the map in colour.
CompanyID company
Company to display. Only valid for company entries of the owner legend.
bool end
This is the end of the list.
bool col_break
Perform a column break and go further at the next column.
IndustryType type
Type of industry. Only valid for industry entries.
static uint MaxY()
Gets the maximum Y coordinate within the map, including TileType::Void.
Definition map_func.h:298
static uint MaxX()
Gets the maximum X coordinate within the map, including TileType::Void.
Definition map_func.h:289
void ClampToMap()
Clamp the tile area to map borders.
Definition tilearea.cpp:142
Colour for pixel/line drawing.
Definition gfx_type.h:307
uint8_t p
Palette index.
Definition gfx_type.h:308
static Pool::IterateWrapper< Company > Iterate(size_t from=0)
static constexpr size_t MAX_SIZE
Specification of a rectangle with absolute coordinates of all edges.
Rect WithWidth(int width, bool end) const
Copy Rect and set its width.
int Width() const
Get width of Rect.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Rect WithHeight(int height, bool end=false) const
Copy Rect and set its height.
Rect Indent(int indent, bool end) const
Copy Rect and indent it from its position.
Rect Translate(int x, int y) const
Copy and translate Rect by x,y pixels.
Colour scheme of the smallmap.
std::vector< uint32_t > height_colours
Cached colours for each level in a map.
uint32_t default_colour
Default colour of the land.
std::span< const uint32_t > height_colours_base
Base table for determining the colours.
Station data structure.
Town data structure.
Definition town.h:64
Vehicle data structure.
Data structure for viewport, display of a part of the world.
int virtual_top
Virtual top coordinate.
int virtual_left
Virtual left coordinate.
int virtual_width
width << zoom
int virtual_height
height << zoom
Container with the data associated to a single widget.
High level window description.
Definition window_gui.h:172
Data structure for an opened window.
Definition window_gui.h:273
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:987
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1112
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1817
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:792
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing).
Definition window.cpp:3258
void RaiseWidget(WidgetID widget_index)
Marks a widget as raised.
Definition window_gui.h:469
std::unique_ptr< ViewportData > viewport
Pointer to viewport data, if present.
Definition window_gui.h:318
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:513
WidgetID mouse_capture_widget
ID of current mouse capture widget (e.g. dragged scrollbar). INVALID_WIDGET if no widget has mouse ca...
Definition window_gui.h:326
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1807
void SetWidgetLoweredState(WidgetID widget_index, bool lowered_stat)
Sets the lowered/raised status of a widget.
Definition window_gui.h:441
int left
x position of left edge of the window
Definition window_gui.h:309
int top
y position of top edge of the window
Definition window_gui.h:310
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
Definition window.cpp:1841
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:989
void LowerWidget(WidgetID widget_index)
Marks a widget as lowered.
Definition window_gui.h:460
void HandleButtonClick(WidgetID widget)
Do all things to make a button look clicked and mark it to be unclicked in a few ticks.
Definition window.cpp:604
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:381
int height
Height of the window (number of pixels down in y direction).
Definition window_gui.h:312
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:311
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:302
static uint TileHeight(Tile tile)
Returns the height of a tile.
Definition tile_map.h:29
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition tile_map.h:178
TropicZone GetTropicZone(Tile tile)
Get the tropic zone.
Definition tile_map.h:238
Slope GetTileSlope(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.h:279
static TileType GetTileType(Tile tile)
Get the tiletype of a given tile.
Definition tile_map.h:96
static constexpr uint MAX_TILE_HEIGHT
Maximum allowed tile height.
Definition tile_type.h:24
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
@ Rainforest
Rainforest tile.
Definition tile_type.h:84
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:100
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
TileType
The different types of tiles.
Definition tile_type.h:48
@ TunnelBridge
Tunnel entry/exit and bridge heads.
Definition tile_type.h:58
@ Water
Water tile.
Definition tile_type.h:55
@ Station
A tile of a station or airport.
Definition tile_type.h:54
@ Industry
Part of an industry.
Definition tile_type.h:57
@ Railway
A tile with railway.
Definition tile_type.h:50
@ Void
Invisible tiles at the SW and SE border.
Definition tile_type.h:56
@ Trees
Tile with one or more trees.
Definition tile_type.h:53
@ House
A house by a town.
Definition tile_type.h:52
@ Road
A tile with road and/or tram tracks.
Definition tile_type.h:51
@ Clear
A tile without any structures, i.e. grass, rocks, farm fields etc.
Definition tile_type.h:49
OrthogonalTileArea TileArea
Shorthand for the much more common orthogonal tile area.
Definition of Interval and OneShot timers.
Definition of the Window system.
static constexpr std::chrono::milliseconds TIMER_BLINK_INTERVAL
Interval used by blinking interface elements.
Base of the town class.
TransportType
Available types of transport.
@ Rail
Transport by train.
@ Road
Transport by road vehicle.
Map accessors for tree tiles.
TreeGround GetTreeGround(Tile t)
Returns the groundtype for tree tiles.
Definition tree_map.h:102
@ SnowOrDesert
Snow or desert, depending on landscape.
Definition tree_map.h:55
@ RoughSnow
A snow tile that is rough underneath.
Definition tree_map.h:57
Functions that have tunnels and bridges in common.
TransportType GetTunnelBridgeTransportType(Tile t)
Tunnel: Get the transport type of the tunnel (road or rail) Bridge: Get the transport type of the bri...
Base class for all vehicles.
@ Effect
Effect vehicle type (smoke, explosions, sparks, bubbles).
EnumIndexArray< T, VehicleType, Tend > VehicleTypeIndexArray
Array with VehicleType as index.
bool ScrollWindowTo(int x, int y, int z, Window *w, bool instant)
Scrolls the viewport in a window to a given location.
Functions related to (drawing on) viewports.
static const int TILE_HEIGHT_STEP
One Z unit tile height difference is displayed as 50m.
bool IsTileOnWater(Tile t)
Tests if the tile was built on water.
Definition water_map.h:138
bool IsCoastTile(Tile t)
Is it a coast tile.
Definition water_map.h:213
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
@ WWT_INSET
Pressed (inset) panel, most commonly used as combo box text area.
Definition widget_type.h:40
@ WWT_IMGBTN
(Toggle) Button with image
Definition widget_type.h:41
@ NWID_CUSTOM
General Custom widget.
Definition widget_type.h:77
@ WWT_PUSHIMGBTN
Normal push-button (no toggle button) with image caption.
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:66
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:44
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:39
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX).
Definition widget_type.h:57
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX).
Definition widget_type.h:55
@ WWT_CAPTION
Window caption (window title between closebox and stickybox).
Definition widget_type.h:52
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:68
@ WWT_CLOSEBOX
Close box (at top-left of a window).
Definition widget_type.h:60
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window).
Definition widget_type.h:59
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX).
Definition widget_type.h:56
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:71
@ SZSP_NONE
Display plane with zero size in both directions (none filling and resizing).
SizingType
Different forms of sizing nested widgets, using NWidgetBase::AssignSizePosition().
@ Resize
Resize the nested widget tree.
@ Smallest
Initialize nested widget tree to smallest size. Also updates current_x and current_y.
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1190
bool _scrolling_viewport
A viewport is being scrolled with the mouse.
Definition window.cpp:88
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1161
void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
Mark window data of all windows of a given class as invalid (in need of re-computing) Note that by de...
Definition window.cpp:3336
Window functions not directly related to making/drawing windows.
Twindow * AllocateWindowDescFront(WindowDesc &desc, WindowNumber window_number, Targs... extra_arguments)
Open a new window.
@ Automatic
Find a place automatically.
Definition window_gui.h:146
int WidgetID
Widget ID.
Definition window_type.h:21
static constexpr WidgetID INVALID_WIDGET
An invalid widget index.
Definition window_type.h:24
Functions related to zooming.