1
0
mirror of https://github.com/OpenTTD/OpenTTD synced 2026-01-31 08:04:36 +01:00

Add: Town cargo history graphs. (#14461)

This commit is contained in:
Peter Nelson
2025-08-02 23:19:43 +01:00
committed by GitHub
parent aac3a5b683
commit bd57aee3a9
13 changed files with 387 additions and 63 deletions

View File

@@ -18,6 +18,7 @@
#include "../landscape.h"
#include "../subsidy_func.h"
#include "../strings_func.h"
#include "../misc/history_func.hpp"
#include "../safeguards.h"
@@ -112,7 +113,8 @@ void UpdateHousesAndTowns()
RebuildTownCaches();
}
class SlTownSupplied : public DefaultSaveLoadHandler<SlTownSupplied, Town> {
class SlTownOldSupplied : public DefaultSaveLoadHandler<SlTownOldSupplied, Town> {
public:
static inline const SaveLoad description[] = {
SLE_CONDVAR(TransportedCargoStat<uint32_t>, old_max, SLE_UINT32, SLV_165, SL_MAX_VERSION),
@@ -134,23 +136,64 @@ public:
return SlGetStructListLength(NUM_CARGO);
}
void Save(Town *t) const override
{
SlSetStructListLength(std::size(t->supplied));
for (auto &supplied : t->supplied) {
SlObject(&supplied, this->GetDescription());
}
}
void Load(Town *t) const override
{
size_t num_cargo = this->GetNumCargo();
for (size_t i = 0; i < num_cargo; i++) {
SlObject(&t->supplied[i], this->GetLoadDescription());
TransportedCargoStat<uint32_t> cargo_stat;
SlObject(&cargo_stat, this->GetLoadDescription());
/* Ignore empty statistics. */
if (cargo_stat.new_act == 0 && cargo_stat.new_max == 0 && cargo_stat.old_act == 0 && cargo_stat.old_max == 0) continue;
auto &s = t->supplied.emplace_back(static_cast<CargoType>(i));
s.history[LAST_MONTH].production = cargo_stat.old_max;
s.history[LAST_MONTH].transported = cargo_stat.old_act;
s.history[THIS_MONTH].production = cargo_stat.new_max;
s.history[THIS_MONTH].transported = cargo_stat.new_act;
}
}
};
class SlTownSuppliedHistory : public DefaultSaveLoadHandler<SlTownSuppliedHistory, Town::SuppliedCargo> {
public:
static inline const SaveLoad description[] = {
SLE_VAR(Town::SuppliedHistory, production, SLE_UINT32),
SLE_VAR(Town::SuppliedHistory, transported, SLE_UINT32),
};
static inline const SaveLoadCompatTable compat_description = {};
void Save(Town::SuppliedCargo *p) const override
{
SlSetStructListLength(p->history.size());
for (auto &h : p->history) {
SlObject(&h, this->GetDescription());
}
}
void Load(Town::SuppliedCargo *p) const override
{
size_t len = SlGetStructListLength(p->history.size());
for (auto &h : p->history) {
if (--len > p->history.size()) break; // unsigned so wraps after hitting zero.
SlObject(&h, this->GetDescription());
}
}
};
class SlTownSupplied : public VectorSaveLoadHandler<SlTownSupplied, Town, Town::SuppliedCargo> {
public:
inline static const SaveLoad description[] = {
SLE_VAR(Town::SuppliedCargo, cargo, SLE_UINT8),
SLEG_STRUCTLIST("history", SlTownSuppliedHistory),
};
inline const static SaveLoadCompatTable compat_description = {};
std::vector<Town::SuppliedCargo> &GetVector(Town *t) const override { return t->supplied; }
};
class SlTownReceived : public DefaultSaveLoadHandler<SlTownReceived, Town> {
public:
static inline const SaveLoad description[] = {
@@ -205,6 +248,9 @@ public:
}
};
static std::array<Town::SuppliedHistory, 2> _old_pass_supplied{};
static std::array<Town::SuppliedHistory, 2> _old_mail_supplied{};
static const SaveLoad _town_desc[] = {
SLE_CONDVAR(Town, xy, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
SLE_CONDVAR(Town, xy, SLE_UINT32, SLV_6, SL_MAX_VERSION),
@@ -226,22 +272,22 @@ static const SaveLoad _town_desc[] = {
SLE_CONDARR(Town, unwanted, SLE_INT8, MAX_COMPANIES, SLV_104, SL_MAX_VERSION),
/* Slots 0 and 2 are passengers and mail respectively for old saves. */
SLE_CONDVARNAME(Town, supplied[0].old_max, "supplied[CT_PASSENGERS].old_max", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLE_CONDVARNAME(Town, supplied[0].old_max, "supplied[CT_PASSENGERS].old_max", SLE_UINT32, SLV_9, SLV_165),
SLE_CONDVARNAME(Town, supplied[2].old_max, "supplied[CT_MAIL].old_max", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLE_CONDVARNAME(Town, supplied[2].old_max, "supplied[CT_MAIL].old_max", SLE_UINT32, SLV_9, SLV_165),
SLE_CONDVARNAME(Town, supplied[0].new_max, "supplied[CT_PASSENGERS].new_max", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLE_CONDVARNAME(Town, supplied[0].new_max, "supplied[CT_PASSENGERS].new_max", SLE_UINT32, SLV_9, SLV_165),
SLE_CONDVARNAME(Town, supplied[2].new_max, "supplied[CT_MAIL].new_max", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLE_CONDVARNAME(Town, supplied[2].new_max, "supplied[CT_MAIL].new_max", SLE_UINT32, SLV_9, SLV_165),
SLE_CONDVARNAME(Town, supplied[0].old_act, "supplied[CT_PASSENGERS].old_act", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLE_CONDVARNAME(Town, supplied[0].old_act, "supplied[CT_PASSENGERS].old_act", SLE_UINT32, SLV_9, SLV_165),
SLE_CONDVARNAME(Town, supplied[2].old_act, "supplied[CT_MAIL].old_act", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLE_CONDVARNAME(Town, supplied[2].old_act, "supplied[CT_MAIL].old_act", SLE_UINT32, SLV_9, SLV_165),
SLE_CONDVARNAME(Town, supplied[0].new_act, "supplied[CT_PASSENGERS].new_act", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLE_CONDVARNAME(Town, supplied[0].new_act, "supplied[CT_PASSENGERS].new_act", SLE_UINT32, SLV_9, SLV_165),
SLE_CONDVARNAME(Town, supplied[2].new_act, "supplied[CT_MAIL].new_act", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLE_CONDVARNAME(Town, supplied[2].new_act, "supplied[CT_MAIL].new_act", SLE_UINT32, SLV_9, SLV_165),
SLEG_CONDVAR("supplied[CT_PASSENGERS].old_max", _old_pass_supplied[LAST_MONTH].production, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLEG_CONDVAR("supplied[CT_PASSENGERS].old_max", _old_pass_supplied[LAST_MONTH].production, SLE_UINT32, SLV_9, SLV_165),
SLEG_CONDVAR( "supplied[CT_MAIL].old_max", _old_mail_supplied[LAST_MONTH].production, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLEG_CONDVAR( "supplied[CT_MAIL].old_max", _old_mail_supplied[LAST_MONTH].production, SLE_UINT32, SLV_9, SLV_165),
SLEG_CONDVAR("supplied[CT_PASSENGERS].new_max", _old_pass_supplied[THIS_MONTH].production, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLEG_CONDVAR("supplied[CT_PASSENGERS].new_max", _old_pass_supplied[THIS_MONTH].production, SLE_UINT32, SLV_9, SLV_165),
SLEG_CONDVAR( "supplied[CT_MAIL].new_max", _old_mail_supplied[THIS_MONTH].production, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLEG_CONDVAR( "supplied[CT_MAIL].new_max", _old_mail_supplied[THIS_MONTH].production, SLE_UINT32, SLV_9, SLV_165),
SLEG_CONDVAR("supplied[CT_PASSENGERS].old_act", _old_pass_supplied[LAST_MONTH].transported, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLEG_CONDVAR("supplied[CT_PASSENGERS].old_act", _old_pass_supplied[LAST_MONTH].transported, SLE_UINT32, SLV_9, SLV_165),
SLEG_CONDVAR( "supplied[CT_MAIL].old_act", _old_mail_supplied[LAST_MONTH].transported, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLEG_CONDVAR( "supplied[CT_MAIL].old_act", _old_mail_supplied[LAST_MONTH].transported, SLE_UINT32, SLV_9, SLV_165),
SLEG_CONDVAR("supplied[CT_PASSENGERS].new_act", _old_pass_supplied[THIS_MONTH].transported, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLEG_CONDVAR("supplied[CT_PASSENGERS].new_act", _old_pass_supplied[THIS_MONTH].transported, SLE_UINT32, SLV_9, SLV_165),
SLEG_CONDVAR( "supplied[CT_MAIL].new_act", _old_mail_supplied[THIS_MONTH].transported, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLEG_CONDVAR( "supplied[CT_MAIL].new_act", _old_mail_supplied[THIS_MONTH].transported, SLE_UINT32, SLV_9, SLV_165),
SLE_CONDVARNAME(Town, received[TAE_FOOD].old_act, "received[TE_FOOD].old_act", SLE_UINT16, SL_MIN_VERSION, SLV_165),
SLE_CONDVARNAME(Town, received[TAE_WATER].old_act, "received[TE_WATER].old_act", SLE_UINT16, SL_MIN_VERSION, SLV_165),
@@ -268,10 +314,12 @@ static const SaveLoad _town_desc[] = {
SLE_CONDVAR(Town, larger_town, SLE_BOOL, SLV_56, SL_MAX_VERSION),
SLE_CONDVAR(Town, layout, SLE_UINT8, SLV_113, SL_MAX_VERSION),
SLE_CONDVAR(Town, valid_history, SLE_UINT64, SLV_TOWN_SUPPLY_HISTORY, SL_MAX_VERSION),
SLE_CONDREFVECTOR(Town, psa_list, REF_STORAGE, SLV_161, SL_MAX_VERSION),
SLEG_CONDSTRUCTLIST("supplied", SlTownSupplied, SLV_165, SL_MAX_VERSION),
SLEG_CONDSTRUCTLIST("supplied", SlTownOldSupplied, SLV_165, SLV_TOWN_SUPPLY_HISTORY),
SLEG_CONDSTRUCTLIST("supplied", SlTownSupplied, SLV_TOWN_SUPPLY_HISTORY, SL_MAX_VERSION),
SLEG_CONDSTRUCTLIST("received", SlTownReceived, SLV_165, SL_MAX_VERSION),
SLEG_CONDSTRUCTLIST("acceptance_matrix", SlTownAcceptanceMatrix, SLV_166, SLV_REMOVE_TOWN_CARGO_CACHE),
};
@@ -303,6 +351,20 @@ struct CITYChunkHandler : ChunkHandler {
Town *t = new (TownID(index)) Town();
SlObject(t, slt);
if (IsSavegameVersionBefore(SLV_165)) {
/* Passengers and mail were always treated as slots 0 and 2 in older saves. */
auto &pass = t->supplied.emplace_back(0);
pass.history[LAST_MONTH] = _old_pass_supplied[LAST_MONTH];
pass.history[THIS_MONTH] = _old_pass_supplied[THIS_MONTH];
auto &mail = t->supplied.emplace_back(2);
mail.history[LAST_MONTH] = _old_mail_supplied[LAST_MONTH];
mail.history[THIS_MONTH] = _old_mail_supplied[THIS_MONTH];
}
if (IsSavegameVersionBefore(SLV_TOWN_SUPPLY_HISTORY)) {
t->valid_history = 1U << LAST_MONTH;
}
if (t->townnamegrfid == 0 && !IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_END) && GetStringTab(t->townnametype) != TEXT_TAB_OLD_CUSTOM) {
SlErrorCorrupt("Invalid town name generator");
}