1
0
Fork 0

Compare commits

..

6 Commits

Author SHA1 Message Date
Peter Nelson 6fa7dd17e3
Change: Add support for different horizontal graph scales. 2025-07-20 22:37:58 +01:00
Peter Nelson cd95712dd1
Codechange: Extend industry cargo history to 24 years.
Monthly data is stored for the current 24 months.
Quarterly data is stored for a further 2-6 years.
Yearly data is stored for a further 6-24 years.
2025-07-20 22:37:57 +01:00
Jonathan G Rennison 821784004d Fix: [Linkgraph] Incorrect NodeID to StationID conversion for EraseFlows 2025-07-20 16:07:11 +02:00
Jonathan G Rennison f0447d59d4 Codechange: Use StationID as StationIDStack Titem type 2025-07-20 16:06:03 +02:00
Jonathan G Rennison cbdd358ae8 Codechange: Allow SmallStack Titem type to be non-structural 2025-07-20 16:06:03 +02:00
Peter Nelson 2cdd50f40e
Fix 03f5f7145f: Wrong colour used when string POP_COLOURs back to initial colour. (#14468)
Fixing ellipsis colour broke the PUSH_COLOUR/POP_COLOUR system, reverting to the last used colour instead of the initial colour.
2025-07-20 14:30:18 +01:00
15 changed files with 78 additions and 70 deletions

View File

@ -410,7 +410,7 @@ void VehicleCargoList::AgeCargo()
return (accepted && cp->first_station != current_station) ? MTA_DELIVER : MTA_KEEP;
} else if (cargo_next == current_station) {
return MTA_DELIVER;
} else if (next_station.Contains(cargo_next.base())) {
} else if (next_station.Contains(cargo_next)) {
return MTA_KEEP;
} else {
return MTA_TRANSFER;
@ -470,7 +470,7 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID
new_shares.ChangeShare(current_station, INT_MIN);
StationIDStack excluded = next_station;
while (!excluded.IsEmpty() && !new_shares.GetShares()->empty()) {
new_shares.ChangeShare(StationID{excluded.Pop()}, INT_MIN);
new_shares.ChangeShare(excluded.Pop(), INT_MIN);
}
if (new_shares.GetShares()->empty()) {
cargo_next = StationID::Invalid();
@ -743,7 +743,7 @@ uint StationCargoList::ShiftCargo(Taction action, StationIDStack next, bool incl
{
uint max_move = action.MaxMove();
while (!next.IsEmpty()) {
this->ShiftCargo(action, StationID{next.Pop()});
this->ShiftCargo(action, next.Pop());
if (action.MaxMove() == 0) break;
}
if (include_invalid && action.MaxMove() > 0) {
@ -853,7 +853,7 @@ uint StationCargoList::Load(uint max_move, VehicleCargoList *dest, StationIDStac
*/
uint StationCargoList::Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
{
return this->ShiftCargo(StationCargoReroute(this, dest, max_move, avoid, avoid2, ge), avoid.base(), false);
return this->ShiftCargo(StationCargoReroute(this, dest, max_move, avoid, avoid2, ge), avoid, false);
}
/*

View File

@ -555,7 +555,7 @@ public:
inline bool HasCargoFor(StationIDStack next) const
{
while (!next.IsEmpty()) {
if (this->packets.find(StationID{next.Pop()}) != this->packets.end()) return true;
if (this->packets.find(next.Pop()) != this->packets.end()) return true;
}
/* Packets for StationID::Invalid() can go anywhere. */
return this->packets.find(StationID::Invalid()) != this->packets.end();

View File

@ -113,13 +113,14 @@ struct SmallStackItem {
* index types of the same length.
* @tparam Titem Value type to be used.
* @tparam Tindex Index type to use for the pool.
* @tparam Tinvalid Invalid item to keep at the bottom of each stack.
* @tparam Tinvalid_value Value to construct invalid item to keep at the bottom of each stack.
* @tparam Tgrowth_step Growth step for pool.
* @tparam Tmax_size Maximum size for pool.
*/
template <typename Titem, typename Tindex, Titem Tinvalid, Tindex Tgrowth_step, Tindex Tmax_size>
template <typename Titem, typename Tindex, auto Tinvalid_value, Tindex Tgrowth_step, Tindex Tmax_size>
class SmallStack : public SmallStackItem<Titem, Tindex> {
public:
static constexpr Titem Tinvalid{Tinvalid_value};
typedef SmallStackItem<Titem, Tindex> Item;

View File

@ -580,10 +580,11 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
const uint shadow_offset = ScaleGUITrad(1);
auto draw_line = [&](const ParagraphLayouter::Line &line, bool do_shadow, int left, int min_x, int max_x, bool truncation, TextColour &last_colour) {
auto draw_line = [&](const ParagraphLayouter::Line &line, bool do_shadow, int left, int min_x, int max_x, bool truncation, TextColour initial_colour) {
const DrawPixelInfo *dpi = _cur_dpi;
int dpi_left = dpi->left;
int dpi_right = dpi->left + dpi->width - 1;
TextColour last_colour = initial_colour;
for (int run_index = 0; run_index < line.CountRuns(); run_index++) {
const ParagraphLayouter::VisualRun &run = line.GetVisualRun(run_index);
@ -593,13 +594,10 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
FontCache *fc = f->fc;
TextColour colour = f->colour;
if (colour == TC_INVALID || HasFlag(last_colour, TC_FORCED)) {
colour = last_colour;
} else {
/* Update the last colour for the truncation ellipsis. */
last_colour = colour;
}
if (colour == TC_INVALID || HasFlag(initial_colour, TC_FORCED)) colour = initial_colour;
bool colour_has_shadow = (colour & TC_NO_SHADE) == 0 && colour != TC_BLACK;
/* Update the last colour for the truncation ellipsis. */
last_colour = colour;
if (do_shadow && (!fc->GetDrawGlyphShadow() || !colour_has_shadow)) continue;
SetColourRemap(do_shadow ? TC_BLACK : colour);
@ -625,16 +623,16 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
GfxMainBlitter(sprite, begin_x + (do_shadow ? shadow_offset : 0), top + (do_shadow ? shadow_offset : 0), BlitterMode::ColourRemap);
}
}
return last_colour;
};
/* Draw shadow, then foreground */
for (bool do_shadow : {true, false}) {
TextColour last_colour = default_colour;
draw_line(line, do_shadow, left - offset_x, min_x, max_x, truncation, last_colour);
TextColour colour = draw_line(line, do_shadow, left - offset_x, min_x, max_x, truncation, default_colour);
if (truncation) {
int x = (_current_text_dir == TD_RTL) ? left : (right - truncation_width);
draw_line(*truncation_layout->front(), do_shadow, x, INT32_MIN, INT32_MAX, false, last_colour);
draw_line(*truncation_layout->front(), do_shadow, x, INT32_MIN, INT32_MAX, false, colour);
}
}

View File

@ -1842,7 +1842,7 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
p.history[LAST_MONTH].production += ScaleByCargoScale(p.rate * 8, false);
}
UpdateValidHistory(i->valid_history, TimerGameEconomy::month);
UpdateValidHistory(i->valid_history, HISTORY_YEAR, TimerGameEconomy::month);
}
if (indspec->callback_mask.Test(IndustryCallbackMask::DecideColour)) {
@ -2519,13 +2519,13 @@ Industry::AcceptedHistory SumHistory(std::span<const Industry::AcceptedHistory>
static void UpdateIndustryStatistics(Industry *i)
{
auto month = TimerGameEconomy::month;
UpdateValidHistory(i->valid_history, month);
UpdateValidHistory(i->valid_history, HISTORY_YEAR, month);
for (auto &p : i->produced) {
if (IsValidCargoType(p.cargo)) {
if (p.history[THIS_MONTH].production != 0) i->last_prod_year = TimerGameEconomy::year;
RotateHistory(p.history, i->valid_history, month);
RotateHistory(p.history, i->valid_history, HISTORY_YEAR, month);
}
}
@ -2534,7 +2534,7 @@ static void UpdateIndustryStatistics(Industry *i)
if (a.history == nullptr) continue;
(*a.history)[THIS_MONTH].waiting = GetAndResetAccumulatedAverage<uint16_t>(a.accumulated_waiting);
RotateHistory(*a.history, i->valid_history, month);
RotateHistory(*a.history, i->valid_history, HISTORY_YEAR, month);
}
}

View File

@ -42,13 +42,13 @@ LinkGraphJob::LinkGraphJob(const LinkGraph &orig) :
}
/**
* Erase all flows originating at a specific node.
* @param from Node to erase flows for.
* Erase all flows originating at a specific station.
* @param from StationID to erase flows for.
*/
void LinkGraphJob::EraseFlows(NodeID from)
void LinkGraphJob::EraseFlows(StationID from)
{
for (NodeID node_id = 0; node_id < this->Size(); ++node_id) {
(*this)[node_id].flows.erase(StationID{from});
(*this)[node_id].flows.erase(from);
}
}
@ -106,7 +106,7 @@ LinkGraphJob::~LinkGraphJob()
/* The station can have been deleted. Remove all flows originating from it then. */
Station *st = Station::GetIfValid(from.base.station);
if (st == nullptr) {
this->EraseFlows(node_id);
this->EraseFlows(from.base.station);
continue;
}
@ -114,7 +114,7 @@ LinkGraphJob::~LinkGraphJob()
* sure that everything is still consistent or ignore it otherwise. */
GoodsEntry &ge = st->goods[this->Cargo()];
if (ge.link_graph != this->link_graph.index || ge.node != node_id) {
this->EraseFlows(node_id);
this->EraseFlows(from.base.station);
continue;
}
@ -136,7 +136,7 @@ LinkGraphJob::~LinkGraphJob()
/* Delete old flows for source stations which have been deleted
* from the new flows. This avoids flow cycles between old and
* new flows. */
while (!erased.IsEmpty()) geflows.erase(StationID{erased.Pop()});
while (!erased.IsEmpty()) geflows.erase(erased.Pop());
} else if ((*lg)[node_id][dest_id].last_unrestricted_update == EconomyTime::INVALID_DATE) {
/* Edge is fully restricted. */
flows.RestrictFlows(to);

View File

@ -167,7 +167,7 @@ protected:
std::atomic<bool> job_completed = false; ///< Is the job still running. This is accessed by multiple threads and reads may be stale.
std::atomic<bool> job_aborted = false; ///< Has the job been aborted. This is accessed by multiple threads and reads may be stale.
void EraseFlows(NodeID from);
void EraseFlows(StationID from);
void JoinThread();
void SpawnThread();

View File

@ -7,9 +7,6 @@
/** @file history.cpp Implementation of functions for storing historical data. */
#ifndef HISTORY_CPP
#define HISTORY_CPP
#include "../stdafx.h"
#include "../core/bitmath_func.hpp"
@ -18,26 +15,35 @@
#include "../safeguards.h"
static void UpdateValidHistory(ValidHistoryMask &valid_history, const HistoryRange &hr, uint cur_month)
/**
* Update mask of valid records for a historical data.
* @note Call only for the largest history range sub-division.
* @param[in,out] valid_history Valid history records.
* @param hr History range to update mask for.
* @param cur_month Current economy month.
*/
void UpdateValidHistory(ValidHistoryMask &valid_history, const HistoryRange &hr, uint cur_month)
{
/* Update for subdivisions first. */
if (hr.hr != nullptr) UpdateValidHistory(valid_history, *hr.hr, cur_month);
/* No need to update if our last entry is marked valid. */
if (HasBit(valid_history, hr.last - 1)) return;
/* Is it the right time for this history range? */
if (cur_month % hr.total_division != 0) return;
/* Is the previous history range valid yet? */
if (hr.division != 1 && !HasBit(valid_history, hr.first - hr.division)) return;
SB(valid_history, hr.first, hr.records, GB(valid_history, hr.first, hr.records) << 1ULL | 1ULL);
}
/**
* Update mask of valid records.
* @param[in,out] valid_history Valid history records.
* @param age Current economy month.
* Test if history data is valid, without extracting data.
* @param valid_history Mask of valid history records.
* @param hr History range to test.
* @param age Age of data to test.
* @return True iff the data for history range and age is valid.
*/
void UpdateValidHistory(ValidHistoryMask &valid_history, uint cur_month)
{
UpdateValidHistory(valid_history, HISTORY_MONTH, cur_month);
UpdateValidHistory(valid_history, HISTORY_QUARTER, cur_month);
UpdateValidHistory(valid_history, HISTORY_YEAR, cur_month);
}
bool IsValidHistory(ValidHistoryMask valid_history, const HistoryRange &hr, uint age)
{
if (hr.hr == nullptr) {
@ -48,7 +54,7 @@ bool IsValidHistory(ValidHistoryMask valid_history, const HistoryRange &hr, uint
} else {
if (age * hr.division < static_cast<uint>(hr.hr->periods - hr.division)) {
uint start = age * hr.division + ((TimerGameEconomy::month / hr.hr->division) % hr.division);
return IsValidHistory(valid_history, *hr.hr, start/* + hr.division - 1*/);
return IsValidHistory(valid_history, *hr.hr, start);
}
if (age < hr.periods) {
uint slot = hr.first + age - ((hr.hr->periods / hr.division) - 1);
@ -57,5 +63,3 @@ bool IsValidHistory(ValidHistoryMask valid_history, const HistoryRange &hr, uint
}
return false;
}
#endif /* HISTORY_CPP */

View File

@ -15,7 +15,7 @@
#include "../timer/timer_game_economy.h"
#include "history_type.hpp"
void UpdateValidHistory(ValidHistoryMask &valid_history, uint cur_month);
void UpdateValidHistory(ValidHistoryMask &valid_history, const HistoryRange &hr, uint cur_month);
bool IsValidHistory(ValidHistoryMask valid_history, const HistoryRange &hr, uint age);
/**
@ -29,19 +29,25 @@ template <typename T>
T SumHistory(typename std::span<const T> history);
/**
* Rotate history.
* Rotate historical data.
* @note Call only for the largest history range sub-division.
* @tparam T type of history data element.
* @param history Historical data to rotate.
* @param valid_history Mask of valid history records.
* @param hr History range to rotate..
* @param cur_month Current economy month.
*/
template <typename T>
void RotateHistory(HistoryData<T> &history, ValidHistoryMask valid_history, const HistoryRange &hr, uint cur_month)
{
if (hr.hr != nullptr) RotateHistory(history, valid_history, *hr.hr, cur_month);
if (cur_month % hr.total_division != 0) return;
std::move_backward(std::next(std::begin(history), hr.first), std::next(std::begin(history), hr.last - 1), std::next(std::begin(history), hr.last));
if (hr.division == 1) {
if (hr.total_division == 1) {
history[hr.first] = history[hr.first - 1];
history.front() = {};
} else if (HasBit(valid_history, hr.first - hr.division)) {
auto first = std::next(std::begin(history), hr.first - hr.division);
auto last = std::next(first, hr.division);
@ -49,15 +55,6 @@ void RotateHistory(HistoryData<T> &history, ValidHistoryMask valid_history, cons
}
}
template <typename T>
void RotateHistory(HistoryData<T> &history, ValidHistoryMask valid_history, uint cur_month)
{
RotateHistory(history, valid_history, HISTORY_MONTH, cur_month);
RotateHistory(history, valid_history, HISTORY_QUARTER, cur_month);
RotateHistory(history, valid_history, HISTORY_YEAR, cur_month);
history.front() = {};
}
/**
* Get an average value for the previous month, as reset for the next month.
* @param total Accrued total to average. Will be reset to zero.
@ -71,6 +68,17 @@ T GetAndResetAccumulatedAverage(Taccrued &total)
return result;
}
/**
* Get historical data.
* @tparam T type of history data element.
* @param history History data to extract from.
* @param valid_history Mask of valid history records.
* @param hr History range to get.
* @param age Age of data to get.
* @param cur_month Current economy month.
* @param[out] result Extracted historical data.
* @return True iff the data for this history range and age is valid.
*/
template <typename T>
bool GetHistory(const HistoryData<T> &history, ValidHistoryMask valid_history, const HistoryRange &hr, uint age, T &result)
{

View File

@ -10,10 +10,6 @@
#ifndef HISTORY_TYPE_HPP
#define HISTORY_TYPE_HPP
#include "../stdafx.h"
static constexpr uint8_t HISTORY_PERIODS = 24;
struct HistoryRange {
const HistoryRange *hr;
const uint8_t periods; ///< Number of periods for this range.
@ -35,6 +31,7 @@ struct HistoryRange {
}
};
static constexpr uint8_t HISTORY_PERIODS = 24;
static constexpr HistoryRange HISTORY_MONTH{HISTORY_PERIODS};
static constexpr HistoryRange HISTORY_QUARTER{HISTORY_MONTH, 3, HISTORY_PERIODS};
static constexpr HistoryRange HISTORY_YEAR{HISTORY_QUARTER, 4, HISTORY_PERIODS};

View File

@ -368,7 +368,7 @@ StationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, VehicleOrderI
next = v->cur_implicit_order_index;
if (next >= this->GetNumOrders()) {
next = this->GetFirstOrder();
if (next == INVALID_VEH_ORDER_ID) return StationID::Invalid().base();
if (next == INVALID_VEH_ORDER_ID) return StationID::Invalid();
} else {
/* GetNext never returns INVALID_VEH_ORDER_ID if there is a valid station in the list.
* As the given "next" is already valid and a station in the list, we
@ -404,11 +404,11 @@ StationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, VehicleOrderI
if (next == INVALID_VEH_ORDER_ID || ((orders[next].IsType(OT_GOTO_STATION) || orders[next].IsType(OT_IMPLICIT)) &&
orders[next].GetDestination() == v->last_station_visited &&
(orders[next].GetUnloadType() & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0)) {
return StationID::Invalid().base();
return StationID::Invalid();
}
} while (orders[next].IsType(OT_GOTO_DEPOT) || orders[next].GetDestination() == v->last_station_visited);
return orders[next].GetDestination().ToStationID().base();
return orders[next].GetDestination().ToStationID();
}
/**

View File

@ -5079,7 +5079,7 @@ StationIDStack FlowStatMap::DeleteFlows(StationID via)
FlowStat &s_flows = f_it->second;
s_flows.ChangeShare(via, INT_MIN);
if (s_flows.GetShares()->empty()) {
ret.Push(f_it->first.base());
ret.Push(f_it->first);
this->erase(f_it++);
} else {
++f_it;

View File

@ -26,7 +26,7 @@ struct RoadStop;
struct StationSpec;
struct Waypoint;
using StationIDStack = SmallStack<StationID::BaseType, StationID::BaseType, StationID::Invalid().base(), 8, StationID::End().base()>;
using StationIDStack = SmallStack<StationID, StationID::BaseType, StationID::Invalid().base(), 8, StationID::End().base()>;
/** Station types */
enum class StationType : uint8_t {

View File

@ -48,8 +48,8 @@ TEST_CASE("History Rotation and Reporting tests")
uint16_t i = 12 * HISTORY_PERIODS;
for (uint date = 1; date <= 12 * HISTORY_PERIODS; ++date, --i) {
history[THIS_MONTH] = i;
UpdateValidHistory(valid_history, date % 12);
RotateHistory(history, valid_history, date % 12);
UpdateValidHistory(valid_history, HISTORY_YEAR, date % 12);
RotateHistory(history, valid_history, HISTORY_YEAR, date % 12);
}
/* With the decreasing sequence, the expected value is triangle number (x*x+n)/2 and the square of the total divisions.

View File

@ -741,7 +741,7 @@ public:
*/
inline StationIDStack GetNextStoppingStation() const
{
return (this->orders == nullptr) ? StationID::Invalid().base() : this->orders->GetNextStoppingStation(this);
return (this->orders == nullptr) ? StationID::Invalid() : this->orders->GetNextStoppingStation(this);
}
void ResetRefitCaps();