Changeset - r28549:6bc33bc6ee38
[Not reviewed]
master
! ! !
Tyler Trahan - 11 months ago 2024-01-22 14:04:34
tyler@tylertrahan.com
Codechange: Split dates and timers into Economy and Calendar time (#10700)
65 files changed with 952 insertions and 550 deletions:
0 comments (0 inline, 0 general)
src/aircraft.h
Show inline comments
 
@@ -107,7 +107,8 @@ struct Aircraft FINAL : public Specializ
 
	}
 

	
 
	bool Tick() override;
 
	void OnNewDay() override;
 
	void OnNewCalendarDay() override;
 
	void OnNewEconomyDay() override;
 
	uint Crash(bool flooded = false) override;
 
	TileIndex GetOrderStationLocation(StationID station) override;
 
	ClosestDepot FindClosestDepot() override;
src/aircraft_cmd.cpp
Show inline comments
 
@@ -22,6 +22,7 @@
 
#include "command_func.h"
 
#include "window_func.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "vehicle_func.h"
 
#include "sound_func.h"
 
#include "cheat_type.h"
 
@@ -338,7 +339,7 @@ CommandCost CmdBuildAircraft(DoCommandFl
 

	
 
		v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_aircraft);
 

	
 
		v->date_of_last_service = TimerGameCalendar::date;
 
		v->date_of_last_service = TimerGameEconomy::date;
 
		v->date_of_last_service_newgrf = TimerGameCalendar::date;
 
		v->build_year = u->build_year = TimerGameCalendar::year;
 

	
 
@@ -439,7 +440,15 @@ Money Aircraft::GetRunningCost() const
 
	return GetPrice(PR_RUNNING_AIRCRAFT, cost_factor, e->GetGRF());
 
}
 

	
 
void Aircraft::OnNewDay()
 
/** Calendar day handler */
 
void Aircraft::OnNewCalendarDay()
 
{
 
	if (!this->IsNormalAircraft()) return;
 
	AgeVehicle(this);
 
}
 

	
 
/** Economy day handler */
 
void Aircraft::OnNewEconomyDay()
 
