From 290144c5c9a3801660f652dd7a4424a9f6b1d3b1 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 6 Jul 2025 15:33:22 +0100 Subject: [PATCH] Fix #14396: Industry production graph showed zero instead of N/A. Record the number of valid history records per industry so that the graph avoids showing values which are not present as zero. --- src/graph_gui.cpp | 3 ++- src/industry.h | 1 + src/industry_cmd.cpp | 4 ++++ src/misc/history_func.hpp | 21 ++++++++++++++++++--- src/misc/history_type.hpp | 3 +++ src/saveload/industry_sl.cpp | 20 ++++++++++++++++++++ src/saveload/saveload.h | 1 + 7 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp index d8a57e93d8..af642d7493 100644 --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -222,6 +222,7 @@ protected: const Tprojection &proj; ///< Projection to apply. inline void Fill(uint i, const auto &data) const { this->dataset.values[i] = std::invoke(this->proj, data); } + inline void MakeInvalid(uint i) const { this->dataset.values[i] = INVALID_DATAPOINT; } }; /** @@ -1685,7 +1686,7 @@ struct IndustryProductionGraphWindow : BaseCargoGraphWindow { transported.dash = 2; auto transported_filler = Filler{transported, &Industry::ProducedHistory::transported}; - FillFromHistory(p.history, produced_filler, transported_filler); + FillFromHistory(p.history, i->valid_history, produced_filler, transported_filler); } this->SetDirty(); diff --git a/src/industry.h b/src/industry.h index 29d8756948..0122de124c 100644 --- a/src/industry.h +++ b/src/industry.h @@ -89,6 +89,7 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> { TileArea location{INVALID_TILE, 0, 0}; ///< Location of the industry Town *town = nullptr; ///< Nearest town Station *neutral_station = nullptr; ///< Associated neutral station + ValidHistoryMask valid_history = 0; ///< Mask of valid history records. ProducedCargoes produced{}; ///< produced cargo slots AcceptedCargoes accepted{}; ///< accepted cargo slots uint8_t prod_level = 0; ///< general production level diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index 3f9606b059..18f4d94a48 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -1837,6 +1837,8 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type, for (auto &p : i->produced) { p.history[LAST_MONTH].production += ScaleByCargoScale(p.rate * 8, false); } + + UpdateValidHistory(i->valid_history); } if (indspec->callback_mask.Test(IndustryCallbackMask::DecideColour)) { @@ -2494,6 +2496,8 @@ void GenerateIndustries() */ static void UpdateIndustryStatistics(Industry *i) { + UpdateValidHistory(i->valid_history); + for (auto &p : i->produced) { if (IsValidCargoType(p.cargo)) { if (p.history[THIS_MONTH].production != 0) i->last_prod_year = TimerGameEconomy::year; diff --git a/src/misc/history_func.hpp b/src/misc/history_func.hpp index 1eb1f23fe3..b8c5b33050 100644 --- a/src/misc/history_func.hpp +++ b/src/misc/history_func.hpp @@ -10,8 +10,18 @@ #ifndef HISTORY_FUNC_HPP #define HISTORY_FUNC_HPP +#include "../core/bitmath_func.hpp" #include "history_type.hpp" +/** + * Update mask of valid history records. + * @param[in,out] valid_history Valid history records. + */ +inline void UpdateValidHistory(ValidHistoryMask &valid_history) +{ + SB(valid_history, LAST_MONTH, HISTORY_RECORDS - LAST_MONTH, GB(valid_history, LAST_MONTH, HISTORY_RECORDS - LAST_MONTH) << 1ULL | 1ULL); +} + /** * Rotate history. * @tparam T type of history data element. @@ -27,14 +37,19 @@ void RotateHistory(HistoryData &history) /** * Fill some data with historical data. * @param history Historical data to fill from. + * @param valid_history Mask of valid history records. * @param fillers Fillers to fill with history data. */ template -void FillFromHistory(const HistoryData &history, Tfillers... fillers) +void FillFromHistory(const HistoryData &history, ValidHistoryMask valid_history, Tfillers... fillers) { for (uint i = 0; i != N; ++i) { - auto &data = history[N - i]; - (fillers.Fill(i, data), ...); + if (HasBit(valid_history, N - i)) { + auto &data = history[N - i]; + (fillers.Fill(i, data), ...); + } else { + (fillers.MakeInvalid(i), ...); + } } } diff --git a/src/misc/history_type.hpp b/src/misc/history_type.hpp index 5036ca3b40..98bcee97a2 100644 --- a/src/misc/history_type.hpp +++ b/src/misc/history_type.hpp @@ -22,4 +22,7 @@ static constexpr uint8_t LAST_MONTH = 1; template using HistoryData = std::array; +/** Mask of valid history records. */ +using ValidHistoryMask = uint64_t; + #endif /* HISTORY_TYPE_HPP */ diff --git a/src/saveload/industry_sl.cpp b/src/saveload/industry_sl.cpp index 750a729136..6fc5dfe258 100644 --- a/src/saveload/industry_sl.cpp +++ b/src/saveload/industry_sl.cpp @@ -162,6 +162,8 @@ static const SaveLoad _industry_desc[] = { SLE_CONDVAR(Industry, random, SLE_UINT16, SLV_82, SL_MAX_VERSION), SLE_CONDSSTR(Industry, text, SLE_STR | SLF_ALLOW_CONTROL, SLV_INDUSTRY_TEXT, SL_MAX_VERSION), + SLE_CONDVAR(Industry, valid_history, SLE_UINT64, SLV_INDUSTRY_NUM_VALID_HISTORY, SL_MAX_VERSION), + SLEG_CONDSTRUCTLIST("accepted", SlIndustryAccepted, SLV_INDUSTRY_CARGO_REORGANISE, SL_MAX_VERSION), SLEG_CONDSTRUCTLIST("produced", SlIndustryProduced, SLV_INDUSTRY_CARGO_REORGANISE, SL_MAX_VERSION), }; @@ -228,6 +230,24 @@ struct INDYChunkHandler : ChunkHandler { } else if (IsSavegameVersionBefore(SLV_INDUSTRY_CARGO_REORGANISE)) { LoadMoveAcceptsProduced(i, INDUSTRY_NUM_INPUTS, INDUSTRY_NUM_OUTPUTS); } + + if (IsSavegameVersionBefore(SLV_INDUSTRY_NUM_VALID_HISTORY)) { + /* The last month has always been recorded. */ + size_t oldest_valid = LAST_MONTH; + if (!IsSavegameVersionBefore(SLV_PRODUCTION_HISTORY)) { + /* History was extended but we did not keep track of valid history, so assume it from the oldest non-zero value. */ + for (const auto &p : i->produced) { + if (!IsValidCargoType(p.cargo)) continue; + for (size_t n = LAST_MONTH; n < std::size(p.history); ++n) { + if (p.history[n].production == 0 && p.history[n].transported == 0) continue; + oldest_valid = std::max(oldest_valid, n); + } + } + } + /* Set mask bits up to and including the oldest valid record. */ + i->valid_history = (std::numeric_limits::max() >> (std::numeric_limits::digits - (oldest_valid + 1 - LAST_MONTH))) << LAST_MONTH; + } + Industry::industries[i->type].insert(i->index); } } diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 7ab7c06742..8a150e468b 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -404,6 +404,7 @@ enum SaveLoadVersion : uint16_t { SLV_ORDERS_OWNED_BY_ORDERLIST, ///< 354 PR#13948 Orders stored in OrderList, pool removed. SLV_FACE_STYLES, ///< 355 PR#14319 Addition of face styles, replacing gender and ethnicity. + SLV_INDUSTRY_NUM_VALID_HISTORY, ///< 356 PR#14416 Store number of valid history records for industries. SL_MAX_VERSION, ///< Highest possible saveload version };