OpenTTD Source 20260206-master-g4d4e37dbf1
timer_game_economy.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
22
23#include "../stdafx.h"
24#include "../openttd.h"
25#include "timer.h"
26#include "timer_game_economy.h"
27#include "timer_game_tick.h"
28#include "../vehicle_base.h"
30
31#include "../safeguards.h"
32
33TimerGameEconomy::Year TimerGameEconomy::year = {};
35TimerGameEconomy::Date TimerGameEconomy::date = {};
38
44/* static */ TimerGameEconomy::YearMonthDay TimerGameEconomy::ConvertDateToYMD(TimerGameEconomy::Date date)
45{
46 /* If we're not using wallclock units, we keep the economy date in sync with the calendar. */
48
49 /* If we're using wallclock units, economy months have 30 days and an economy year has 360 days. */
50 TimerGameEconomy::YearMonthDay ymd;
51 ymd.year = Year{date.base() / EconomyTime::DAYS_IN_ECONOMY_YEAR};
53 ymd.day = (date.base() % EconomyTime::DAYS_IN_ECONOMY_MONTH) + 1;
54 return ymd;
55}
56
64/* static */ TimerGameEconomy::Date TimerGameEconomy::ConvertYMDToDate(TimerGameEconomy::Year year, TimerGameEconomy::Month month, TimerGameEconomy::Day day)
65{
66 /* If we're not using wallclock units, we keep the economy date in sync with the calendar. */
68
69 /* If we're using wallclock units, economy months have 30 days and an economy year has 360 days. */
70 const int total_months = (year.base() * EconomyTime::MONTHS_IN_YEAR) + month;
71 return TimerGameEconomy::Date{(total_months * EconomyTime::DAYS_IN_ECONOMY_MONTH) + day - 1}; // Day is 1-indexed but Date is 0-indexed, hence the - 1.
72}
73
79/* static */ void TimerGameEconomy::SetDate(TimerGameEconomy::Date date, TimerGameEconomy::DateFract fract)
80{
81 assert(fract < Ticks::DAY_TICKS);
82
85 TimerGameEconomy::YearMonthDay ymd = TimerGameEconomy::ConvertDateToYMD(date);
86 TimerGameEconomy::year = ymd.year;
87 TimerGameEconomy::month = ymd.month;
88}
89
95/* static */ bool TimerGameEconomy::UsingWallclockUnits(bool newgame)
96{
97 if (newgame) return (_settings_newgame.economy.timekeeping_units == TimekeepingUnits::Wallclock);
98
99 return (_settings_game.economy.timekeeping_units == TimekeepingUnits::Wallclock);
100}
101
102template <>
103void IntervalTimer<TimerGameEconomy>::Elapsed(TimerGameEconomy::TElapsed trigger)
104{
105 if (trigger == this->period.trigger) {
106 this->callback(1);
107 }
108}
109
110template <>
111void TimeoutTimer<TimerGameEconomy>::Elapsed(TimerGameEconomy::TElapsed trigger)
112{
113 if (this->fired) return;
114
115 if (trigger == this->period.trigger) {
116 this->callback();
117 this->fired = true;
118 }
119}
120
121template <>
122bool TimerManager<TimerGameEconomy>::Elapsed(TimerGameEconomy::TElapsed)
123{
124 if (_game_mode == GM_MENU) return false;
125
129
130 /* increase day counter */
133
134 TimerGameEconomy::YearMonthDay ymd = TimerGameEconomy::ConvertDateToYMD(TimerGameEconomy::date);
135
136 /* check if we entered a new month? */
137 bool new_month = ymd.month != TimerGameEconomy::month;
138
139 /* check if we entered a new year? */
140 bool new_year = ymd.year != TimerGameEconomy::year;
141
142 /* update internal variables before calling the daily/monthly/yearly loops */
143 TimerGameEconomy::month = ymd.month;
144 TimerGameEconomy::year = ymd.year;
145
146 /* Make a temporary copy of the timers, as a timer's callback might add/remove other timers. */
148
149 for (auto timer : timers) {
150 timer->Elapsed(TimerGameEconomy::Trigger::Day);
151 }
152
153 if ((TimerGameEconomy::date.base() % 7) == 3) {
154 for (auto timer : timers) {
155 timer->Elapsed(TimerGameEconomy::Trigger::Week);
156 }
157 }
158
159 if (new_month) {
160 for (auto timer : timers) {
161 timer->Elapsed(TimerGameEconomy::Trigger::Month);
162 }
163
164 if ((TimerGameEconomy::month % 3) == 0) {
165 for (auto timer : timers) {
166 timer->Elapsed(TimerGameEconomy::Trigger::Quarter);
167 }
168 }
169 }
170
171 if (new_year) {
172 for (auto timer : timers) {
173 timer->Elapsed(TimerGameEconomy::Trigger::Year);
174 }
175 }
176
178
179 /* check if we reached the maximum year, decrement dates by a year */
183 TimerGameEconomy::date -= TimerGameEconomy::Date{days_this_year};
184 for (Vehicle *v : Vehicle::Iterate()) v->ShiftDates(TimerGameEconomy::Date{-days_this_year});
185 for (LinkGraph *lg : LinkGraph::Iterate()) lg->ShiftDates(TimerGameEconomy::Date{-days_this_year});
186 }
187
188 return true;
189}
190
191#ifdef WITH_ASSERT
192template <>
193void TimerManager<TimerGameEconomy>::Validate(TimerGameEconomy::TPeriod period)
194{
195 if (period.priority == TimerGameEconomy::Priority::None) return;
196
197 /* Validate we didn't make a developer error and scheduled more than one
198 * entry on the same priority/trigger. There can only be one timer on
199 * a specific trigger/priority, to ensure we are deterministic. */
200 for (const auto &timer : TimerManager<TimerGameEconomy>::GetTimers()) {
201 if (timer->period.trigger != period.trigger) continue;
202
203 assert(timer->period.priority != period.priority);
204 }
205}
206#endif /* WITH_ASSERT */
static constexpr int DAYS_IN_ECONOMY_MONTH
Days in an economy month, when in wallclock timekeeping mode.
static constexpr int DAYS_IN_ECONOMY_YEAR
Days in an economy year, when in wallclock timekeeping mode.
void Elapsed(TElapsed count) override
Called by the timer manager to notify the timer that the given amount of time has elapsed.
A connected component of a link graph.
Definition linkgraph.h:37
static constexpr TimerGameTick::Ticks DAY_TICKS
1 day is 74 ticks; TimerGameCalendar::date_fract used to be uint16_t and incremented by 885.
void Elapsed(TElapsed count) override
Called by the timer manager to notify the timer that the given amount of time has elapsed.
static constexpr TimerGame< struct Economy >::Year MAX_YEAR
static Date date
Current date in days (day counter).
static Year year
Current year, starting at 0.
static Month month
Current month (0..11).
static bool UsingWallclockUnits(bool newgame=false)
Check if we are using wallclock units.
static DateFract date_fract
Fractional part of the day.
static YearMonthDay ConvertDateToYMD(Date date)
Converts a Date to a Year, Month & Day.
static void SetDate(Date date, DateFract fract)
Set the date.
static uint days_since_last_month
Number of days that have elapsed since the last month.
static Date ConvertYMDToDate(Year year, Month month, Day day)
Converts a tuple of Year, Month and Day to a Date.
static YearMonthDay CalendarConvertDateToYMD(Date date)
static Date CalendarConvertYMDToDate(Year year, Month month, Day day)
static constexpr bool IsLeapYear(Year year)
The TimerManager manages a single Timer-type.
static std::set< BaseTimer< TTimerType > *, base_timer_sorter > & GetTimers()
Singleton list, to store all the active timers.
static bool Elapsed(TElapsed value)
Called when time for this timer elapsed.
Declaration of link graph classes used for cargo distribution.
Some generic types.
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
GameSettings _settings_newgame
Game settings for new games (updated from the intro screen).
Definition settings.cpp:62
Definition of base types and functions in a cross-platform compatible way.
static Pool::IterateWrapper< Vehicle > Iterate(size_t from=0)
Vehicle data structure.
Definition of Interval and OneShot timers.
Definition of the game-economy-timer.
Definition of the tick-based game-timer.
Base class for all vehicles.