{
 
	if (!this->IsNormalAircraft()) return;
 

	
 
@@ -448,7 +457,6 @@ void Aircraft::OnNewDay()
 
	CheckOrders(this);
 

	
 
	CheckVehicleBreakdown(this);
 
	AgeVehicle(this);
 
	CheckIfAircraftNeedsService(this);
 

	
 
	if (this->running_ticks == 0) return;
 
@@ -1555,7 +1563,7 @@ static void AircraftEventHandler_AtTermi
 
		if (_settings_game.order.serviceathelipad) {
 
			if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
 
				/* an excerpt of ServiceAircraft, without the invisibility stuff */
 
				v->date_of_last_service = TimerGameCalendar::date;
 
				v->date_of_last_service = TimerGameEconomy::date;
 
				v->date_of_last_service_newgrf = TimerGameCalendar::date;
 
				v->breakdowns_since_last_service = 0;
 
				v->reliability = v->GetEngine()->reliability;
src/cheat_gui.cpp
Show inline comments
 
@@ -31,6 +31,7 @@
 
#include "core/geometry_func.hpp"
 
#include "timer/timer.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 

	
 
#include "widgets/cheat_widget.h"
 

	
 
@@ -91,7 +92,7 @@ static int32_t ClickSetProdCheat(int32_t
 
	return _cheats.setup_prod.value;
 
}
 

	
 
extern void EnginesMonthlyLoop();
 
extern void CalendarEnginesMonthlyLoop();
 

	
 
/**
 
 * Handle changing of the current year.
 
@@ -105,16 +106,19 @@ static int32_t ClickChangeDateCheat(int3
 
	if (new_year == TimerGameCalendar::year) return TimerGameCalendar::year.base();
 

	
 
	TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::date);
 
	TimerGameCalendar::Date new_date = TimerGameCalendar::ConvertYMDToDate(new_year, ymd.month, ymd.day);
 
	TimerGameCalendar::Date new_calendar_date = TimerGameCalendar::ConvertYMDToDate(new_year, ymd.month, ymd.day);
 
	/* Keep economy and calendar dates synced. */
 
	TimerGameEconomy::Date new_economy_date = new_calendar_date.base();
 

	
 
	/* Shift cached dates before we change the date. */
 
	for (auto v : Vehicle::Iterate()) v->ShiftDates(new_date - TimerGameCalendar::date);
 
	LinkGraphSchedule::instance.ShiftDates(new_date - TimerGameCalendar::date);
 
	for (auto v : Vehicle::Iterate()) v->ShiftDates(new_economy_date - TimerGameEconomy::date);
 
	LinkGraphSchedule::instance.ShiftDates(new_economy_date - TimerGameEconomy::date);
 

	
 
	/* Now it's safe to actually change the date. */
 
	TimerGameCalendar::SetDate(new_date, TimerGameCalendar::date_fract);
 
	TimerGameCalendar::SetDate(new_calendar_date, TimerGameCalendar::date_fract);
 
	TimerGameEconomy::SetDate(new_economy_date, TimerGameEconomy::date_fract);
 

	
 
	EnginesMonthlyLoop();
 
	CalendarEnginesMonthlyLoop();
 
	SetWindowDirty(WC_STATUS_BAR, 0);
 
	InvalidateWindowClassesData(WC_BUILD_STATION, 0);
 
	InvalidateWindowClassesData(WC_BUS_STATION, 0);
src/command.cpp
Show inline comments
 
@@ -18,7 +18,7 @@
 
#include "strings_func.h"
 
#include "texteff.hpp"
 
#include "town.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "company_func.h"
 
#include "company_base.h"
 
#include "signal_func.h"
 
@@ -274,7 +274,7 @@ void CommandHelperBase::InternalPostResu
 
/** Helper to make a desync log for a command. */
 
void CommandHelperBase::LogCommandExecution(Commands cmd, StringID err_message, const CommandDataBuffer &args, bool failed)
 
{
 
	Debug(desync, 1, "{}: {:08x}; {:02x}; {:02x}; {:08x}; {:08x}; {} ({})", failed ? "cmdf" : "cmd", (uint32_t)TimerGameCalendar::date.base(), TimerGameCalendar::date_fract, (int)_current_company, cmd, err_message, FormatArrayAsHex(args), GetCommandName(cmd));
 
	Debug(desync, 1, "{}: {:08x}; {:02x}; {:02x}; {:08x}; {:08x}; {} ({})", failed ? "cmdf" : "cmd", (uint32_t)TimerGameEconomy::date.base(), TimerGameEconomy::date_fract, (int)_current_company, cmd, err_message, FormatArrayAsHex(args), GetCommandName(cmd));
 
}
 

	
 
/**
src/company_base.h
Show inline comments
 
@@ -14,7 +14,7 @@
 
#include "livery.h"
 
#include "autoreplace_type.h"
 
#include "tile_type.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "settings_type.h"
 
#include "group.h"
 

	
 
@@ -74,7 +74,7 @@ struct CompanyProperties {
 
	TileIndex location_of_HQ;        ///< Northern tile of HQ; #INVALID_TILE when there is none.
 
	TileIndex last_build_coordinate; ///< Coordinate of the last build thing by this company.
 

	
 
	TimerGameCalendar::Year inaugurated_year; ///< Year of starting the company.
 
	TimerGameEconomy::Year inaugurated_year; ///< Economy year of starting the company.
 

	
 
	byte months_of_bankruptcy;       ///< Number of months that the company is unable to pay its debts
 
	CompanyMask bankrupt_asked;      ///< which companies were asked about buying it?
src/company_cmd.cpp
Show inline comments
 
@@ -36,7 +36,7 @@
 
#include "widgets/statusbar_widget.h"
 
#include "company_cmd.h"
 
#include "timer/timer.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "timer/timer_game_tick.h"
 

	
 
#include "table/strings.h"
 
@@ -566,7 +566,7 @@ Company *DoStartupNewCompany(bool is_ai,
 

	
 
	c->avail_railtypes = GetCompanyRailTypes(c->index);
 
	c->avail_roadtypes = GetCompanyRoadTypes(c->index);
 
	c->inaugurated_year = TimerGameCalendar::year;
 
	c->inaugurated_year = TimerGameEconomy::year;
 

	
 
	/* If starting a player company in singleplayer and a favorite company manager face is selected, choose it. Otherwise, use a random face.
 
	 * In a network game, we'll choose the favorite face later in CmdCompanyCtrl to sync it to all clients. */
 
@@ -747,7 +747,7 @@ void OnTick_Companies()
 
 * A year has passed, update the economic data of all companies, and perhaps show the
 
 * financial overview window of the local company.
 
 */
 
static IntervalTimer<TimerGameCalendar> _companies_yearly({TimerGameCalendar::YEAR, TimerGameCalendar::Priority::COMPANY}, [](auto)
 
static IntervalTimer<TimerGameEconomy> _economy_companies_yearly({TimerGameEconomy::YEAR, TimerGameEconomy::Priority::COMPANY}, [](auto)
 
{
 
	/* Copy statistics */
 
	for (Company *c : Company::Iterate()) {
src/company_gui.cpp
Show inline comments
 
@@ -22,7 +22,7 @@
 
#include "newgrf.h"
 
#include "company_manager_face.h"
 
#include "strings_func.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "widgets/dropdown_type.h"
 
#include "tilehighlight_func.h"
 
#include "company_base.h"
 
@@ -255,7 +255,7 @@ static Money DrawYearCategory(const Rect
 
 * @param tbl  Reference to table of amounts for \a year.
 
 * @note The environment must provide padding at the left and right of \a r.
 
 */
 
static void DrawYearColumn(const Rect &r, TimerGameCalendar::Year year, const Expenses &tbl)
 
static void DrawYearColumn(const Rect &r, TimerGameEconomy::Year year, const Expenses &tbl)
 
{
 
	int y = r.top;
 
	Money sum;
 
@@ -421,10 +421,10 @@ struct CompanyFinancesWindow : Window {
 
			case WID_CF_EXPS_PRICE2:
 
			case WID_CF_EXPS_PRICE3: {
 
				const Company *c = Company::Get((CompanyID)this->window_number);
 
				auto age = std::min(TimerGameCalendar::year - c->inaugurated_year, TimerGameCalendar::Year(2));
 
				auto age = std::min(TimerGameEconomy::year - c->inaugurated_year, TimerGameEconomy::Year(2));
 
				int wid_offset = widget - WID_CF_EXPS_PRICE1;
 
				if (wid_offset <= age) {
 
					DrawYearColumn(r, TimerGameCalendar::year - (age - wid_offset), c->yearly_expenses[(age - wid_offset).base()]);
 
					DrawYearColumn(r, TimerGameEconomy::year - (age - wid_offset), c->yearly_expenses[(age - wid_offset).base()]);
 
				}
 
				break;
 
			}
src/core/random_func.cpp
Show inline comments
 
@@ -84,7 +84,7 @@ void SetRandomSeed(uint32_t seed)
 
uint32_t DoRandom(int line, const char *file)
 
{
 
	if (_networking && (!_network_server || (NetworkClientSocket::IsValidID(0) && NetworkClientSocket::Get(0)->status != NetworkClientSocket::STATUS_INACTIVE))) {
 
		Debug(random, 0, "{:08x}; {:02x}; {:04x}; {:02x}; {}:{}", TimerGameCalendar::date, TimerGameCalendar::date_fract, _frame_counter, (byte)_current_company, file, line);
 
		Debug(random, 0, "{:08x}; {:02x}; {:04x}; {:02x}; {}:{}", TimerGameEconomy::date, TimerGameEconomy::date_fract, _frame_counter, (byte)_current_company, file, line);
 
	}
 

	
 
	return _random.Next();
src/date_gui.cpp
Show inline comments
 
@@ -9,7 +9,7 @@
 

	
 
#include "stdafx.h"
 
#include "strings_func.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "window_func.h"
 
#include "window_gui.h"
 
#include "date_gui.h"
 
@@ -25,9 +25,9 @@
 
struct SetDateWindow : Window {
 
	SetDateCallback *callback; ///< Callback to call when a date has been selected
 
	void *callback_data;       ///< Callback data pointer.
 
	TimerGameCalendar::YearMonthDay date; ///< The currently selected date
 
	TimerGameCalendar::Year min_year; ///< The minimum year in the year dropdown
 
	TimerGameCalendar::Year max_year; ///< The maximum year (inclusive) in the year dropdown
 
	TimerGameEconomy::YearMonthDay date; ///< The currently selected date
 
	TimerGameEconomy::Year min_year; ///< The minimum year in the year dropdown
 
	TimerGameEconomy::Year max_year; ///< The maximum year (inclusive) in the year dropdown
 

	
 
	/**
 
	 * Create the new 'set date' window
 
@@ -39,19 +39,19 @@ struct SetDateWindow : Window {
 
	 * @param max_year the maximum year (inclusive) to show in the year dropdown
 
	 * @param callback the callback to call once a date has been selected
 
	 */
 
	SetDateWindow(WindowDesc *desc, WindowNumber window_number, Window *parent, TimerGameCalendar::Date initial_date, TimerGameCalendar::Year min_year, TimerGameCalendar::Year max_year, SetDateCallback *callback, void *callback_data) :
 
	SetDateWindow(WindowDesc *desc, WindowNumber window_number, Window *parent, TimerGameEconomy::Date initial_date, TimerGameEconomy::Year min_year, TimerGameEconomy::Year max_year, SetDateCallback *callback, void *callback_data) :
 
			Window(desc),
 
			callback(callback),
 
			callback_data(callback_data),
 
			min_year(std::max(CalendarTime::MIN_YEAR, min_year)),
 
			max_year(std::min(CalendarTime::MAX_YEAR, max_year))
 
			min_year(std::max(EconomyTime::MIN_YEAR, min_year)),
 
			max_year(std::min(EconomyTime::MAX_YEAR, max_year))
 
	{
 
		assert(this->min_year <= this->max_year);
 
		this->parent = parent;
 
		this->InitNested(window_number);
 

	
 
		if (initial_date == 0) initial_date = TimerGameCalendar::date;
 
		this->date = TimerGameCalendar::ConvertDateToYMD(initial_date);
 
		if (initial_date == 0) initial_date = TimerGameEconomy::date;
 
		this->date = TimerGameEconomy::ConvertDateToYMD(initial_date);
 
		this->date.year = Clamp(this->date.year, min_year, max_year);
 
	}
 

	
 
@@ -88,7 +88,7 @@ struct SetDateWindow : Window {
 
				break;
 

	
 
			case WID_SD_YEAR:
 
				for (TimerGameCalendar::Year i = this->min_year; i <= this->max_year; i++) {
 
				for (TimerGameEconomy::Year i = this->min_year; i <= this->max_year; i++) {
 
					SetDParam(0, i);
 
					list.push_back(std::make_unique<DropDownListStringItem>(STR_JUST_INT, i.base(), false));
 
				}
 
@@ -147,7 +147,7 @@ struct SetDateWindow : Window {
 
				break;
 

	
 
			case WID_SD_SET_DATE:
 
				if (this->callback != nullptr) this->callback(this, TimerGameCalendar::ConvertYMDToDate(this->date.year, this->date.month, this->date.day), this->callback_data);
 
				if (this->callback != nullptr) this->callback(this, TimerGameEconomy::ConvertYMDToDate(this->date.year, this->date.month, this->date.day), this->callback_data);
 
				this->Close();
 
				break;
 
		}
 
@@ -212,7 +212,7 @@ static WindowDesc _set_date_desc(__FILE_
 
 * @param callback the callback to call once a date has been selected
 
 * @param callback_data extra callback data
 
 */
 
void ShowSetDateWindow(Window *parent, int window_number, TimerGameCalendar::Date initial_date, TimerGameCalendar::Year min_year, TimerGameCalendar::Year max_year, SetDateCallback *callback, void *callback_data)
 
void ShowSetDateWindow(Window *parent, int window_number, TimerGameEconomy::Date initial_date, TimerGameEconomy::Year min_year, TimerGameEconomy::Year max_year, SetDateCallback *callback, void *callback_data)
 
{
 
	CloseWindowByClass(WC_SET_DATE);
 
	new SetDateWindow(&_set_date_desc, window_number, parent, initial_date, min_year, max_year, callback, callback_data);
src/date_gui.h
Show inline comments
 
@@ -18,8 +18,8 @@
 
 * @param w the window that sends the callback
 
 * @param date the date that has been chosen
 
 */
 
typedef void SetDateCallback(const Window *w, TimerGameCalendar::Date date, void *data);
 
typedef void SetDateCallback(const Window *w, TimerGameEconomy::Date date, void *data);
 

	
 
void ShowSetDateWindow(Window *parent, int window_number, TimerGameCalendar::Date initial_date, TimerGameCalendar::Year min_year, TimerGameCalendar::Year max_year, SetDateCallback *callback, void *callback_data);
 
void ShowSetDateWindow(Window *parent, int window_number, TimerGameEconomy::Date initial_date, TimerGameEconomy::Year min_year, TimerGameEconomy::Year max_year, SetDateCallback *callback, void *callback_data);
 

	
 
#endif /* DATE_GUI_H */
src/disaster_vehicle.cpp
Show inline comments
 
@@ -47,7 +47,7 @@
 
#include "core/backup_type.hpp"
 
#include "landscape_cmd.h"
 
#include "timer/timer.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 

	
 
#include "table/strings.h"
 

	
 
@@ -936,7 +936,7 @@ static void ResetDisasterDelay()
 
	_disaster_delay = GB(Random(), 0, 9) + 730;
 
}
 

	
 
static IntervalTimer<TimerGameCalendar> _disaster_daily({TimerGameCalendar::DAY, TimerGameCalendar::Priority::DISASTER}, [](auto)
 
static IntervalTimer<TimerGameEconomy> _economy_disaster_daily({TimerGameEconomy::DAY, TimerGameEconomy::Priority::DISASTER}, [](auto)
 
{
 
	if (--_disaster_delay != 0) return;
 

	
src/economy.cpp
Show inline comments
 
@@ -54,6 +54,7 @@
 
#include "vehicle_cmd.h"
 
#include "timer/timer.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 

	
 
#include "table/strings.h"
 
#include "table/pricebase.h"
 
@@ -690,8 +691,8 @@ static void CompaniesGenStatistics()
 
	}
 
	cur_company.Restore();
 

	
 
	/* Only run the economic statics and update company stats every 3rd month (1st of quarter). */
 
	if (!HasBit(1 << 0 | 1 << 3 | 1 << 6 | 1 << 9, TimerGameCalendar::month)) return;
 
	/* Only run the economic statics and update company stats every 3rd economy month (1st of quarter). */
 
	if (!HasBit(1 << 0 | 1 << 3 | 1 << 6 | 1 << 9, TimerGameEconomy::month)) return;
 

	
 
	for (Company *c : Company::Iterate()) {
 
		/* Drop the oldest history off the end */
 
@@ -844,8 +845,8 @@ static void CompaniesPayInterest()
 
		if (c->money < 0) {
 
			yearly_fee += -c->money *_economy.interest_rate / 100;
 
		}
 
		Money up_to_previous_month = yearly_fee * TimerGameCalendar::month / 12;
 
		Money up_to_this_month = yearly_fee * (TimerGameCalendar::month + 1) / 12;
 
		Money up_to_previous_month = yearly_fee * TimerGameEconomy::month / 12;
 
		Money up_to_this_month = yearly_fee * (TimerGameEconomy::month + 1) / 12;
 

	
 
		SubtractMoneyFromCompany(CommandCost(EXPENSES_LOAN_INTEREST, up_to_this_month - up_to_previous_month));
 

	
 
@@ -1080,7 +1081,7 @@ static uint DeliverGoodsToIndustry(const
 

	
 
		uint amount = std::min(num_pieces, 0xFFFFu - it->waiting);
 
		it->waiting += amount;
 
		it->last_accepted = TimerGameCalendar::date;
 
		it->last_accepted = TimerGameEconomy::date;
 
		num_pieces -= amount;
 
		accepted += amount;
 

	
 
@@ -1980,16 +1981,23 @@ void LoadUnloadStation(Station *st)
 
}
 

	
 
/**
 
 * Monthly update of the economic data (of the companies as well as economic fluctuations).
 
 * Every calendar month update of inflation.
 
 */
 
static IntervalTimer<TimerGameCalendar> _companies_monthly({TimerGameCalendar::MONTH, TimerGameCalendar::Priority::COMPANY}, [](auto)
 
static IntervalTimer<TimerGameCalendar> _calendar_inflation_monthly({TimerGameCalendar::MONTH, TimerGameCalendar::Priority::COMPANY}, [](auto)
 
{
 
	CompaniesPayInterest();
 
	CompaniesGenStatistics();
 
	if (_settings_game.economy.inflation) {
 
		AddInflation();
 
		RecomputePrices();
 
	}
 
});
 

	
 
/**
 
 * Every economy month update of company economic data, plus economy fluctuations.
 
 */
 
static IntervalTimer<TimerGameEconomy> _economy_companies_monthly({ TimerGameEconomy::MONTH, TimerGameEconomy::Priority::COMPANY }, [](auto)
 
{
 
	CompaniesGenStatistics();
 
	CompaniesPayInterest();
 
	HandleEconomyFluctuations();
 
});
 

	
src/engine.cpp
Show inline comments
 
@@ -919,7 +919,7 @@ static bool IsVehicleTypeDisabled(Vehicl
 
}
 

	
 
/** Daily check to offer an exclusive engine preview to the companies. */
 
static IntervalTimer<TimerGameCalendar> _engines_daily({TimerGameCalendar::DAY, TimerGameCalendar::Priority::ENGINE}, [](auto)
 
static IntervalTimer<TimerGameCalendar> _calendar_engines_daily({TimerGameCalendar::DAY, TimerGameCalendar::Priority::ENGINE}, [](auto)
 
{
 
	for (Company *c : Company::Iterate()) {
 
		c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes, TimerGameCalendar::date);
 
@@ -1104,7 +1104,7 @@ static void NewVehicleAvailable(Engine *
 
}
 

	
 
/** Monthly update of the availability, reliability, and preview offers of the engines. */
 
void EnginesMonthlyLoop()
 
void CalendarEnginesMonthlyLoop()
 
{
 
	if (TimerGameCalendar::year < _year_engine_aging_stops) {
 
		bool refresh = false;
 
@@ -1151,9 +1151,9 @@ void EnginesMonthlyLoop()
 
	}
 
}
 

	
 
static IntervalTimer<TimerGameCalendar> _engines_monthly({TimerGameCalendar::MONTH, TimerGameCalendar::Priority::ENGINE}, [](auto)
 
static IntervalTimer<TimerGameCalendar> _calendar_engines_monthly({TimerGameCalendar::MONTH, TimerGameCalendar::Priority::ENGINE}, [](auto)
 
{
 
	EnginesMonthlyLoop();
 
	CalendarEnginesMonthlyLoop();
 
});
 

	
 
/**
src/graph_gui.cpp
Show inline comments
 
@@ -23,6 +23,7 @@
 
#include "timer/timer_window.h"
 
#include "timer/timer_game_tick.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "zoom_func.h"
 

	
 
#include "widgets/graph_widget.h"
 
@@ -181,8 +182,8 @@ protected:
 
	byte num_vert_lines;
 

	
 
	/* The starting month and year that values are plotted against. */
 
	TimerGameCalendar::Month month;
 
	TimerGameCalendar::Year year;
 
	TimerGameEconomy::Month month;
 
	TimerGameEconomy::Year year;
 

	
 
	bool draw_dates = true; ///< Should we draw months and years on the time axis?
 

	
 
@@ -380,8 +381,8 @@ protected:
 
		if (this->draw_dates) {
 
			x = r.left;
 
			y = r.bottom + ScaleGUITrad(2);
 
			TimerGameCalendar::Month month = this->month;
 
			TimerGameCalendar::Year year = this->year;
 
			TimerGameEconomy::Month month = this->month;
 
			TimerGameEconomy::Year year = this->year;
 
			for (int i = 0; i < this->num_on_x_axis; i++) {
 
				SetDParam(0, month + STR_MONTH_ABBREV_JAN);
 
				SetDParam(1, year);
 
@@ -497,8 +498,8 @@ public:
 

	
 
		/* Draw x-axis labels and markings for graphs based on financial quarters and years.  */
 
		if (this->draw_dates) {
 
			TimerGameCalendar::Month month = this->month;
 
			TimerGameCalendar::Year year = this->year;
 
			TimerGameEconomy::Month month = this->month;
 
			TimerGameEconomy::Year year = this->year;
 
			for (int i = 0; i < this->num_on_x_axis; i++) {
 
				SetDParam(0, month + STR_MONTH_ABBREV_JAN);
 
				SetDParam(1, year);
 
@@ -577,8 +578,8 @@ public:
 
			nums = std::min(this->num_vert_lines, std::max(nums, c->num_valid_stat_ent));
 
		}
 

	
 
		int mo = (TimerGameCalendar::month / 3 - nums) * 3;
 
		auto yr = TimerGameCalendar::year;
 
		int mo = (TimerGameEconomy::month / 3 - nums) * 3;
 
		auto yr = TimerGameEconomy::year;
 
		while (mo < 0) {
 
			yr--;
 
			mo += 12;
src/industry.h
Show inline comments
 
@@ -17,12 +17,13 @@
 
#include "tilearea_type.h"
 
#include "station_base.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 

	
 

	
 
typedef Pool<Industry, IndustryID, 64, 64000> IndustryPool;
 
extern IndustryPool _industry_pool;
 

	
 
static const TimerGameCalendar::Year PROCESSING_INDUSTRY_ABANDONMENT_YEARS = 5; ///< If a processing industry doesn't produce for this many consecutive years, it may close.
 
static const TimerGameEconomy::Year PROCESSING_INDUSTRY_ABANDONMENT_YEARS = 5; ///< If a processing industry doesn't produce for this many consecutive economy years, it may close.
 

	
 
/**
 
 * Production level maximum, minimum and default values.
 
@@ -86,7 +87,7 @@ struct Industry : IndustryPool::PoolItem
 
	struct AcceptedCargo {
 
		CargoID cargo; ///< Cargo type
 
		uint16_t waiting; ///< Amount of cargo waiting to processed
 
		TimerGameCalendar::Date last_accepted; ///< Last day cargo was accepted by this industry
 
		TimerGameEconomy::Date last_accepted; ///< Last day cargo was accepted by this industry
 
	};
 

	
 
	using ProducedCargoArray = std::array<ProducedCargo, INDUSTRY_NUM_OUTPUTS>;
 
@@ -103,7 +104,7 @@ struct Industry : IndustryPool::PoolItem
 
	IndustryType type;             ///< type of industry.
 
	Owner owner;                   ///< owner of the industry.  Which SHOULD always be (imho) OWNER_NONE
 
	Colours random_colour;         ///< randomized colour of the industry, for display purpose
 
	TimerGameCalendar::Year last_prod_year; ///< last year of production
 
	TimerGameEconomy::Year last_prod_year; ///< last economy year of production
 
	byte was_cargo_delivered;      ///< flag that indicate this has been the closest industry chosen for cargo delivery by a station. see DeliverGoodsToIndustry
 
	IndustryControlFlags ctlflags; ///< flags overriding standard behaviours
 

	
 
@@ -296,7 +297,7 @@ struct IndustryBuildData {
 
	void SetupTargetCount();
 
	void TryBuildNewIndustry();
 

	
 
	void MonthlyLoop();
 
	void EconomyMonthlyLoop();
 
};
 

	
 
extern IndustryBuildData _industry_builder;
src/industry_cmd.cpp
Show inline comments
 
@@ -45,6 +45,7 @@
 
#include "terraform_cmd.h"
 
#include "timer/timer.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "timer/timer_game_tick.h"
 

	
 
#include "table/strings.h"
 
@@ -1782,7 +1783,7 @@ static void DoCreateNewIndustry(Industry
 
	i->counter = GB(r, 4, 12);
 
	i->random = initial_random_bits;
 
	i->was_cargo_delivered = false;
 
	i->last_prod_year = TimerGameCalendar::year;
 
	i->last_prod_year = TimerGameEconomy::year;
 
	i->founder = founder;
 
	i->ctlflags = INDCTL_NONE;
 

	
 
@@ -2393,7 +2394,7 @@ void IndustryBuildData::Reset()
 
}
 

	
 
/** Monthly update of industry build data. */
 
void IndustryBuildData::MonthlyLoop()
 
void IndustryBuildData::EconomyMonthlyLoop()
 
{
 
	static const int NEWINDS_PER_MONTH = 0x38000 / (10 * 12); // lower 16 bits is a float fraction, 3.5 industries per decade, divided by 10 * 12 months.
 
	if (_settings_game.difficulty.industry_density == ID_FUND_ONLY) return; // 'no industries' setting.
 
@@ -2465,7 +2466,7 @@ static void UpdateIndustryStatistics(Ind
 
{
 
	for (auto &p : i->produced) {
 
		if (IsValidCargoID(p.cargo)) {
 
			if (p.history[THIS_MONTH].production != 0) i->last_prod_year = TimerGameCalendar::year;
 
			if (p.history[THIS_MONTH].production != 0) i->last_prod_year = TimerGameEconomy::year;
 

	
 
			/* Move history from this month to last month. */
 
			std::rotate(std::rbegin(p.history), std::rbegin(p.history) + 1, std::rend(p.history));
 
@@ -2888,7 +2889,7 @@ static void ChangeIndustryProduction(Ind
 
	}
 

	
 
	if (!callback_enabled && (indspec->life_type & INDUSTRYLIFE_PROCESSING)) {
 
		if (TimerGameCalendar::year - i->last_prod_year >= PROCESSING_INDUSTRY_ABANDONMENT_YEARS && Chance16(1, original_economy ? 2 : 180)) {
 
		if (TimerGameEconomy::year - i->last_prod_year >= PROCESSING_INDUSTRY_ABANDONMENT_YEARS && Chance16(1, original_economy ? 2 : 180)) {
 
			closeit = true;
 
		}
 
	}
 
@@ -2970,13 +2971,13 @@ static void ChangeIndustryProduction(Ind
 
}
 

	
 
/**
 
 * Daily handler for the industry changes
 
 * Every economy day handler for the industry changes
 
 * Taking the original map size of 256*256, the number of random changes was always of just one unit.
 
 * But it cannot be the same on smaller or bigger maps. That number has to be scaled up or down.
 
 * For small maps, it implies that less than one change per month is required, while on bigger maps,
 
 * it would be way more. The daily loop handles those changes.
 
 */
 
static IntervalTimer<TimerGameCalendar> _industries_daily({TimerGameCalendar::DAY, TimerGameCalendar::Priority::INDUSTRY}, [](auto)
 
static IntervalTimer<TimerGameEconomy> _economy_industries_daily({TimerGameEconomy::DAY, TimerGameEconomy::Priority::INDUSTRY}, [](auto)
 
{
 
	_economy.industry_daily_change_counter += _economy.industry_daily_increment;
 

	
 
@@ -3018,11 +3019,11 @@ static IntervalTimer<TimerGameCalendar> 
 
	InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, IDIWD_PRODUCTION_CHANGE);
 
});
 

	
 
static IntervalTimer<TimerGameCalendar> _industries_monthly({TimerGameCalendar::MONTH, TimerGameCalendar::Priority::INDUSTRY}, [](auto)
 
static IntervalTimer<TimerGameEconomy> _economy_industries_monthly({TimerGameEconomy::MONTH, TimerGameEconomy::Priority::INDUSTRY}, [](auto)
 
{
 
	Backup<CompanyID> cur_company(_current_company, OWNER_NONE, FILE_LINE);
 

	
 
	_industry_builder.MonthlyLoop();
 
	_industry_builder.EconomyMonthlyLoop();
 

	
 
	for (Industry *i : Industry::Iterate()) {
 
		UpdateIndustryStatistics(i);
src/linkgraph/linkgraph.cpp
Show inline comments
 
@@ -29,7 +29,7 @@ LinkGraph::BaseNode::BaseNode(TileIndex 
 
	this->supply = 0;
 
	this->demand = demand;
 
	this->station = st;
 
	this->last_update = CalendarTime::INVALID_DATE;
 
	this->last_update = EconomyTime::INVALID_DATE;
 
}
 

	
 
/**
 
@@ -40,8 +40,8 @@ LinkGraph::BaseEdge::BaseEdge(NodeID des
 
	this->capacity = 0;
 
	this->usage = 0;
 
	this->travel_time_sum = 0;
 
	this->last_unrestricted_update = CalendarTime::INVALID_DATE;
 
	this->last_restricted_update = CalendarTime::INVALID_DATE;
 
	this->last_unrestricted_update = EconomyTime::INVALID_DATE;
 
	this->last_restricted_update = EconomyTime::INVALID_DATE;
 
	this->dest_node = dest_node;
 
}
 

	
 
@@ -50,22 +50,22 @@ LinkGraph::BaseEdge::BaseEdge(NodeID des
 
 * This is useful if the date has been modified with the cheat menu.
 
 * @param interval Number of days to be added or subtracted.
 
 */
 
void LinkGraph::ShiftDates(TimerGameCalendar::Date interval)
 
void LinkGraph::ShiftDates(TimerGameEconomy::Date interval)
 
{
 
	this->last_compression += interval;
 
	for (NodeID node1 = 0; node1 < this->Size(); ++node1) {
 
		BaseNode &source = this->nodes[node1];
 
		if (source.last_update != CalendarTime::INVALID_DATE) source.last_update += interval;
 
		if (source.last_update != EconomyTime::INVALID_DATE) source.last_update += interval;
 
		for (BaseEdge &edge : this->nodes[node1].edges) {
 
			if (edge.last_unrestricted_update != CalendarTime::INVALID_DATE) edge.last_unrestricted_update += interval;
 
			if (edge.last_restricted_update != CalendarTime::INVALID_DATE) edge.last_restricted_update += interval;
 
			if (edge.last_unrestricted_update != EconomyTime::INVALID_DATE) edge.last_unrestricted_update += interval;
 
			if (edge.last_restricted_update != EconomyTime::INVALID_DATE) edge.last_restricted_update += interval;
 
		}
 
	}
 
}
 

	
 
void LinkGraph::Compress()
 
{
 
	this->last_compression = (TimerGameCalendar::date + this->last_compression).base() / 2;
 
	this->last_compression = (TimerGameEconomy::date + this->last_compression).base() / 2;
 
	for (NodeID node1 = 0; node1 < this->Size(); ++node1) {
 
		this->nodes[node1].supply /= 2;
 
		for (BaseEdge &edge : this->nodes[node1].edges) {
 
@@ -89,8 +89,8 @@ void LinkGraph::Compress()
 
 */
 
void LinkGraph::Merge(LinkGraph *other)
 
{
 
	TimerGameCalendar::Date age = TimerGameCalendar::date - this->last_compression + 1;
 
	TimerGameCalendar::Date other_age = TimerGameCalendar::date - other->last_compression + 1;
 
	TimerGameEconomy::Date age = TimerGameEconomy::date - this->last_compression + 1;
 
	TimerGameEconomy::Date other_age = TimerGameEconomy::date - other->last_compression + 1;
 
	NodeID first = this->Size();
 
	for (NodeID node1 = 0; node1 < other->Size(); ++node1) {
 
		Station *st = Station::Get(other->nodes[node1].station);
 
@@ -172,8 +172,8 @@ void LinkGraph::BaseNode::AddEdge(NodeID
 
	edge.capacity = capacity;
 
	edge.usage = usage;
 
	edge.travel_time_sum = static_cast<uint64_t>(travel_time) * capacity;
 
	if (mode & EUM_UNRESTRICTED)  edge.last_unrestricted_update = TimerGameCalendar::date;
 
	if (mode & EUM_RESTRICTED) edge.last_restricted_update = TimerGameCalendar::date;
 
	if (mode & EUM_UNRESTRICTED)  edge.last_unrestricted_update = TimerGameEconomy::date;
 
	if (mode & EUM_RESTRICTED) edge.last_restricted_update = TimerGameEconomy::date;
 
}
 

	
 
/**
 
@@ -239,8 +239,8 @@ void LinkGraph::BaseEdge::Update(uint ca
 
		}
 
		this->usage = std::max(this->usage, usage);
 
	}
 
	if (mode & EUM_UNRESTRICTED) this->last_unrestricted_update = TimerGameCalendar::date;
 
	if (mode & EUM_RESTRICTED) this->last_restricted_update = TimerGameCalendar::date;
 
	if (mode & EUM_UNRESTRICTED) this->last_unrestricted_update = TimerGameEconomy::date;
 
	if (mode & EUM_RESTRICTED) this->last_restricted_update = TimerGameEconomy::date;
 
}
 

	
 
/**
src/linkgraph/linkgraph.h
Show inline comments
 
@@ -13,7 +13,7 @@
 
#include "../core/pool_type.hpp"
 
#include "../station_base.h"
 
#include "../cargotype.h"
 
#include "../timer/timer_game_calendar.h"
 
#include "../timer/timer_game_economy.h"
 
#include "../saveload/saveload.h"
 
#include "linkgraph_type.h"
 
#include <utility>
 
@@ -43,8 +43,8 @@ public:
 
		uint capacity;                 ///< Capacity of the link.
 
		uint usage;                    ///< Usage of the link.
 
		uint64_t travel_time_sum;        ///< Sum of the travel times of the link, in ticks.
 
		TimerGameCalendar::Date last_unrestricted_update; ///< When the unrestricted part of the link was last updated.
 
		TimerGameCalendar::Date last_restricted_update;   ///< When the restricted part of the link was last updated.
 
		TimerGameEconomy::Date last_unrestricted_update; ///< When the unrestricted part of the link was last updated.
 
		TimerGameEconomy::Date last_restricted_update;   ///< When the restricted part of the link was last updated.
 
		NodeID dest_node;              ///< Destination of the edge.
 

	
 
		BaseEdge(NodeID dest_node = INVALID_NODE);
 
@@ -59,11 +59,11 @@ public:
 
		 * Get the date of the last update to any part of the edge's capacity.
 
		 * @return Last update.
 
		 */
 
		TimerGameCalendar::Date LastUpdate() const { return std::max(this->last_unrestricted_update, this->last_restricted_update); }
 
		TimerGameEconomy::Date LastUpdate() const { return std::max(this->last_unrestricted_update, this->last_restricted_update); }
 

	
 
		void Update(uint capacity, uint usage, uint32_t time, EdgeUpdateMode mode);
 
		void Restrict() { this->last_unrestricted_update = CalendarTime::INVALID_DATE; }
 
		void Release() { this->last_restricted_update = CalendarTime::INVALID_DATE; }
 
		void Restrict() { this->last_unrestricted_update = EconomyTime::INVALID_DATE; }
 
		void Release() { this->last_restricted_update = EconomyTime::INVALID_DATE; }
 

	
 
		/** Comparison operator based on \c dest_node. */
 
		bool operator <(const BaseEdge &rhs) const
 
@@ -92,7 +92,7 @@ public:
 
		uint demand;             ///< Acceptance at the station.
 
		StationID station;       ///< Station ID.
 
		TileIndex xy;            ///< Location of the station referred to by the node.
 
		TimerGameCalendar::Date last_update;        ///< When the supply was last updated.
 
		TimerGameEconomy::Date last_update;        ///< When the supply was last updated.
 

	
 
		std::vector<BaseEdge> edges; ///< Sorted list of outgoing edges from this node.
 

	
 
@@ -105,7 +105,7 @@ public:
 
		void UpdateSupply(uint supply)
 
		{
 
			this->supply += supply;
 
			this->last_update = TimerGameCalendar::date;
 
			this->last_update = TimerGameEconomy::date;
 
		}
 

	
 
		/**
 
@@ -170,10 +170,10 @@ public:
 
	static const uint MIN_TIMEOUT_DISTANCE = 32;
 

	
 
	/** Number of days before deleting links served only by vehicles stopped in depot. */
 
	static constexpr TimerGameCalendar::Date STALE_LINK_DEPOT_TIMEOUT = 1024;
 
	static constexpr TimerGameEconomy::Date STALE_LINK_DEPOT_TIMEOUT = 1024;
 

	
 
	/** Minimum number of days between subsequent compressions of a LG. */
 
	static constexpr TimerGameCalendar::Date COMPRESSION_INTERVAL = 256;
 
	static constexpr TimerGameEconomy::Date COMPRESSION_INTERVAL = 256;
 

	
 
	/**
 
	 * Scale a value from a link graph of age orig_age for usage in one of age
 
@@ -183,7 +183,7 @@ public:
 
	 * @param orig_age Age of the original link graph.
 
	 * @return scaled value.
 
	 */
 
	inline static uint Scale(uint val, TimerGameCalendar::Date target_age, TimerGameCalendar::Date orig_age)
 
	inline static uint Scale(uint val, TimerGameEconomy::Date target_age, TimerGameEconomy::Date orig_age)
 
	{
 
		return val > 0 ? std::max(1U, val * target_age.base() / orig_age.base()) : 0;
 
	}
 
@@ -194,10 +194,10 @@ public:
 
	 * Real constructor.
 
	 * @param cargo Cargo the link graph is about.
 
	 */
 
	LinkGraph(CargoID cargo) : cargo(cargo), last_compression(TimerGameCalendar::date) {}
 
	LinkGraph(CargoID cargo) : cargo(cargo), last_compression(TimerGameEconomy::date) {}
 

	
 
	void Init(uint size);
 
	void ShiftDates(TimerGameCalendar::Date interval);
 
	void ShiftDates(TimerGameEconomy::Date interval);
 
	void Compress();
 
	void Merge(LinkGraph *other);
 

	
 
@@ -233,7 +233,7 @@ public:
 
	 * Get date of last compression.
 
	 * @return Date of last compression.
 
	 */
 
	inline TimerGameCalendar::Date LastCompression() const { return this->last_compression; }
 
	inline TimerGameEconomy::Date LastCompression() const { return this->last_compression; }
 

	
 
	/**
 
	 * Get the cargo ID this component's link graph refers to.
 
@@ -248,7 +248,7 @@ public:
 
	 */
 
	inline uint Monthly(uint base) const
 
	{
 
		return base * 30 / (TimerGameCalendar::date - this->last_compression + 1).base();
 
		return base * 30 / (TimerGameEconomy::date - this->last_compression + 1).base();
 
	}
 

	
 
	NodeID AddNode(const Station *st);
 
@@ -262,7 +262,7 @@ protected:
 
	friend class LinkGraphJob;
 

	
 
	CargoID cargo;         ///< Cargo of this component's link graph.
 
	TimerGameCalendar::Date last_compression; ///< Last time the capacities and supplies were compressed.
 
	TimerGameEconomy::Date last_compression; ///< Last time the capacities and supplies were compressed.
 
	NodeVector nodes;      ///< Nodes in the component.
 
};
 

	
src/linkgraph/linkgraphjob.cpp
Show inline comments
 
@@ -37,7 +37,7 @@ LinkGraphJob::LinkGraphJob(const LinkGra
 
		 * This is on purpose. */
 
		link_graph(orig),
 
		settings(_settings_game.linkgraph),
 
		join_date(TimerGameCalendar::date + (_settings_game.linkgraph.recalc_time / CalendarTime::SECONDS_PER_DAY)),
 
		join_date(TimerGameEconomy::date + (_settings_game.linkgraph.recalc_time / EconomyTime::SECONDS_PER_DAY)),
 
		job_completed(false),
 
		job_aborted(false)
 
{
 
@@ -131,14 +131,14 @@ LinkGraphJob::~LinkGraphJob()
 
			if (st2 == nullptr || st2->goods[this->Cargo()].link_graph != this->link_graph.index ||
 
					st2->goods[this->Cargo()].node != dest_id ||
 
					!(*lg)[node_id].HasEdgeTo(dest_id) ||
 
					(*lg)[node_id][dest_id].LastUpdate() == CalendarTime::INVALID_DATE) {
 
					(*lg)[node_id][dest_id].LastUpdate() == EconomyTime::INVALID_DATE) {
 
				/* Edge has been removed. Delete flows. */
 
				StationIDStack erased = flows.DeleteFlows(to);
 
				/* 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()) ge.flows.erase(erased.Pop());
 
			} else if ((*lg)[node_id][dest_id].last_unrestricted_update == CalendarTime::INVALID_DATE) {
 
			} else if ((*lg)[node_id][dest_id].last_unrestricted_update == EconomyTime::INVALID_DATE) {
 
				/* Edge is fully restricted. */
 
				flows.RestrictFlows(to);
 
			}
src/linkgraph/linkgraphjob.h
Show inline comments
 
@@ -163,7 +163,7 @@ protected:
 
	const LinkGraph link_graph;        ///< Link graph to by analyzed. Is copied when job is started and mustn't be modified later.
 
	const LinkGraphSettings settings;  ///< Copy of _settings_game.linkgraph at spawn time.
 
	std::thread thread;                ///< Thread the job is running in or a default-constructed thread if it's running in the main thread.
 
	TimerGameCalendar::Date join_date; ///< Date when the job is to be joined.
 
	TimerGameEconomy::Date join_date; ///< Date when the job is to be joined.
 
	NodeAnnotationVector nodes;        ///< Extra node data necessary for link graph calculation.
 
	std::atomic<bool> job_completed;   ///< Is the job still running. This is accessed by multiple threads and reads may be stale.
 
	std::atomic<bool> job_aborted;     ///< Has the job been aborted. This is accessed by multiple threads and reads may be stale.
 
@@ -178,7 +178,7 @@ public:
 
	 * settings have to be brutally const-casted in order to populate them.
 
	 */
 
	LinkGraphJob() : settings(_settings_game.linkgraph),
 
			join_date(CalendarTime::INVALID_DATE), job_completed(false), job_aborted(false) {}
 
			join_date(EconomyTime::INVALID_DATE), job_completed(false), job_aborted(false) {}
 

	
 
	LinkGraphJob(const LinkGraph &orig);
 
	~LinkGraphJob();
 
@@ -211,19 +211,19 @@ public:
 
	 * Check if job is supposed to be finished.
 
	 * @return True if job should be finished by now, false if not.
 
	 */
 
	inline bool IsScheduledToBeJoined() const { return this->join_date <= TimerGameCalendar::date; }
 
	inline bool IsScheduledToBeJoined() const { return this->join_date <= TimerGameEconomy::date; }
 

	
 
	/**
 
	 * Get the date when the job should be finished.
 
	 * @return Join date.
 
	 */
 
	inline TimerGameCalendar::Date JoinDate() const { return join_date; }
 
	inline TimerGameEconomy::Date JoinDate() const { return join_date; }
 

	
 
	/**
 
	 * Change the join date on date cheating.
 
	 * @param interval Number of days to add.
 
	 */
 
	inline void ShiftJoinDate(TimerGameCalendar::Date interval) { this->join_date += interval; }
 
	inline void ShiftJoinDate(TimerGameEconomy::Date interval) { this->join_date += interval; }
 

	
 
	/**
 
	 * Get the link graph settings for this component.
 
@@ -254,7 +254,7 @@ public:
 
	 * Get the date when the underlying link graph was last compressed.
 
	 * @return Compression date.
 
	 */
 
	inline TimerGameCalendar::Date LastCompression() const { return this->link_graph.LastCompression(); }
 
	inline TimerGameEconomy::Date LastCompression() const { return this->link_graph.LastCompression(); }
 

	
 
	/**
 
	 * Get the ID of the underlying link graph.
src/linkgraph/linkgraphschedule.cpp
Show inline comments
 
@@ -132,7 +132,7 @@ void LinkGraphSchedule::SpawnAll()
 
 * graph jobs by the number of days given.
 
 * @param interval Number of days to be added or subtracted.
 
 */
 
void LinkGraphSchedule::ShiftDates(TimerGameCalendar::Date interval)
 
void LinkGraphSchedule::ShiftDates(TimerGameEconomy::Date interval)
 
{
 
	for (LinkGraph *lg : LinkGraph::Iterate()) lg->ShiftDates(interval);
 
	for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) lgj->ShiftJoinDate(interval);
 
@@ -163,10 +163,10 @@ LinkGraphSchedule::~LinkGraphSchedule()
 
}
 

	
 
/**
 
 * Pause the game if in 2 TimerGameCalendar::date_fract ticks, we would do a join with the next
 
 * Pause the game if in 2 TimerGameEconomy::date_fract ticks, we would do a join with the next
 
 * link graph job, but it is still running.
 
 * The check is done 2 TimerGameCalendar::date_fract ticks early instead of 1, as in multiplayer
 
 * calls to DoCommandP are executed after a delay of 1 TimerGameCalendar::date_fract tick.
 
 * The check is done 2 TimerGameEconomy::date_fract ticks early instead of 1, as in multiplayer
 
 * calls to DoCommandP are executed after a delay of 1 TimerGameEconomy::date_fract tick.
 
 * If we previously paused, unpause if the job is now ready to be joined with.
 
 */
 
void StateGameLoop_LinkGraphPauseControl()
 
@@ -177,10 +177,10 @@ void StateGameLoop_LinkGraphPauseControl
 
			Command<CMD_PAUSE>::Post(PM_PAUSED_LINK_GRAPH, false);
 
		}
 
	} else if (_pause_mode == PM_UNPAUSED &&
 
			TimerGameCalendar::date_fract == LinkGraphSchedule::SPAWN_JOIN_TICK - 2 &&
 
			TimerGameCalendar::date.base() % (_settings_game.linkgraph.recalc_interval / CalendarTime::SECONDS_PER_DAY) == (_settings_game.linkgraph.recalc_interval / CalendarTime::SECONDS_PER_DAY) / 2 &&
 
			TimerGameEconomy::date_fract == LinkGraphSchedule::SPAWN_JOIN_TICK - 2 &&
 
			TimerGameEconomy::date.base() % (_settings_game.linkgraph.recalc_interval / EconomyTime::SECONDS_PER_DAY) == (_settings_game.linkgraph.recalc_interval / EconomyTime::SECONDS_PER_DAY) / 2 &&
 
			LinkGraphSchedule::instance.IsJoinWithUnfinishedJobDue()) {
 
		/* Perform check two TimerGameCalendar::date_fract ticks before we would join, to make
 
		/* Perform check two TimerGameEconomy::date_fract ticks before we would join, to make
 
		 * sure it also works in multiplayer. */
 
		Command<CMD_PAUSE>::Post(PM_PAUSED_LINK_GRAPH, true);
 
	}
 
@@ -204,11 +204,11 @@ void AfterLoad_LinkGraphPauseControl()
 
 */
 
void OnTick_LinkGraph()
 
{
 
	if (TimerGameCalendar::date_fract != LinkGraphSchedule::SPAWN_JOIN_TICK) return;
 
	TimerGameCalendar::Date offset = TimerGameCalendar::date.base() % (_settings_game.linkgraph.recalc_interval / CalendarTime::SECONDS_PER_DAY);
 
	if (TimerGameEconomy::date_fract != LinkGraphSchedule::SPAWN_JOIN_TICK) return;
 
	TimerGameEconomy::Date offset = TimerGameEconomy::date.base() % (_settings_game.linkgraph.recalc_interval / EconomyTime::SECONDS_PER_DAY);
 
	if (offset == 0) {
 
		LinkGraphSchedule::instance.SpawnNext();
 
	} else if (offset == (_settings_game.linkgraph.recalc_interval / CalendarTime::SECONDS_PER_DAY) / 2) {
 
	} else if (offset == (_settings_game.linkgraph.recalc_interval / EconomyTime::SECONDS_PER_DAY) / 2) {
 
		if (!_networking || _network_server) {
 
			PerformanceMeasurer::SetInactive(PFE_GL_LINKGRAPH);
 
			LinkGraphSchedule::instance.JoinNext();
src/linkgraph/linkgraphschedule.h
Show inline comments
 
@@ -58,7 +58,7 @@ public:
 
	bool IsJoinWithUnfinishedJobDue() const;
 
	void JoinNext();
 
	void SpawnAll();
 
	void ShiftDates(TimerGameCalendar::Date interval);
 
	void ShiftDates(TimerGameEconomy::Date interval);
 

	
 
	/**
 
	 * Queue a link graph for execution.
src/misc.cpp
Show inline comments
 
@@ -16,6 +16,7 @@
 
#include "newgrf_house.h"
 
#include "economy_func.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "timer/timer_game_tick.h"
 
#include "texteff.hpp"
 
#include "gfx_func.h"
 
@@ -109,7 +110,10 @@ void InitializeGame(uint size_x, uint si
 
	_newgrf_profilers.clear();
 

	
 
	if (reset_date) {
 
		TimerGameCalendar::SetDate(TimerGameCalendar::ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1), 0);
 
		TimerGameCalendar::Date new_date = TimerGameCalendar::ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1);
 
		TimerGameCalendar::SetDate(new_date, 0);
 
		/* Keep the economy date synced with the calendar date. */
 
		TimerGameEconomy::SetDate(new_date.base(), 0);
 
		InitializeOldNames();
 
	}
 

	
src/network/network.cpp
Show inline comments
 
@@ -12,7 +12,7 @@
 
#include "../strings_func.h"
 
#include "../command_func.h"
 
#include "../timer/timer_game_tick.h"
 
#include "../timer/timer_game_calendar.h"
 
#include "../timer/timer_game_economy.h"
 
#include "network_admin.h"
 
#include "network_client.h"
 
#include "network_query.h"
 
@@ -263,7 +263,7 @@ void NetworkTextMessage(NetworkAction ac
 
	Utf8Encode(iterator, _current_text_dir == TD_LTR ? CHAR_TD_LRM : CHAR_TD_RLM);
 
	std::string message = stream.str() + GetString(strid);
 

	
 
	Debug(desync, 1, "msg: {:08x}; {:02x}; {}", TimerGameCalendar::date, TimerGameCalendar::date_fract, message);
 
	Debug(desync, 1, "msg: {:08x}; {:02x}; {}", TimerGameEconomy::date, TimerGameEconomy::date_fract, message);
 
	IConsolePrint(colour, message);
 
	NetworkAddChatMessage(colour, _settings_client.gui.network_chat_timeout, message);
 
}
 
@@ -1043,42 +1043,42 @@ void NetworkGameLoop()
 

	
 
	if (_network_server) {
 
		/* Log the sync state to check for in-syncedness of replays. */
 
		if (TimerGameCalendar::date_fract == 0) {
 
		if (TimerGameEconomy::date_fract == 0) {
 
			/* We don't want to log multiple times if paused. */
 
			static TimerGameCalendar::Date last_log;
 
			if (last_log != TimerGameCalendar::date) {
 
				Debug(desync, 1, "sync: {:08x}; {:02x}; {:08x}; {:08x}", TimerGameCalendar::date, TimerGameCalendar::date_fract, _random.state[0], _random.state[1]);
 
				last_log = TimerGameCalendar::date;
 
			static TimerGameEconomy::Date last_log;
 
			if (last_log != TimerGameEconomy::date) {
 
				Debug(desync, 1, "sync: {:08x}; {:02x}; {:08x}; {:08x}", TimerGameEconomy::date, TimerGameEconomy::date_fract, _random.state[0], _random.state[1]);
 
				last_log = TimerGameEconomy::date;
 
			}
 
		}
 

	
 
#ifdef DEBUG_DUMP_COMMANDS
 
		/* Loading of the debug commands from -ddesync>=1 */
 
		static FILE *f = FioFOpenFile("commands.log", "rb", SAVE_DIR);
 
		static TimerGameCalendar::Date next_date(0);
 
		static TimerGameEconomy::Date next_date(0);
 
		static uint32_t next_date_fract;
 
		static CommandPacket *cp = nullptr;
 
		static bool check_sync_state = false;
 
		static uint32_t sync_state[2];
 
		if (f == nullptr && next_date == 0) {
 
			Debug(desync, 0, "Cannot open commands.log");
 
			next_date = TimerGameCalendar::Date(1);
 
			next_date = TimerGameEconomy::Date(1);
 
		}
 

	
 
		while (f != nullptr && !feof(f)) {
 
			if (TimerGameCalendar::date == next_date && TimerGameCalendar::date_fract == next_date_fract) {
 
			if (TimerGameEconomy::date == next_date && TimerGameEconomy::date_fract == next_date_fract) {
 
				if (cp != nullptr) {
 
					NetworkSendCommand(cp->cmd, cp->err_msg, nullptr, cp->company, cp->data);
 
					Debug(desync, 0, "Injecting: {:08x}; {:02x}; {:02x}; {:08x}; {} ({})", TimerGameCalendar::date, TimerGameCalendar::date_fract, (int)_current_company, cp->cmd, FormatArrayAsHex(cp->data), GetCommandName(cp->cmd));
 
					Debug(desync, 0, "Injecting: {:08x}; {:02x}; {:02x}; {:08x}; {} ({})", TimerGameEconomy::date, TimerGameEconomy::date_fract, (int)_current_company, cp->cmd, FormatArrayAsHex(cp->data), GetCommandName(cp->cmd));
 
					delete cp;
 
					cp = nullptr;
 
				}
 
				if (check_sync_state) {
 
					if (sync_state[0] == _random.state[0] && sync_state[1] == _random.state[1]) {
 
						Debug(desync, 0, "Sync check: {:08x}; {:02x}; match", TimerGameCalendar::date, TimerGameCalendar::date_fract);
 
						Debug(desync, 0, "Sync check: {:08x}; {:02x}; match", TimerGameEconomy::date, TimerGameEconomy::date_fract);
 
					} else {
 
						Debug(desync, 0, "Sync check: {:08x}; {:02x}; mismatch expected {{{:08x}, {:08x}}}, got {{{:08x}, {:08x}}}",
 
									TimerGameCalendar::date, TimerGameCalendar::date_fract, sync_state[0], sync_state[1], _random.state[0], _random.state[1]);
 
									TimerGameEconomy::date, TimerGameEconomy::date_fract, sync_state[0], sync_state[1], _random.state[0], _random.state[1]);
 
						NOT_REACHED();
 
					}
 
					check_sync_state = false;
 
@@ -1112,7 +1112,7 @@ void NetworkGameLoop()
 
				uint32_t next_date_raw;
 
				int ret = sscanf(p, "%x; %x; %x; %x; %x; %255s", &next_date_raw, &next_date_fract, &company, &cmd, &cp->err_msg, buffer);
 
				assert(ret == 6);
 
				next_date = TimerGameCalendar::Date((int32_t)next_date_raw);
 
				next_date = TimerGameEconomy::Date((int32_t)next_date_raw);
 
				cp->company = (CompanyID)company;
 
				cp->cmd = (Commands)cmd;
 

	
 
@@ -1129,7 +1129,7 @@ void NetworkGameLoop()
 
				/* Manually insert a pause when joining; this way the client can join at the exact right time. */
 
				uint32_t next_date_raw;
 
				int ret = sscanf(p + 6, "%x; %x", &next_date_raw, &next_date_fract);
 
				next_date = TimerGameCalendar::Date((int32_t)next_date_raw);
 
				next_date = TimerGameEconomy::Date((int32_t)next_date_raw);
 
				assert(ret == 2);
 
				Debug(desync, 0, "Injecting pause for join at {:08x}:{:02x}; please join when paused", next_date, next_date_fract);
 
				cp = new CommandPacket();
 
@@ -1140,7 +1140,7 @@ void NetworkGameLoop()
 
			} else if (strncmp(p, "sync: ", 6) == 0) {
 
				uint32_t next_date_raw;
 
				int ret = sscanf(p + 6, "%x; %x; %x; %x", &next_date_raw, &next_date_fract, &sync_state[0], &sync_state[1]);
 
				next_date = TimerGameCalendar::Date((int32_t)next_date_raw);
 
				next_date = TimerGameEconomy::Date((int32_t)next_date_raw);
 
				assert(ret == 4);
 
				check_sync_state = true;
 
			} else if (strncmp(p, "msg: ", 5) == 0 || strncmp(p, "client: ", 8) == 0 ||
src/network/network_base.h
Show inline comments
 
@@ -14,7 +14,7 @@
 
#include "core/address.h"
 
#include "../core/pool_type.hpp"
 
#include "../company_type.h"
 
#include "../timer/timer_game_calendar.h"
 
#include "../timer/timer_game_economy.h"
 

	
 
/** Type for the pool with client information. */
 
typedef Pool<NetworkClientInfo, ClientIndex, 8, MAX_CLIENT_SLOTS, PT_NCLIENT> NetworkClientInfoPool;
 
@@ -25,7 +25,7 @@ struct NetworkClientInfo : NetworkClient
 
	ClientID client_id;      ///< Client identifier (same as ClientState->client_id)
 
	std::string client_name; ///< Name of the client
 
	CompanyID client_playas; ///< As which company is this client playing (CompanyID)
 
	TimerGameCalendar::Date join_date; ///< Gamedate the client has joined
 
	TimerGameEconomy::Date join_date; ///< Gamedate the client has joined
 

	
 
	/**
 
	 * Create a new client.
src/network/network_client.cpp
Show inline comments
 
@@ -282,7 +282,7 @@ void ClientNetworkGameSocketHandler::Cli
 
			if (_sync_seed_1 != _random.state[0]) {
 
#endif
 
				ShowNetworkError(STR_NETWORK_ERROR_DESYNC);
 
				Debug(desync, 1, "sync_err: {:08x}; {:02x}", TimerGameCalendar::date, TimerGameCalendar::date_fract);
 
				Debug(desync, 1, "sync_err: {:08x}; {:02x}", TimerGameEconomy::date, TimerGameEconomy::date_fract);
 
				Debug(net, 0, "Sync error detected");
 
				my_client->ClientError(NETWORK_RECV_STATUS_DESYNC);
 
				return false;
src/network/network_server.cpp
Show inline comments
 
@@ -32,6 +32,7 @@
 
#include "../rev.h"
 
#include "../timer/timer.h"
 
#include "../timer/timer_game_calendar.h"
 
#include "../timer/timer_game_economy.h"
 
#include <mutex>
 
#include <condition_variable>
 

	
 
@@ -882,10 +883,10 @@ NetworkRecvStatus ServerNetworkGameSocke
 
	assert(NetworkClientInfo::CanAllocateItem());
 
	NetworkClientInfo *ci = new NetworkClientInfo(this->client_id);
 
	this->SetInfo(ci);
 
	ci->join_date = TimerGameCalendar::date;
 
	ci->join_date = TimerGameEconomy::date;
 
	ci->client_name = client_name;
 
	ci->client_playas = playas;
 
	Debug(desync, 1, "client: {:08x}; {:02x}; {:02x}; {:02x}", TimerGameCalendar::date, TimerGameCalendar::date_fract, (int)ci->client_playas, (int)ci->index);
 
	Debug(desync, 1, "client: {:08x}; {:02x}; {:02x}; {:02x}", TimerGameEconomy::date, TimerGameEconomy::date_fract, (int)ci->client_playas, (int)ci->index);
 

	
 
	/* Make sure companies to which people try to join are not autocleaned */
 
	if (Company::IsValidID(playas)) _network_company_states[playas].months_empty = 0;
 
@@ -1479,7 +1480,7 @@ void NetworkUpdateClientInfo(ClientID cl
 

	
 
	if (ci == nullptr) return;
 

	
 
	Debug(desync, 1, "client: {:08x}; {:02x}; {:02x}; {:04x}", TimerGameCalendar::date, TimerGameCalendar::date_fract, (int)ci->client_playas, client_id);
 
	Debug(desync, 1, "client: {:08x}; {:02x}; {:02x}; {:04x}", TimerGameEconomy::date, TimerGameEconomy::date_fract, (int)ci->client_playas, client_id);
 

	
 
	for (NetworkClientSocket *cs : NetworkClientSocket::Iterate()) {
 
		if (cs->status >= ServerNetworkGameSocketHandler::STATUS_AUTHORIZED) {
 
@@ -1810,17 +1811,23 @@ void NetworkServer_Tick(bool send_frame)
 
	}
 
}
 

	
 
/** Yearly "callback". Called whenever the year changes. */
 
static IntervalTimer<TimerGameCalendar> _network_yearly({TimerGameCalendar::YEAR, TimerGameCalendar::Priority::NONE}, [](auto)
 
{
 
/** Calendar yearly "callback". Called whenever the calendar year changes. */
 
static IntervalTimer<TimerGameCalendar> _calendar_network_yearly({ TimerGameCalendar::YEAR, TimerGameCalendar::Priority::NONE }, [](auto) {
 
	if (!_network_server) return;
 

	
 
	NetworkCheckRestartMap();
 
});
 

	
 
/** Economy yearly "callback". Called whenever the economy year changes. */
 
static IntervalTimer<TimerGameEconomy> _economy_network_yearly({TimerGameEconomy::YEAR, TimerGameEconomy::Priority::NONE}, [](auto)
 
{
 
	if (!_network_server) return;
 

	
 
	NetworkAdminUpdate(ADMIN_FREQUENCY_ANUALLY);
 
});
 

	
 
/** Quarterly "callback". Called whenever the quarter changes. */
 
static IntervalTimer<TimerGameCalendar> _network_quarterly({TimerGameCalendar::QUARTER, TimerGameCalendar::Priority::NONE}, [](auto)
 
/** Quarterly "callback". Called whenever the economy quarter changes. */
 
static IntervalTimer<TimerGameEconomy> _network_quarterly({TimerGameEconomy::QUARTER, TimerGameEconomy::Priority::NONE}, [](auto)
 
{
 
	if (!_network_server) return;
 

	
 
@@ -1828,8 +1835,8 @@ static IntervalTimer<TimerGameCalendar> 
 
	NetworkAdminUpdate(ADMIN_FREQUENCY_QUARTERLY);
 
});
 

	
 
/** Monthly "callback". Called whenever the month changes. */
 
static IntervalTimer<TimerGameCalendar> _network_monthly({TimerGameCalendar::MONTH, TimerGameCalendar::Priority::NONE}, [](auto)
 
/** Economy monthly "callback". Called whenever the economy month changes. */
 
static IntervalTimer<TimerGameEconomy> _network_monthly({TimerGameEconomy::MONTH, TimerGameEconomy::Priority::NONE}, [](auto)
 
{
 
	if (!_network_server) return;
 

	
 
@@ -1837,16 +1844,16 @@ static IntervalTimer<TimerGameCalendar> 
 
	NetworkAdminUpdate(ADMIN_FREQUENCY_MONTHLY);
 
});
 

	
 
/** Weekly "callback". Called whenever the week changes. */
 
static IntervalTimer<TimerGameCalendar> _network_weekly({TimerGameCalendar::WEEK, TimerGameCalendar::Priority::NONE}, [](auto)
 
/** Economy weekly "callback". Called whenever the economy week changes. */
 
static IntervalTimer<TimerGameEconomy> _network_weekly({TimerGameEconomy::WEEK, TimerGameEconomy::Priority::NONE}, [](auto)
 
{
 
	if (!_network_server) return;
 

	
 
	NetworkAdminUpdate(ADMIN_FREQUENCY_WEEKLY);
 
});
 

	
 
/** Daily "callback". Called whenever the date changes. */
 
static IntervalTimer<TimerGameCalendar> _network_daily({TimerGameCalendar::DAY, TimerGameCalendar::Priority::NONE}, [](auto)
 
/** Daily "callback". Called whenever the economy date changes. */
 
static IntervalTimer<TimerGameEconomy> _economy_network_daily({TimerGameEconomy::DAY, TimerGameEconomy::Priority::NONE}, [](auto)
 
{
 
	if (!_network_server) return;
 

	
src/newgrf.cpp
Show inline comments
 
@@ -9945,6 +9945,11 @@ void LoadNewGRF(uint load_index, uint nu
 
	TimerGameCalendar::Date date            = TimerGameCalendar::date;
 
	TimerGameCalendar::Year year            = TimerGameCalendar::year;
 
	TimerGameCalendar::DateFract date_fract = TimerGameCalendar::date_fract;
 

	
 
	TimerGameEconomy::Date economy_date = TimerGameEconomy::date;
 
	TimerGameEconomy::Year economy_year = TimerGameEconomy::year;
 
	TimerGameEconomy::DateFract economy_date_fract = TimerGameEconomy::date_fract;
 

	
 
	uint64_t tick_counter  = TimerGameTick::counter;
 
	byte display_opt     = _display_opt;
 

	
 
@@ -9952,6 +9957,11 @@ void LoadNewGRF(uint load_index, uint nu
 
		TimerGameCalendar::year = _settings_game.game_creation.starting_year;
 
		TimerGameCalendar::date = TimerGameCalendar::ConvertYMDToDate(TimerGameCalendar::year, 0, 1);
 
		TimerGameCalendar::date_fract = 0;
 

	
 
		TimerGameEconomy::year = _settings_game.game_creation.starting_year.base();
 
		TimerGameEconomy::date = TimerGameEconomy::ConvertYMDToDate(TimerGameEconomy::year, 0, 1);
 
		TimerGameEconomy::date_fract = 0;
 

	
 
		TimerGameTick::counter = 0;
 
		_display_opt  = 0;
 
	}
 
@@ -10049,6 +10059,11 @@ void LoadNewGRF(uint load_index, uint nu
 
	TimerGameCalendar::year = year;
 
	TimerGameCalendar::date = date;
 
	TimerGameCalendar::date_fract = date_fract;
 

	
 
	TimerGameEconomy::year = economy_year;
 
	TimerGameEconomy::date = economy_date;
 
	TimerGameEconomy::date_fract = economy_date_fract;
 

	
 
	TimerGameTick::counter = tick_counter;
 
	_display_opt  = display_opt;
 
}
src/newgrf_industries.cpp
Show inline comments
 
@@ -396,7 +396,7 @@ static uint32_t GetCountAndDistanceOfClo
 
		case 0xA6: return indspec->grf_prop.local_id;
 
		case 0xA7: return this->industry->founder;
 
		case 0xA8: return this->industry->random_colour;
 
		case 0xA9: return ClampTo<uint8_t>(this->industry->last_prod_year - CalendarTime::ORIGINAL_BASE_YEAR);
 
		case 0xA9: return ClampTo<uint8_t>(this->industry->last_prod_year - EconomyTime::ORIGINAL_BASE_YEAR);
 
		case 0xAA: return this->industry->counter;
 
		case 0xAB: return GB(this->industry->counter, 8, 8);
 
		case 0xAC: return this->industry->was_cargo_delivered;
 
@@ -405,7 +405,7 @@ static uint32_t GetCountAndDistanceOfClo
 
		case 0xB3: return this->industry->construction_type; // Construction type
 
		case 0xB4: {
 
			auto it = std::max_element(std::begin(this->industry->accepted), std::end(this->industry->accepted), [](const auto &a, const auto &b) { return a.last_accepted < b.last_accepted; });
 
			return ClampTo<uint16_t>(it->last_accepted - CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR); // Date last cargo accepted since 1920 (in days)
 
			return ClampTo<uint16_t>(it->last_accepted - EconomyTime::DAYS_TILL_ORIGINAL_BASE_YEAR); // Date last cargo accepted since 1920 (in days)
 
		}
 
	}
 

	
src/news_gui.cpp
Show inline comments
 
@@ -690,7 +690,7 @@ static void MoveToNextTickerItem()
 
		const NewsType type = ni->type;
 

	
 
		/* check the date, don't show too old items */
 
		if (TimerGameCalendar::date - _news_type_data[type].age > ni->date) continue;
 
		if (TimerGameEconomy::date - _news_type_data[type].age > ni->economy_date) continue;
 

	
 
		switch (_news_type_data[type].GetDisplay()) {
 
			default: NOT_REACHED();
 
@@ -727,7 +727,7 @@ static void MoveToNextNewsItem()
 
		const NewsType type = ni->type;
 

	
 
		/* check the date, don't show too old items */
 
		if (TimerGameCalendar::date - _news_type_data[type].age > ni->date) continue;
 
		if (TimerGameEconomy::date - _news_type_data[type].age > ni->economy_date) continue;
 

	
 
		switch (_news_type_data[type].GetDisplay()) {
 
			default: NOT_REACHED();
 
@@ -804,7 +804,7 @@ static void DeleteNewsItem(NewsItem *ni)
 
 * @see NewsSubtype
 
 */
 
NewsItem::NewsItem(StringID string_id, NewsType type, NewsFlag flags, NewsReferenceType reftype1, uint32_t ref1, NewsReferenceType reftype2, uint32_t ref2, const NewsAllocatedData *data) :
 
	string_id(string_id), date(TimerGameCalendar::date), type(type), flags(flags), reftype1(reftype1), reftype2(reftype2), ref1(ref1), ref2(ref2), data(data)
 
	string_id(string_id), date(TimerGameCalendar::date), economy_date(TimerGameEconomy::date), type(type), flags(flags), reftype1(reftype1), reftype2(reftype2), ref1(ref1), ref2(ref2), data(data)
 
{
 
	/* show this news message in colour? */
 
	if (TimerGameCalendar::year >= _settings_client.gui.coloured_news_year) this->flags |= NF_INCOLOUR;
 
@@ -987,7 +987,7 @@ static void RemoveOldNewsItems()
 
	NewsItem *next;
 
	for (NewsItem *cur = _oldest_news; _total_news > MIN_NEWS_AMOUNT && cur != nullptr; cur = next) {
 
		next = cur->next;
 
		if (TimerGameCalendar::date - _news_type_data[cur->type].age * _settings_client.gui.news_message_timeout > cur->date) DeleteNewsItem(cur);
 
		if (TimerGameEconomy::date - _news_type_data[cur->type].age * _settings_client.gui.news_message_timeout > cur->economy_date) DeleteNewsItem(cur);
 
	}
 
}
 

	
 
@@ -1011,11 +1011,11 @@ void NewsLoop()
 
	/* no news item yet */
 
	if (_total_news == 0) return;
 

	
 
	static byte _last_clean_month = 0;
 
	static TimerGameEconomy::Month _last_clean_month = 0;
 

	
 
	if (_last_clean_month != TimerGameCalendar::month) {
 
	if (_last_clean_month != TimerGameEconomy::month) {
 
		RemoveOldNewsItems();
 
		_last_clean_month = TimerGameCalendar::month;
 
		_last_clean_month = TimerGameEconomy::month;
 
	}
 

	
 
	if (ReadyForNextTickerItem()) MoveToNextTickerItem();
src/news_type.h
Show inline comments
 
@@ -13,6 +13,7 @@
 
#include "core/enum_type.hpp"
 
#include "gfx_type.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "strings_type.h"
 
#include "sound_type.h"
 

	
 
@@ -128,7 +129,8 @@ struct NewsItem {
 
	NewsItem *prev;               ///< Previous news item
 
	NewsItem *next;               ///< Next news item
 
	StringID string_id;           ///< Message text
 
	TimerGameCalendar::Date date; ///< Date of the news
 
	TimerGameCalendar::Date date; ///< Calendar date to show for the news
 
	TimerGameEconomy::Date economy_date; ///< Economy date of the news item, never shown but used to calculate age
 
	NewsType type;                ///< Type of the news
 
	NewsFlag flags;               ///< NewsFlags bits @see NewsFlag
 

	
src/openttd.cpp
Show inline comments
 
@@ -72,6 +72,7 @@
 
#include "misc_cmd.h"
 
#include "timer/timer.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "timer/timer_game_realtime.h"
 
#include "timer/timer_game_tick.h"
 

	
 
@@ -830,7 +831,7 @@ static void OnStartScenario()
 

	
 
	/* Make sure all industries were built "this year", to avoid too early closures. (#9918) */
 
	for (Industry *i : Industry::Iterate()) {
 
		i->last_prod_year = TimerGameCalendar::year;
 
		i->last_prod_year = TimerGameEconomy::year;
 
	}
 
}
 

	
 
@@ -1396,7 +1397,7 @@ void StateGameLoop()
 
		CallWindowGameTickEvent();
 
		NewsLoop();
 
	} else {
 
		if (_debug_desync_level > 2 && TimerGameCalendar::date_fract == 0 && (TimerGameCalendar::date.base() & 0x1F) == 0) {
 
		if (_debug_desync_level > 2 && TimerGameEconomy::date_fract == 0 && (TimerGameEconomy::date.base() & 0x1F) == 0) {
 
			/* Save the desync savegame if needed. */
 
			std::string name = fmt::format("dmp_cmds_{:08x}_{:08x}.sav", _settings_game.game_creation.generation_seed, TimerGameCalendar::date);
 
			SaveOrLoad(name, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false);
 
@@ -1411,8 +1412,10 @@ void StateGameLoop()
 
		BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP);
 
		AnimateAnimatedTiles();
 
		TimerManager<TimerGameCalendar>::Elapsed(1);
 
		TimerManager<TimerGameEconomy>::Elapsed(1);
 
		TimerManager<TimerGameTick>::Elapsed(1);
 
		RunTileLoop();
 
		RunVehicleCalendarDayProc();
 
		CallVehicleTicks();
 
		CallLandscapeTick();
 
		BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP);
src/order_cmd.cpp
Show inline comments
 
@@ -1963,7 +1963,7 @@ bool UpdateOrderDest(Vehicle *v, const O
 
			if (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) {
 
				/* If the vehicle can't find its destination, delay its next search.
 
				 * In case many vehicles are in this state, use the vehicle index to spread out pathfinder calls. */
 
				if (v->dest_tile == 0 && TimerGameCalendar::date_fract != (v->index % Ticks::DAY_TICKS)) break;
 
				if (v->dest_tile == 0 && TimerGameEconomy::date_fract != (v->index % Ticks::DAY_TICKS)) break;
 

	
 
				/* We need to search for the nearest depot (hangar). */
 
				ClosestDepot closestDepot = v->FindClosestDepot();
src/roadveh.h
Show inline comments
 
@@ -134,7 +134,8 @@ struct RoadVehicle FINAL : public Ground
 
	int GetDisplayImageWidth(Point *offset = nullptr) const;
 
	bool IsInDepot() const override { return this->state == RVSB_IN_DEPOT; }
 
	bool Tick() override;
 
	void OnNewDay() override;
 
	void OnNewCalendarDay() override;
 
	void OnNewEconomyDay() override;
 
	uint Crash(bool flooded = false) override;
 
	Trackdir GetVehicleTrackdir() const override;
 
	TileIndex GetOrderStationLocation(StationID station) override;
src/roadveh_cmd.cpp
Show inline comments
 
@@ -21,6 +21,7 @@
 
#include "strings_func.h"
 
#include "tunnelbridge_map.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "vehicle_func.h"
 
#include "sound_func.h"
 
#include "ai/ai.hpp"
 
@@ -299,7 +300,7 @@ CommandCost CmdBuildRoadVehicle(DoComman
 

	
 
		v->SetServiceInterval(Company::Get(v->owner)->settings.vehicle.servint_roadveh);
 

	
 
		v->date_of_last_service = TimerGameCalendar::date;
 
		v->date_of_last_service = TimerGameEconomy::date;
 
		v->date_of_last_service_newgrf = TimerGameCalendar::date;
 
		v->build_year = TimerGameCalendar::year;
 

	
 
@@ -1705,10 +1706,16 @@ static void CheckIfRoadVehNeedsService(R
 
	SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
 
}
 

	
 
void RoadVehicle::OnNewDay()
 
/** Calandar day handler */
 
void RoadVehicle::OnNewCalendarDay()
 
{
 
	if (!this->IsFrontEngine()) return;
 
	AgeVehicle(this);
 
}
 

	
 
/** Economy day handler. */
 
void RoadVehicle::OnNewEconomyDay()
 
{
 
	if (!this->IsFrontEngine()) return;
 

	
 
	if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
src/saveload/afterload.cpp
Show inline comments
 
@@ -60,6 +60,7 @@
 
#include "../water.h"
 
#include "../timer/timer.h"
 
#include "../timer/timer_game_calendar.h"
 
#include "../timer/timer_game_economy.h"
 
#include "../timer/timer_game_tick.h"
 

	
 
#include "saveload_internal.h"
 
@@ -260,8 +261,8 @@ static void InitializeWindowsAndCaches()
 
		/* For each company, verify (while loading a scenario) that the inauguration date is the current year and set it
 
		 * accordingly if it is not the case.  No need to set it on companies that are not been used already,
 
		 * thus the MIN_YEAR (which is really nothing more than Zero, initialized value) test */
 
		if (_file_to_saveload.abstract_ftype == FT_SCENARIO && c->inaugurated_year != CalendarTime::MIN_YEAR) {
 
			c->inaugurated_year = TimerGameCalendar::year;
 
		if (_file_to_saveload.abstract_ftype == FT_SCENARIO && c->inaugurated_year != EconomyTime::MIN_YEAR) {
 
			c->inaugurated_year = TimerGameEconomy::year;
 
		}
 
	}
 

	
 
@@ -734,6 +735,13 @@ bool AfterLoadGame()
 
	 * must be done before loading sprites as some newgrfs check it */
 
	TimerGameCalendar::SetDate(TimerGameCalendar::date, TimerGameCalendar::date_fract);
 

	
 
	/* Update economy year. If we don't have a separate economy date saved, follow the calendar date. */
 
	if (IsSavegameVersionBefore(SLV_ECONOMY_DATE)) {
 
		TimerGameEconomy::SetDate(TimerGameCalendar::date.base(), TimerGameCalendar::date_fract);
 
	} else {
 
		TimerGameEconomy::SetDate(TimerGameEconomy::date, TimerGameEconomy::date_fract);
 
	}
 

	
 
	/*
 
	 * Force the old behaviour for compatibility reasons with old savegames. As new
 
	 * settings can only be loaded from new savegames loading old savegames with new
 
@@ -1429,11 +1437,11 @@ bool AfterLoadGame()
 
		for (Station *st : Station::Iterate())   st->build_date      += CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR;
 
		for (Waypoint *wp : Waypoint::Iterate()) wp->build_date      += CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR;
 
		for (Engine *e : Engine::Iterate())      e->intro_date       += CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR;
 
		for (Company *c : Company::Iterate()) c->inaugurated_year += CalendarTime::ORIGINAL_BASE_YEAR;
 
		for (Industry *i : Industry::Iterate())  i->last_prod_year   += CalendarTime::ORIGINAL_BASE_YEAR;
 
		for (Company *c : Company::Iterate())    c->inaugurated_year += EconomyTime::ORIGINAL_BASE_YEAR;
 
		for (Industry *i : Industry::Iterate())  i->last_prod_year   += EconomyTime::ORIGINAL_BASE_YEAR;
 

	
 
		for (Vehicle *v : Vehicle::Iterate()) {
 
			v->date_of_last_service += CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR;
 
			v->date_of_last_service += EconomyTime::DAYS_TILL_ORIGINAL_BASE_YEAR;
 
			v->build_year += CalendarTime::ORIGINAL_BASE_YEAR;
 
		}
 
	}
 
@@ -3258,7 +3266,7 @@ bool AfterLoadGame()
 
	if (IsSavegameVersionBefore(SLV_NEWGRF_LAST_SERVICE)) {
 
		/* Set service date provided to NewGRF. */
 
		for (Vehicle *v : Vehicle::Iterate()) {
 
			v->date_of_last_service_newgrf = v->date_of_last_service;
 
			v->date_of_last_service_newgrf = v->date_of_last_service.base();
 
		}
 
	}
 

	
src/saveload/industry_sl.cpp
Show inline comments
 
@@ -50,12 +50,12 @@ public:
 
	/* Old array structure used for savegames before SLV_INDUSTRY_CARGO_REORGANISE. */
 
	static CargoID old_cargo[INDUSTRY_NUM_INPUTS];
 
	static uint16_t old_waiting[INDUSTRY_NUM_INPUTS];
 
	static TimerGameCalendar::Date old_last_accepted[INDUSTRY_NUM_INPUTS];
 
	static TimerGameEconomy::Date old_last_accepted[INDUSTRY_NUM_INPUTS];
 
};
 

	
 
/* static */ CargoID SlIndustryAccepted::old_cargo[INDUSTRY_NUM_INPUTS];
 
/* static */ uint16_t SlIndustryAccepted::old_waiting[INDUSTRY_NUM_INPUTS];
 
/* static */ TimerGameCalendar::Date SlIndustryAccepted::old_last_accepted[INDUSTRY_NUM_INPUTS];
 
/* static */ TimerGameEconomy::Date SlIndustryAccepted::old_last_accepted[INDUSTRY_NUM_INPUTS];
 

	
 
class SlIndustryProducedHistory : public DefaultSaveLoadHandler<SlIndustryProducedHistory, Industry::ProducedCargo> {
 
public:
src/saveload/misc_sl.cpp
Show inline comments
 
@@ -13,6 +13,7 @@
 
#include "compat/misc_sl_compat.h"
 

	
 
#include "../timer/timer_game_calendar.h"
 
#include "../timer/timer_game_economy.h"
 
#include "../zoom_func.h"
 
#include "../window_gui.h"
 
#include "../window_func.h"
 
@@ -85,6 +86,8 @@ static const SaveLoad _date_desc[] = {
 
	    SLEG_VAR("date_fract",             TimerGameCalendar::date_fract,             SLE_UINT16),
 
	SLEG_CONDVAR("tick_counter",           TimerGameTick::counter,           SLE_FILE_U16 | SLE_VAR_U64,  SL_MIN_VERSION, SLV_U64_TICK_COUNTER),
 
	SLEG_CONDVAR("tick_counter",           TimerGameTick::counter,           SLE_UINT64,                  SLV_U64_TICK_COUNTER, SL_MAX_VERSION),
 
	SLEG_CONDVAR("economy_date",           TimerGameEconomy::date,           SLE_INT32,                   SLV_ECONOMY_DATE, SL_MAX_VERSION),
 
	SLEG_CONDVAR("economy_date_fract",     TimerGameEconomy::date_fract,     SLE_UINT16,                  SLV_ECONOMY_DATE, SL_MAX_VERSION),
 
	SLEG_CONDVAR("age_cargo_skip_counter", _age_cargo_skip_counter, SLE_UINT8,                   SL_MIN_VERSION, SLV_162),
 
	SLEG_CONDVAR("cur_tileloop_tile",      _cur_tileloop_tile,      SLE_FILE_U16 | SLE_VAR_U32,  SL_MIN_VERSION, SLV_6),
 
	SLEG_CONDVAR("cur_tileloop_tile",      _cur_tileloop_tile,      SLE_UINT32,                  SLV_6, SL_MAX_VERSION),
src/saveload/oldloader_sl.cpp
Show inline comments
 
@@ -859,7 +859,7 @@ static bool LoadOldIndustry(LoadgameStat
 
			if (i->type > 0x06) i->type++; // Printing Works were added
 
			if (i->type == 0x0A) i->type = 0x12; // Iron Ore Mine has different ID
 

	
 
			TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::date);
 
			TimerGameEconomy::YearMonthDay ymd = TimerGameEconomy::ConvertDateToYMD(TimerGameEconomy::date);
 
			i->last_prod_year = ymd.year;
 

	
 
			i->random_colour = RemapTTOColour(i->random_colour);
 
@@ -1034,7 +1034,7 @@ static bool LoadOldCompany(LoadgameState
 
	}
 

	
 
	_company_colours[num] = c->colour;
 
	c->inaugurated_year -= CalendarTime::ORIGINAL_BASE_YEAR;
 
	c->inaugurated_year -= EconomyTime::ORIGINAL_BASE_YEAR;
 

	
 
	return true;
 
}
src/saveload/saveload.h
Show inline comments
 
@@ -368,6 +368,7 @@ enum SaveLoadVersion : uint16_t {
 
	SLV_WATER_REGIONS,                      ///< 324  PR#10543 Water Regions for ship pathfinder.
 

	
 
	SLV_WATER_REGION_EVAL_SIMPLIFIED,       ///< 325  PR#11750 Simplified Water Region evaluation.
 
	SLV_ECONOMY_DATE,                       ///< 326  PR#10700 Split calendar and economy timers and dates.
 

	
 
	SL_MAX_VERSION,                         ///< Highest possible saveload version
 
};
src/ship.h
Show inline comments
 
@@ -45,7 +45,8 @@ struct Ship FINAL : public SpecializedVe
 
	Money GetRunningCost() const override;
 
	bool IsInDepot() const override { return this->state == TRACK_BIT_DEPOT; }
 
	bool Tick() override;
 
	void OnNewDay() override;
 
	void OnNewCalendarDay() override;
 
	void OnNewEconomyDay() override;
 
	Trackdir GetVehicleTrackdir() const override;
 
	TileIndex GetOrderStationLocation(StationID station) override;
 
	ClosestDepot FindClosestDepot() override;
src/ship_cmd.cpp
Show inline comments
 
@@ -23,6 +23,7 @@
 
#include "strings_func.h"
 
#include "window_func.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "vehicle_func.h"
 
#include "sound_func.h"
 
#include "ai/ai.hpp"
 
@@ -222,14 +223,20 @@ Money Ship::GetRunningCost() const
 
	return GetPrice(PR_RUNNING_SHIP, cost_factor, e->GetGRF());
 
}
 

	
 
void Ship::OnNewDay()
 
/** Calendar day handler. */
 
void Ship::OnNewCalendarDay()
 
{
 
	AgeVehicle(this);
 
}
 

	
 
/** Economy day handler. */
 
void Ship::OnNewEconomyDay()
 
{
 
	if ((++this->day_counter & 7) == 0) {
 
		DecreaseVehicleValue(this);
 
	}
 

	
 
	CheckVehicleBreakdown(this);
 
	AgeVehicle(this);
 
	CheckIfShipNeedsService(this);
 

	
 
	CheckOrders(this);
 
@@ -902,7 +909,7 @@ CommandCost CmdBuildShip(DoCommandFlag f
 
		v->state = TRACK_BIT_DEPOT;
 

	
 
		v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_ships);
 
		v->date_of_last_service = TimerGameCalendar::date;
 
		v->date_of_last_service = TimerGameEconomy::date;
 
		v->date_of_last_service_newgrf = TimerGameCalendar::date;
 
		v->build_year = TimerGameCalendar::year;
 
		v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
src/station.cpp
Show inline comments
 
@@ -105,7 +105,7 @@ Station::~Station()
 
		for (NodeID node = 0; node < lg->Size(); ++node) {
 
			Station *st = Station::Get((*lg)[node].station);
 
			st->goods[c].flows.erase(this->index);
 
			if ((*lg)[node].HasEdgeTo(this->goods[c].node) && (*lg)[node][this->goods[c].node].LastUpdate() != CalendarTime::INVALID_DATE) {
 
			if ((*lg)[node].HasEdgeTo(this->goods[c].node) && (*lg)[node][this->goods[c].node].LastUpdate() != EconomyTime::INVALID_DATE) {
 
				st->goods[c].flows.DeleteFlows(this->index);
 
				RerouteCargo(st, c, this->index, st->index);
 
			}
src/station_cmd.cpp
Show inline comments
 
@@ -63,6 +63,7 @@
 
#include "newgrf_roadstop.h"
 
#include "timer/timer.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "timer/timer_game_tick.h"
 
#include "cheat_type.h"
 

	
 
@@ -3824,9 +3825,9 @@ void DeleteStaleLinks(Station *from)
 
		for (Edge &edge : (*lg)[ge.node].edges) {
 
			Station *to = Station::Get((*lg)[edge.dest_node].station);
 
			assert(to->goods[c].node == edge.dest_node);
 
			assert(TimerGameCalendar::date >= edge.LastUpdate());
 
			auto timeout = TimerGameCalendar::Date(LinkGraph::MIN_TIMEOUT_DISTANCE + (DistanceManhattan(from->xy, to->xy) >> 3));
 
			if (TimerGameCalendar::date - edge.LastUpdate() > timeout) {
 
			assert(TimerGameEconomy::date >= edge.LastUpdate());
 
			auto timeout = TimerGameEconomy::Date(LinkGraph::MIN_TIMEOUT_DISTANCE + (DistanceManhattan(from->xy, to->xy) >> 3));
 
			if (TimerGameEconomy::date - edge.LastUpdate() > timeout) {
 
				bool updated = false;
 

	
 
				if (auto_distributed) {
 
@@ -3854,10 +3855,10 @@ void DeleteStaleLinks(Station *from)
 
					while (iter != vehicles.end()) {
 
						Vehicle *v = *iter;
 
						/* Do not refresh links of vehicles that have been stopped in depot for a long time. */
 
						if (!v->IsStoppedInDepot() || TimerGameCalendar::date - v->date_of_last_service <= LinkGraph::STALE_LINK_DEPOT_TIMEOUT) {
 
						if (!v->IsStoppedInDepot() || TimerGameEconomy::date - v->date_of_last_service <= LinkGraph::STALE_LINK_DEPOT_TIMEOUT) {
 
							LinkRefresher::Run(v, false); // Don't allow merging. Otherwise lg might get deleted.
 
						}
 
						if (edge.LastUpdate() == TimerGameCalendar::date) {
 
						if (edge.LastUpdate() == TimerGameEconomy::date) {
 
							updated = true;
 
							break;
 
						}
 
@@ -3880,19 +3881,19 @@ void DeleteStaleLinks(Station *from)
 
					ge.flows.DeleteFlows(to->index);
 
					RerouteCargo(from, c, to->index, from->index);
 
				}
 
			} else if (edge.last_unrestricted_update != CalendarTime::INVALID_DATE && TimerGameCalendar::date - edge.last_unrestricted_update > timeout) {
 
			} else if (edge.last_unrestricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_unrestricted_update > timeout) {
 
				edge.Restrict();
 
				ge.flows.RestrictFlows(to->index);
 
				RerouteCargo(from, c, to->index, from->index);
 
			} else if (edge.last_restricted_update != CalendarTime::INVALID_DATE && TimerGameCalendar::date - edge.last_restricted_update > timeout) {
 
			} else if (edge.last_restricted_update != EconomyTime::INVALID_DATE && TimerGameEconomy::date - edge.last_restricted_update > timeout) {
 
				edge.Release();
 
			}
 
		}
 
		/* Remove dead edges. */
 
		for (NodeID r : to_remove) (*lg)[ge.node].RemoveEdge(r);
 

	
 
		assert(TimerGameCalendar::date >= lg->LastCompression());
 
		if (TimerGameCalendar::date - lg->LastCompression() > LinkGraph::COMPRESSION_INTERVAL) {
 
		assert(TimerGameEconomy::date >= lg->LastCompression());
 
		if (TimerGameEconomy::date - lg->LastCompression() > LinkGraph::COMPRESSION_INTERVAL) {
 
			lg->Compress();
 
		}
 
	}
 
@@ -4014,8 +4015,8 @@ void OnTick_Station()
 
	}
 
}
 

	
 
/** Monthly loop for stations. */
 
static IntervalTimer<TimerGameCalendar> _stations_monthly({TimerGameCalendar::MONTH, TimerGameCalendar::Priority::STATION}, [](auto)
 
/** Economy monthly loop for stations. */
 
static IntervalTimer<TimerGameEconomy> _economy_stations_monthly({TimerGameEconomy::MONTH, TimerGameEconomy::Priority::STATION}, [](auto)
 
{
 
	for (Station *st : Station::Iterate()) {
 
		for (GoodsEntry &ge : st->goods) {
src/subsidy.cpp
Show inline comments
 
@@ -27,7 +27,7 @@
 
#include "tile_cmd.h"
 
#include "subsidy_cmd.h"
 
#include "timer/timer.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 

	
 
#include "table/strings.h"
 

	
 
@@ -474,8 +474,8 @@ bool FindSubsidyCargoDestination(CargoID
 
	return true;
 
}
 

	
 
/** Perform the monthly update of open subsidies, and try to create a new one. */
 
static IntervalTimer<TimerGameCalendar> _subsidies_monthly({TimerGameCalendar::MONTH, TimerGameCalendar::Priority::SUBSIDY}, [](auto)
 
/** Perform the economy monthly update of open subsidies, and try to create a new one. */
 
static IntervalTimer<TimerGameEconomy> _economy_subsidies_monthly({TimerGameEconomy::MONTH, TimerGameEconomy::Priority::SUBSIDY}, [](auto)
 
{
 
	bool modified = false;
 

	
src/subsidy_gui.cpp
Show inline comments
 
@@ -142,7 +142,7 @@ struct SubsidyListWindow : Window {
 
	{
 
		if (widget != WID_SUL_PANEL) return;
 

	
 
		TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::date);
 
		TimerGameEconomy::YearMonthDay ymd = TimerGameEconomy::ConvertDateToYMD(TimerGameEconomy::date);
 

	
 
		Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
 

	
 
@@ -159,7 +159,7 @@ struct SubsidyListWindow : Window {
 
				if (IsInsideMM(pos, 0, cap)) {
 
					/* Displays the two offered towns */
 
					SetupSubsidyDecodeParam(s, SubsidyDecodeParamType::Gui);
 
					SetDParam(7, TimerGameCalendar::date - ymd.day + s->remaining * 32);
 
					SetDParam(7, TimerGameEconomy::date - ymd.day + s->remaining * 32);
 
					DrawString(tr.left, tr.right, tr.top + pos * GetCharacterHeight(FS_NORMAL), STR_SUBSIDIES_OFFERED_FROM_TO);
 
				}
 
				pos++;
 
@@ -183,7 +183,7 @@ struct SubsidyListWindow : Window {
 
				if (IsInsideMM(pos, 0, cap)) {
 
					SetupSubsidyDecodeParam(s, SubsidyDecodeParamType::Gui);
 
					SetDParam(7, s->awarded);
 
					SetDParam(8, TimerGameCalendar::date - ymd.day + s->remaining * 32);
 
					SetDParam(8, TimerGameEconomy::date - ymd.day + s->remaining * 32);
 

	
 
					/* Displays the two connected stations */
 
					DrawString(tr.left, tr.right, tr.top + pos * GetCharacterHeight(FS_NORMAL), STR_SUBSIDIES_SUBSIDISED_FROM_TO);
src/survey.cpp
Show inline comments
 
@@ -16,6 +16,8 @@
 
#include "rev.h"
 
#include "settings_type.h"
 
#include "timer/timer_game_tick.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 

	
 
#include "currency.h"
 
#include "fontcache.h"
 
@@ -317,6 +319,9 @@ void SurveyTimers(nlohmann::json &survey
 
	survey["ticks"] = TimerGameTick::counter;
 
	survey["seconds"] = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - _switch_mode_time).count();
 

	
 
	TimerGameEconomy::YearMonthDay economy_ymd = TimerGameEconomy::ConvertDateToYMD(TimerGameEconomy::date);
 
	survey["economy"] = fmt::format("{:04}-{:02}-{:02} ({})", economy_ymd.year, economy_ymd.month + 1, economy_ymd.day, TimerGameEconomy::date_fract);
 

	
 
	TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::date);
 
	survey["calendar"] = fmt::format("{:04}-{:02}-{:02} ({})", ymd.year, ymd.month + 1, ymd.day, TimerGameCalendar::date_fract);
 
}
src/timer/CMakeLists.txt
Show inline comments
 
add_files(
 
    timer_game_common.cpp
 
    timer_game_common.h
 
    timer_game_calendar.cpp
 
    timer_game_calendar.h
 
    timer_game_economy.cpp
 
    timer_game_economy.h
 
    timer_game_realtime.cpp
 
    timer_game_realtime.h
 
    timer_game_tick.cpp
src/timer/timer_game_calendar.cpp
Show inline comments
 
@@ -10,12 +10,21 @@
 
 * This file implements the timer logic for the game-calendar-timer.
 
 */
 

	
 
/**
 
 * Calendar time is used for technology and time-of-year changes, including:
 
 * - Vehicle, airport, station, object introduction and obsolescence
 
 * - Vehicle and engine age
 
 * - NewGRF variables for visual styles or behavior based on year or time of year (e.g. variable snow line)
 
 * - Inflation, since it is tied to original game years. One interpretation of inflation is that it compensates for faster and higher capacity vehicles,
 
 *   another is that it compensates for more established companies. Each of these point to a different choice of calendar versus economy time, but we have to pick one
 
 *   so we follow a previous decision to tie inflation to original TTD game years.
 
 */
 

	
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "timer.h"
 
#include "timer_game_calendar.h"
 
#include "../vehicle_base.h"
 
#include "../linkgraph/linkgraph.h"
 

	
 
#include "../safeguards.h"
 

	
 
@@ -24,127 +33,6 @@ TimerGameCalendar::Month TimerGameCalend
 
TimerGameCalendar::Date TimerGameCalendar::date = {};
 
TimerGameCalendar::DateFract TimerGameCalendar::date_fract = {};
 

	
 
#define M(a, b) ((a << 5) | b)
 
static const uint16_t _month_date_from_year_day[] = {
 
	M(0, 1), M(0, 2), M(0, 3), M(0, 4), M(0, 5), M(0, 6), M(0, 7), M(0, 8), M(0, 9), M(0, 10), M(0, 11), M(0, 12), M(0, 13), M(0, 14), M(0, 15), M(0, 16), M(0, 17), M(0, 18), M(0, 19), M(0, 20), M(0, 21), M(0, 22), M(0, 23), M(0, 24), M(0, 25), M(0, 26), M(0, 27), M(0, 28), M(0, 29), M(0, 30), M(0, 31),
 
	M(1, 1), M(1, 2), M(1, 3), M(1, 4), M(1, 5), M(1, 6), M(1, 7), M(1, 8), M(1, 9), M(1, 10), M(1, 11), M(1, 12), M(1, 13), M(1, 14), M(1, 15), M(1, 16), M(1, 17), M(1, 18), M(1, 19), M(1, 20), M(1, 21), M(1, 22), M(1, 23), M(1, 24), M(1, 25), M(1, 26), M(1, 27), M(1, 28), M(1, 29),
 
	M(2, 1), M(2, 2), M(2, 3), M(2, 4), M(2, 5), M(2, 6), M(2, 7), M(2, 8), M(2, 9), M(2, 10), M(2, 11), M(2, 12), M(2, 13), M(2, 14), M(2, 15), M(2, 16), M(2, 17), M(2, 18), M(2, 19), M(2, 20), M(2, 21), M(2, 22), M(2, 23), M(2, 24), M(2, 25), M(2, 26), M(2, 27), M(2, 28), M(2, 29), M(2, 30), M(2, 31),
 
	M(3, 1), M(3, 2), M(3, 3), M(3, 4), M(3, 5), M(3, 6), M(3, 7), M(3, 8), M(3, 9), M(3, 10), M(3, 11), M(3, 12), M(3, 13), M(3, 14), M(3, 15), M(3, 16), M(3, 17), M(3, 18), M(3, 19), M(3, 20), M(3, 21), M(3, 22), M(3, 23), M(3, 24), M(3, 25), M(3, 26), M(3, 27), M(3, 28), M(3, 29), M(3, 30),
 
	M(4, 1), M(4, 2), M(4, 3), M(4, 4), M(4, 5), M(4, 6), M(4, 7), M(4, 8), M(4, 9), M(4, 10), M(4, 11), M(4, 12), M(4, 13), M(4, 14), M(4, 15), M(4, 16), M(4, 17), M(4, 18), M(4, 19), M(4, 20), M(4, 21), M(4, 22), M(4, 23), M(4, 24), M(4, 25), M(4, 26), M(4, 27), M(4, 28), M(4, 29), M(4, 30), M(4, 31),
 
	M(5, 1), M(5, 2), M(5, 3), M(5, 4), M(5, 5), M(5, 6), M(5, 7), M(5, 8), M(5, 9), M(5, 10), M(5, 11), M(5, 12), M(5, 13), M(5, 14), M(5, 15), M(5, 16), M(5, 17), M(5, 18), M(5, 19), M(5, 20), M(5, 21), M(5, 22), M(5, 23), M(5, 24), M(5, 25), M(5, 26), M(5, 27), M(5, 28), M(5, 29), M(5, 30),
 
	M(6, 1), M(6, 2), M(6, 3), M(6, 4), M(6, 5), M(6, 6), M(6, 7), M(6, 8), M(6, 9), M(6, 10), M(6, 11), M(6, 12), M(6, 13), M(6, 14), M(6, 15), M(6, 16), M(6, 17), M(6, 18), M(6, 19), M(6, 20), M(6, 21), M(6, 22), M(6, 23), M(6, 24), M(6, 25), M(6, 26), M(6, 27), M(6, 28), M(6, 29), M(6, 30), M(6, 31),
 
	M(7, 1), M(7, 2), M(7, 3), M(7, 4), M(7, 5), M(7, 6), M(7, 7), M(7, 8), M(7, 9), M(7, 10), M(7, 11), M(7, 12), M(7, 13), M(7, 14), M(7, 15), M(7, 16), M(7, 17), M(7, 18), M(7, 19), M(7, 20), M(7, 21), M(7, 22), M(7, 23), M(7, 24), M(7, 25), M(7, 26), M(7, 27), M(7, 28), M(7, 29), M(7, 30), M(7, 31),
 
	M(8, 1), M(8, 2), M(8, 3), M(8, 4), M(8, 5), M(8, 6), M(8, 7), M(8, 8), M(8, 9), M(8, 10), M(8, 11), M(8, 12), M(8, 13), M(8, 14), M(8, 15), M(8, 16), M(8, 17), M(8, 18), M(8, 19), M(8, 20), M(8, 21), M(8, 22), M(8, 23), M(8, 24), M(8, 25), M(8, 26), M(8, 27), M(8, 28), M(8, 29), M(8, 30),
 
	M(9, 1), M(9, 2), M(9, 3), M(9, 4), M(9, 5), M(9, 6), M(9, 7), M(9, 8), M(9, 9), M(9, 10), M(9, 11), M(9, 12), M(9, 13), M(9, 14), M(9, 15), M(9, 16), M(9, 17), M(9, 18), M(9, 19), M(9, 20), M(9, 21), M(9, 22), M(9, 23), M(9, 24), M(9, 25), M(9, 26), M(9, 27), M(9, 28), M(9, 29), M(9, 30), M(9, 31),
 
	M(10, 1), M(10, 2), M(10, 3), M(10, 4), M(10, 5), M(10, 6), M(10, 7), M(10, 8), M(10, 9), M(10, 10), M(10, 11), M(10, 12), M(10, 13), M(10, 14), M(10, 15), M(10, 16), M(10, 17), M(10, 18), M(10, 19), M(10, 20), M(10, 21), M(10, 22), M(10, 23), M(10, 24), M(10, 25), M(10, 26), M(10, 27), M(10, 28), M(10, 29), M(10, 30),
 
	M(11, 1), M(11, 2), M(11, 3), M(11, 4), M(11, 5), M(11, 6), M(11, 7), M(11, 8), M(11, 9), M(11, 10), M(11, 11), M(11, 12), M(11, 13), M(11, 14), M(11, 15), M(11, 16), M(11, 17), M(11, 18), M(11, 19), M(11, 20), M(11, 21), M(11, 22), M(11, 23), M(11, 24), M(11, 25), M(11, 26), M(11, 27), M(11, 28), M(11, 29), M(11, 30), M(11, 31),
 
};
 
#undef M
 

	
 
enum DaysTillMonth {
 
	ACCUM_JAN = 0,
 
	ACCUM_FEB = ACCUM_JAN + 31,
 
	ACCUM_MAR = ACCUM_FEB + 29,
 
	ACCUM_APR = ACCUM_MAR + 31,
 
	ACCUM_MAY = ACCUM_APR + 30,
 
	ACCUM_JUN = ACCUM_MAY + 31,
 
	ACCUM_JUL = ACCUM_JUN + 30,
 
	ACCUM_AUG = ACCUM_JUL + 31,
 
	ACCUM_SEP = ACCUM_AUG + 31,
 
	ACCUM_OCT = ACCUM_SEP + 30,
 
	ACCUM_NOV = ACCUM_OCT + 31,
 
	ACCUM_DEC = ACCUM_NOV + 30,
 
};
 

	
 
/** Number of days to pass from the first day in the year before reaching the first of a month. */
 
static const uint16_t _accum_days_for_month[] = {
 
	ACCUM_JAN, ACCUM_FEB, ACCUM_MAR, ACCUM_APR,
 
	ACCUM_MAY, ACCUM_JUN, ACCUM_JUL, ACCUM_AUG,
 
	ACCUM_SEP, ACCUM_OCT, ACCUM_NOV, ACCUM_DEC,
 
};
 

	
 
/**
 
 * Converts a Date to a Year, Month & Day.
 
 * @param date the date to convert from
 
 * @returns YearMonthDay representation of the Date.
 
 */
 
/* static */ TimerGameCalendar::YearMonthDay TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::Date date)
 
{
 
	/* Year determination in multiple steps to account for leap
 
	 * years. First do the large steps, then the smaller ones.
 
	 */
 

	
 
	/* There are 97 leap years in 400 years */
 
	TimerGameCalendar::Year yr = 400 * (date.base() / (CalendarTime::DAYS_IN_YEAR * 400 + 97));
 
	int rem = date.base() % (CalendarTime::DAYS_IN_YEAR * 400 + 97);
 

	
 
	if (rem >= CalendarTime::DAYS_IN_YEAR * 100 + 25) {
 
		/* There are 25 leap years in the first 100 years after
 
		 * every 400th year, as every 400th year is a leap year */
 
		yr += 100;
 
		rem -= CalendarTime::DAYS_IN_YEAR * 100 + 25;
 

	
 
		/* There are 24 leap years in the next couple of 100 years */
 
		yr += 100 * (rem / (CalendarTime::DAYS_IN_YEAR * 100 + 24));
 
		rem = (rem % (CalendarTime::DAYS_IN_YEAR * 100 + 24));
 
	}
 

	
 
	if (!TimerGameCalendar::IsLeapYear(yr) && rem >= CalendarTime::DAYS_IN_YEAR * 4) {
 
		/* The first 4 year of the century are not always a leap year */
 
		yr += 4;
 
		rem -= CalendarTime::DAYS_IN_YEAR * 4;
 
	}
 

	
 
	/* There is 1 leap year every 4 years */
 
	yr += 4 * (rem / (CalendarTime::DAYS_IN_YEAR * 4 + 1));
 
	rem = rem % (CalendarTime::DAYS_IN_YEAR * 4 + 1);
 

	
 
	/* The last (max 3) years to account for; the first one
 
	 * can be, but is not necessarily a leap year */
 
	while (rem >= (TimerGameCalendar::IsLeapYear(yr) ? CalendarTime::DAYS_IN_LEAP_YEAR : CalendarTime::DAYS_IN_YEAR)) {
 
		rem -= TimerGameCalendar::IsLeapYear(yr) ? CalendarTime::DAYS_IN_LEAP_YEAR : CalendarTime::DAYS_IN_YEAR;
 
		yr++;
 
	}
 

	
 
	/* Skip the 29th of February in non-leap years */
 
	if (!TimerGameCalendar::IsLeapYear(yr) && rem >= ACCUM_MAR - 1) rem++;
 

	
 
	uint16_t x = _month_date_from_year_day[rem];
 

	
 
	YearMonthDay ymd;
 
	ymd.year = yr;
 
	ymd.month = x >> 5;
 
	ymd.day = x & 0x1F;
 
	return ymd;
 
}
 

	
 
/**
 
 * Converts a tuple of Year, Month and Day to a Date.
 
 * @param year  is a number between 0..MAX_YEAR
 
 * @param month is a number between 0..11
 
 * @param day   is a number between 1..31
 
 */
 
/* static */ TimerGameCalendar::Date TimerGameCalendar::ConvertYMDToDate(TimerGameCalendar::Year year, TimerGameCalendar::Month month, TimerGameCalendar::Day day)
 
{
 
	/* Day-offset in a leap year */
 
	int days = _accum_days_for_month[month] + day - 1;
 

	
 
	/* Account for the missing of the 29th of February in non-leap years */
 
	if (!TimerGameCalendar::IsLeapYear(year) && days >= ACCUM_MAR) days--;
 

	
 
	return TimerGameCalendar::DateAtStartOfYear(year) + days;
 
}
 

	
 
/**
 
 * Checks whether the given year is a leap year or not.
 
 * @param yr The year to check.
 
 * @return True if \c yr is a leap year, otherwise false.
 
 */
 
/* static */ bool TimerGameCalendar::IsLeapYear(TimerGameCalendar::Year yr)
 
{
 
	return yr.base() % 4 == 0 && (yr.base() % 100 != 0 || yr.base() % 400 == 0);
 
}
 

	
 
/**
 
 * Set the date.
 
 * @param date  New date
 
@@ -213,22 +101,10 @@ void TimerManager<TimerGameCalendar>::El
 
		timer->Elapsed(TimerGameCalendar::DAY);
 
	}
 

	
 
	if ((TimerGameCalendar::date.base() % 7) == 3) {
 
		for (auto timer : timers) {
 
			timer->Elapsed(TimerGameCalendar::WEEK);
 
		}
 
	}
 

	
 
	if (new_month) {
 
		for (auto timer : timers) {
 
			timer->Elapsed(TimerGameCalendar::MONTH);
 
		}
 

	
 
		if ((TimerGameCalendar::month % 3) == 0) {
 
			for (auto timer : timers) {
 
				timer->Elapsed(TimerGameCalendar::QUARTER);
 
			}
 
		}
 
	}
 

	
 
	if (new_year) {
 
@@ -244,8 +120,6 @@ void TimerManager<TimerGameCalendar>::El
 
		TimerGameCalendar::year--;
 
		days_this_year = TimerGameCalendar::IsLeapYear(TimerGameCalendar::year) ? CalendarTime::DAYS_IN_LEAP_YEAR : CalendarTime::DAYS_IN_YEAR;
 
		TimerGameCalendar::date -= days_this_year;
 
		for (Vehicle *v : Vehicle::Iterate()) v->ShiftDates(-days_this_year);
 
		for (LinkGraph *lg : LinkGraph::Iterate()) lg->ShiftDates(-days_this_year);
 
	}
 
}
 

	
src/timer/timer_game_calendar.h
Show inline comments
 
@@ -12,6 +12,7 @@
 

	
 
#include "../stdafx.h"
 
#include "../core/strong_typedef_type.hpp"
 
#include "timer_game_common.h"
 

	
 
/**
 
 * Timer that is increased every 27ms, and counts towards ticks / days / months / years.
 
@@ -19,173 +20,26 @@
 
 * The amount of days in a month depends on the month and year (leap-years).
 
 * There are always 74 ticks in a day (and with 27ms, this makes 1 day 1.998 seconds).
 
 *
 
 * IntervalTimer and TimeoutTimer based on this Timer are a bit unusual, as their count is always one.
 
 * You create those timers based on a transition: a new day, a new month or a new year.
 
 *
 
 * Additionally, you need to set a priority. To ensure deterministic behaviour, events are executed
 
 * in priority. It is important that if you assign NONE, you do not use Random() in your callback.
 
 * Other than that, make sure you only set one callback per priority.
 
 *
 
 * For example:
 
 *   IntervalTimer<TimerGameCalendar>({TimerGameCalendar::DAY, TimerGameCalendar::Priority::NONE}, [](uint count){});
 
 *
 
 * @note Callbacks are executed in the game-thread.
 
 * Calendar time is used for technology and time-of-year changes, including:
 
 * - Vehicle, airport, station, object introduction and obsolescence
 
 * - NewGRF variables for visual styles or behavior based on year or time of year (e.g. variable snow line)
 
 * - Inflation, since it is tied to original game years. One interpretation of inflation is that it compensates for faster and higher capacity vehicles,
 
 *   another is that it compensates for more established companies. Each of these point to a different choice of calendar versus economy time, but we have to pick one
 
 *   so we follow a previous decision to tie inflation to original TTD game years.
 
 */
 
class TimerGameCalendar {
 
class TimerGameCalendar : public TimerGame<struct Calendar> {
 
public:
 
	/** The type to store our dates in. */
 
	using Date = StrongType::Typedef<int32_t, struct DateTag, StrongType::Compare, StrongType::Integer>;
 

	
 
	/** The fraction of a date we're in, i.e. the number of ticks since the last date changeover. */
 
	using DateFract = uint16_t;
 

	
 
	/** Type for the year, note: 0 based, i.e. starts at the year 0. */
 
	using Year = StrongType::Typedef<int32_t, struct YearTag, StrongType::Compare, StrongType::Integer>;
 
	/** Type for the month, note: 0 based, i.e. 0 = January, 11 = December. */
 
	using Month = uint8_t;
 
	/** Type for the day of the month, note: 1 based, first day of a month is 1. */
 
	using Day = uint8_t;
 

	
 
	/**
 
	 * Data structure to convert between Date and triplet (year, month, and day).
 
	 * @see TimerGameCalendar::ConvertDateToYMD(), TimerGameCalendar::ConvertYMDToDate()
 
	 */
 
	struct YearMonthDay {
 
		Year  year;   ///< Year (0...)
 
		Month month;  ///< Month (0..11)
 
		Day   day;    ///< Day (1..31)
 
	};
 

	
 
	enum Trigger {
 
		DAY,
 
		WEEK,
 
		MONTH,
 
		QUARTER,
 
		YEAR,
 
	};
 
	enum Priority {
 
		NONE, ///< These timers can be executed in any order; there is no Random() in them, so order is not relevant.
 

	
 
		/* All other may have a Random() call in them, so order is important.
 
		 * For safety, you can only setup a single timer on a single priority. */
 
		COMPANY,
 
		DISASTER,
 
		ENGINE,
 
		INDUSTRY,
 
		STATION,
 
		SUBSIDY,
 
		TOWN,
 
		VEHICLE,
 
	};
 

	
 
	struct TPeriod {
 
		Trigger trigger;
 
		Priority priority;
 

	
 
		TPeriod(Trigger trigger, Priority priority) : trigger(trigger), priority(priority) {}
 

	
 
		bool operator < (const TPeriod &other) const
 
		{
 
			if (this->trigger != other.trigger) return this->trigger < other.trigger;
 
			return this->priority < other.priority;
 
		}
 

	
 
		bool operator == (const TPeriod &other) const
 
		{
 
			return this->trigger == other.trigger && this->priority == other.priority;
 
		}
 
	};
 

	
 
	using TElapsed = uint;
 
	struct TStorage {
 
	};
 

	
 
	static bool IsLeapYear(Year yr);
 
	static YearMonthDay ConvertDateToYMD(Date date);
 
	static Date ConvertYMDToDate(Year year, Month month, Day day);
 
	static void SetDate(Date date, DateFract fract);
 

	
 
	/**
 
	 * Calculate the year of a given date.
 
	 * @param date The date to consider.
 
	 * @return the year.
 
	 */
 
	static constexpr Year DateToYear(Date date)
 
	{
 
		/* Hardcode the number of days in a year because we can't access CalendarTime from here. */
 
		return date.base() / 366;
 
	}
 

	
 
	/**
 
	 * Calculate the date of the first day of a given year.
 
	 * @param year the year to get the first day of.
 
	 * @return the date.
 
	 */
 
	static constexpr Date DateAtStartOfYear(Year year)
 
	{
 
		int32_t year_as_int = year.base();
 
		uint number_of_leap_years = (year == 0) ? 0 : ((year_as_int - 1) / 4 - (year_as_int - 1) / 100 + (year_as_int - 1) / 400 + 1);
 

	
 
		/* Hardcode the number of days in a year because we can't access CalendarTime from here. */
 
		return (365 * year_as_int) + number_of_leap_years;
 
	}
 

	
 
	static Year year; ///< Current year, starting at 0.
 
	static Month month; ///< Current month (0..11).
 
	static Date date; ///< Current date in days (day counter).
 
	static DateFract date_fract; ///< Fractional part of the day.
 

	
 
	static void SetDate(Date date, DateFract fract);
 
};
 

	
 
/**
 
 * Storage class for Calendar time constants.
 
 */
 
class CalendarTime {
 
public:
 
	static constexpr int DAYS_IN_YEAR = 365; ///< days per year
 
	static constexpr int DAYS_IN_LEAP_YEAR = 366; ///< sometimes, you need one day more...
 
	static constexpr int MONTHS_IN_YEAR = 12; ///< months per year
 

	
 
	static constexpr int SECONDS_PER_DAY = 2;   ///< approximate seconds per day, not for precise calculations
 

	
 
	/*
 
	 * ORIGINAL_BASE_YEAR, ORIGINAL_MAX_YEAR and DAYS_TILL_ORIGINAL_BASE_YEAR are
 
	 * primarily used for loading newgrf and savegame data and returning some
 
	 * newgrf (callback) functions that were in the original (TTD) inherited
 
	 * format, where 'TimerGameCalendar::date == 0' meant that it was 1920-01-01.
 
	 */
 

	
 
	/** The minimum starting year/base year of the original TTD */
 
	static constexpr TimerGameCalendar::Year ORIGINAL_BASE_YEAR = 1920;
 
	/** The original ending year */
 
	static constexpr TimerGameCalendar::Year ORIGINAL_END_YEAR = 2051;
 
	/** The maximum year of the original TTD */
 
	static constexpr TimerGameCalendar::Year ORIGINAL_MAX_YEAR = 2090;
 

	
 
	/** The absolute minimum & maximum years in OTTD */
 
	static constexpr TimerGameCalendar::Year MIN_YEAR = 0;
 

	
 
	/** The default starting year */
 
	static constexpr TimerGameCalendar::Year DEF_START_YEAR = 1950;
 
	/** The default scoring end year */
 
	static constexpr TimerGameCalendar::Year DEF_END_YEAR = ORIGINAL_END_YEAR - 1;
 

	
 
	/**
 
	 * MAX_YEAR, nicely rounded value of the number of years that can
 
	 * be encoded in a single 32 bits date, about 2^31 / 366 years.
 
	 */
 
	static constexpr TimerGameCalendar::Year MAX_YEAR = 5000000;
 

	
 
	/** The date of the first day of the original base year. */
 
	static constexpr TimerGameCalendar::Date DAYS_TILL_ORIGINAL_BASE_YEAR = TimerGameCalendar::DateAtStartOfYear(ORIGINAL_BASE_YEAR);
 

	
 
	/** The absolute minimum date. */
 
	static constexpr TimerGameCalendar::Date MIN_DATE = 0;
 

	
 
	/** The date of the last day of the max year. */
 
	static constexpr TimerGameCalendar::Date MAX_DATE = TimerGameCalendar::DateAtStartOfYear(CalendarTime::MAX_YEAR + 1) - 1;
 

	
 
	static constexpr TimerGameCalendar::Year INVALID_YEAR = -1; ///< Representation of an invalid year
 
	static constexpr TimerGameCalendar::Date INVALID_DATE = -1; ///< Representation of an invalid date
 
};
 
class CalendarTime : public TimerGameConst<struct Calendar> {};
 

	
 
#endif /* TIMER_GAME_CALENDAR_H */
src/timer/timer_game_common.cpp
Show inline comments
 
new file 100644
 
/*
 
 * This file is part of OpenTTD.
 
 * 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.
 
 * 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.
 
 * 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 <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/**
 
 * @file timer_game_common.cpp
 
 * This file implements the common timer logic for the game-calendar timers.
 
 */
 

	
 
#include "../stdafx.h"
 

	
 
#include "timer_game_common.h"
 
#include "timer_game_calendar.h"
 
#include "timer_game_economy.h"
 

	
 
#include "../safeguards.h"
 

	
 
#define M(a, b) ((a << 5) | b)
 
static constexpr uint16_t _month_date_from_year_day[] = {
 
	M(0, 1), M(0, 2), M(0, 3), M(0, 4), M(0, 5), M(0, 6), M(0, 7), M(0, 8), M(0, 9), M(0, 10), M(0, 11), M(0, 12), M(0, 13), M(0, 14), M(0, 15), M(0, 16), M(0, 17), M(0, 18), M(0, 19), M(0, 20), M(0, 21), M(0, 22), M(0, 23), M(0, 24), M(0, 25), M(0, 26), M(0, 27), M(0, 28), M(0, 29), M(0, 30), M(0, 31),
 
	M(1, 1), M(1, 2), M(1, 3), M(1, 4), M(1, 5), M(1, 6), M(1, 7), M(1, 8), M(1, 9), M(1, 10), M(1, 11), M(1, 12), M(1, 13), M(1, 14), M(1, 15), M(1, 16), M(1, 17), M(1, 18), M(1, 19), M(1, 20), M(1, 21), M(1, 22), M(1, 23), M(1, 24), M(1, 25), M(1, 26), M(1, 27), M(1, 28), M(1, 29),
 
	M(2, 1), M(2, 2), M(2, 3), M(2, 4), M(2, 5), M(2, 6), M(2, 7), M(2, 8), M(2, 9), M(2, 10), M(2, 11), M(2, 12), M(2, 13), M(2, 14), M(2, 15), M(2, 16), M(2, 17), M(2, 18), M(2, 19), M(2, 20), M(2, 21), M(2, 22), M(2, 23), M(2, 24), M(2, 25), M(2, 26), M(2, 27), M(2, 28), M(2, 29), M(2, 30), M(2, 31),
 
	M(3, 1), M(3, 2), M(3, 3), M(3, 4), M(3, 5), M(3, 6), M(3, 7), M(3, 8), M(3, 9), M(3, 10), M(3, 11), M(3, 12), M(3, 13), M(3, 14), M(3, 15), M(3, 16), M(3, 17), M(3, 18), M(3, 19), M(3, 20), M(3, 21), M(3, 22), M(3, 23), M(3, 24), M(3, 25), M(3, 26), M(3, 27), M(3, 28), M(3, 29), M(3, 30),
 
	M(4, 1), M(4, 2), M(4, 3), M(4, 4), M(4, 5), M(4, 6), M(4, 7), M(4, 8), M(4, 9), M(4, 10), M(4, 11), M(4, 12), M(4, 13), M(4, 14), M(4, 15), M(4, 16), M(4, 17), M(4, 18), M(4, 19), M(4, 20), M(4, 21), M(4, 22), M(4, 23), M(4, 24), M(4, 25), M(4, 26), M(4, 27), M(4, 28), M(4, 29), M(4, 30), M(4, 31),
 
	M(5, 1), M(5, 2), M(5, 3), M(5, 4), M(5, 5), M(5, 6), M(5, 7), M(5, 8), M(5, 9), M(5, 10), M(5, 11), M(5, 12), M(5, 13), M(5, 14), M(5, 15), M(5, 16), M(5, 17), M(5, 18), M(5, 19), M(5, 20), M(5, 21), M(5, 22), M(5, 23), M(5, 24), M(5, 25), M(5, 26), M(5, 27), M(5, 28), M(5, 29), M(5, 30),
 
	M(6, 1), M(6, 2), M(6, 3), M(6, 4), M(6, 5), M(6, 6), M(6, 7), M(6, 8), M(6, 9), M(6, 10), M(6, 11), M(6, 12), M(6, 13), M(6, 14), M(6, 15), M(6, 16), M(6, 17), M(6, 18), M(6, 19), M(6, 20), M(6, 21), M(6, 22), M(6, 23), M(6, 24), M(6, 25), M(6, 26), M(6, 27), M(6, 28), M(6, 29), M(6, 30), M(6, 31),
 
	M(7, 1), M(7, 2), M(7, 3), M(7, 4), M(7, 5), M(7, 6), M(7, 7), M(7, 8), M(7, 9), M(7, 10), M(7, 11), M(7, 12), M(7, 13), M(7, 14), M(7, 15), M(7, 16), M(7, 17), M(7, 18), M(7, 19), M(7, 20), M(7, 21), M(7, 22), M(7, 23), M(7, 24), M(7, 25), M(7, 26), M(7, 27), M(7, 28), M(7, 29), M(7, 30), M(7, 31),
 
	M(8, 1), M(8, 2), M(8, 3), M(8, 4), M(8, 5), M(8, 6), M(8, 7), M(8, 8), M(8, 9), M(8, 10), M(8, 11), M(8, 12), M(8, 13), M(8, 14), M(8, 15), M(8, 16), M(8, 17), M(8, 18), M(8, 19), M(8, 20), M(8, 21), M(8, 22), M(8, 23), M(8, 24), M(8, 25), M(8, 26), M(8, 27), M(8, 28), M(8, 29), M(8, 30),
 
	M(9, 1), M(9, 2), M(9, 3), M(9, 4), M(9, 5), M(9, 6), M(9, 7), M(9, 8), M(9, 9), M(9, 10), M(9, 11), M(9, 12), M(9, 13), M(9, 14), M(9, 15), M(9, 16), M(9, 17), M(9, 18), M(9, 19), M(9, 20), M(9, 21), M(9, 22), M(9, 23), M(9, 24), M(9, 25), M(9, 26), M(9, 27), M(9, 28), M(9, 29), M(9, 30), M(9, 31),
 
	M(10, 1), M(10, 2), M(10, 3), M(10, 4), M(10, 5), M(10, 6), M(10, 7), M(10, 8), M(10, 9), M(10, 10), M(10, 11), M(10, 12), M(10, 13), M(10, 14), M(10, 15), M(10, 16), M(10, 17), M(10, 18), M(10, 19), M(10, 20), M(10, 21), M(10, 22), M(10, 23), M(10, 24), M(10, 25), M(10, 26), M(10, 27), M(10, 28), M(10, 29), M(10, 30),
 
	M(11, 1), M(11, 2), M(11, 3), M(11, 4), M(11, 5), M(11, 6), M(11, 7), M(11, 8), M(11, 9), M(11, 10), M(11, 11), M(11, 12), M(11, 13), M(11, 14), M(11, 15), M(11, 16), M(11, 17), M(11, 18), M(11, 19), M(11, 20), M(11, 21), M(11, 22), M(11, 23), M(11, 24), M(11, 25), M(11, 26), M(11, 27), M(11, 28), M(11, 29), M(11, 30), M(11, 31),
 
};
 
#undef M
 

	
 
enum DaysTillMonth {
 
	ACCUM_JAN = 0,
 
	ACCUM_FEB = ACCUM_JAN + 31,
 
	ACCUM_MAR = ACCUM_FEB + 29,
 
	ACCUM_APR = ACCUM_MAR + 31,
 
	ACCUM_MAY = ACCUM_APR + 30,
 
	ACCUM_JUN = ACCUM_MAY + 31,
 
	ACCUM_JUL = ACCUM_JUN + 30,
 
	ACCUM_AUG = ACCUM_JUL + 31,
 
	ACCUM_SEP = ACCUM_AUG + 31,
 
	ACCUM_OCT = ACCUM_SEP + 30,
 
	ACCUM_NOV = ACCUM_OCT + 31,
 
	ACCUM_DEC = ACCUM_NOV + 30,
 
};
 

	
 
/** Number of days to pass from the first day in the year before reaching the first of a month. */
 
static constexpr uint16_t _accum_days_for_month[] = {
 
	ACCUM_JAN, ACCUM_FEB, ACCUM_MAR, ACCUM_APR,
 
	ACCUM_MAY, ACCUM_JUN, ACCUM_JUL, ACCUM_AUG,
 
	ACCUM_SEP, ACCUM_OCT, ACCUM_NOV, ACCUM_DEC,
 
};
 

	
 
/**
 
 * Converts a Date to a Year, Month & Day.
 
 * @param date the date to convert from
 
 * @returns YearMonthDay representation of the Date.
 
 */
 
template <class T>
 
/* static */ typename TimerGame<T>::YearMonthDay TimerGame<T>::ConvertDateToYMD(Date date)
 
{
 
	/* Year determination in multiple steps to account for leap
 
	 * years. First do the large steps, then the smaller ones.
 
	 */
 

	
 
	/* There are 97 leap years in 400 years */
 
	Year yr = 400 * (date.base() / (TimerGameConst<T>::DAYS_IN_YEAR * 400 + 97));
 
	int rem = date.base() % (TimerGameConst<T>::DAYS_IN_YEAR * 400 + 97);
 

	
 
	if (rem >= TimerGameConst<T>::DAYS_IN_YEAR * 100 + 25) {
 
		/* There are 25 leap years in the first 100 years after
 
		 * every 400th year, as every 400th year is a leap year */
 
		yr += 100;
 
		rem -= TimerGameConst<T>::DAYS_IN_YEAR * 100 + 25;
 

	
 
		/* There are 24 leap years in the next couple of 100 years */
 
		yr += 100 * (rem / (TimerGameConst<T>::DAYS_IN_YEAR * 100 + 24));
 
		rem = (rem % (TimerGameConst<T>::DAYS_IN_YEAR * 100 + 24));
 
	}
 

	
 
	if (!IsLeapYear(yr) && rem >= TimerGameConst<T>::DAYS_IN_YEAR * 4) {
 
		/* The first 4 year of the century are not always a leap year */
 
		yr += 4;
 
		rem -= TimerGameConst<T>::DAYS_IN_YEAR * 4;
 
	}
 

	
 
	/* There is 1 leap year every 4 years */
 
	yr += 4 * (rem / (TimerGameConst<T>::DAYS_IN_YEAR * 4 + 1));
 
	rem = rem % (TimerGameConst<T>::DAYS_IN_YEAR * 4 + 1);
 

	
 
	/* The last (max 3) years to account for; the first one
 
	 * can be, but is not necessarily a leap year */
 
	while (rem >= (IsLeapYear(yr) ? TimerGameConst<T>::DAYS_IN_LEAP_YEAR : TimerGameConst<T>::DAYS_IN_YEAR)) {
 
		rem -= IsLeapYear(yr) ? TimerGameConst<T>::DAYS_IN_LEAP_YEAR : TimerGameConst<T>::DAYS_IN_YEAR;
 
		yr++;
 
	}
 

	
 
	/* Skip the 29th of February in non-leap years */
 
	if (!IsLeapYear(yr) && rem >= ACCUM_MAR - 1) rem++;
 

	
 
	uint16_t x = _month_date_from_year_day[rem];
 

	
 
	YearMonthDay ymd;
 
	ymd.year = yr;
 
	ymd.month = x >> 5;
 
	ymd.day = x & 0x1F;
 
	return ymd;
 
}
 

	
 
/**
 
 * Converts a tuple of Year, Month and Day to a Date.
 
 * @param year  is a number between 0..MAX_YEAR
 
 * @param month is a number between 0..11
 
 * @param day   is a number between 1..31
 
 */
 
template <class T>
 
/* static */ typename TimerGame<T>::Date TimerGame<T>::ConvertYMDToDate(Year year, Month month, Day day)
 
{
 
	/* Day-offset in a leap year */
 
	int days = _accum_days_for_month[month] + day - 1;
 

	
 
	/* Account for the missing of the 29th of February in non-leap years */
 
	if (!IsLeapYear(year) && days >= ACCUM_MAR) days--;
 

	
 
	return DateAtStartOfYear(year) + days;
 
}
 

	
 
/* Create instances of the two template variants that we have.
 
 * This is needed, as this templated functions are not in a header-file. */
 
template TimerGame<struct Calendar>::YearMonthDay TimerGame<struct Calendar>::ConvertDateToYMD(Date date);
 
template TimerGame<struct Economy>::YearMonthDay TimerGame<struct Economy>::ConvertDateToYMD(Date date);
 

	
 
template TimerGame<struct Calendar>::Date TimerGame<struct Calendar>::ConvertYMDToDate(Year year, Month month, Day day);
 
template TimerGame<struct Economy>::Date TimerGame<struct Economy>::ConvertYMDToDate(Year year, Month month, Day day);
src/timer/timer_game_common.h
Show inline comments
 
new file 100644
 
/*
 
 * This file is part of OpenTTD.
 
 * 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.
 
 * 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.
 
 * 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 <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file timer_game_common.h Definition of the common class inherited by both calendar and economy timers. */
 

	
 
#ifndef TIMER_GAME_COMMON_H
 
#define TIMER_GAME_COMMON_H
 

	
 
#include "../core/strong_typedef_type.hpp"
 

	
 
/**
 
 * Template class for all TimerGame based timers. As Calendar and Economy are very similar, this class is used to share code between them.
 
 *
 
 * IntervalTimer and TimeoutTimer based on this Timer are a bit unusual, as their count is always one.
 
 * You create those timers based on a transition: a new day, a new month or a new year.
 
 *
 
 * Additionally, you need to set a priority. To ensure deterministic behaviour, events are executed
 
 * in priority. It is important that if you assign NONE, you do not use Random() in your callback.
 
 * Other than that, make sure you only set one callback per priority.
 
 *
 
 * For example:
 
 *   IntervalTimer<TimerGameCalendar>({TimerGameCalendar::DAY, TimerGameCalendar::Priority::NONE}, [](uint count){});
 
 *
 
 * @note Callbacks are executed in the game-thread.
 
 */
 
template <class T>
 
class TimerGame {
 
public:
 
	/** The type to store our dates in. */
 
	template <class ST> struct DateTag;
 
	using Date = StrongType::Typedef<int32_t, DateTag<T>, StrongType::Compare, StrongType::Integer>;
 

	
 
	/** The fraction of a date we're in, i.e. the number of ticks since the last date changeover. */
 
	using DateFract = uint16_t;
 

	
 
	/** Type for the year, note: 0 based, i.e. starts at the year 0. */
 
	template <class ST> struct YearTag;
 
	using Year = StrongType::Typedef<int32_t, struct YearTag<T>, StrongType::Compare, StrongType::Integer>;
 
	/** Type for the month, note: 0 based, i.e. 0 = January, 11 = December. */
 
	using Month = uint8_t;
 
	/** Type for the day of the month, note: 1 based, first day of a month is 1. */
 
	using Day = uint8_t;
 

	
 
	/**
 
	 * Data structure to convert between Date and triplet (year, month, and day).
 
	 * @see ConvertDateToYMD(), ConvertYMDToDate()
 
	 */
 
	struct YearMonthDay {
 
		Year  year;   ///< Year (0...)
 
		Month month;  ///< Month (0..11)
 
		Day   day;    ///< Day (1..31)
 
	};
 

	
 
	/**
 
	 * Checks whether the given year is a leap year or not.
 
	 * @param year The year to check.
 
	 * @return True if \c year is a leap year, otherwise false.
 
	 */
 
	static constexpr bool IsLeapYear(Year year)
 
	{
 
		int32_t year_as_int = year.base();
 
		return year_as_int % 4 == 0 && (year_as_int % 100 != 0 || year_as_int % 400 == 0);
 
	}
 

	
 
	static YearMonthDay ConvertDateToYMD(Date date);
 
	static Date ConvertYMDToDate(Year year, Month month, Day day);
 

	
 
	/**
 
	 * Calculate the year of a given date.
 
	 * @param date The date to consider.
 
	 * @return the year.
 
	 */
 
	static constexpr Year DateToYear(Date date)
 
	{
 
		/* Hardcode the number of days in a year because we can't access CalendarTime from here. */
 
		return date.base() / 366;
 
	}
 

	
 
	/**
 
	 * Calculate the date of the first day of a given year.
 
	 * @param year the year to get the first day of.
 
	 * @return the date.
 
	 */
 
	static constexpr Date DateAtStartOfYear(Year year)
 
	{
 
		int32_t year_as_int = year.base();
 
		uint number_of_leap_years = (year == 0) ? 0 : ((year_as_int - 1) / 4 - (year_as_int - 1) / 100 + (year_as_int - 1) / 400 + 1);
 

	
 
		/* Hardcode the number of days in a year because we can't access CalendarTime from here. */
 
		return (365 * year_as_int) + number_of_leap_years;
 
	}
 

	
 
	enum Trigger {
 
		DAY,
 
		WEEK,
 
		MONTH,
 
		QUARTER,
 
		YEAR,
 
	};
 

	
 
	enum Priority {
 
		NONE, ///< These timers can be executed in any order; there is no Random() in them, so order is not relevant.
 

	
 
		/* All other may have a Random() call in them, so order is important.
 
		 * For safety, you can only setup a single timer on a single priority. */
 
		COMPANY,
 
		DISASTER,
 
		ENGINE,
 
		INDUSTRY,
 
		STATION,
 
		SUBSIDY,
 
		TOWN,
 
		VEHICLE,
 
	};
 

	
 
	struct TPeriod {
 
		Trigger trigger;
 
		Priority priority;
 

	
 
		TPeriod(Trigger trigger, Priority priority) : trigger(trigger), priority(priority)
 
		{}
 

	
 
		bool operator < (const TPeriod &other) const
 
		{
 
			if (this->trigger != other.trigger) return this->trigger < other.trigger;
 
			return this->priority < other.priority;
 
		}
 

	
 
		bool operator == (const TPeriod &other) const
 
		{
 
			return this->trigger == other.trigger && this->priority == other.priority;
 
		}
 
	};
 

	
 
	using TElapsed = uint;
 
	struct TStorage {};
 
};
 

	
 
/**
 
 * Template class for time constants shared by both Calendar and Economy time.
 
 */
 
template <class T>
 
class TimerGameConst {
 
public:
 
	static constexpr int DAYS_IN_YEAR = 365; ///< days per year
 
	static constexpr int DAYS_IN_LEAP_YEAR = 366; ///< sometimes, you need one day more...
 
	static constexpr int MONTHS_IN_YEAR = 12; ///< months per year
 

	
 
	static constexpr int SECONDS_PER_DAY = 2;   ///< approximate seconds per day, not for precise calculations
 

	
 
	/*
 
	 * ORIGINAL_BASE_YEAR, ORIGINAL_MAX_YEAR and DAYS_TILL_ORIGINAL_BASE_YEAR are
 
	 * primarily used for loading newgrf and savegame data and returning some
 
	 * newgrf (callback) functions that were in the original (TTD) inherited
 
	 * format, where 'TimerGame<T>::date == 0' meant that it was 1920-01-01.
 
	 */
 

	
 
	/** The minimum starting year/base year of the original TTD */
 
	static constexpr typename TimerGame<T>::Year ORIGINAL_BASE_YEAR = 1920;
 
	/** The original ending year */
 
	static constexpr typename TimerGame<T>::Year ORIGINAL_END_YEAR = 2051;
 
	/** The maximum year of the original TTD */
 
	static constexpr typename TimerGame<T>::Year ORIGINAL_MAX_YEAR = 2090;
 

	
 
	/**
 
	 * MAX_YEAR, nicely rounded value of the number of years that can
 
	 * be encoded in a single 32 bits date, about 2^31 / 366 years.
 
	 */
 
	static constexpr typename TimerGame<T>::Year MAX_YEAR = 5000000;
 

	
 
	/** The absolute minimum year in OTTD */
 
	static constexpr typename TimerGame<T>::Year MIN_YEAR = 0;
 

	
 
	/** The default starting year */
 
	static constexpr typename TimerGame<T>::Year DEF_START_YEAR = 1950;
 
	/** The default scoring end year */
 
	static constexpr typename TimerGame<T>::Year DEF_END_YEAR = ORIGINAL_END_YEAR - 1;
 

	
 
	/** The date of the first day of the original base year. */
 
	static constexpr typename TimerGame<T>::Date DAYS_TILL_ORIGINAL_BASE_YEAR = TimerGame<T>::DateAtStartOfYear(ORIGINAL_BASE_YEAR);
 

	
 
	/** The date of the last day of the max year. */
 
	static constexpr typename TimerGame<T>::Date MAX_DATE = TimerGame<T>::DateAtStartOfYear(MAX_YEAR + 1) - 1;
 

	
 
	/** The date on January 1, year 0. */
 
	static constexpr typename TimerGame<T>::Date MIN_DATE = 0;
 

	
 
	static constexpr typename TimerGame<T>::Year INVALID_YEAR = -1; ///< Representation of an invalid year
 
	static constexpr typename TimerGame<T>::Date INVALID_DATE = -1; ///< Representation of an invalid date
 
};
 

	
 
#endif /* TIMER_GAME_COMMON_H */
src/timer/timer_game_economy.cpp
Show inline comments
 
new file 100644
 
/*
 
 * This file is part of OpenTTD.
 
 * 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.
 
 * 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.
 
 * 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 <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/**
 
 * @file timer_game_economy.cpp
 
 * This file implements the timer logic for the game-economy-timer.
 
 */
 

	
 
/**
 
 * Economy time is used for the regular pace of the game, including:
 
 * - Industry and house production/consumption
 
 * - Industry production changes, closure, and spawning
 
 * - Town growth
 
 * - Company age and financial statistics
 
 * - Vehicle financial statistics
 
 * - Vehicle aging, depreciation, reliability, and renewal
 
 * - Payment intervals for running and maintenance costs, loan interest, etc.
 
 * - Cargo payment "time" calculation
 
 * - Local authority and station ratings change intervals
 
 */
 

	
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "timer.h"
 
#include "timer_game_economy.h"
 
#include "timer_game_tick.h"
 
#include "../vehicle_base.h"
 
#include "../linkgraph/linkgraph.h"
 

	
 
#include "../safeguards.h"
 

	
 
TimerGameEconomy::Year TimerGameEconomy::year = {};
 
TimerGameEconomy::Month TimerGameEconomy::month = {};
 
TimerGameEconomy::Date TimerGameEconomy::date = {};
 
TimerGameEconomy::DateFract TimerGameEconomy::date_fract = {};
 

	
 
/**
 
 * Set the date.
 
 * @param date The new date
 
 * @param fract The number of ticks that have passed on this date.
 
 */
 
/* static */ void TimerGameEconomy::SetDate(TimerGameEconomy::Date date, TimerGameEconomy::DateFract fract)
 
{
 
	assert(fract < Ticks::DAY_TICKS);
 

	
 
	TimerGameEconomy::date = date;
 
	TimerGameEconomy::date_fract = fract;
 
	TimerGameEconomy::YearMonthDay ymd = TimerGameEconomy::ConvertDateToYMD(date);
 
	TimerGameEconomy::year = ymd.year;
 
	TimerGameEconomy::month = ymd.month;
 
}
 

	
 
template<>
 
void IntervalTimer<TimerGameEconomy>::Elapsed(TimerGameEconomy::TElapsed trigger)
 
{
 
	if (trigger == this->period.trigger) {
 
		this->callback(1);
 
	}
 
}
 

	
 
template<>
 
void TimeoutTimer<TimerGameEconomy>::Elapsed(TimerGameEconomy::TElapsed trigger)
 
{
 
	if (this->fired) return;
 

	
 
	if (trigger == this->period.trigger) {
 
		this->callback();
 
		this->fired = true;
 
	}
 
}
 

	
 
template<>
 
void TimerManager<TimerGameEconomy>::Elapsed([[maybe_unused]] TimerGameEconomy::TElapsed delta)
 
{
 
	assert(delta == 1);
 

	
 
	if (_game_mode == GM_MENU) return;
 

	
 
	TimerGameEconomy::date_fract++;
 
	if (TimerGameEconomy::date_fract < Ticks::DAY_TICKS) return;
 
	TimerGameEconomy::date_fract = 0;
 

	
 
	/* increase day counter */
 
	TimerGameEconomy::date++;
 

	
 
	TimerGameEconomy::YearMonthDay ymd = TimerGameEconomy::ConvertDateToYMD(TimerGameEconomy::date);
 

	
 
	/* check if we entered a new month? */
 
	bool new_month = ymd.month != TimerGameEconomy::month;
 

	
 
	/* check if we entered a new year? */
 
	bool new_year = ymd.year != TimerGameEconomy::year;
 

	
 
	/* update internal variables before calling the daily/monthly/yearly loops */
 
	TimerGameEconomy::month = ymd.month;
 
	TimerGameEconomy::year = ymd.year;
 

	
 
	/* Make a temporary copy of the timers, as a timer's callback might add/remove other timers. */
 
	auto timers = TimerManager<TimerGameEconomy>::GetTimers();
 

	
 
	for (auto timer : timers) {
 
		timer->Elapsed(TimerGameEconomy::DAY);
 
	}
 

	
 
	if ((TimerGameEconomy::date.base() % 7) == 3) {
 
		for (auto timer : timers) {
 
			timer->Elapsed(TimerGameEconomy::WEEK);
 
		}
 
	}
 

	
 
	if (new_month) {
 
		for (auto timer : timers) {
 
			timer->Elapsed(TimerGameEconomy::MONTH);
 
		}
 

	
 
		if ((TimerGameEconomy::month % 3) == 0) {
 
			for (auto timer : timers) {
 
				timer->Elapsed(TimerGameEconomy::QUARTER);
 
			}
 
		}
 
	}
 

	
 
	if (new_year) {
 
		for (auto timer : timers) {
 
			timer->Elapsed(TimerGameEconomy::YEAR);
 
		}
 
	}
 

	
 
	/* check if we reached the maximum year, decrement dates by a year */
 
	if (TimerGameEconomy::year == EconomyTime::MAX_YEAR + 1) {
 
		int days_this_year;
 

	
 
		TimerGameEconomy::year--;
 
		days_this_year = TimerGameEconomy::IsLeapYear(TimerGameEconomy::year) ? EconomyTime::DAYS_IN_LEAP_YEAR : EconomyTime::DAYS_IN_YEAR;
 
		TimerGameEconomy::date -= days_this_year;
 
		for (Vehicle *v : Vehicle::Iterate()) v->ShiftDates(-days_this_year);
 
		for (LinkGraph *lg : LinkGraph::Iterate()) lg->ShiftDates(-days_this_year);
 
	}
 
}
 

	
 
#ifdef WITH_ASSERT
 
template<>
 
void TimerManager<TimerGameEconomy>::Validate(TimerGameEconomy::TPeriod period)
 
{
 
	if (period.priority == TimerGameEconomy::Priority::NONE) return;
 

	
 
	/* Validate we didn't make a developer error and scheduled more than one
 
	 * entry on the same priority/trigger. There can only be one timer on
 
	 * a specific trigger/priority, to ensure we are deterministic. */
 
	for (const auto &timer : TimerManager<TimerGameEconomy>::GetTimers()) {
 
		if (timer->period.trigger != period.trigger) continue;
 

	
 
		assert(timer->period.priority != period.priority);
 
	}
 
}
 
#endif /* WITH_ASSERT */
src/timer/timer_game_economy.h
Show inline comments
 
new file 100644
 
/*
 
 * This file is part of OpenTTD.
 
 * 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.
 
 * 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.
 
 * 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 <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file timer_game_economy.h Definition of the game-economy-timer */
 

	
 
#ifndef TIMER_GAME_ECONOMY_H
 
#define TIMER_GAME_ECONOMY_H
 

	
 
#include "../core/strong_typedef_type.hpp"
 
#include "timer_game_common.h"
 

	
 
/**
 
 * Timer that is increased every 27ms, and counts towards economy time units, expressed in days / months / years.
 
 *
 
 * For now, this is kept in sync with the calendar date, so the amount of days in a month depends on the month and year (leap-years).
 
 * There are always 74 ticks in a day (and with 27ms, this makes 1 day 1.998 seconds).
 
 *
 
 * Economy time is used for the regular pace of the game, including:
 
 * - Industry and house production/consumption
 
 * - Industry production changes, closure, and spawning
 
 * - Town growth
 
 * - Company age and periodical finance stats
 
 * - Vehicle age and profit statistics, both individual and group
 
 * - Vehicle aging, depreciation, reliability, and renewal
 
 * - Payment intervals for running and maintenance costs, loan interest, etc.
 
 * - Cargo payment "time" calculation
 
 * - Local authority and station ratings change intervals
 
 */
 
class TimerGameEconomy : public TimerGame<struct Economy> {
 
public:
 
	static Year year; ///< Current year, starting at 0.
 
	static Month month; ///< Current month (0..11).
 
	static Date date; ///< Current date in days (day counter).
 
	static DateFract date_fract; ///< Fractional part of the day.
 

	
 
	static void SetDate(Date date, DateFract fract);
 
};
 

	
 
/**
 
 * Storage class for Economy time constants.
 
 */
 
class EconomyTime : public TimerGameConst<struct Economy> {};
 

	
 
#endif /* TIMER_GAME_ECONOMY_H */
src/timetable.h
Show inline comments
 
@@ -11,10 +11,10 @@
 
#define TIMETABLE_H
 

	
 
#include "timer/timer_game_tick.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "vehicle_type.h"
 

	
 
static const TimerGameCalendar::Year MAX_TIMETABLE_START_YEARS = 15; ///< The maximum start date offset, in years.
 
static const TimerGameEconomy::Year MAX_TIMETABLE_START_YEARS = 15; ///< The maximum start date offset, in economy years.
 

	
 
enum class TimetableMode : uint8_t {
 
	Days,
 
@@ -22,8 +22,8 @@ enum class TimetableMode : uint8_t {
 
	Ticks,
 
};
 

	
 
TimerGameTick::TickCounter GetStartTickFromDate(TimerGameCalendar::Date start_date);
 
TimerGameCalendar::Date GetDateFromStartTick(TimerGameTick::TickCounter start_tick);
 
TimerGameTick::TickCounter GetStartTickFromDate(TimerGameEconomy::Date start_date);
 
TimerGameEconomy::Date GetDateFromStartTick(TimerGameTick::TickCounter start_tick);
 

	
 
void ShowTimetableWindow(const Vehicle *v);
 
void UpdateVehicleTimetable(Vehicle *v, bool travelling);
src/timetable_cmd.cpp
Show inline comments
 
@@ -11,7 +11,7 @@
 
#include "command_func.h"
 
#include "company_func.h"
 
#include "timer/timer_game_tick.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "window_func.h"
 
#include "vehicle_base.h"
 
#include "timetable_cmd.h"
 
@@ -26,13 +26,13 @@
 
 * @param start_date The date when the timetable starts.
 
 * @return The first tick of this date.
 
 */
 
TimerGameTick::TickCounter GetStartTickFromDate(TimerGameCalendar::Date start_date)
 
TimerGameTick::TickCounter GetStartTickFromDate(TimerGameEconomy::Date start_date)
 
{
 
	/* Calculate the offset in ticks from the current date. */
 
	TimerGameTick::Ticks tick_offset = (start_date - TimerGameCalendar::date).base() * Ticks::DAY_TICKS;
 
	TimerGameTick::Ticks tick_offset = (start_date - TimerGameEconomy::date).base() * Ticks::DAY_TICKS;
 

	
 
	/* Compensate for the current date_fract. */
 
	tick_offset -= TimerGameCalendar::date_fract;
 
	tick_offset -= TimerGameEconomy::date_fract;
 

	
 
	/* Return the current tick plus the offset. */
 
	return TimerGameTick::counter + tick_offset;
 
@@ -43,16 +43,16 @@ TimerGameTick::TickCounter GetStartTickF
 
 * @param start_tick The TimerGameTick::TickCounter when the timetable starts.
 
 * @return The date when we reach this tick.
 
 */
 
TimerGameCalendar::Date GetDateFromStartTick(TimerGameTick::TickCounter start_tick)
 
TimerGameEconomy::Date GetDateFromStartTick(TimerGameTick::TickCounter start_tick)
 
{
 
	/* Calculate the offset in ticks from the current counter tick. */
 
	TimerGameTick::Ticks tick_offset = start_tick - TimerGameTick::counter;
 

	
 
	/* Compensate for the current date_fract. */
 
	tick_offset += TimerGameCalendar::date_fract;
 
	tick_offset += TimerGameEconomy::date_fract;
 

	
 
	/* Return the current date plus the offset in days. */
 
	return TimerGameCalendar::date + (tick_offset / Ticks::DAY_TICKS);
 
	return TimerGameEconomy::date + (tick_offset / Ticks::DAY_TICKS);
 
}
 

	
 
/**
 
@@ -347,21 +347,21 @@ CommandCost CmdSetTimetableStart(DoComma
 

	
 
	TimerGameTick::Ticks total_duration = v->orders->GetTimetableTotalDuration();
 

	
 
	TimerGameCalendar::Date start_date = GetDateFromStartTick(start_tick);
 
	TimerGameEconomy::Date start_date = GetDateFromStartTick(start_tick);
 

	
 
	/* Don't let a timetable start at an invalid date. */
 
	if (start_date < 0 || start_date > CalendarTime::MAX_DATE) return CMD_ERROR;
 
	if (start_date < 0 || start_date > EconomyTime::MAX_DATE) return CMD_ERROR;
 

	
 
	/* Don't let a timetable start more than 15 years into the future... */
 
	if (start_date - TimerGameCalendar::date > TimerGameCalendar::DateAtStartOfYear(MAX_TIMETABLE_START_YEARS)) return CMD_ERROR;
 
	if (start_date - TimerGameEconomy::date > TimerGameEconomy::DateAtStartOfYear(MAX_TIMETABLE_START_YEARS)) return CMD_ERROR;
 
	/* ...or 1 year in the past. */
 
	if (TimerGameCalendar::date - start_date > CalendarTime::DAYS_IN_LEAP_YEAR) return CMD_ERROR;
 
	if (TimerGameEconomy::date - start_date > EconomyTime::DAYS_IN_LEAP_YEAR) return CMD_ERROR;
 

	
 
	/* If trying to distribute start dates over a shared order group, we need to know the total duration. */
 
	if (timetable_all && !v->orders->IsCompleteTimetable()) return CommandCost(STR_ERROR_TIMETABLE_INCOMPLETE);
 

	
 
	/* Don't allow invalid start dates for other vehicles in the shared order group. */
 
	if (timetable_all && start_date + (total_duration / Ticks::DAY_TICKS) > CalendarTime::MAX_DATE) return CMD_ERROR;
 
	if (timetable_all && start_date + (total_duration / Ticks::DAY_TICKS) > EconomyTime::MAX_DATE) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		std::vector<Vehicle *> vehs;
src/timetable_gui.cpp
Show inline comments
 
@@ -20,7 +20,7 @@
 
#include "company_func.h"
 
#include "timer/timer.h"
 
#include "timer/timer_game_tick.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "timer/timer_window.h"
 
#include "date_gui.h"
 
#include "vehicle_gui.h"
 
@@ -192,7 +192,7 @@ static void FillTimetableArrivalDepartur
 
 * @param w the window related to the setting of the date
 
 * @param date the actually chosen date
 
 */
 
static void ChangeTimetableStartCallback(const Window *w, TimerGameCalendar::Date date, void *data)
 
static void ChangeTimetableStartCallback(const Window *w, TimerGameEconomy::Date date, void *data)
 
{
 
	Command<CMD_SET_TIMETABLE_START>::Post(STR_ERROR_CAN_T_TIMETABLE_VEHICLE, (VehicleID)w->window_number, reinterpret_cast<std::uintptr_t>(data) != 0, GetStartTickFromDate(date));
 
}
 
@@ -235,7 +235,7 @@ struct TimetableWindow : Window {
 
		TimerGameTick::Ticks start_time = -v->current_order_time;
 

	
 
		/* If arrival and departure times are in days, compensate for the current date_fract. */
 
		if (_settings_client.gui.timetable_mode != TimetableMode::Seconds) start_time += TimerGameCalendar::date_fract;
 
		if (_settings_client.gui.timetable_mode != TimetableMode::Seconds) start_time += TimerGameEconomy::date_fract;
 

	
 
		FillTimetableArrivalDepartureTable(v, v->cur_real_order_index % v->GetNumOrders(), travelling, table, start_time);
 

	
 
@@ -252,7 +252,7 @@ struct TimetableWindow : Window {
 
					SetDParamMaxDigits(1, 4, FS_SMALL);
 
					size->width = std::max(GetStringBoundingBox(STR_TIMETABLE_ARRIVAL_SECONDS_IN_FUTURE).width, GetStringBoundingBox(STR_TIMETABLE_DEPARTURE_SECONDS_IN_FUTURE).width) + WidgetDimensions::scaled.hsep_wide + padding.width;
 
				} else {
 
					SetDParamMaxValue(1, TimerGameCalendar::DateAtStartOfYear(CalendarTime::MAX_YEAR), 0, FS_SMALL);
 
					SetDParamMaxValue(1, TimerGameEconomy::DateAtStartOfYear(EconomyTime::MAX_YEAR), 0, FS_SMALL);
 
					size->width = std::max(GetStringBoundingBox(STR_TIMETABLE_ARRIVAL_DATE).width, GetStringBoundingBox(STR_TIMETABLE_DEPARTURE_DATE).width) + WidgetDimensions::scaled.hsep_wide + padding.width;
 
				}
 
				FALLTHROUGH;
 
@@ -525,7 +525,7 @@ struct TimetableWindow : Window {
 
						DrawString(tr.left, tr.right, tr.top, STR_TIMETABLE_ARRIVAL_SECONDS_IN_FUTURE, i == selected ? TC_WHITE : TC_BLACK);
 
					} else {
 
						/* Show a date. */
 
						SetDParam(1, TimerGameCalendar::date + (arr_dep[i / 2].arrival + this_offset) / Ticks::DAY_TICKS);
 
						SetDParam(1, TimerGameEconomy::date + (arr_dep[i / 2].arrival + this_offset) / Ticks::DAY_TICKS);
 
						DrawString(tr.left, tr.right, tr.top, STR_TIMETABLE_ARRIVAL_DATE, i == selected ? TC_WHITE : TC_BLACK);
 
					}
 
				}
 
@@ -538,7 +538,7 @@ struct TimetableWindow : Window {
 
						DrawString(tr.left, tr.right, tr.top, STR_TIMETABLE_DEPARTURE_SECONDS_IN_FUTURE, i == selected ? TC_WHITE : TC_BLACK);
 
					} else {
 
						/* Show a date. */
 
						SetDParam(1, TimerGameCalendar::date + (arr_dep[i / 2].departure + offset) / Ticks::DAY_TICKS);
 
						SetDParam(1, TimerGameEconomy::date + (arr_dep[i / 2].departure + offset) / Ticks::DAY_TICKS);
 
						DrawString(tr.left, tr.right, tr.top, STR_TIMETABLE_DEPARTURE_DATE, i == selected ? TC_WHITE : TC_BLACK);
 
					}
 
				}
 
@@ -572,7 +572,7 @@ struct TimetableWindow : Window {
 
				SetDParam(0, (static_cast<TimerGameTick::Ticks>(v->timetable_start - TimerGameTick::counter) / Ticks::TICKS_PER_SECOND));
 
				DrawString(tr, STR_TIMETABLE_STATUS_START_IN_SECONDS);
 
			} else {
 
				/* Calendar units use dates. */
 
				/* Other units use dates. */
 
				SetDParam(0, STR_JUST_DATE_TINY);
 
				SetDParam(1, GetDateFromStartTick(v->timetable_start));
 
				DrawString(tr, STR_TIMETABLE_STATUS_START_AT_DATE);
 
@@ -643,7 +643,7 @@ struct TimetableWindow : Window {
 
					this->change_timetable_all = _ctrl_pressed;
 
					ShowQueryString(STR_EMPTY, STR_TIMETABLE_START_SECONDS_QUERY, 6, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED);
 
				} else {
 
					ShowSetDateWindow(this, v->index, TimerGameCalendar::date, TimerGameCalendar::year, TimerGameCalendar::year + MAX_TIMETABLE_START_YEARS, ChangeTimetableStartCallback, reinterpret_cast<void*>(static_cast<uintptr_t>(_ctrl_pressed)));
 
					ShowSetDateWindow(this, v->index, TimerGameEconomy::date, TimerGameEconomy::year, TimerGameEconomy::year + MAX_TIMETABLE_START_YEARS, ChangeTimetableStartCallback, reinterpret_cast<void*>(static_cast<uintptr_t>(_ctrl_pressed)));
 
				}
 
				break;
 

	
src/toolbar_gui.cpp
Show inline comments
 
@@ -1103,10 +1103,18 @@ void ToggleWidgetOutlines()
 
void SetStartingYear(TimerGameCalendar::Year year)
 
{
 
	_settings_game.game_creation.starting_year = Clamp(year, CalendarTime::MIN_YEAR, CalendarTime::MAX_YEAR);
 
	TimerGameCalendar::Date new_date = TimerGameCalendar::ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1);
 
	/* If you open a savegame as scenario there may already be link graphs.*/
 
	LinkGraphSchedule::instance.ShiftDates(new_date - TimerGameCalendar::date);
 
	TimerGameCalendar::SetDate(new_date, 0);
 
	TimerGameCalendar::Date new_calendar_date = TimerGameCalendar::ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1);
 
	TimerGameEconomy::Date new_economy_date = new_calendar_date.base();
 

	
 
	/* We must set both Calendar and Economy dates to keep them in sync. Calendar first. */
 
	TimerGameCalendar::SetDate(new_calendar_date, 0);
 

	
 
	/* If you open a savegame as a scenario, there may already be link graphs and/or vehicles. These use economy date. */
 
	LinkGraphSchedule::instance.ShiftDates(new_economy_date - TimerGameEconomy::date);
 
	for (auto v : Vehicle::Iterate()) v->ShiftDates(new_economy_date - TimerGameEconomy::date);
 

	
 
	/* Only change the date after changing cached values above. */
 
	TimerGameEconomy::SetDate(new_economy_date, 0);
 
}
 

	
 
/**
src/town_cmd.cpp
Show inline comments
 
@@ -54,6 +54,7 @@
 
#include "tunnelbridge_cmd.h"
 
#include "timer/timer.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "timer/timer_game_tick.h"
 

	
 
#include "table/strings.h"
 
@@ -3837,7 +3838,7 @@ CommandCost CheckforTownRating(DoCommand
 
	return CommandCost();
 
}
 

	
 
static IntervalTimer<TimerGameCalendar> _towns_monthly({TimerGameCalendar::MONTH, TimerGameCalendar::Priority::TOWN}, [](auto)
 
static IntervalTimer<TimerGameEconomy> _economy_towns_monthly({TimerGameEconomy::MONTH, TimerGameEconomy::Priority::TOWN}, [](auto)
 
{
 
	for (Town *t : Town::Iterate()) {
 
		/* Check for active town actions and decrement their counters. */
 
@@ -3864,7 +3865,7 @@ static IntervalTimer<TimerGameCalendar> 
 
	}
 
});
 

	
 
static IntervalTimer<TimerGameCalendar> _towns_yearly({TimerGameCalendar::YEAR, TimerGameCalendar::Priority::TOWN}, [](auto)
 
static IntervalTimer<TimerGameEconomy> _economy_towns_yearly({TimerGameEconomy::YEAR, TimerGameEconomy::Priority::TOWN}, [](auto)
 
{
 
	/* Increment house ages */
 
	for (TileIndex t = 0; t < Map::Size(); t++) {
src/train.h
Show inline comments
 
@@ -123,7 +123,8 @@ struct Train FINAL : public GroundVehicl
 
	int GetDisplayImageWidth(Point *offset = nullptr) const;
 
	bool IsInDepot() const override { return this->track == TRACK_BIT_DEPOT; }
 
	bool Tick() override;
 
	void OnNewDay() override;
 
	void OnNewCalendarDay() override;
 
	void OnNewEconomyDay() override;
 
	uint Crash(bool flooded = false) override;
 
	Trackdir GetVehicleTrackdir() const override;
 
	TileIndex GetOrderStationLocation(StationID station) override;
src/train_cmd.cpp
Show inline comments
 
@@ -38,6 +38,7 @@
 
#include "train_cmd.h"
 
#include "misc_cmd.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 

	
 
#include "table/strings.h"
 
#include "table/train_sprites.h"
 
@@ -653,7 +654,7 @@ static CommandCost CmdBuildRailWagon(DoC
 

	
 
		v->railtype = rvi->railtype;
 

	
 
		v->date_of_last_service = TimerGameCalendar::date;
 
		v->date_of_last_service = TimerGameEconomy::date;
 
		v->date_of_last_service_newgrf = TimerGameCalendar::date;
 
		v->build_year = TimerGameCalendar::year;
 
		v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
 
@@ -787,7 +788,7 @@ CommandCost CmdBuildRailVehicle(DoComman
 
		v->railtype = rvi->railtype;
 

	
 
		v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_trains);
 
		v->date_of_last_service = TimerGameCalendar::date;
 
		v->date_of_last_service = TimerGameEconomy::date;
 
		v->date_of_last_service_newgrf = TimerGameCalendar::date;
 
		v->build_year = TimerGameCalendar::year;
 
		v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
 
@@ -4163,11 +4164,15 @@ static void CheckIfTrainNeedsService(Tra
 
	SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
 
}
 

	
 
/** Update day counters of the train vehicle. */
 
void Train::OnNewDay()
 
/** Calendar day handler. */
 
void Train::OnNewCalendarDay()
 
{
 
	AgeVehicle(this);
 

	
 
}
 

	
 
/** Economy day handler. */
 
void Train::OnNewEconomyDay()
 
{
 
	if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
 

	
 
	if (this->IsFrontEngine()) {
src/vehicle.cpp
Show inline comments
 
@@ -58,6 +58,7 @@
 
#include "newgrf_roadstop.h"
 
#include "timer/timer.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "timer/timer_game_tick.h"
 

	
 
#include "table/strings.h"
 
@@ -169,7 +170,7 @@ void VehicleServiceInDepot(Vehicle *v)
 
	SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
 

	
 
	do {
 
		v->date_of_last_service = TimerGameCalendar::date;
 
		v->date_of_last_service = TimerGameEconomy::date;
 
		v->date_of_last_service_newgrf = TimerGameCalendar::date;
 
		v->breakdowns_since_last_service = 0;
 
		v->reliability = v->GetEngine()->reliability;
 
@@ -196,7 +197,7 @@ bool Vehicle::NeedsServicing() const
 
	const Company *c = Company::Get(this->owner);
 
	if (this->ServiceIntervalIsPercent() ?
 
			(this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) :
 
			(this->date_of_last_service + this->GetServiceInterval() >= TimerGameCalendar::date)) {
 
			(this->date_of_last_service + this->GetServiceInterval() >= TimerGameEconomy::date)) {
 
		return false;
 
	}
 

	
 
@@ -766,9 +767,9 @@ uint32_t Vehicle::GetGRFID() const
 
 * This is useful if the date has been modified with the cheat menu.
 
 * @param interval Number of days to be added or substracted.
 
 */
 
void Vehicle::ShiftDates(TimerGameCalendar::Date interval)
 
void Vehicle::ShiftDates(TimerGameEconomy::Date interval)
 
{
 
	this->date_of_last_service = std::max(this->date_of_last_service + interval, TimerGameCalendar::Date(0));
 
	this->date_of_last_service = std::max(this->date_of_last_service + interval, TimerGameEconomy::Date(0));
 
	/* date_of_last_service_newgrf is not updated here as it must stay stable
 
	 * for vehicles outside of a depot. */
 
}
 
@@ -916,16 +917,31 @@ void VehicleEnteredDepotThisTick(Vehicle
 
}
 

	
 
/**
 
 * Age all vehicles, spreading out the action using the current TimerGameCalendar::date_fract.
 
 */
 
void RunVehicleCalendarDayProc()
 
{
 
	if (_game_mode != GM_NORMAL) return;
 

	
 
	/* Run the calendar day proc for every DAY_TICKS vehicle starting at TimerGameCalendar::date_fract. */
 
	for (size_t i = TimerGameCalendar::date_fract; i < Vehicle::GetPoolSize(); i += Ticks::DAY_TICKS) {
 
		Vehicle *v = Vehicle::Get(i);
 
		if (v == nullptr) continue;
 
		v->OnNewCalendarDay();
 
	}
 
}
 

	
 
/**
 
 * Increases the day counter for all vehicles and calls 1-day and 32-day handlers.
 
 * Each tick, it processes vehicles with "index % DAY_TICKS == TimerGameCalendar::date_fract",
 
 * Each tick, it processes vehicles with "index % DAY_TICKS == TimerGameEconomy::date_fract",
 
 * so each day, all vehicles are processes in DAY_TICKS steps.
 
 */
 
static void RunVehicleDayProc()
 
static void RunEconomyVehicleDayProc()
 
{
 
	if (_game_mode != GM_NORMAL) return;
 

	
 
	/* Run the day_proc for every DAY_TICKS vehicle starting at TimerGameCalendar::date_fract. */
 
	for (size_t i = TimerGameCalendar::date_fract; i < Vehicle::GetPoolSize(); i += Ticks::DAY_TICKS) {
 
	/* Run the economy day proc for every DAY_TICKS vehicle starting at TimerGameEconomy::date_fract. */
 
	for (size_t i = TimerGameEconomy::date_fract; i < Vehicle::GetPoolSize(); i += Ticks::DAY_TICKS) {
 
		Vehicle *v = Vehicle::Get(i);
 
		if (v == nullptr) continue;
 

	
 
@@ -946,7 +962,7 @@ static void RunVehicleDayProc()
 
		}
 

	
 
		/* This is called once per day for each vehicle, but not in the first tick of the day */
 
		v->OnNewDay();
 
		v->OnNewEconomyDay();
 
	}
 
}
 

	
 
@@ -954,7 +970,7 @@ void CallVehicleTicks()
 
{
 
	_vehicles_to_autoreplace.clear();
 

	
 
	RunVehicleDayProc();
 
	RunEconomyVehicleDayProc();
 

	
 
	{
 
		PerformanceMeasurer framerate(PFE_GL_ECONOMY);
 
@@ -2819,7 +2835,7 @@ void Vehicle::RemoveFromShared()
 
	this->previous_shared = nullptr;
 
}
 

	
 
static IntervalTimer<TimerGameCalendar> _vehicles_yearly({TimerGameCalendar::YEAR, TimerGameCalendar::Priority::VEHICLE}, [](auto)
 
static IntervalTimer<TimerGameEconomy> _economy_vehicles_yearly({TimerGameEconomy::YEAR, TimerGameEconomy::Priority::VEHICLE}, [](auto)
 
{
 
	for (Vehicle *v : Vehicle::Iterate()) {
 
		if (v->IsPrimaryVehicle()) {
src/vehicle_base.h
Show inline comments
 
@@ -287,8 +287,8 @@ public:
 
	TimerGameCalendar::Year build_year;           ///< Year the vehicle has been built.
 
	TimerGameCalendar::Date age;                  ///< Age in days
 
	TimerGameCalendar::Date max_age;              ///< Maximum age
 
	TimerGameCalendar::Date date_of_last_service; ///< Last date the vehicle had a service at a depot.
 
	TimerGameCalendar::Date date_of_last_service_newgrf; ///< Last date the vehicle had a service at a depot, unchanged by the date cheat to protect against unsafe NewGRF behavior.
 
	TimerGameEconomy::Date date_of_last_service; ///< Last economy date the vehicle had a service at a depot.
 
	TimerGameCalendar::Date date_of_last_service_newgrf; ///< Last calendar date the vehicle had a service at a depot, unchanged by the date cheat to protect against unsafe NewGRF behavior.
 
	uint16_t reliability;                 ///< Reliability.
 
	uint16_t reliability_spd_dec;         ///< Reliability decrease speed.
 
	byte breakdown_ctr;                 ///< Counter for managing breakdown events. @see Vehicle::HandleBreakdown
 
@@ -567,11 +567,16 @@ public:
 
	virtual bool Tick() { return true; };
 

	
 
	/**
 
	 * Calls the new day handler of the vehicle
 
	 * Calls the new calendar day handler of the vehicle.
 
	 */
 
	virtual void OnNewDay() {};
 
	virtual void OnNewCalendarDay() {};
 

	
 
	void ShiftDates(TimerGameCalendar::Date interval);
 
	/**
 
	 * Calls the new economy day handler of the vehicle.
 
	 */
 
	virtual void OnNewEconomyDay() {};
 

	
 
	void ShiftDates(TimerGameEconomy::Date interval);
 

	
 
	/**
 
	 * Crash the (whole) vehicle chain.
src/vehicle_func.h
Show inline comments
 
@@ -62,6 +62,7 @@ CommandCost TunnelBridgeIsFree(TileIndex
 
void DecreaseVehicleValue(Vehicle *v);
 
void CheckVehicleBreakdown(Vehicle *v);
 
void AgeVehicle(Vehicle *v);
 
void RunVehicleCalendarDayProc();
 
void VehicleEnteredDepotThisTick(Vehicle *v);
 

	
 
UnitID GetFreeUnitNumber(VehicleType type);
src/vehicle_gui.cpp
Show inline comments
 
@@ -2431,7 +2431,7 @@ struct VehicleDetailsWindow : Window {
 

	
 
			case WID_VD_SERVICING_INTERVAL:
 
				SetDParamMaxValue(0, MAX_SERVINT_DAYS); // Roughly the maximum interval
 
				SetDParamMaxValue(1, TimerGameCalendar::DateAtStartOfYear(CalendarTime::MAX_YEAR)); // Roughly the maximum year
 
				SetDParamMaxValue(1, TimerGameEconomy::DateAtStartOfYear(EconomyTime::MAX_YEAR)); // Roughly the maximum year
 
				size->width = std::max(
 
					GetStringBoundingBox(STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT).width,
 
					GetStringBoundingBox(STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS).width
0 comments (0 inline, 0 general)