From 03920496f04d765ecb9e4ec0e563bcbe73183baf Mon Sep 17 00:00:00 2001 From: dP Date: Thu, 30 Mar 2023 02:35:04 +0400 Subject: [PATCH] Show raw industry production on minimap on max zoom level --- src/citymania/cm_minimap.cpp | 147 +++++++++++++++++++++++++++++++++++ src/citymania/cm_minimap.hpp | 9 +-- src/industry_cmd.cpp | 5 ++ src/saveload/afterload.cpp | 2 + 4 files changed, 158 insertions(+), 5 deletions(-) diff --git a/src/citymania/cm_minimap.cpp b/src/citymania/cm_minimap.cpp index aac3ae4cd1..276dffe7b7 100644 --- a/src/citymania/cm_minimap.cpp +++ b/src/citymania/cm_minimap.cpp @@ -11,6 +11,8 @@ #include "../town.h" #include "../tunnelbridge_map.h" #include "../core/endian_func.hpp" +#include "../core/geometry_func.hpp" +#include "../core/kdtree.hpp" #include "../vehicle_base.h" #include "../sound_func.h" #include "../window_func.h" @@ -170,10 +172,104 @@ static bool _smallmap_show_heightmap = false; /** Highlight a specific industry type */ static IndustryType _smallmap_industry_highlight = INVALID_INDUSTRYTYPE; /** State of highlight blinking */ + static bool _smallmap_industry_highlight_state; /** For connecting company ID to position in owner list (small map legend) */ static uint _company_to_list_pos[MAX_COMPANIES]; + +struct IconTextSizeHelper { +protected: + RectPadding padding; +public: + Dimension text_dim = {}; + uint num_lines = 0; + Dimension icon_dim; + uint line_height; + uint text_ofs_y; + uint icon_ofs_y; + uint text_ofs_x; + Dimension size; + + IconTextSizeHelper(SpriteID icon, RectPadding &padding) { + this->padding = padding; + this->icon_dim = GetSpriteSize(icon); + this->text_ofs_x = this->icon_dim.width + WidgetDimensions::scaled.hsep_normal; + } + + void add(StringID string_id, FontSize font_size) { + this->text_dim = maxdim(this->text_dim, GetStringBoundingBox(string_id, font_size)); + num_lines++; + }; + + void calculate() { + // TODO handle RTL + this->line_height = std::max(this->text_dim.height, this->icon_dim.height); + this->text_ofs_y = (this->line_height - this->text_dim.height + 1) / 2; + this->icon_ofs_y = (this->line_height - this->icon_dim.height) / 2; + this->size = { + this->text_ofs_x + this->text_dim.width + this->padding.Horizontal(), + this->line_height * this->num_lines + this->padding.Vertical() + }; + } + + std::pair make_rects(int left, int top) { + Rect r{left, top, left + (int)this->size.width, top + (int)this->size.height}; + return {r, r.Shrink(this->padding)}; + } +}; + +struct MinimapIndustryKdtreeEntry { + int16 mx; // subtile (y - x) / 2 + int16 my; // subtile (y + x) / 2 + IndustryID index; +}; + +constexpr bool operator==(const MinimapIndustryKdtreeEntry &lhs, const MinimapIndustryKdtreeEntry &rhs) { + return lhs.index == rhs.index; +} + +inline int16 Kdtree_MinimapIndustryXYFunc(const MinimapIndustryKdtreeEntry &e, int dim) { return dim == 0 ? e.mx : e.my; } +typedef Kdtree MinimapIndustryKdtree; + + +MinimapIndustryKdtree _minimap_industry_idx{Kdtree_MinimapIndustryXYFunc}; +uint _max_industry_outputs = 0; + +bool is_cached_industry(const Industry *ind) { + const IndustrySpec *indspec = GetIndustrySpec(ind->type); + return ((indspec->life_type & (INDUSTRYLIFE_ORGANIC | INDUSTRYLIFE_EXTRACTIVE)) != 0); +} + +MinimapIndustryKdtreeEntry get_industry_entry(const Industry *ind) { + auto x = TileX(ind->location.tile) * TILE_SIZE + ind->location.w * TILE_SIZE / 2; + auto y = TileY(ind->location.tile) * TILE_SIZE + ind->location.h * TILE_SIZE / 2; + uint num_outputs = 0; + for (auto i = 0; i < INDUSTRY_NUM_OUTPUTS; i++) + if (ind->produced_cargo[i] != INVALID_CARGO) + num_outputs++; + _max_industry_outputs = std::max(_max_industry_outputs, num_outputs); + return {(int16)((y - x) / 8), (int16)((y + x) / 8), ind->index}; +} + +void minimap_add_industry(const Industry *ind) { + if (!is_cached_industry(ind)) return; + _minimap_industry_idx.Insert(get_industry_entry(ind)); +} + +void minimap_remove_industry(const Industry *ind) { + if (!is_cached_industry(ind)) return; + _minimap_industry_idx.Remove(get_industry_entry(ind)); +} + +void minimap_init_industries() { + _max_industry_outputs = 0; + for (auto i : Industry::Iterate()) { + minimap_add_industry(i); + } +} + + /** * Fills an array for the industries legends. */ @@ -910,6 +1006,48 @@ void SmallMapWindow::DrawVehicles(const DrawPixelInfo *dpi, Blitter *blitter) co } } +void SmallMapWindow::DrawIndustryProduction(const DrawPixelInfo *dpi) const +{ + if (this->map_type != CM_SMT_IMBA) return; + + // Debug(misc, 0, "DrawIndustryProduction {},{} {},{}", dpi->left, dpi->top, sign_w, sign_h); + auto ptl = this->PixelToTile(dpi->left - this->industry_max_sign.width, dpi->top - this->industry_max_sign.height); + auto pbr = this->PixelToTile(dpi->left + dpi->width, dpi->top + dpi->height); + + _minimap_industry_idx.FindContained( + (ptl.y - ptl.x) / 8, + (ptl.y + ptl.x) / 8, + (pbr.y - pbr.x) / 8, + (pbr.y + pbr.x) / 8, + [this] (auto &e) { + auto ind = Industry::GetIfValid(e.index); + if (ind == nullptr) return; + auto pt = this->TileToPixel( + TileX(ind->location.tile) * TILE_SIZE + ind->location.w * TILE_SIZE / 2, + TileY(ind->location.tile) * TILE_SIZE + ind->location.h * TILE_SIZE / 2 + ); + + IconTextSizeHelper its{SPR_CARGO_COAL, WidgetDimensions::scaled.framerect}; + for (auto i = 0; i < INDUSTRY_NUM_OUTPUTS; i++) { + if (ind->produced_cargo[i] == INVALID_CARGO) continue; + SetDParam(0, ind->last_month_production[i]); + its.add(STR_JUST_INT, FS_SMALL); + } + its.calculate(); + this->industry_max_sign = maxdim(this->industry_max_sign, its.size); + auto [r, ir] = its.make_rects(pt.x, pt.y); + GfxFillRect(r, PALETTE_TO_TRANSPARENT, FILLRECT_RECOLOUR); + for (auto i = 0; i < INDUSTRY_NUM_OUTPUTS; i++) { + if (ind->produced_cargo[i] == INVALID_CARGO) continue; + DrawSprite(CargoSpec::Get(ind->produced_cargo[i])->GetCargoIcon(), PAL_NONE, ir.left, ir.top + its.icon_ofs_y); + SetDParam(0, ind->last_month_production[i]); + DrawString(ir.left + its.text_ofs_x, ir.right, ir.top + its.text_ofs_y, STR_JUST_INT, TC_WHITE, SA_LEFT, false, FS_SMALL); + ir.top += its.line_height; + } + } + ); +} + /** * Adds town names to the smallmap. * @param dpi the part of the smallmap to be drawn into @@ -1024,6 +1162,8 @@ void SmallMapWindow::DrawSmallMap(DrawPixelInfo *dpi) const /* Draw vehicles */ if (this->map_type == SMT_CONTOUR || this->map_type == SMT_VEHICLES) this->DrawVehicles(dpi, blitter); + if (this->ui_zoom == 4) this->DrawIndustryProduction(dpi); + /* Draw link stat overlay */ if (this->map_type == SMT_LINKSTATS) this->overlay->Draw(dpi); @@ -1198,6 +1338,13 @@ void SmallMapWindow::RebuildColourIndexIfNecessary() SetDParam(0, 9999999); // max reasonable population this->town_cache.max_sign = GetStringBoundingBox(CM_STR_SMALLMAP_POPULATION); + + SetDParam(0, 9999); + auto text_dim = GetStringBoundingBox(STR_JUST_INT, FS_SMALL); + this->industry_max_sign = { + text_dim.width + WidgetDimensions::scaled.framerect.Horizontal(), + text_dim.height * _max_industry_outputs + WidgetDimensions::scaled.framerect.Vertical(), + }; } /* virtual */ void SmallMapWindow::OnPaint() diff --git a/src/citymania/cm_minimap.hpp b/src/citymania/cm_minimap.hpp index a0b48b5a5e..b86b19393d 100644 --- a/src/citymania/cm_minimap.hpp +++ b/src/citymania/cm_minimap.hpp @@ -44,11 +44,9 @@ void ShowSmallMap(); void BuildLandLegend(); void BuildOwnerLegend(); - - -//struct Mi -// inline uint16 Kdtree_MinimapSignXYFunc(TownID tid, int dim) { return (dim == 0) ? TileX(Town::Get(tid)->xy) : TileY(Town::Get(tid)->xy); } -// typedef Kdtree TownKdtree; +void minimap_add_industry(const Industry *ind); +void minimap_remove_industry(const Industry *ind); +void minimap_init_industries(); class NWidgetSmallmapDisplay; @@ -108,6 +106,7 @@ protected: Dimension max_sign; std::vector> towns; } town_cache; + mutable Dimension industry_max_sign; static void BreakIndustryChainLink(); Point SmallmapRemapCoords(int x, int y) const; diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index 436a8da726..efaa9e6e1a 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -50,6 +50,7 @@ #include "table/build_industry.h" #include "citymania/cm_highlight.hpp" +#include "citymania/cm_minimap.hpp" #include "safeguards.h" @@ -151,6 +152,8 @@ Industry::~Industry() * Also we must not decrement industry counts in that case. */ if (this->location.w == 0) return; + citymania::minimap_remove_industry(this); + const bool has_neutral_station = this->neutral_station != nullptr; for (TileIndex tile_cur : this->location) { @@ -1931,6 +1934,8 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type, InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, IDIWD_FORCE_REBUILD); if (!_generating_world) PopulateStationsNearby(i); + + citymania::minimap_add_industry(i); citymania::UpdateIndustryHighlight(); } diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 4136e3db40..74945f37a6 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -64,6 +64,7 @@ #include "../table/control_codes.h" #include "../citymania/cm_highlight.hpp" +#include "../citymania/cm_minimap.hpp" #include "saveload_internal.h" @@ -3258,6 +3259,7 @@ bool AfterLoadGame() AfterLoadLinkGraphs(); AfterLoadFindBTProCBInfo(); citymania::InitializeZoningMap(); + citymania::minimap_init_industries(); if ((!_networking || _network_server ) && _settings_client.gui.cm_pause_after_load) _pause_mode = PM_PAUSED_NORMAL;