OpenTTD Source  20241120-master-g6d3adc6169
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 <http://www.gnu.org/licenses/>.
6  */
7 
26 #include "../stdafx.h"
27 #include "../openttd.h"
28 #include "timer.h"
29 #include "timer_game_economy.h"
30 #include "timer_game_tick.h"
31 #include "../vehicle_base.h"
32 #include "../linkgraph/linkgraph.h"
33 
34 #include "../safeguards.h"
35 
36 TimerGameEconomy::Year TimerGameEconomy::year = {};
38 TimerGameEconomy::Date TimerGameEconomy::date = {};
40 
46 /* static */ TimerGameEconomy::YearMonthDay TimerGameEconomy::ConvertDateToYMD(TimerGameEconomy::Date date)
47 {
48  /* If we're not using wallclock units, we keep the economy date in sync with the calendar. */
50 
51  /* If we're using wallclock units, economy months have 30 days and an economy year has 360 days. */
52  TimerGameEconomy::YearMonthDay ymd;
53  ymd.year = date.base() / EconomyTime::DAYS_IN_ECONOMY_YEAR;
55  ymd.day = (date.base() % EconomyTime::DAYS_IN_ECONOMY_MONTH) + 1;
56  return ymd;
57 }
58 
66 /* static */ TimerGameEconomy::Date TimerGameEconomy::ConvertYMDToDate(TimerGameEconomy::Year year, TimerGameEconomy::Month month, TimerGameEconomy::Day day)
67 {
68  /* If we're not using wallclock units, we keep the economy date in sync with the calendar. */
70 
71  /* If we're using wallclock units, economy months have 30 days and an economy year has 360 days. */
72  const int total_months = (year.base() * EconomyTime::MONTHS_IN_YEAR) + month;
73  return (total_months * EconomyTime::DAYS_IN_ECONOMY_MONTH) + day - 1; // Day is 1-indexed but Date is 0-indexed, hence the - 1.
74 }
75 
81 /* static */ void TimerGameEconomy::SetDate(TimerGameEconomy::Date date, TimerGameEconomy::DateFract fract)
82 {
83  assert(fract < Ticks::DAY_TICKS);
84 
87  TimerGameEconomy::YearMonthDay ymd = TimerGameEconomy::ConvertDateToYMD(date);
88  TimerGameEconomy::year = ymd.year;
89  TimerGameEconomy::month = ymd.month;
90 }
91 
97 /* static */ bool TimerGameEconomy::UsingWallclockUnits(bool newgame)
98 {
99  if (newgame) return (_settings_newgame.economy.timekeeping_units == TKU_WALLCLOCK);
100 
101  return (_settings_game.economy.timekeeping_units == TKU_WALLCLOCK);
102 }
103 
104 template<>
105 void IntervalTimer<TimerGameEconomy>::Elapsed(TimerGameEconomy::TElapsed trigger)
106 {
107  if (trigger == this->period.trigger) {
108  this->callback(1);
109  }
110 }
111 
112 template<>
113 void TimeoutTimer<TimerGameEconomy>::Elapsed(TimerGameEconomy::TElapsed trigger)
114 {
115  if (this->fired) return;
116 
117  if (trigger == this->period.trigger) {
118  this->callback();
119  this->fired = true;
120  }
121 }
122 
123 template<>
124 bool TimerManager<TimerGameEconomy>::Elapsed([[maybe_unused]] TimerGameEconomy::TElapsed delta)
125 {
126  assert(delta == 1);
127 
128  if (_game_mode == GM_MENU) return false;
129 
133 
134  /* increase day counter */
136 
137  TimerGameEconomy::YearMonthDay ymd = TimerGameEconomy::ConvertDateToYMD(TimerGameEconomy::date);
138 
139  /* check if we entered a new month? */
140  bool new_month = ymd.month != TimerGameEconomy::month;
141 
142  /* check if we entered a new year? */
143  bool new_year = ymd.year != TimerGameEconomy::year;
144 
145  /* update internal variables before calling the daily/monthly/yearly loops */
146  TimerGameEconomy::month = ymd.month;
147  TimerGameEconomy::year = ymd.year;
148 
149  /* Make a temporary copy of the timers, as a timer's callback might add/remove other timers. */
151 
152  for (auto timer : timers) {
153  timer->Elapsed(TimerGameEconomy::DAY);
154  }
155 
156  if ((TimerGameEconomy::date.base() % 7) == 3) {
157  for (auto timer : timers) {
158  timer->Elapsed(TimerGameEconomy::WEEK);
159  }
160  }
161 
162  if (new_month) {
163  for (auto timer : timers) {
164  timer->Elapsed(TimerGameEconomy::MONTH);
165  }
166 
167  if ((TimerGameEconomy::month % 3) == 0) {
168  for (auto timer : timers) {
169  timer->Elapsed(TimerGameEconomy::QUARTER);
170  }
171  }
172  }
173 
174  if (new_year) {
175  for (auto timer : timers) {
176  timer->Elapsed(TimerGameEconomy::YEAR);
177  }
178  }
179 
180  /* check if we reached the maximum year, decrement dates by a year */
182  int days_this_year;
183 
186  TimerGameEconomy::date -= days_this_year;
187  for (Vehicle *v : Vehicle::Iterate()) v->ShiftDates(-days_this_year);
188  for (LinkGraph *lg : LinkGraph::Iterate()) lg->ShiftDates(-days_this_year);
189  }
190 
191  return true;
192 }
193 
194 #ifdef WITH_ASSERT
195 template<>
196 void TimerManager<TimerGameEconomy>::Validate(TimerGameEconomy::TPeriod period)
197 {
198  if (period.priority == TimerGameEconomy::Priority::NONE) return;
199 
200  /* Validate we didn't make a developer error and scheduled more than one
201  * entry on the same priority/trigger. There can only be one timer on
202  * a specific trigger/priority, to ensure we are deterministic. */
203  for (const auto &timer : TimerManager<TimerGameEconomy>::GetTimers()) {
204  if (timer->period.trigger != period.trigger) continue;
205 
206  assert(timer->period.priority != period.priority);
207  }
208 }
209 #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 int MONTHS_IN_YEAR
months per year
static constexpr int DAYS_IN_YEAR
days per year
static constexpr TimerGame< struct Economy >::Year MAX_YEAR
MAX_YEAR, nicely rounded value of the number of years that can be encoded in a single 32 bits date,...
static constexpr int DAYS_IN_LEAP_YEAR
sometimes, you need one day more...
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 Date ConvertYMDToDate(Year year, Month month, Day day)
Converts a tuple of Year, Month and Day to a Date.
uint16_t DateFract
The fraction of a date we're in, i.e.
uint8_t Day
Type for the day of the month, note: 1 based, first day of a month is 1.
static Date CalendarConvertYMDToDate(Year year, Month month, Day day)
Converts a tuple of Year, Month and Day to a Date.
uint8_t Month
Type for the month, note: 0 based, i.e.
static constexpr bool IsLeapYear(Year year)
Checks whether the given year is a leap year or not.
static YearMonthDay CalendarConvertDateToYMD(Date date)
Converts a Date to a Year, Month & Day.
The TimerManager manages a single Timer-type.
Definition: timer_manager.h:27
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.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition: settings.cpp:57
GameSettings _settings_newgame
Game settings for new games (updated from the intro screen).
Definition: settings.cpp:58
TimekeepingUnits timekeeping_units
time units to use for the game economy, either calendar or wallclock
EconomySettings economy
settings to change the economy
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
Definition: pool_type.hpp:388
Vehicle data structure.
Definition: vehicle_base.h:244
Definition of Interval and OneShot timers.
Definition of the game-economy-timer.
Definition of the tick-based game-timer.