Codechange: Split dates and timers into Economy and Calendar time (#10700)

This commit is contained in:
Tyler Trahan 2024-01-22 09:04:34 -05:00 committed by GitHub
parent 419f48dfb3
commit 735abfe111
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
65 changed files with 952 additions and 550 deletions

View File

@ -107,7 +107,8 @@ struct Aircraft FINAL : public SpecializedVehicle<Aircraft, VEH_AIRCRAFT> {
}
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;

View File

@ -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(DoCommandFlag flags, TileIndex tile, const Engine *
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_AtTerminal(Aircraft *v, const AirportFTAClass *
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;

View File

@ -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 new_value, 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(int32_t new_value, int32_t)
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);

View File

@ -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::InternalPostResult(const CommandCost &res, TileIndex til
/** 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));
}
/**

View File

@ -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?

View File

@ -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, CompanyID company = INVALID_COMPANY)
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()) {

View File

@ -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 &r, int start_y, const ExpensesList &li
* @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;
}

View File

@ -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();

View File

@ -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__, __LINE__,
* @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);

View File

@ -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 */

View File

@ -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;

View File

@ -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 Station *st, CargoID cargo_type, uint n
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();
});

View File

@ -919,7 +919,7 @@ static bool IsVehicleTypeDisabled(VehicleType type, bool ai)
}
/** 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 *e)
}
/** 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();
});
/**

View File

@ -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;

View File

@ -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<&_industry_pool> {
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<&_industry_pool> {
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;

View File

@ -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, TileIndex tile, IndustryType type,
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(Industry *i)
{
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(Industry *i, bool monthly)
}
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(Industry *i, bool monthly)
}
/**
* 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> _industries_daily({TimerGameCalendar::DA
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);

View File

@ -29,7 +29,7 @@ LinkGraph::BaseNode::BaseNode(TileIndex xy, StationID st, uint demand)
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 dest_node)
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 dest_node)
* 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 to, uint capacity, uint usage, uint32_t
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 capacity, uint usage, uint32_t travel_time
}
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;
}
/**

View File

@ -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.
};

View File

@ -37,7 +37,7 @@ LinkGraphJob::LinkGraphJob(const LinkGraph &orig) :
* 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);
}

View File

@ -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.

View File

@ -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();

View File

@ -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.

View File

@ -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 size_y, bool reset_date, bool reset_settin
_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();
}

View File

@ -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 action, TextColour colour, bool self_send,
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 ||

View File

@ -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 : NetworkClientInfoPool::PoolItem<&_networkclientinfo_p
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.

View File

@ -282,7 +282,7 @@ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res)
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;

View File

@ -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 ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p)
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 client_id)
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> _network_quarterly({TimerGameCalendar::Q
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> _network_monthly({TimerGameCalendar::MON
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;

View File

@ -9945,6 +9945,11 @@ void LoadNewGRF(uint load_index, uint num_baseset)
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 num_baseset)
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 num_baseset)
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;
}

View File

@ -396,7 +396,7 @@ static uint32_t GetCountAndDistanceOfClosestInstance(byte param_setID, byte layo
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 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layo
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)
}
}

View File

@ -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();

View File

@ -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

View File

@ -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);

View File

@ -1963,7 +1963,7 @@ bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool
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();

View File

@ -134,7 +134,8 @@ struct RoadVehicle FINAL : public GroundVehicle<RoadVehicle, VEH_ROAD> {
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;

View File

@ -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(DoCommandFlag flags, TileIndex tile, const Engin
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(RoadVehicle *v)
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);

View File

@ -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();
}
}

View File

@ -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:

View File

@ -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),

View File

@ -859,7 +859,7 @@ static bool LoadOldIndustry(LoadgameState *ls, int num)
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 *ls, int num)
}
_company_colours[num] = c->colour;
c->inaugurated_year -= CalendarTime::ORIGINAL_BASE_YEAR;
c->inaugurated_year -= EconomyTime::ORIGINAL_BASE_YEAR;
return true;
}

View File

@ -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
};

View File

@ -45,7 +45,8 @@ struct Ship FINAL : public SpecializedVehicle<Ship, VEH_SHIP> {
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;

View File

@ -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 flags, TileIndex tile, const Engine *e, V
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);

View File

@ -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);
}

View File

@ -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) {

View File

@ -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 cid, SourceType src_type, SourceID src)
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;

View File

@ -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);

View File

@ -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);
}

View File

@ -1,6 +1,10 @@
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

View File

@ -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 TimerGameCalendar::month = {};
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>::Elapsed([[maybe_unused]] TimerGameCalendar
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>::Elapsed([[maybe_unused]] TimerGameCalendar
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);
}
}

View File

@ -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 */

View File

@ -0,0 +1,140 @@
/*
* 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);

View File

@ -0,0 +1,196 @@
/*
* 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 */

View File

@ -0,0 +1,160 @@
/*
* 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 */

View File

@ -0,0 +1,48 @@
/*
* 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 */

View File

@ -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);

View File

@ -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 GetStartTickFromDate(TimerGameCalendar::Date start_da
* @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(DoCommandFlag flags, VehicleID veh_id, bool tim
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;

View File

@ -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 FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID
* @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;

View File

@ -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);
}
/**

View File

@ -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(DoCommandFlag flags, Town *t, TownRatingCheckType
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> _towns_monthly({TimerGameCalendar::MONTH
}
});
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++) {

View File

@ -123,7 +123,8 @@ struct Train FINAL : public GroundVehicle<Train, VEH_TRAIN> {
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;

View File

@ -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(DoCommandFlag flags, TileIndex tile, const
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(DoCommandFlag flags, TileIndex tile, const Engin
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(Train *v)
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()) {

View File

@ -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,18 +917,33 @@ void VehicleEnteredDepotThisTick(Vehicle *v)
}
/**
* 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",
* so each day, all vehicles are processes in DAY_TICKS steps.
* Age all vehicles, spreading out the action using the current TimerGameCalendar::date_fract.
*/
static void RunVehicleDayProc()
void RunVehicleCalendarDayProc()
{
if (_game_mode != GM_NORMAL) return;
/* Run the day_proc for every DAY_TICKS vehicle starting at TimerGameCalendar::date_fract. */
/* 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 == TimerGameEconomy::date_fract",
* so each day, all vehicles are processes in DAY_TICKS steps.
*/
static void RunEconomyVehicleDayProc()
{
if (_game_mode != GM_NORMAL) return;
/* 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;
/* Call the 32-day callback if needed */
if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
@ -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()) {

View File

@ -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.

View File

@ -62,6 +62,7 @@ CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle
void DecreaseVehicleValue(Vehicle *v);
void CheckVehicleBreakdown(Vehicle *v);
void AgeVehicle(Vehicle *v);
void RunVehicleCalendarDayProc();
void VehicleEnteredDepotThisTick(Vehicle *v);
UnitID GetFreeUnitNumber(VehicleType type);

View File

@ -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