diff --git a/src/openttd.cpp b/src/openttd.cpp index c507abae78..117a1661f1 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -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" @@ -1424,6 +1425,7 @@ void StateGameLoop() BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP); AnimateAnimatedTiles(); TimerManager::Elapsed(1); + TimerManager::Elapsed(1); TimerManager::Elapsed(1); RunTileLoop(); CallVehicleTicks(); diff --git a/src/timer/CMakeLists.txt b/src/timer/CMakeLists.txt index 1441881a5a..ae18530833 100644 --- a/src/timer/CMakeLists.txt +++ b/src/timer/CMakeLists.txt @@ -3,6 +3,8 @@ add_files( 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 diff --git a/src/timer/timer_game_calendar.cpp b/src/timer/timer_game_calendar.cpp index 6643a5b27b..a30ff2bc8d 100644 --- a/src/timer/timer_game_calendar.cpp +++ b/src/timer/timer_game_calendar.cpp @@ -95,22 +95,10 @@ void TimerManager::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) { diff --git a/src/timer/timer_game_common.cpp b/src/timer/timer_game_common.cpp index 0b288cfc4f..614996b310 100644 --- a/src/timer/timer_game_common.cpp +++ b/src/timer/timer_game_common.cpp @@ -132,5 +132,7 @@ template /* Create instances of the two template variants that we have. * This is needed, as this templated functions are not in a header-file. */ template void TimerGame::ConvertDateToYMD(Date date, YearMonthDay *ymd); +template void TimerGame::ConvertDateToYMD(Date date, YearMonthDay *ymd); template TimerGame::Date TimerGame::ConvertYMDToDate(Year year, Month month, Day day); +template TimerGame::Date TimerGame::ConvertYMDToDate(Year year, Month month, Day day); diff --git a/src/timer/timer_game_economy.cpp b/src/timer/timer_game_economy.cpp new file mode 100644 index 0000000000..010043e21a --- /dev/null +++ b/src/timer/timer_game_economy.cpp @@ -0,0 +1,150 @@ +/* + * 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 . + */ + +/** + * @file timer_game_economy.cpp + * This file implements the timer logic for the game-economy-timer. + */ + +#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::YearMonthDay ymd; + + TimerGameEconomy::date = date; + TimerGameEconomy::date_fract = fract; + ConvertDateToYMD(date, &ymd); + TimerGameEconomy::year = ymd.year; + TimerGameEconomy::month = ymd.month; +} + +template<> +void IntervalTimer::Elapsed(TimerGameEconomy::TElapsed trigger) +{ + if (trigger == this->period.trigger) { + this->callback(1); + } +} + +template<> +void TimeoutTimer::Elapsed(TimerGameEconomy::TElapsed trigger) +{ + if (this->fired) return; + + if (trigger == this->period.trigger) { + this->callback(); + this->fired = true; + } +} + +template<> +void TimerManager::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, &ymd); + + /* 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::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::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::GetTimers()) { + if (timer->period.trigger != period.trigger) continue; + + assert(timer->period.priority != period.priority); + } +} +#endif /* WITH_ASSERT */ diff --git a/src/timer/timer_game_economy.h b/src/timer/timer_game_economy.h new file mode 100644 index 0000000000..14a693e512 --- /dev/null +++ b/src/timer/timer_game_economy.h @@ -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 . + */ + +/** @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 { +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 {}; + +#endif /* TIMER_GAME_ECONOMY_H */