diff --git a/src/timer/CMakeLists.txt b/src/timer/CMakeLists.txt index f4f82e1a5a..1441881a5a 100644 --- a/src/timer/CMakeLists.txt +++ b/src/timer/CMakeLists.txt @@ -1,4 +1,6 @@ add_files( + timer_game_common.cpp + timer_game_common.h timer_game_calendar.cpp timer_game_calendar.h timer_game_realtime.cpp diff --git a/src/timer/timer_game_calendar.cpp b/src/timer/timer_game_calendar.cpp index 4b62da7f6a..6643a5b27b 100644 --- a/src/timer/timer_game_calendar.cpp +++ b/src/timer/timer_game_calendar.cpp @@ -24,126 +24,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 - * @param ymd the year, month and day to write to - */ -/* static */ void TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::Date date, TimerGameCalendar::YearMonthDay *ymd) -{ - /* 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); - uint16_t x; - - 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++; - - ymd->year = yr; - - x = _month_date_from_year_day[rem]; - ymd->month = x >> 5; - ymd->day = x & 0x1F; -} - -/** - * 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 diff --git a/src/timer/timer_game_calendar.h b/src/timer/timer_game_calendar.h index 0b82db80c6..3ec2b66fdd 100644 --- a/src/timer/timer_game_calendar.h +++ b/src/timer/timer_game_calendar.h @@ -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::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 { public: - /** The type to store our dates in. */ - using Date = StrongType::Typedef; - - /** 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; - /** 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 void ConvertDateToYMD(Date date, YearMonthDay * ymd); - 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 {}; #endif /* TIMER_GAME_CALENDAR_H */ diff --git a/src/timer/timer_game_common.cpp b/src/timer/timer_game_common.cpp new file mode 100644 index 0000000000..0b288cfc4f --- /dev/null +++ b/src/timer/timer_game_common.cpp @@ -0,0 +1,136 @@ +/* + * 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_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 + * @param ymd the year, month and day to write to + */ +template +/* static */ void TimerGame::ConvertDateToYMD(Date date, YearMonthDay *ymd) +{ + /* 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::DAYS_IN_YEAR * 400 + 97)); + int rem = date.base() % (TimerGameConst::DAYS_IN_YEAR * 400 + 97); + uint16_t x; + + if (rem >= TimerGameConst::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::DAYS_IN_YEAR * 100 + 25; + + /* There are 24 leap years in the next couple of 100 years */ + yr += 100 * (rem / (TimerGameConst::DAYS_IN_YEAR * 100 + 24)); + rem = (rem % (TimerGameConst::DAYS_IN_YEAR * 100 + 24)); + } + + if (!IsLeapYear(yr) && rem >= TimerGameConst::DAYS_IN_YEAR * 4) { + /* The first 4 year of the century are not always a leap year */ + yr += 4; + rem -= TimerGameConst::DAYS_IN_YEAR * 4; + } + + /* There is 1 leap year every 4 years */ + yr += 4 * (rem / (TimerGameConst::DAYS_IN_YEAR * 4 + 1)); + rem = rem % (TimerGameConst::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::DAYS_IN_LEAP_YEAR : TimerGameConst::DAYS_IN_YEAR)) { + rem -= IsLeapYear(yr) ? TimerGameConst::DAYS_IN_LEAP_YEAR : TimerGameConst::DAYS_IN_YEAR; + yr++; + } + + /* Skip the 29th of February in non-leap years */ + if (!IsLeapYear(yr) && rem >= ACCUM_MAR - 1) rem++; + + ymd->year = yr; + + x = _month_date_from_year_day[rem]; + ymd->month = x >> 5; + ymd->day = x & 0x1F; +} + +/** +* Converts a Date to a Year, Month & Day. +* @param date the date to convert from +* @param ymd the year, month and day to write to +*/ +template +/* static */ typename TimerGame::Date TimerGame::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 void TimerGame::ConvertDateToYMD(Date date, YearMonthDay *ymd); + +template TimerGame::Date TimerGame::ConvertYMDToDate(Year year, Month month, Day day); diff --git a/src/timer/timer_game_common.h b/src/timer/timer_game_common.h new file mode 100644 index 0000000000..0c74b20b83 --- /dev/null +++ b/src/timer/timer_game_common.h @@ -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 . + */ + +/** @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::DAY, TimerGameCalendar::Priority::NONE}, [](uint count){}); + * + * @note Callbacks are executed in the game-thread. + */ +template +class TimerGame { +public: + /** The type to store our dates in. */ + template struct DateTag; + using Date = StrongType::Typedef, 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 struct YearTag; + using Year = StrongType::Typedef, 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 void ConvertDateToYMD(Date date, YearMonthDay *ymd); + 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 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::date == 0' meant that it was 1920-01-01. + */ + + /** The minimum starting year/base year of the original TTD */ + static constexpr typename TimerGame::Year ORIGINAL_BASE_YEAR = 1920; + /** The original ending year */ + static constexpr typename TimerGame::Year ORIGINAL_END_YEAR = 2051; + /** The maximum year of the original TTD */ + static constexpr typename TimerGame::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::Year MAX_YEAR = 5000000; + + /** The absolute minimum year in OTTD */ + static constexpr typename TimerGame::Year MIN_YEAR = 0; + + /** The default starting year */ + static constexpr typename TimerGame::Year DEF_START_YEAR = 1950; + /** The default scoring end year */ + static constexpr typename TimerGame::Year DEF_END_YEAR = ORIGINAL_END_YEAR - 1; + + /** The date of the first day of the original base year. */ + static constexpr typename TimerGame::Date DAYS_TILL_ORIGINAL_BASE_YEAR = TimerGame::DateAtStartOfYear(ORIGINAL_BASE_YEAR); + + /** The date of the last day of the max year. */ + static constexpr typename TimerGame::Date MAX_DATE = TimerGame::DateAtStartOfYear(MAX_YEAR + 1) - 1; + + /** The date on January 1, year 0. */ + static constexpr typename TimerGame::Date MIN_DATE = 0; + + static constexpr typename TimerGame::Year INVALID_YEAR = -1; ///< Representation of an invalid year + static constexpr typename TimerGame::Date INVALID_DATE = -1; ///< Representation of an invalid date +}; + +#endif /* TIMER_GAME_COMMON_H */