Strong typedef: Use strong typedefs for date, date tick, minutes types

Add delta types
Adjust/add type conversion functions
Add various utility methods on types
Remove the various minute macros
Fix some minute conversion inconsistencies
wip-string
Jonathan G Rennison 5 months ago
parent 1e7b56e13a
commit 03e0ec8276

@ -1177,7 +1177,7 @@ int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number,
if (e->type != VEH_TRAIN || e->u.rail.railveh_type != RAILVEH_WAGON) { if (e->type != VEH_TRAIN || e->u.rail.railveh_type != RAILVEH_WAGON) {
/* Design date - Life length */ /* Design date - Life length */
SetDParam(0, ymd.year); SetDParam(0, ymd.year);
SetDParam(1, DateToYear(e->GetLifeLengthInDays())); SetDParam(1, DateDeltaToYears(e->GetLifeLengthInDays()));
DrawString(left, right, y, STR_PURCHASE_INFO_DESIGNED_LIFE); DrawString(left, right, y, STR_PURCHASE_INFO_DESIGNED_LIFE);
y += GetCharacterHeight(FS_NORMAL); y += GetCharacterHeight(FS_NORMAL);

@ -1173,7 +1173,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint64 p3,
if (!random_state.Check()) { if (!random_state.Check()) {
std::string msg = stdstr_fmt("Random seed changed in test command: company: %02x; tile: %06x (%u x %u); p1: %08x; p2: %08x; p3: " OTTD_PRINTFHEX64PAD "; cmd: %08x; \"%s\"%s (%s)", std::string msg = stdstr_fmt("Random seed changed in test command: company: %02x; tile: %06x (%u x %u); p1: %08x; p2: %08x; p3: " OTTD_PRINTFHEX64PAD "; cmd: %08x; \"%s\"%s (%s)",
(int)_current_company, tile, TileX(tile), TileY(tile), p1, p2, p3, cmd & ~CMD_NETWORK_COMMAND, text, aux_data != nullptr ? ", aux data present" : "", GetCommandName(cmd)); (int)_current_company, tile, TileX(tile), TileY(tile), p1, p2, p3, cmd & ~CMD_NETWORK_COMMAND, text, aux_data != nullptr ? ", aux data present" : "", GetCommandName(cmd));
DEBUG(desync, 0, "msg: date{%08x; %02x; %02x}; %s", _date, _date_fract, _tick_skip_counter, msg.c_str()); DEBUG(desync, 0, "msg: date{%08x; %02x; %02x}; %s", _date.base(), _date_fract, _tick_skip_counter, msg.c_str());
LogDesyncMsg(std::move(msg)); LogDesyncMsg(std::move(msg));
} }
@ -1191,7 +1191,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint64 p3,
/* Log the failed command as well. Just to be able to be find /* Log the failed command as well. Just to be able to be find
* causes of desyncs due to bad command test implementations. */ * causes of desyncs due to bad command test implementations. */
DEBUG(desync, 1, "cmdf: date{%08x; %02x; %02x}; company: %02x; tile: %06x (%u x %u); p1: %08x; p2: %08x; p3: " OTTD_PRINTFHEX64PAD "; cmd: %08x; \"%s\"%s (%s)", DEBUG(desync, 1, "cmdf: date{%08x; %02x; %02x}; company: %02x; tile: %06x (%u x %u); p1: %08x; p2: %08x; p3: " OTTD_PRINTFHEX64PAD "; cmd: %08x; \"%s\"%s (%s)",
_date, _date_fract, _tick_skip_counter, (int)_current_company, tile, TileX(tile), TileY(tile), p1, p2, p3, cmd & ~CMD_NETWORK_COMMAND, text, aux_data != nullptr ? ", aux data present" : "", GetCommandName(cmd)); _date.base(), _date_fract, _tick_skip_counter, (int)_current_company, tile, TileX(tile), TileY(tile), p1, p2, p3, cmd & ~CMD_NETWORK_COMMAND, text, aux_data != nullptr ? ", aux data present" : "", GetCommandName(cmd));
} }
cur_company.Restore(); cur_company.Restore();
return_dcpi(res); return_dcpi(res);
@ -1212,7 +1212,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint64 p3,
return_dcpi(CommandCost()); return_dcpi(CommandCost());
} }
DEBUG(desync, 1, "cmd: date{%08x; %02x; %02x}; company: %02x; tile: %06x (%u x %u); p1: %08x; p2: %08x; p3: " OTTD_PRINTFHEX64PAD "; cmd: %08x; \"%s\"%s(%s)", DEBUG(desync, 1, "cmd: date{%08x; %02x; %02x}; company: %02x; tile: %06x (%u x %u); p1: %08x; p2: %08x; p3: " OTTD_PRINTFHEX64PAD "; cmd: %08x; \"%s\"%s(%s)",
_date, _date_fract, _tick_skip_counter, (int)_current_company, tile, TileX(tile), TileY(tile), p1, p2, p3, cmd & ~CMD_NETWORK_COMMAND, text, aux_data != nullptr ? ", aux data present" : "", GetCommandName(cmd)); _date.base(), _date_fract, _tick_skip_counter, (int)_current_company, tile, TileX(tile), TileY(tile), p1, p2, p3, cmd & ~CMD_NETWORK_COMMAND, text, aux_data != nullptr ? ", aux data present" : "", GetCommandName(cmd));
/* Actually try and execute the command. If no cost-type is given /* Actually try and execute the command. If no cost-type is given
* use the construction one */ * use the construction one */

@ -949,7 +949,7 @@ CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
} }
NetworkServerNewCompany(c, ci); NetworkServerNewCompany(c, ci);
DEBUG(desync, 1, "new_company: date{%08x; %02x; %02x}, company_id: %u", _date, _date_fract, _tick_skip_counter, c->index); DEBUG(desync, 1, "new_company: date{%08x; %02x; %02x}, company_id: %u", _date.base(), _date_fract, _tick_skip_counter, c->index);
break; break;
} }
@ -967,7 +967,7 @@ CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
Company *c = DoStartupNewCompany(DSNC_AI, company_id); Company *c = DoStartupNewCompany(DSNC_AI, company_id);
if (c != nullptr) { if (c != nullptr) {
NetworkServerNewCompany(c, nullptr); NetworkServerNewCompany(c, nullptr);
DEBUG(desync, 1, "new_company_ai: date{%08x; %02x; %02x}, company_id: %u", _date, _date_fract, _tick_skip_counter, c->index); DEBUG(desync, 1, "new_company_ai: date{%08x; %02x; %02x}, company_id: %u", _date.base(), _date_fract, _tick_skip_counter, c->index);
} }
break; break;
} }
@ -984,7 +984,7 @@ CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
if (!(flags & DC_EXEC)) return CommandCost(); if (!(flags & DC_EXEC)) return CommandCost();
DEBUG(desync, 1, "delete_company: date{%08x; %02x; %02x}, company_id: %u, reason: %u", _date, _date_fract, _tick_skip_counter, company_id, reason); DEBUG(desync, 1, "delete_company: date{%08x; %02x; %02x}, company_id: %u, reason: %u", _date.base(), _date_fract, _tick_skip_counter, company_id, reason);
CompanyNewsInformation *cni = new CompanyNewsInformation(c); CompanyNewsInformation *cni = new CompanyNewsInformation(c);

@ -1876,7 +1876,7 @@ struct CompanyInfrastructureWindow : Window
} }
/* Get the date introduced railtypes as well. */ /* Get the date introduced railtypes as well. */
this->railtypes = AddDateIntroducedRailTypes(this->railtypes, MAX_DAY); this->railtypes = AddDateIntroducedRailTypes(this->railtypes, MAX_DATE);
/* Find the used roadtypes. */ /* Find the used roadtypes. */
for (const Engine *e : Engine::IterateType(VEH_ROAD)) { for (const Engine *e : Engine::IterateType(VEH_ROAD)) {
@ -1886,7 +1886,7 @@ struct CompanyInfrastructureWindow : Window
} }
/* Get the date introduced roadtypes as well. */ /* Get the date introduced roadtypes as well. */
this->roadtypes = AddDateIntroducedRoadTypes(this->roadtypes, MAX_DAY); this->roadtypes = AddDateIntroducedRoadTypes(this->roadtypes, MAX_DATE);
this->roadtypes &= ~_roadtypes_hidden_mask; this->roadtypes &= ~_roadtypes_hidden_mask;
} }

@ -2490,7 +2490,7 @@ DEF_CONSOLE_CMD(ConMergeLinkgraphJobsAsap)
return true; return true;
} }
for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) lgj->ShiftJoinDate((((_date * DAY_TICKS) + _date_fract) - lgj->JoinDateTicks()) / DAY_TICKS); for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) lgj->ShiftJoinDate((NowDateTicks() - lgj->JoinDateTicks()).base() / DAY_TICKS);
return true; return true;
} }
@ -2739,14 +2739,14 @@ DEF_CONSOLE_CMD(ConDumpLinkgraphJobs)
IConsolePrintF(CC_DEFAULT, PRINTF_SIZE " link graph jobs", LinkGraphJob::GetNumItems()); IConsolePrintF(CC_DEFAULT, PRINTF_SIZE " link graph jobs", LinkGraphJob::GetNumItems());
for (const LinkGraphJob *lgj : LinkGraphJob::Iterate()) { for (const LinkGraphJob *lgj : LinkGraphJob::Iterate()) {
YearMonthDay start_ymd; YearMonthDay start_ymd;
ConvertDateToYMD(lgj->StartDateTicks() / DAY_TICKS, &start_ymd); ConvertDateToYMD(lgj->StartDateTicks().ToDate(), &start_ymd);
YearMonthDay join_ymd; YearMonthDay join_ymd;
ConvertDateToYMD(lgj->JoinDateTicks() / DAY_TICKS, &join_ymd); ConvertDateToYMD(lgj->JoinDateTicks().ToDate(), &join_ymd);
IConsolePrintF(CC_DEFAULT, " Job: %5u, nodes: %u, cost: " OTTD_PRINTF64U ", start: (%u, %4i-%02i-%02i, %i), end: (%u, %4i-%02i-%02i, %i), duration: %u", IConsolePrintF(CC_DEFAULT, " Job: %5u, nodes: %u, cost: " OTTD_PRINTF64U ", start: (%u, %4i-%02i-%02i, %i), end: (%u, %4i-%02i-%02i, %i), duration: %u",
lgj->index, lgj->Graph().Size(), lgj->Graph().CalculateCostEstimate(), lgj->index, lgj->Graph().Size(), lgj->Graph().CalculateCostEstimate(),
lgj->StartDateTicks(), start_ymd.year, start_ymd.month + 1, start_ymd.day, lgj->StartDateTicks() % DAY_TICKS, lgj->StartDateTicks().base(), start_ymd.year, start_ymd.month + 1, start_ymd.day, lgj->StartDateTicks().ToDateFractRemainder(),
lgj->JoinDateTicks(), join_ymd.year, join_ymd.month + 1, join_ymd.day, lgj->JoinDateTicks() % DAY_TICKS, lgj->JoinDateTicks().base(), join_ymd.year, join_ymd.month + 1, join_ymd.day, lgj->JoinDateTicks().ToDateFractRemainder(),
lgj->JoinDateTicks() - lgj->StartDateTicks()); (lgj->JoinDateTicks() - lgj->StartDateTicks()).base());
} }
return true; return true;
} }
@ -3686,20 +3686,20 @@ DEF_CONSOLE_CMD(ConIfDay)
DEF_CONSOLE_CMD(ConIfHour) DEF_CONSOLE_CMD(ConIfHour)
{ {
Minutes minutes = _scaled_date_ticks / _settings_time.ticks_per_minute + _settings_time.clock_offset; TickMinutes minutes = _settings_time.NowInTickMinutes();
return ConConditionalCommon(argc, argv, MINUTES_HOUR(minutes), "the current hour (in game, assuming time is in minutes)", "if_hour"); return ConConditionalCommon(argc, argv, minutes.ClockHour(), "the current hour (in game, assuming time is in minutes)", "if_hour");
} }
DEF_CONSOLE_CMD(ConIfMinute) DEF_CONSOLE_CMD(ConIfMinute)
{ {
Minutes minutes = _scaled_date_ticks / _settings_time.ticks_per_minute + _settings_time.clock_offset; TickMinutes minutes = _settings_time.NowInTickMinutes();
return ConConditionalCommon(argc, argv, MINUTES_MINUTE(minutes), "the current minute (in game, assuming time is in minutes)", "if_minute"); return ConConditionalCommon(argc, argv, minutes.ClockMinute(), "the current minute (in game, assuming time is in minutes)", "if_minute");
} }
DEF_CONSOLE_CMD(ConIfHourMinute) DEF_CONSOLE_CMD(ConIfHourMinute)
{ {
Minutes minutes = _scaled_date_ticks / _settings_time.ticks_per_minute + _settings_time.clock_offset; TickMinutes minutes = _settings_time.NowInTickMinutes();
return ConConditionalCommon(argc, argv, (MINUTES_HOUR(minutes) * 100) + MINUTES_MINUTE(minutes), "the current hour and minute 0000 - 2359 (in game, assuming time is in minutes)", "if_hour_minute"); return ConConditionalCommon(argc, argv, minutes.ClockHHMM(), "the current hour and minute 0000 - 2359 (in game, assuming time is in minutes)", "if_hour_minute");
} }
#ifdef _DEBUG #ifdef _DEBUG

@ -44,7 +44,7 @@ inline bool ShouldLogUpdateStateChecksum()
return _networking && (!_network_server || (NetworkClientSocket::IsValidID(0) && NetworkClientSocket::Get(0)->status != NetworkClientSocket::STATUS_INACTIVE)); return _networking && (!_network_server || (NetworkClientSocket::IsValidID(0) && NetworkClientSocket::Get(0)->status != NetworkClientSocket::STATUS_INACTIVE));
} }
# define DEBUG_UPDATESTATECHECKSUM(str, ...) if (ShouldLogUpdateStateChecksum()) DEBUG(statecsum, 0, "date{%08x; %02x; %02x}; %04x; %02x; " OTTD_PRINTFHEX64PAD "; %s:%d " str, \ # define DEBUG_UPDATESTATECHECKSUM(str, ...) if (ShouldLogUpdateStateChecksum()) DEBUG(statecsum, 0, "date{%08x; %02x; %02x}; %04x; %02x; " OTTD_PRINTFHEX64PAD "; %s:%d " str, \
_date, _date_fract, _tick_skip_counter, _frame_counter, (byte)_current_company, _state_checksum.state, __FILE__, __LINE__, __VA_ARGS__); _date.base(), _date_fract, _tick_skip_counter, _frame_counter, (byte)_current_company, _state_checksum.state, __FILE__, __LINE__, __VA_ARGS__);
#else #else
# define DEBUG_UPDATESTATECHECKSUM(str, ...) # define DEBUG_UPDATESTATECHECKSUM(str, ...)
#endif /* RANDOM_DEBUG */ #endif /* RANDOM_DEBUG */

@ -72,7 +72,7 @@ void SetRandomSeed(uint32 seed)
uint32 DoRandom(int line, const char *file) uint32 DoRandom(int line, const char *file)
{ {
if (_networking && (!_network_server || (NetworkClientSocket::IsValidID(0) && NetworkClientSocket::Get(0)->status != NetworkClientSocket::STATUS_INACTIVE))) { if (_networking && (!_network_server || (NetworkClientSocket::IsValidID(0) && NetworkClientSocket::Get(0)->status != NetworkClientSocket::STATUS_INACTIVE))) {
DEBUG(random, 0, "date{%08x; %02x; %02x}; %04x; %02x; %s:%d", _date, _date_fract, _tick_skip_counter, _frame_counter, (byte)_current_company, file, line); DEBUG(random, 0, "date{%08x; %02x; %02x}; %04x; %02x; %s:%d", _date.base(), _date_fract, _tick_skip_counter, _frame_counter, (byte)_current_company, file, line);
} }
return _random.Next(); return _random.Next();

@ -44,15 +44,15 @@ extern void ClearOutOfDateSignalSpeedRestrictions();
void CheckScaledDateTicksWrap() void CheckScaledDateTicksWrap()
{ {
DateTicksScaled tick_adjust = 0; DateTicksScaledDelta tick_adjust = 0;
auto get_tick_adjust = [&](DateTicksScaled target) { auto get_tick_adjust = [&](DateTicksScaled target) {
int32 rounding = _settings_time.time_in_minutes * 1440; int32 rounding = _settings_time.time_in_minutes * 1440;
return target - (target % rounding); return target.AsDelta() - (target.base() % rounding);
}; };
if (_scaled_date_ticks >= ((int64)1 << 60)) { if (_scaled_date_ticks >= ((int64)1 << 60)) {
tick_adjust = get_tick_adjust(_scaled_date_ticks); tick_adjust = get_tick_adjust(_scaled_date_ticks);
} else if (_scaled_date_ticks <= -((int64)1 << 60)) { } else if (_scaled_date_ticks <= -((int64)1 << 60)) {
tick_adjust = -get_tick_adjust(-_scaled_date_ticks); tick_adjust = -get_tick_adjust(-(_scaled_date_ticks.base()));
} else { } else {
return; return;
} }
@ -60,13 +60,13 @@ void CheckScaledDateTicksWrap()
_scaled_date_ticks_offset -= tick_adjust; _scaled_date_ticks_offset -= tick_adjust;
_scaled_date_ticks -= tick_adjust; _scaled_date_ticks -= tick_adjust;
extern void AdjustAllSignalSpeedRestrictionTickValues(DateTicksScaled delta); extern void AdjustAllSignalSpeedRestrictionTickValues(DateTicksScaledDelta delta);
AdjustAllSignalSpeedRestrictionTickValues(-tick_adjust); AdjustAllSignalSpeedRestrictionTickValues(-tick_adjust);
extern void AdjustVehicleScaledTickBase(int64 delta); extern void AdjustVehicleScaledTickBase(DateTicksScaledDelta delta);
AdjustVehicleScaledTickBase(-tick_adjust); AdjustVehicleScaledTickBase(-tick_adjust);
extern void AdjustLinkGraphScaledTickBase(int64 delta); extern void AdjustLinkGraphScaledTickBase(DateTicksScaledDelta delta);
AdjustLinkGraphScaledTickBase(-tick_adjust); AdjustLinkGraphScaledTickBase(-tick_adjust);
} }
@ -106,7 +106,7 @@ void SetDate(Date date, DateFract fract, bool preserve_scaled_ticks)
void SetScaledTickVariables() void SetScaledTickVariables()
{ {
_scaled_date_ticks = ((((DateTicksScaled)_date * DAY_TICKS) + _date_fract) * _settings_game.economy.day_length_factor) + _tick_skip_counter + _scaled_date_ticks_offset; _scaled_date_ticks = ((int64)(DateToDateTicks(_date, _date_fract).base()) * _settings_game.economy.day_length_factor) + _tick_skip_counter + _scaled_date_ticks_offset;
} }
#define M(a, b) ((a << 5) | b) #define M(a, b) ((a << 5) | b)
@ -160,8 +160,8 @@ void ConvertDateToYMD(Date date, YearMonthDay *ymd)
*/ */
/* There are 97 leap years in 400 years */ /* There are 97 leap years in 400 years */
Year yr = 400 * (date / (DAYS_IN_YEAR * 400 + 97)); Year yr = 400 * (date.base() / (DAYS_IN_YEAR * 400 + 97));
int rem = date % (DAYS_IN_YEAR * 400 + 97); int rem = date.base() % (DAYS_IN_YEAR * 400 + 97);
uint16 x; uint16 x;
if (rem >= DAYS_IN_YEAR * 100 + 25) { if (rem >= DAYS_IN_YEAR * 100 + 25) {
@ -216,7 +216,7 @@ Date ConvertYMDToDate(Year year, Month month, Day day)
/* Account for the missing of the 29th of February in non-leap years */ /* Account for the missing of the 29th of February in non-leap years */
if (!IsLeapYear(year) && days >= ACCUM_MAR) days--; if (!IsLeapYear(year) && days >= ACCUM_MAR) days--;
return DAYS_TILL(year) + days; return DateAtStartOfYear(year) + days;
} }
/** Functions used by the IncreaseDate function */ /** Functions used by the IncreaseDate function */

@ -38,7 +38,7 @@ inline Date ConvertYMDToDate(const YearMonthDay &ymd)
return ConvertYMDToDate(ymd.year, ymd.month, ymd.day); return ConvertYMDToDate(ymd.year, ymd.month, ymd.day);
} }
#define _cur_year (static_cast<Year>(_cur_date_ymd.year)) #define _cur_year (_cur_date_ymd.year)
/** /**
* Checks whether the given year is a leap year or not. * Checks whether the given year is a leap year or not.
@ -52,22 +52,22 @@ static inline bool IsLeapYear(Year yr)
static inline Date ScaledDateTicksToDate(DateTicksScaled ticks) static inline Date ScaledDateTicksToDate(DateTicksScaled ticks)
{ {
return (ticks - _scaled_date_ticks_offset) / (DAY_TICKS * _settings_game.economy.day_length_factor); return (ticks.base() - _scaled_date_ticks_offset.base()) / (DAY_TICKS * _settings_game.economy.day_length_factor);
} }
static inline DateTicksScaled DateToScaledDateTicks(Date date) static inline DateTicksScaled DateToScaledDateTicks(Date date)
{ {
return ((int64)date * DAY_TICKS * _settings_game.economy.day_length_factor) + _scaled_date_ticks_offset; return ((int64)date.base() * DAY_TICKS * _settings_game.economy.day_length_factor) + _scaled_date_ticks_offset.base();
} }
static inline DateTicks ScaledDateTicksToDateTicks(DateTicksScaled ticks) static inline DateTicks ScaledDateTicksToDateTicks(DateTicksScaled ticks)
{ {
return (ticks - _scaled_date_ticks_offset) / _settings_game.economy.day_length_factor; return (ticks.base() - _scaled_date_ticks_offset.base()) / _settings_game.economy.day_length_factor;
} }
static inline DateTicksScaled DateTicksToScaledDateTicks(DateTicks date_ticks) static inline DateTicksScaled DateTicksToScaledDateTicks(DateTicks date_ticks)
{ {
return ((int64)date_ticks * _settings_game.economy.day_length_factor) + _scaled_date_ticks_offset; return ((int64)date_ticks.base() * _settings_game.economy.day_length_factor) + _scaled_date_ticks_offset.base();
} }
/** /**
@ -77,21 +77,27 @@ static inline DateTicksScaled DateTicksToScaledDateTicks(DateTicks date_ticks)
*/ */
static constexpr Year DateToYear(Date date) static constexpr Year DateToYear(Date date)
{ {
return date / DAYS_IN_LEAP_YEAR; return date.base() / DAYS_IN_LEAP_YEAR;
} }
/** static constexpr Year DateDeltaToYears(DateDelta date)
* Calculate the date of the first day of a given year. {
* @param year the year to get the first day of. return date.base() / DAYS_IN_LEAP_YEAR;
* @return the date. }
*/
static constexpr Date DateAtStartOfYear(Year year) static constexpr DateTicks DateToDateTicks(Date date, DateFract fract = 0)
{ {
int32 year_as_int = year; return (date.base() * DAY_TICKS) + fract;
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. */ static constexpr DateTicksDelta DateDeltaToDateTicksDelta(DateDelta date, DateFract fract = 0)
return (365 * year_as_int) + number_of_leap_years; {
return (date.base() * DAY_TICKS) + fract;
}
static inline DateTicks NowDateTicks()
{
return DateToDateTicks(_date, _date_fract);
} }
#endif /* DATE_FUNC_H */ #endif /* DATE_FUNC_H */

@ -181,13 +181,13 @@ struct SetDateWindow : Window {
struct SetMinutesWindow : SetDateWindow struct SetMinutesWindow : SetDateWindow
{ {
Minutes minutes; TickMinutes minutes;
/** Constructor. */ /** Constructor. */
SetMinutesWindow(WindowDesc *desc, WindowNumber window_number, Window *parent, DateTicksScaled initial_date, Year min_year, Year max_year, SetMinutesWindow(WindowDesc *desc, WindowNumber window_number, Window *parent, DateTicksScaled initial_date, Year min_year, Year max_year,
SetDateCallback *callback, StringID button_text, StringID button_tooltip) : SetDateCallback *callback, StringID button_text, StringID button_tooltip) :
SetDateWindow(desc, window_number, parent, initial_date, min_year, max_year, callback, button_text, button_tooltip), SetDateWindow(desc, window_number, parent, 0, min_year, max_year, callback, button_text, button_tooltip),
minutes(initial_date / _settings_time.ticks_per_minute) minutes(_settings_time.ToTickMinutes(initial_date))
{ {
} }
@ -208,7 +208,7 @@ struct SetMinutesWindow : SetDateWindow
SetDParam(0, i); SetDParam(0, i);
list.emplace_back(new DropDownListStringItem(STR_JUST_INT, i, false)); list.emplace_back(new DropDownListStringItem(STR_JUST_INT, i, false));
} }
selected = MINUTES_MINUTE(minutes); selected = this->minutes.ClockMinute();
break; break;
case WID_SD_MONTH: case WID_SD_MONTH:
@ -216,7 +216,7 @@ struct SetMinutesWindow : SetDateWindow
SetDParam(0, i); SetDParam(0, i);
list.emplace_back(new DropDownListStringItem(STR_JUST_INT, i, false)); list.emplace_back(new DropDownListStringItem(STR_JUST_INT, i, false));
} }
selected = MINUTES_HOUR(minutes); selected = this->minutes.ClockHour();
break; break;
} }
@ -253,8 +253,8 @@ struct SetMinutesWindow : SetDateWindow
virtual void SetStringParameters(int widget) const override virtual void SetStringParameters(int widget) const override
{ {
switch (widget) { switch (widget) {
case WID_SD_DAY: SetDParam(0, MINUTES_MINUTE(minutes)); break; case WID_SD_DAY: SetDParam(0, this->minutes.ClockMinute()); break;
case WID_SD_MONTH: SetDParam(0, MINUTES_HOUR(minutes)); break; case WID_SD_MONTH: SetDParam(0, this->minutes.ClockHour()); break;
} }
} }
@ -269,7 +269,7 @@ struct SetMinutesWindow : SetDateWindow
case WID_SD_SET_DATE: case WID_SD_SET_DATE:
if (this->callback != nullptr) { if (this->callback != nullptr) {
this->callback(this, ((DateTicks)minutes - _settings_time.clock_offset) * (DateTicksScaled)_settings_time.ticks_per_minute); this->callback(this, _settings_time.FromTickMinutes(this->minutes));
} }
this->Close(); this->Close();
break; break;
@ -278,19 +278,23 @@ struct SetMinutesWindow : SetDateWindow
virtual void OnDropdownSelect(int widget, int index) override virtual void OnDropdownSelect(int widget, int index) override
{ {
Minutes current = 0; const TickMinutes now = _settings_time.NowInTickMinutes();
TickMinutes current = 0;
switch (widget) { switch (widget) {
case WID_SD_DAY: case WID_SD_DAY:
current = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), MINUTES_HOUR(minutes), index); current = now.ToSameDayClockTime(now.ClockHour(), index);
break; break;
case WID_SD_MONTH: case WID_SD_MONTH:
current = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), index, MINUTES_MINUTE(minutes)); current = now.ToSameDayClockTime(index, now.ClockMinute());
break; break;
default:
return;
} }
if (current < (CURRENT_MINUTE - 60)) current += 60 * 24; if (current < (now - 60)) current += 60 * 24;
minutes = current; this->minutes = current;
this->SetDirty(); this->SetDirty();
} }

@ -10,16 +10,8 @@
#ifndef DATE_TYPE_H #ifndef DATE_TYPE_H
#define DATE_TYPE_H #define DATE_TYPE_H
typedef int32 Date; ///< The type to store our dates in #include "core/strong_typedef_type.hpp"
typedef uint16 DateFract; ///< The fraction of a date we're in, i.e. the number of ticks since the last date changeover #include "core/math_func.hpp"
typedef int32 Ticks; ///< The type to store ticks in
typedef int32 DateTicks; ///< The type to store dates in when tick-precision is required
typedef int64 DateTicksScaled; ///< The type to store dates scaled by the day length factor in when tick-precision is required
typedef int64 Minutes; ///< The type to store minutes in
typedef int32 Year; ///< Type for the year, note: 0 based, i.e. starts at the year 0.
typedef uint8 Month; ///< Type for the month, note: 0 based, i.e. 0 = January, 11 = December.
typedef uint8 Day; ///< Type for the day of the month, note: 1 based, first day of a month is 1.
/** /**
* 1 day is 74 ticks; _date_fract used to be uint16 and incremented by 885. On * 1 day is 74 ticks; _date_fract used to be uint16 and incremented by 885. On
@ -34,6 +26,104 @@ static const int MONTHS_IN_YEAR = 12; ///< months per year
static const int SECONDS_PER_DAY = 2; ///< approximate seconds per day, not for precise calculations static const int SECONDS_PER_DAY = 2; ///< approximate seconds per day, not for precise calculations
typedef uint16 DateFract; ///< The fraction of a date we're in, i.e. the number of ticks since the last date changeover
typedef int32 Ticks; ///< The type to store ticks in
typedef int32 Year; ///< Type for the year, note: 0 based, i.e. starts at the year 0.
typedef uint8 Month; ///< Type for the month, note: 0 based, i.e. 0 = January, 11 = December.
typedef uint8 Day; ///< Type for the day of the month, note: 1 based, first day of a month is 1.
/* The type to store our dates in */
using DateDelta = StrongType::Typedef<int32_t, struct DateDeltaTag, StrongType::Compare, StrongType::IntegerScalable>;
using Date = StrongType::Typedef<int32_t, struct DateTag, StrongType::Compare, StrongType::IntegerDelta<DateDelta>>;
/* Mixin for DateTicks */
struct DateTicksOperations {
template <typename TType, typename TBaseType>
struct mixin {
private:
TBaseType GetBase() const { return static_cast<const TType &>(*this).base(); }
public:
Date ToDate() const { return this->GetBase() / DAY_TICKS; }
DateFract ToDateFractRemainder() const { return this->GetBase() % DAY_TICKS; }
};
};
/* The type to store dates in when tick-precision is required */
using DateTicksDelta = StrongType::Typedef<int32_t, struct DateTicksDeltaTag, StrongType::Compare, StrongType::IntegerScalable>;
using DateTicks = StrongType::Typedef<int32_t, struct DateTicksTag, StrongType::Compare, StrongType::IntegerDelta<DateTicksDelta>, DateTicksOperations>;
/* Mixin for DateTicksScaledDelta */
struct DateTicksScaledDeltaOperations {
template <typename TType, typename TBaseType>
struct mixin {
private:
TBaseType GetBase() const { return static_cast<const TType &>(*this).base(); }
public:
Ticks AsTicks() const { return (Ticks)this->GetBase(); }
};
};
/* The type to store dates scaled by the day length factor in when tick-precision is required */
using DateTicksScaledDelta = StrongType::Typedef<int64_t, struct DateTicksScaledDeltaTag, StrongType::Compare, StrongType::IntegerScalable, DateTicksScaledDeltaOperations>;
using DateTicksScaled = StrongType::Typedef<int64_t, struct DateTicksScaledTag, StrongType::Compare, StrongType::IntegerDelta<DateTicksScaledDelta>>;
/* Mixin for TickMinutes, ClockFaceMinutes */
struct MinuteOperations {
template <typename TType, typename TBaseType>
struct mixin {
private:
TBaseType GetBase() const { return static_cast<const TType &>(*this).base(); }
public:
int ClockMinute() const { return this->GetBase() % 60; }
int ClockHour() const { return (this->GetBase() / 60) % 24; }
int ClockHHMM() const { return (this->ClockHour() * 100) + this->ClockMinute(); }
};
};
/* Mixin for ClockFaceMinutes */
struct ClockFaceMinuteOperations {
template <typename TType, typename TBaseType>
struct mixin {
static constexpr TType FromClockFace(int hours, int minutes)
{
return (TBaseType(hours) * 60) + minutes;
}
};
};
/* The type to store general clock-face minutes in (i.e. 0..1440) */
using ClockFaceMinutes = StrongType::Typedef<int, struct ClockFaceMinutesTag, StrongType::Compare, StrongType::Integer, MinuteOperations, ClockFaceMinuteOperations>;
/* Mixin for TickMinutes */
struct TickMinuteOperations {
template <typename TType, typename TBaseType>
struct mixin {
private:
TBaseType GetBase() const { return static_cast<const TType &>(*this).base(); }
public:
TType ToSameDayClockTime(int hour, int minute) const
{
TBaseType day = DivTowardsNegativeInf<TBaseType>(this->GetBase(), 1440);
return (day * 1440) + (hour * 60) + minute;
}
ClockFaceMinutes ToClockFaceMinutes() const
{
TBaseType minutes = this->GetBase() % 1440;
if (minutes < 0) minutes += 1440;
return minutes;
}
};
};
/* The type to store DateTicksScaled-based minutes in */
using TickMinutes = StrongType::Typedef<int64_t, struct TickMinutesTag, StrongType::Compare, StrongType::Integer, MinuteOperations, TickMinuteOperations>;
#define DATE_UNIT_SIZE (_settings_time.time_in_minutes ? _settings_time.ticks_per_minute : (DAY_TICKS * _settings_game.economy.day_length_factor)) #define DATE_UNIT_SIZE (_settings_time.time_in_minutes ? _settings_time.ticks_per_minute : (DAY_TICKS * _settings_game.economy.day_length_factor))
static const int STATION_RATING_TICKS = 185; ///< cycle duration for updating station rating static const int STATION_RATING_TICKS = 185; ///< cycle duration for updating station rating
@ -52,48 +142,41 @@ static const int INDUSTRY_CUT_TREE_TICKS = INDUSTRY_PRODUCE_TICKS * 2; ///< cyc
*/ */
/** The minimum starting year/base year of the original TTD */ /** The minimum starting year/base year of the original TTD */
static const Year ORIGINAL_BASE_YEAR = 1920; static constexpr Year ORIGINAL_BASE_YEAR = 1920;
/** The original ending year */ /** The original ending year */
static const Year ORIGINAL_END_YEAR = 2051; static constexpr Year ORIGINAL_END_YEAR = 2051;
/** The maximum year of the original TTD */ /** The maximum year of the original TTD */
static const Year ORIGINAL_MAX_YEAR = 2090; static constexpr Year ORIGINAL_MAX_YEAR = 2090;
/**
* Calculate the number of leap years till a given year.
*
* Each passed leap year adds one day to the 'day count'.
*
* A special case for the year 0 as no year has been passed,
* but '(year - 1) / 4' does not yield '-1' to counteract the
* '+1' at the end of the formula as divisions round to zero.
*
* @param year the year to get the leap years till.
* @return the number of leap years.
*/
#define LEAP_YEARS_TILL(year) ((year) == 0 ? 0 : ((year) - 1) / 4 - ((year) - 1) / 100 + ((year) - 1) / 400 + 1)
/** /**
* Calculate the date of the first day of a given year. * Calculate the date of the first day of a given year.
* @param year the year to get the first day of. * @param year the year to get the first day of.
* @return the date. * @return the date.
*/ */
#define DAYS_TILL(year) (DAYS_IN_YEAR * (year) + LEAP_YEARS_TILL(year)) static constexpr Date DateAtStartOfYear(Year year)
{
int32 year_as_int = year;
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;
}
/** /**
* The offset in days from the '_date == 0' till * The offset in days from the '_date == 0' till
* 'ConvertYMDToDate(ORIGINAL_BASE_YEAR, 0, 1)' * 'ConvertYMDToDate(ORIGINAL_BASE_YEAR, 0, 1)'
*/ */
#define DAYS_TILL_ORIGINAL_BASE_YEAR DAYS_TILL(ORIGINAL_BASE_YEAR) static constexpr Date DAYS_TILL_ORIGINAL_BASE_YEAR = DateAtStartOfYear(ORIGINAL_BASE_YEAR);
static const Date MIN_DATE = 0; static constexpr Date MIN_DATE = 0;
/** The absolute minimum & maximum years in OTTD */ /** The absolute minimum & maximum years in OTTD */
static const Year MIN_YEAR = 0; static constexpr Year MIN_YEAR = 0;
/** The default starting year */ /** The default starting year */
static const Year DEF_START_YEAR = 1950; static constexpr Year DEF_START_YEAR = 1950;
/** The default scoring end year */ /** The default scoring end year */
static const Year DEF_END_YEAR = ORIGINAL_END_YEAR - 1; static constexpr Year DEF_END_YEAR = ORIGINAL_END_YEAR - 1;
/** /**
* MAX_YEAR, nicely rounded value of the number of years that can * MAX_YEAR, nicely rounded value of the number of years that can
@ -102,22 +185,7 @@ static const Year DEF_END_YEAR = ORIGINAL_END_YEAR - 1;
static const Year MAX_YEAR = 5000000; static const Year MAX_YEAR = 5000000;
/** The number of days till the last day */ /** The number of days till the last day */
#define MAX_DAY (DAYS_TILL(MAX_YEAR + 1) - 1) static constexpr Date MAX_DATE = DateAtStartOfYear(MAX_YEAR + 1) - 1;
/** The day when converting to minutes */
#define MINUTES_DAY(minutes) (minutes / 1440)
/** The hour when converting to minutes */
#define MINUTES_HOUR(minutes) ((minutes / 60) % 24)
/** The day when converting to minutes */
#define MINUTES_MINUTE(minutes) (minutes % 60)
/** Convert minutes to a date */
#define MINUTES_DATE(day, hour, minute) ((day * 1440) + (hour * 60) + minute)
/** Get the current date in minutes */
#define CURRENT_MINUTE (_scaled_date_ticks / _settings_time.ticks_per_minute)
/** /**
* Data structure to convert between Date and triplet (year, month, and day). * Data structure to convert between Date and triplet (year, month, and day).
@ -129,8 +197,9 @@ struct YearMonthDay {
Day day; ///< Day (1..31) Day day; ///< Day (1..31)
}; };
static const Year INVALID_YEAR = -1; ///< Representation of an invalid year static constexpr Year INVALID_YEAR = -1; ///< Representation of an invalid year
static const Date INVALID_DATE = -1; ///< Representation of an invalid date static constexpr Date INVALID_DATE = -1; ///< Representation of an invalid date
static const Ticks INVALID_TICKS = -1; ///< Representation of an invalid number of ticks static constexpr DateTicks INVALID_DATE_TICKS = -1; ///< Representation of an invalid date ticks
static constexpr Ticks INVALID_TICKS = -1; ///< Representation of an invalid number of ticks
#endif /* DATE_TYPE_H */ #endif /* DATE_TYPE_H */

@ -43,15 +43,23 @@
typedef btree::btree_map<const DispatchSchedule *, btree::btree_set<DateTicksScaled>> schdispatch_cache_t; typedef btree::btree_map<const DispatchSchedule *, btree::btree_set<DateTicksScaled>> schdispatch_cache_t;
/** A scheduled order. */ /** A scheduled order. */
typedef struct OrderDate struct OrderDate {
{
const Order *order; ///< The order const Order *order; ///< The order
const Vehicle *v; ///< The vehicle carrying out the order const Vehicle *v; ///< The vehicle carrying out the order
DateTicks expected_date;///< The date on which the order is expected to complete Ticks expected_tick; ///< The tick on which the order is expected to complete
Ticks lateness; ///< How late this order is expected to finish Ticks lateness; ///< How late this order is expected to finish
DepartureStatus status; ///< Whether the vehicle has arrived to carry out the order yet DepartureStatus status; ///< Whether the vehicle has arrived to carry out the order yet
uint scheduled_waiting_time; ///< Scheduled waiting time if scheduled dispatch is used Ticks scheduled_waiting_time; ///< Scheduled waiting time if scheduled dispatch is used
} OrderDate;
inline Ticks EffectiveWaitingTime() const
{
if (this->scheduled_waiting_time > 0) {
return this->scheduled_waiting_time;
} else {
return this->order->GetWaitTime();
}
}
};
static bool IsDeparture(const Order *order, StationID station) { static bool IsDeparture(const Order *order, StationID station) {
return (order->GetType() == OT_GOTO_STATION && return (order->GetType() == OT_GOTO_STATION &&
@ -93,7 +101,7 @@ static uint8 GetDepartureConditionalOrderMode(const Order *order, const Vehicle
return _settings_client.gui.departure_conditionals; return _settings_client.gui.departure_conditionals;
} }
static bool VehicleSetNextDepartureTime(DateTicks *previous_departure, uint *waiting_time, const DateTicksScaled date_only_scaled, const Vehicle *v, const Order *order, bool arrived_at_timing_point, schdispatch_cache_t &dept_schedule_last) static bool VehicleSetNextDepartureTime(Ticks *previous_departure, Ticks *waiting_time, const DateTicksScaled date_ticks_base, const Vehicle *v, const Order *order, bool arrived_at_timing_point, schdispatch_cache_t &dept_schedule_last)
{ {
if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) { if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) {
auto is_current_implicit_order = [&v](const Order *o) -> bool { auto is_current_implicit_order = [&v](const Order *o) -> bool {
@ -115,7 +123,7 @@ static bool VehicleSetNextDepartureTime(DateTicks *previous_departure, uint *wai
DateTicksScaled earliest_departure = begin_time + ds.GetScheduledDispatchLastDispatch(); DateTicksScaled earliest_departure = begin_time + ds.GetScheduledDispatchLastDispatch();
/* Earliest possible departure according to vehicle current timetable */ /* Earliest possible departure according to vehicle current timetable */
const uint32 ready_to_depart_time = date_only_scaled + *previous_departure + order->GetTravelTime() + order->GetTimetabledWait(); const DateTicksScaled ready_to_depart_time = date_ticks_base + *previous_departure + order->GetTravelTime() + order->GetTimetabledWait();
if (earliest_departure + max_delay < ready_to_depart_time) { if (earliest_departure + max_delay < ready_to_depart_time) {
earliest_departure = ready_to_depart_time - max_delay - 1; earliest_departure = ready_to_depart_time - max_delay - 1;
/* -1 because this number is actually a moment before actual departure */ /* -1 because this number is actually a moment before actual departure */
@ -141,8 +149,8 @@ static bool VehicleSetNextDepartureTime(DateTicks *previous_departure, uint *wai
} }
} }
*waiting_time = actual_departure - date_only_scaled - *previous_departure - order->GetTravelTime(); *waiting_time = (actual_departure - date_ticks_base).AsTicks() - *previous_departure - order->GetTravelTime();
*previous_departure = actual_departure - date_only_scaled; *previous_departure = (actual_departure - date_ticks_base).AsTicks();
slot_cache.insert(actual_departure); slot_cache.insert(actual_departure);
/* Return true means that vehicle lateness should be clear from this point onward */ /* Return true means that vehicle lateness should be clear from this point onward */
@ -185,18 +193,18 @@ static void ScheduledDispatchDepartureLocalFix(DepartureList *departure_list)
/* Sort the departure list by arrival time */ /* Sort the departure list by arrival time */
std::sort(d_list.begin(), d_list.end(), [](const Departure * const &a, const Departure * const &b) -> bool { std::sort(d_list.begin(), d_list.end(), [](const Departure * const &a, const Departure * const &b) -> bool {
DateTicksScaled arr_a = a->scheduled_date - (a->scheduled_waiting_time > 0 ? a->scheduled_waiting_time : a->order->GetWaitTime()); DateTicksScaled arr_a = a->scheduled_date - a->EffectiveWaitingTime();
DateTicksScaled arr_b = b->scheduled_date - (b->scheduled_waiting_time > 0 ? b->scheduled_waiting_time : b->order->GetWaitTime()); DateTicksScaled arr_b = b->scheduled_date - b->EffectiveWaitingTime();
return arr_a < arr_b; return arr_a < arr_b;
}); });
/* Re-assign them sequentially */ /* Re-assign them sequentially */
for (size_t i = 0; i < d_list.size(); i++) { for (size_t i = 0; i < d_list.size(); i++) {
const DateTicksScaled arrival = d_list[i]->scheduled_date - (d_list[i]->scheduled_waiting_time > 0 ? d_list[i]->scheduled_waiting_time : d_list[i]->order->GetWaitTime()); const DateTicksScaled arrival = d_list[i]->scheduled_date - d_list[i]->EffectiveWaitingTime();
d_list[i]->scheduled_waiting_time = departure_time_list[i] - arrival; d_list[i]->scheduled_waiting_time = (departure_time_list[i] - arrival).AsTicks();
d_list[i]->scheduled_date = departure_time_list[i]; d_list[i]->scheduled_date = departure_time_list[i];
if (d_list[i]->scheduled_waiting_time == d_list[i]->order->GetWaitTime()) { if (d_list[i]->scheduled_waiting_time == (Ticks)d_list[i]->order->GetWaitTime()) {
d_list[i]->scheduled_waiting_time = 0; d_list[i]->scheduled_waiting_time = 0;
} }
} }
@ -237,12 +245,11 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
std::vector<OrderDate*> next_orders; std::vector<OrderDate*> next_orders;
/* The maximum possible date for departures to be scheduled to occur. */ /* The maximum possible date for departures to be scheduled to occur. */
DateTicksScaled max_date = GetDeparturesMaxTicksAhead(); const Ticks max_ticks = GetDeparturesMaxTicksAhead();
DateTicksScaled date_only_scaled = DateToScaledDateTicks(_date); const DateTicksScaled date_ticks_base = _scaled_date_ticks;
DateTicksScaled date_fract_scaled = ((DateTicksScaled)_date_fract * _settings_game.economy.day_length_factor) + _tick_skip_counter;
/* The scheduled order in next_orders with the earliest expected_date field. */ /* The scheduled order in next_orders with the earliest expected_tick field. */
OrderDate *least_order = nullptr; OrderDate *least_order = nullptr;
/* Cache for scheduled departure time */ /* Cache for scheduled departure time */
@ -272,20 +279,20 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
const Order *order = v->GetOrder(v->cur_implicit_order_index % v->GetNumOrders()); const Order *order = v->GetOrder(v->cur_implicit_order_index % v->GetNumOrders());
if (order == nullptr) continue; if (order == nullptr) continue;
DateTicks start_date = date_fract_scaled - v->current_order_time; Ticks start_ticks = -((Ticks)v->current_order_time);
if (v->cur_timetable_order_index != INVALID_VEH_ORDER_ID && v->cur_timetable_order_index != v->cur_real_order_index) { if (v->cur_timetable_order_index != INVALID_VEH_ORDER_ID && v->cur_timetable_order_index != v->cur_real_order_index) {
/* vehicle is taking a conditional order branch, adjust start time to compensate */ /* vehicle is taking a conditional order branch, adjust start time to compensate */
const Order *real_current_order = v->GetOrder(v->cur_real_order_index); const Order *real_current_order = v->GetOrder(v->cur_real_order_index);
const Order *real_timetable_order = v->GetOrder(v->cur_timetable_order_index); const Order *real_timetable_order = v->GetOrder(v->cur_timetable_order_index);
if (real_timetable_order->IsType(OT_CONDITIONAL)) { if (real_timetable_order->IsType(OT_CONDITIONAL)) {
start_date += (real_timetable_order->GetWaitTime() - real_current_order->GetTravelTime()); start_ticks += (real_timetable_order->GetWaitTime() - real_current_order->GetTravelTime());
} else { } else {
/* This can also occur with implicit orders, when there are no real orders, do nothing */ /* This can also occur with implicit orders, when there are no real orders, do nothing */
} }
} }
DepartureStatus status = D_TRAVELLING; DepartureStatus status = D_TRAVELLING;
bool should_reset_lateness = false; bool should_reset_lateness = false;
uint waiting_time = 0; Ticks waiting_time = 0;
/* If the vehicle is stopped in a depot, ignore it. */ /* If the vehicle is stopped in a depot, ignore it. */
if (v->IsStoppedInDepot()) { if (v->IsStoppedInDepot()) {
@ -301,20 +308,20 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
if (v->current_order.IsAnyLoadingType() || v->current_order.IsType(OT_WAITING)) { if (v->current_order.IsAnyLoadingType() || v->current_order.IsType(OT_WAITING)) {
/* Account for the vehicle having reached the current order and being in the loading phase. */ /* Account for the vehicle having reached the current order and being in the loading phase. */
status = D_ARRIVED; status = D_ARRIVED;
start_date -= order->GetTravelTime() + ((v->lateness_counter < 0) ? v->lateness_counter : 0); start_ticks -= order->GetTravelTime() + ((v->lateness_counter < 0) ? v->lateness_counter : 0);
require_travel_time = false; require_travel_time = false;
} }
/* Loop through the vehicle's orders until we've found a suitable order or we've determined that no such order exists. */ /* Loop through the vehicle's orders until we've found a suitable order or we've determined that no such order exists. */
/* We only need to consider each order at most once. */ /* We only need to consider each order at most once. */
for (int i = v->GetNumOrders(); i > 0; --i) { for (int i = v->GetNumOrders(); i > 0; --i) {
if (VehicleSetNextDepartureTime(&start_date, &waiting_time, date_only_scaled, v, order, status == D_ARRIVED, schdispatch_last_planned_dispatch)) { if (VehicleSetNextDepartureTime(&start_ticks, &waiting_time, date_ticks_base, v, order, status == D_ARRIVED, schdispatch_last_planned_dispatch)) {
should_reset_lateness = true; should_reset_lateness = true;
} }
/* If the order is a conditional branch, handle it. */ /* If the order is a conditional branch, handle it. */
if (order->IsType(OT_CONDITIONAL)) { if (order->IsType(OT_CONDITIONAL)) {
switch(GetDepartureConditionalOrderMode(order, v, start_date + date_only_scaled)) { switch(GetDepartureConditionalOrderMode(order, v, date_ticks_base + start_ticks)) {
case 0: { case 0: {
/* Give up */ /* Give up */
break; break;
@ -329,7 +336,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
break; break;
} }
start_date -= order->GetTravelTime(); start_ticks -= order->GetTravelTime();
require_travel_time = false; require_travel_time = false;
continue; continue;
} }
@ -338,7 +345,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
if (status != D_CANCELLED) { if (status != D_CANCELLED) {
status = D_TRAVELLING; status = D_TRAVELLING;
} }
start_date -= order->GetWaitTime(); /* Added previously in VehicleSetNextDepartureTime */ start_ticks -= order->GetWaitTime(); /* Added previously in VehicleSetNextDepartureTime */
order = (order->next == nullptr) ? v->GetFirstOrder() : order->next; order = (order->next == nullptr) ? v->GetFirstOrder() : order->next;
require_travel_time = true; require_travel_time = true;
continue; continue;
@ -348,7 +355,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
} }
/* If the scheduled departure date is too far in the future, stop. */ /* If the scheduled departure date is too far in the future, stop. */
if (start_date - v->lateness_counter > max_date) { if (start_ticks - v->lateness_counter > max_ticks) {
break; break;
} }
@ -363,7 +370,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
(type == D_DEPARTURE && show_vehicles_via && IsVia(order, station)) || (type == D_DEPARTURE && show_vehicles_via && IsVia(order, station)) ||
(type == D_ARRIVAL && IsArrival(order, station))) { (type == D_ARRIVAL && IsArrival(order, station))) {
/* If the departure was scheduled to have already begun and has been cancelled, do not show it. */ /* If the departure was scheduled to have already begun and has been cancelled, do not show it. */
if (start_date < 0 && status == D_CANCELLED) { if (start_ticks < 0 && status == D_CANCELLED) {
break; break;
} }
@ -371,7 +378,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
od->order = order; od->order = order;
od->v = v; od->v = v;
/* We store the expected date for now, so that vehicles will be shown in order of expected time. */ /* We store the expected date for now, so that vehicles will be shown in order of expected time. */
od->expected_date = start_date; od->expected_tick = start_ticks;
od->lateness = v->lateness_counter > 0 ? v->lateness_counter : 0; od->lateness = v->lateness_counter > 0 ? v->lateness_counter : 0;
od->status = status; od->status = status;
od->scheduled_waiting_time = waiting_time; od->scheduled_waiting_time = waiting_time;
@ -383,15 +390,20 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
/* If we are early, use the scheduled date as the expected date. We also take lateness to be zero. */ /* If we are early, use the scheduled date as the expected date. We also take lateness to be zero. */
if (!should_reset_lateness && v->lateness_counter < 0 && !(v->current_order.IsAnyLoadingType() || v->current_order.IsType(OT_WAITING))) { if (!should_reset_lateness && v->lateness_counter < 0 && !(v->current_order.IsAnyLoadingType() || v->current_order.IsType(OT_WAITING))) {
od->expected_date -= v->lateness_counter; od->expected_tick -= v->lateness_counter;
} }
/* Update least_order if this is the current least order. */ /* Update least_order if this is the current least order. */
if (least_order == nullptr) { if (least_order == nullptr) {
least_order = od; least_order = od;
} else if (int(least_order->expected_date - least_order->lateness - (type == D_ARRIVAL ? (least_order->scheduled_waiting_time > 0 ? least_order->scheduled_waiting_time : least_order->order->GetWaitTime()) : 0)) > int(od->expected_date - od->lateness - (type == D_ARRIVAL ? (od->scheduled_waiting_time > 0 ? od->scheduled_waiting_time : od->order->GetWaitTime()) : 0))) { } else if (type == D_ARRIVAL) {
/* Somehow my compiler perform an unsigned comparition above so integer cast is required */ if ((least_order->expected_tick - least_order->lateness - least_order->EffectiveWaitingTime()) > (od->expected_tick - od->lateness - od->EffectiveWaitingTime())) {
least_order = od; least_order = od;
}
} else {
if ((least_order->expected_tick - least_order->lateness) > (od->expected_tick - od->lateness)) {
least_order = od;
}
} }
next_orders.push_back(od); next_orders.push_back(od);
@ -420,22 +432,22 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
for(int i = 10000; i > 0; --i) { for(int i = 10000; i > 0; --i) {
/* I should probably try to convince you that this loop always terminates regardless of the safeguard. */ /* I should probably try to convince you that this loop always terminates regardless of the safeguard. */
/* 1. next_orders contains at least one element. */ /* 1. next_orders contains at least one element. */
/* 2. The loop terminates if result->size() exceeds a fixed (for this loop) value, or if the least order's scheduled date is later than max_date. */ /* 2. The loop terminates if result->size() exceeds a fixed (for this loop) value, or if the least order's scheduled date is later than max_ticks. */
/* (We ignore the case that the least order's scheduled date has overflown, as it is a relative rather than absolute date.) */ /* (We ignore the case that the least order's scheduled date has overflown, as it is a relative rather than absolute date.) */
/* 3. Every time we loop round, either result->size() will have increased -OR- we will have increased the expected_date of one of the elements of next_orders. */ /* 3. Every time we loop round, either result->size() will have increased -OR- we will have increased the expected_tick of one of the elements of next_orders. */
/* 4. Therefore the loop must eventually terminate. */ /* 4. Therefore the loop must eventually terminate. */
/* least_order is the best candidate for the next departure. */ /* least_order is the best candidate for the next departure. */
/* First, we check if we can stop looking for departures yet. */ /* First, we check if we can stop looking for departures yet. */
if (result->size() >= _settings_client.gui.max_departures || if (result->size() >= _settings_client.gui.max_departures ||
least_order->expected_date - least_order->lateness > max_date) { least_order->expected_tick - least_order->lateness > max_ticks) {
break; break;
} }
/* We already know the least order and that it's a suitable departure, so make it into a departure. */ /* We already know the least order and that it's a suitable departure, so make it into a departure. */
Departure *d = new Departure(); Departure *d = new Departure();
d->scheduled_date = date_only_scaled + least_order->expected_date - least_order->lateness; d->scheduled_date = date_ticks_base + least_order->expected_tick - least_order->lateness;
d->lateness = least_order->lateness; d->lateness = least_order->lateness;
d->status = least_order->status; d->status = least_order->status;
d->vehicle = least_order->v; d->vehicle = least_order->v;
@ -751,7 +763,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
/* Go to the next order so we don't add the current order again. */ /* Go to the next order so we don't add the current order again. */
order = (order->next == nullptr) ? least_order->v->GetFirstOrder() : order->next; order = (order->next == nullptr) ? least_order->v->GetFirstOrder() : order->next;
if (VehicleSetNextDepartureTime(&least_order->expected_date, &least_order->scheduled_waiting_time, date_only_scaled, least_order->v, order, false, schdispatch_last_planned_dispatch)) { if (VehicleSetNextDepartureTime(&least_order->expected_tick, &least_order->scheduled_waiting_time, date_ticks_base, least_order->v, order, false, schdispatch_last_planned_dispatch)) {
least_order->lateness = 0; least_order->lateness = 0;
} }
@ -762,7 +774,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
for (int i = least_order->v->GetNumOrders(); i > 0; --i) { for (int i = least_order->v->GetNumOrders(); i > 0; --i) {
/* If the order is a conditional branch, handle it. */ /* If the order is a conditional branch, handle it. */
if (order->IsType(OT_CONDITIONAL)) { if (order->IsType(OT_CONDITIONAL)) {
switch(GetDepartureConditionalOrderMode(order, least_order->v, least_order->expected_date)) { switch(GetDepartureConditionalOrderMode(order, least_order->v, least_order->expected_tick)) {
case 0: { case 0: {
/* Give up */ /* Give up */
break; break;
@ -774,8 +786,8 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
break; break;
} }
least_order->expected_date -= order->GetTravelTime(); /* Added in next VehicleSetNextDepartureTime */ least_order->expected_tick -= order->GetTravelTime(); /* Added in next VehicleSetNextDepartureTime */
if (VehicleSetNextDepartureTime(&least_order->expected_date, &least_order->scheduled_waiting_time, date_only_scaled, least_order->v, order, false, schdispatch_last_planned_dispatch)) { if (VehicleSetNextDepartureTime(&least_order->expected_tick, &least_order->scheduled_waiting_time, date_ticks_base, least_order->v, order, false, schdispatch_last_planned_dispatch)) {
least_order->lateness = 0; least_order->lateness = 0;
} }
require_travel_time = false; require_travel_time = false;
@ -783,9 +795,9 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
} }
case 2: { case 2: {
/* Do not take the branch */ /* Do not take the branch */
least_order->expected_date -= order->GetWaitTime(); /* Added previously in VehicleSetNextDepartureTime */ least_order->expected_tick -= order->GetWaitTime(); /* Added previously in VehicleSetNextDepartureTime */
order = (order->next == nullptr) ? least_order->v->GetFirstOrder() : order->next; order = (order->next == nullptr) ? least_order->v->GetFirstOrder() : order->next;
if (VehicleSetNextDepartureTime(&least_order->expected_date, &least_order->scheduled_waiting_time, date_only_scaled, least_order->v, order, false, schdispatch_last_planned_dispatch)) { if (VehicleSetNextDepartureTime(&least_order->expected_tick, &least_order->scheduled_waiting_time, date_ticks_base, least_order->v, order, false, schdispatch_last_planned_dispatch)) {
least_order->lateness = 0; least_order->lateness = 0;
} }
require_travel_time = true; require_travel_time = true;
@ -801,7 +813,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
} }
/* If the departure is scheduled to be too late, then stop. */ /* If the departure is scheduled to be too late, then stop. */
if (least_order->expected_date - least_order->lateness > max_date) { if (least_order->expected_tick - least_order->lateness > max_ticks) {
break; break;
} }
@ -815,7 +827,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
} }
order = (order->next == nullptr) ? least_order->v->GetFirstOrder() : order->next; order = (order->next == nullptr) ? least_order->v->GetFirstOrder() : order->next;
if (VehicleSetNextDepartureTime(&least_order->expected_date, &least_order->scheduled_waiting_time, date_only_scaled, least_order->v, order, false, schdispatch_last_planned_dispatch)) { if (VehicleSetNextDepartureTime(&least_order->expected_tick, &least_order->scheduled_waiting_time, date_ticks_base, least_order->v, order, false, schdispatch_last_planned_dispatch)) {
least_order->lateness = 0; least_order->lateness = 0;
} }
require_travel_time = true; require_travel_time = true;
@ -826,7 +838,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
/* Make sure we don't try to get departures out of this order. */ /* Make sure we don't try to get departures out of this order. */
/* This is cheaper than deleting it from next_orders. */ /* This is cheaper than deleting it from next_orders. */
/* If we ever get to a state where _date * DAY_TICKS is close to INT_MAX, then we'll have other problems anyway as departures' scheduled dates will wrap around. */ /* If we ever get to a state where _date * DAY_TICKS is close to INT_MAX, then we'll have other problems anyway as departures' scheduled dates will wrap around. */
least_order->expected_date = INT32_MAX; least_order->expected_tick = INT32_MAX;
} }
/* The vehicle can't possibly have arrived at its next candidate departure yet. */ /* The vehicle can't possibly have arrived at its next candidate departure yet. */
@ -838,15 +850,15 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
for (uint i = 0; i < next_orders.size(); ++i) { for (uint i = 0; i < next_orders.size(); ++i) {
OrderDate *od = next_orders[i]; OrderDate *od = next_orders[i];
DateTicks lod = least_order->expected_date - least_order->lateness; DateTicks lod = least_order->expected_tick - least_order->lateness;
DateTicks odd = od->expected_date - od->lateness; DateTicks odd = od->expected_tick - od->lateness;
if (type == D_ARRIVAL) { if (type == D_ARRIVAL) {
lod -= least_order->scheduled_waiting_time > 0 ? least_order->scheduled_waiting_time : least_order->order->GetWaitTime(); lod -= least_order->scheduled_waiting_time > 0 ? least_order->scheduled_waiting_time : least_order->order->GetWaitTime();
odd -= od->scheduled_waiting_time > 0 ? od->scheduled_waiting_time : od->order->GetWaitTime(); odd -= od->scheduled_waiting_time > 0 ? od->scheduled_waiting_time : od->order->GetWaitTime();
} }
if (lod > odd && od->expected_date - od->lateness < max_date) { if (lod > odd && od->expected_tick - od->lateness < max_ticks) {
least_order = od; least_order = od;
} }
} }
@ -864,7 +876,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
return result; return result;
} }
DateTicksScaled GetDeparturesMaxTicksAhead() Ticks GetDeparturesMaxTicksAhead()
{ {
if (_settings_time.time_in_minutes) { if (_settings_time.time_in_minutes) {
return _settings_client.gui.max_departure_time_minutes * _settings_time.ticks_per_minute; return _settings_client.gui.max_departure_time_minutes * _settings_time.ticks_per_minute;

@ -20,6 +20,6 @@
DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehicle *> &vehicles, DepartureType type = D_DEPARTURE, DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehicle *> &vehicles, DepartureType type = D_DEPARTURE,
bool show_vehicles_via = false, bool show_pax = true, bool show_freight = true); bool show_vehicles_via = false, bool show_pax = true, bool show_freight = true);
DateTicksScaled GetDeparturesMaxTicksAhead(); Ticks GetDeparturesMaxTicksAhead();
#endif /* DEPARTURES_FUNC_H */ #endif /* DEPARTURES_FUNC_H */

@ -32,10 +32,10 @@ typedef enum {
struct CallAt { struct CallAt {
StationID station; StationID station;
DateTicks scheduled_date; DateTicksScaled scheduled_date;
CallAt(const StationID& s) : station(s), scheduled_date(0) { } CallAt(const StationID& s) : station(s), scheduled_date(0) { }
CallAt(const StationID& s, const DateTicks& t) : station(s), scheduled_date(t) { } CallAt(const StationID& s, DateTicksScaled t) : station(s), scheduled_date(t) { }
CallAt(const CallAt& c) : station(c.station), scheduled_date(c.scheduled_date) { } CallAt(const CallAt& c) : station(c.station), scheduled_date(c.scheduled_date) { }
inline bool operator==(const CallAt& c) const { inline bool operator==(const CallAt& c) const {
@ -82,7 +82,7 @@ struct Departure {
DepartureType type; ///< The type of the departure (departure or arrival) DepartureType type; ///< The type of the departure (departure or arrival)
const Vehicle *vehicle; ///< The vehicle performing this departure const Vehicle *vehicle; ///< The vehicle performing this departure
const Order *order; ///< The order corresponding to this departure const Order *order; ///< The order corresponding to this departure
uint scheduled_waiting_time; ///< Scheduled waiting time if scheduled dispatch is used Ticks scheduled_waiting_time; ///< Scheduled waiting time if scheduled dispatch is used
Departure() : terminus(INVALID_STATION), via(INVALID_STATION), via2(INVALID_STATION), vehicle(nullptr), order(nullptr) { } Departure() : terminus(INVALID_STATION), via(INVALID_STATION), via2(INVALID_STATION), vehicle(nullptr), order(nullptr) { }
inline bool operator==(const Departure& d) const { inline bool operator==(const Departure& d) const {
@ -93,13 +93,22 @@ struct Departure {
} }
return return
(this->scheduled_date / DATE_UNIT_SIZE) == (d.scheduled_date / DATE_UNIT_SIZE) && (this->scheduled_date.base() / DATE_UNIT_SIZE) == (d.scheduled_date.base() / DATE_UNIT_SIZE) &&
this->vehicle->type == d.vehicle->type && this->vehicle->type == d.vehicle->type &&
this->via == d.via && this->via == d.via &&
this->via2 == d.via2 && this->via2 == d.via2 &&
this->type == d.type this->type == d.type
; ;
} }
inline Ticks EffectiveWaitingTime() const
{
if (this->scheduled_waiting_time > 0) {
return this->scheduled_waiting_time;
} else {
return this->order->GetWaitTime();
}
}
}; };
typedef std::vector<Departure*> DepartureList; typedef std::vector<Departure*> DepartureList;

@ -361,7 +361,7 @@ struct DepotWindow : Window {
DrawSpriteIgnorePadding((v->vehstatus & VS_STOPPED) ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, PAL_NONE, flag, false, SA_CENTER); DrawSpriteIgnorePadding((v->vehstatus & VS_STOPPED) ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, PAL_NONE, flag, false, SA_CENTER);
SetDParam(0, v->unitnumber); SetDParam(0, v->unitnumber);
DrawString(text, STR_JUST_COMMA, (uint16)(v->max_age - DAYS_IN_LEAP_YEAR) >= v->age ? TC_BLACK : TC_RED); DrawString(text, STR_JUST_COMMA, (v->max_age - DAYS_IN_LEAP_YEAR) >= v->age ? TC_BLACK : TC_RED);
} }
} }

@ -2459,7 +2459,7 @@ static void DoAcquireCompany(Company *c, bool hostile_takeover)
{ {
CompanyID ci = c->index; CompanyID ci = c->index;
DEBUG(desync, 1, "buy_company: date{%08x; %02x; %02x}, buyer: %u, bought: %u", _date, _date_fract, _tick_skip_counter, (uint) _current_company, (uint) ci); DEBUG(desync, 1, "buy_company: date{%08x; %02x; %02x}, buyer: %u, bought: %u", _date.base(), _date_fract, _tick_skip_counter, (uint) _current_company, (uint) ci);
CompanyNewsInformation *cni = new CompanyNewsInformation(c, Company::Get(_current_company)); CompanyNewsInformation *cni = new CompanyNewsInformation(c, Company::Get(_current_company));

@ -484,7 +484,7 @@ uint Engine::GetDisplayMaxTractiveEffort() const
* Returns the vehicle's (not model's!) life length in days. * Returns the vehicle's (not model's!) life length in days.
* @return the life length * @return the life length
*/ */
Date Engine::GetLifeLengthInDays() const DateDelta Engine::GetLifeLengthInDays() const
{ {
/* Assume leap years; this gives the player a bit more than the given amount of years, but never less. */ /* Assume leap years; this gives the player a bit more than the given amount of years, but never less. */
return static_cast<int32_t>(this->info.lifelength + _settings_game.vehicle.extend_vehicle_life) * DAYS_IN_LEAP_YEAR; return static_cast<int32_t>(this->info.lifelength + _settings_game.vehicle.extend_vehicle_life) * DAYS_IN_LEAP_YEAR;
@ -752,7 +752,7 @@ void StartupOneEngine(Engine *e, Date aging_date, uint32 seed, Date no_introduce
SavedRandomSeeds saved_seeds; SavedRandomSeeds saved_seeds;
SaveRandomSeeds(&saved_seeds); SaveRandomSeeds(&saved_seeds);
SetRandomSeed(_settings_game.game_creation.generation_seed ^ seed ^ SetRandomSeed(_settings_game.game_creation.generation_seed ^ seed ^
ei->base_intro ^ ei->base_intro.base() ^
e->type ^ e->type ^
e->GetGRFID()); e->GetGRFID());
uint32 r = Random(); uint32 r = Random();
@ -760,9 +760,9 @@ void StartupOneEngine(Engine *e, Date aging_date, uint32 seed, Date no_introduce
/* Don't randomise the start-date in the first two years after gamestart to ensure availability /* Don't randomise the start-date in the first two years after gamestart to ensure availability
* of engines in early starting games. * of engines in early starting games.
* Note: TTDP uses fixed 1922 */ * Note: TTDP uses fixed 1922 */
e->intro_date = ei->base_intro <= ConvertYMDToDate(_settings_game.game_creation.starting_year + 2, 0, 1) ? ei->base_intro : (Date)GB(r, 0, 9) + ei->base_intro; e->intro_date = ei->base_intro <= ConvertYMDToDate(_settings_game.game_creation.starting_year + 2, 0, 1) ? ei->base_intro : (DateDelta)GB(r, 0, 9) + ei->base_intro;
if (e->intro_date <= _date && e->intro_date <= no_introduce_after_date) { if (e->intro_date <= _date && e->intro_date <= no_introduce_after_date) {
e->age = (aging_date - e->intro_date) >> 5; e->age = (aging_date - e->intro_date).base() >> 5;
e->company_avail = MAX_UVALUE(CompanyMask); e->company_avail = MAX_UVALUE(CompanyMask);
e->flags |= ENGINE_AVAILABLE; e->flags |= ENGINE_AVAILABLE;
} }
@ -774,7 +774,7 @@ void StartupOneEngine(Engine *e, Date aging_date, uint32 seed, Date no_introduce
} }
SetRandomSeed(_settings_game.game_creation.generation_seed ^ seed ^ SetRandomSeed(_settings_game.game_creation.generation_seed ^ seed ^
(re->index << 16) ^ (re->info.base_intro << 12) ^ (re->info.decay_speed << 8) ^ (re->index << 16) ^ (re->info.base_intro.base() << 12) ^ (re->info.decay_speed << 8) ^
(re->info.lifelength << 4) ^ re->info.retire_early ^ (re->info.lifelength << 4) ^ re->info.retire_early ^
e->type ^ e->type ^
e->GetGRFID()); e->GetGRFID());

@ -140,7 +140,7 @@ struct Engine : EnginePool::PoolItem<&_engine_pool> {
uint GetPower() const; uint GetPower() const;
uint GetDisplayWeight() const; uint GetDisplayWeight() const;
uint GetDisplayMaxTractiveEffort() const; uint GetDisplayMaxTractiveEffort() const;
Date GetLifeLengthInDays() const; DateDelta GetLifeLengthInDays() const;
uint16 GetRange() const; uint16 GetRange() const;
StringID GetAircraftTypeText() const; StringID GetAircraftTypeText() const;

@ -204,7 +204,7 @@ static void _GenerateWorld()
if (_debug_desync_level > 0) { if (_debug_desync_level > 0) {
char name[MAX_PATH]; char name[MAX_PATH];
seprintf(name, lastof(name), "dmp_cmds_%08x_%08x.sav", _settings_game.game_creation.generation_seed, _date); seprintf(name, lastof(name), "dmp_cmds_%08x_%08x.sav", _settings_game.game_creation.generation_seed, _date.base());
SaveOrLoad(name, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false, SMF_ZSTD_OK); SaveOrLoad(name, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false, SMF_ZSTD_OK);
} }
} catch (AbortGenerateWorldSignal&) { } catch (AbortGenerateWorldSignal&) {

@ -53,7 +53,7 @@ void FlowMapper::Run(LinkGraphJob &job) const
/* Scale by time the graph has been running without being compressed. Add 1 to avoid /* Scale by time the graph has been running without being compressed. Add 1 to avoid
* division by 0 if spawn date == last compression date. This matches * division by 0 if spawn date == last compression date. This matches
* LinkGraph::Monthly(). */ * LinkGraph::Monthly(). */
uint runtime = (uint)Clamp<DateTicksScaled>(DateTicksToScaledDateTicks(job.StartDateTicks()) - job.LastCompression() + 1, 1, UINT32_MAX); uint runtime = (uint)Clamp<DateTicksScaledDelta>(DateTicksToScaledDateTicks(job.StartDateTicks()) - job.LastCompression() + 1, 1, UINT32_MAX).base();
for (auto &it : flows) { for (auto &it : flows) {
it.ScaleToMonthly(runtime); it.ScaleToMonthly(runtime);
} }

@ -38,7 +38,7 @@ inline void LinkGraph::BaseNode::Init(TileIndex xy, StationID st, uint demand)
* This is useful if the date has been modified with the cheat menu. * This is useful if the date has been modified with the cheat menu.
* @param interval Number of days to be added or subtracted. * @param interval Number of days to be added or subtracted.
*/ */
void LinkGraph::ShiftDates(int interval) void LinkGraph::ShiftDates(DateDelta interval)
{ {
for (NodeID node1 = 0; node1 < this->Size(); ++node1) { for (NodeID node1 = 0; node1 < this->Size(); ++node1) {
BaseNode &source = this->nodes[node1]; BaseNode &source = this->nodes[node1];
@ -54,7 +54,7 @@ void LinkGraph::ShiftDates(int interval)
void LinkGraph::Compress() void LinkGraph::Compress()
{ {
this->last_compression = (_scaled_date_ticks + this->last_compression) / 2; this->last_compression = (_scaled_date_ticks.base() + this->last_compression.base()) / 2;
for (NodeID node1 = 0; node1 < this->Size(); ++node1) { for (NodeID node1 = 0; node1 < this->Size(); ++node1) {
this->nodes[node1].supply /= 2; this->nodes[node1].supply /= 2;
} }
@ -79,8 +79,8 @@ void LinkGraph::Compress()
*/ */
void LinkGraph::Merge(LinkGraph *other) void LinkGraph::Merge(LinkGraph *other)
{ {
uint32 age = ClampTo<uint32>(CeilDivT<DateTicksScaled>(_scaled_date_ticks - this->last_compression + 1, DAY_TICKS)); uint32 age = ClampTo<uint32>(CeilDivT<int64>(_scaled_date_ticks.base() - this->last_compression.base() + 1, DAY_TICKS));
uint32 other_age = ClampTo<uint32>(CeilDivT<DateTicksScaled>(_scaled_date_ticks - other->last_compression + 1, DAY_TICKS)); uint32 other_age = ClampTo<uint32>(CeilDivT<int64>(_scaled_date_ticks.base() - other->last_compression.base() + 1, DAY_TICKS));
NodeID first = this->Size(); NodeID first = this->Size();
this->nodes.reserve(first + other->Size()); this->nodes.reserve(first + other->Size());
for (NodeID node1 = 0; node1 < other->Size(); ++node1) { for (NodeID node1 = 0; node1 < other->Size(); ++node1) {
@ -266,7 +266,7 @@ void LinkGraph::Init(uint size)
this->nodes.resize(size); this->nodes.resize(size);
} }
void AdjustLinkGraphScaledTickBase(int64 delta) void AdjustLinkGraphScaledTickBase(DateTicksScaledDelta delta)
{ {
for (LinkGraph *lg : LinkGraph::Iterate()) lg->last_compression += delta; for (LinkGraph *lg : LinkGraph::Iterate()) lg->last_compression += delta;
@ -279,10 +279,10 @@ void AdjustLinkGraphScaledTickBase(int64 delta)
void LinkGraphFixupLastCompressionAfterLoad() void LinkGraphFixupLastCompressionAfterLoad()
{ {
/* last_compression was previously a Date, change it to a DateTicksScaled */ /* last_compression was previously a Date, change it to a DateTicksScaled */
for (LinkGraph *lg : LinkGraph::Iterate()) lg->last_compression = DateToScaledDateTicks((Date)lg->last_compression); for (LinkGraph *lg : LinkGraph::Iterate()) lg->last_compression = DateToScaledDateTicks((Date)lg->last_compression.base());
for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) { for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) {
LinkGraph *lg = &(const_cast<LinkGraph &>(lgj->Graph())); LinkGraph *lg = &(const_cast<LinkGraph &>(lgj->Graph()));
lg->last_compression = DateToScaledDateTicks((Date)lg->last_compression); lg->last_compression = DateToScaledDateTicks((Date)lg->last_compression.base());
} }
} }

@ -287,10 +287,10 @@ public:
static const uint MIN_TIMEOUT_DISTANCE = 32; static const uint MIN_TIMEOUT_DISTANCE = 32;
/** Number of days before deleting links served only by vehicles stopped in depot. */ /** Number of days before deleting links served only by vehicles stopped in depot. */
static const uint STALE_LINK_DEPOT_TIMEOUT = 1024; static constexpr DateDelta STALE_LINK_DEPOT_TIMEOUT = 1024;
/** Minimum number of days between subsequent compressions of a LG. */ /** Minimum number of ticks between subsequent compressions of a LG. */
static const uint COMPRESSION_INTERVAL = 256 * DAY_TICKS; static constexpr DateTicksScaledDelta COMPRESSION_INTERVAL = 256 * DAY_TICKS;
/** /**
* Scale a value from a link graph of age orig_age for usage in one of age * Scale a value from a link graph of age orig_age for usage in one of age
@ -314,7 +314,7 @@ public:
LinkGraph(CargoID cargo) : cargo(cargo), last_compression(_scaled_date_ticks) {} LinkGraph(CargoID cargo) : cargo(cargo), last_compression(_scaled_date_ticks) {}
void Init(uint size); void Init(uint size);
void ShiftDates(int interval); void ShiftDates(DateDelta interval);
void Compress(); void Compress();
void Merge(LinkGraph *other); void Merge(LinkGraph *other);
@ -365,7 +365,7 @@ public:
*/ */
inline uint Monthly(uint base) const inline uint Monthly(uint base) const
{ {
return (static_cast<uint64>(base) * 30 * DAY_TICKS * _settings_game.economy.day_length_factor) / std::max<uint64>(_scaled_date_ticks - this->last_compression, DAY_TICKS); return (static_cast<uint64>(base) * 30 * DAY_TICKS * _settings_game.economy.day_length_factor) / std::max<uint64>((_scaled_date_ticks - this->last_compression).base(), DAY_TICKS);
} }
NodeID AddNode(const Station *st); NodeID AddNode(const Station *st);
@ -392,7 +392,7 @@ protected:
friend upstream_sl::SlLinkgraphNode; friend upstream_sl::SlLinkgraphNode;
friend upstream_sl::SlLinkgraphEdge; friend upstream_sl::SlLinkgraphEdge;
friend void AdjustLinkGraphScaledTickBase(int64 delta); friend void AdjustLinkGraphScaledTickBase(DateTicksScaledDelta delta);
friend void LinkGraphFixupLastCompressionAfterLoad(); friend void LinkGraphFixupLastCompressionAfterLoad();
CargoID cargo; ///< Cargo of this component's link graph. CargoID cargo; ///< Cargo of this component's link graph.

@ -28,8 +28,8 @@ INSTANTIATE_POOL_METHODS(LinkGraphJob)
static DateTicks GetLinkGraphJobJoinDateTicks(uint duration_multiplier) static DateTicks GetLinkGraphJobJoinDateTicks(uint duration_multiplier)
{ {
DateTicks ticks = (_settings_game.linkgraph.recalc_time * DAY_TICKS * duration_multiplier) / (SECONDS_PER_DAY * _settings_game.economy.day_length_factor); DateTicksDelta ticks = (_settings_game.linkgraph.recalc_time * DAY_TICKS * duration_multiplier) / (SECONDS_PER_DAY * _settings_game.economy.day_length_factor);
return ticks + (_date * DAY_TICKS) + _date_fract; return ticks + NowDateTicks();
} }
/** /**
@ -44,7 +44,7 @@ LinkGraphJob::LinkGraphJob(const LinkGraph &orig, uint duration_multiplier) :
link_graph(orig), link_graph(orig),
settings(_settings_game.linkgraph), settings(_settings_game.linkgraph),
join_date_ticks(GetLinkGraphJobJoinDateTicks(duration_multiplier)), join_date_ticks(GetLinkGraphJobJoinDateTicks(duration_multiplier)),
start_date_ticks((_date * DAY_TICKS) + _date_fract), start_date_ticks(NowDateTicks()),
job_completed(false), job_completed(false),
job_aborted(false) job_aborted(false)
{ {

@ -262,7 +262,7 @@ public:
* settings have to be brutally const-casted in order to populate them. * settings have to be brutally const-casted in order to populate them.
*/ */
LinkGraphJob() : settings(_settings_game.linkgraph), LinkGraphJob() : settings(_settings_game.linkgraph),
join_date_ticks(INVALID_DATE), start_date_ticks(INVALID_DATE), job_completed(false), job_aborted(false) {} join_date_ticks(INVALID_DATE_TICKS), start_date_ticks(INVALID_DATE_TICKS), job_completed(false), job_aborted(false) {}
LinkGraphJob(const LinkGraph &orig, uint duration_multiplier); LinkGraphJob(const LinkGraph &orig, uint duration_multiplier);
~LinkGraphJob(); ~LinkGraphJob();
@ -297,7 +297,7 @@ public:
* @param tick_offset Optional number of ticks to add to the current date * @param tick_offset Optional number of ticks to add to the current date
* @return True if job should be finished by now, false if not. * @return True if job should be finished by now, false if not.
*/ */
inline bool IsScheduledToBeJoined(int tick_offset = 0) const { return this->join_date_ticks <= (_date * DAY_TICKS) + _date_fract + tick_offset; } inline bool IsScheduledToBeJoined(int tick_offset = 0) const { return this->join_date_ticks <= NowDateTicks() + tick_offset; }
/** /**
* Get the date when the job should be finished. * Get the date when the job should be finished.
@ -315,10 +315,10 @@ public:
* Change the start and join dates on date cheating. * Change the start and join dates on date cheating.
* @param interval Number of days to add. * @param interval Number of days to add.
*/ */
inline void ShiftJoinDate(int interval) inline void ShiftJoinDate(DateDelta interval)
{ {
this->join_date_ticks += interval * DAY_TICKS; this->join_date_ticks += DateDeltaToDateTicksDelta(interval);
this->start_date_ticks += interval * DAY_TICKS; this->start_date_ticks += DateDeltaToDateTicksDelta(interval);
} }
/** /**

@ -200,7 +200,7 @@ void LinkGraphSchedule::SpawnAll()
* graph jobs by the number of days given. * graph jobs by the number of days given.
* @param interval Number of days to be added or subtracted. * @param interval Number of days to be added or subtracted.
*/ */
void LinkGraphSchedule::ShiftDates(int interval) void LinkGraphSchedule::ShiftDates(DateDelta interval)
{ {
for (LinkGraph *lg : LinkGraph::Iterate()) lg->ShiftDates(interval); for (LinkGraph *lg : LinkGraph::Iterate()) lg->ShiftDates(interval);
for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) lgj->ShiftJoinDate(interval); for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) lgj->ShiftJoinDate(interval);
@ -285,7 +285,7 @@ void LinkGraphJobGroup::JoinThread()
auto flush_bucket = [&]() { auto flush_bucket = [&]() {
if (!bucket_cost) return; if (!bucket_cost) return;
DEBUG(linkgraph, 2, "LinkGraphJobGroup::ExecuteJobSet: Creating Job Group: jobs: " PRINTF_SIZE ", cost: %u, join after: %d", DEBUG(linkgraph, 2, "LinkGraphJobGroup::ExecuteJobSet: Creating Job Group: jobs: " PRINTF_SIZE ", cost: %u, join after: %d",
bucket.size(), bucket_cost, bucket_join_date - ((_date * DAY_TICKS) + _date_fract)); bucket.size(), bucket_cost, (bucket_join_date - NowDateTicks()).base());
auto group = std::make_shared<LinkGraphJobGroup>(constructor_token(), std::move(bucket)); auto group = std::make_shared<LinkGraphJobGroup>(constructor_token(), std::move(bucket));
group->SpawnThread(); group->SpawnThread();
bucket_cost = 0; bucket_cost = 0;
@ -321,9 +321,9 @@ void StateGameLoop_LinkGraphPauseControl()
} else if (_pause_mode == PM_UNPAUSED && _tick_skip_counter == 0) { } else if (_pause_mode == PM_UNPAUSED && _tick_skip_counter == 0) {
if (_settings_game.economy.day_length_factor == 1) { if (_settings_game.economy.day_length_factor == 1) {
if (_date_fract != LinkGraphSchedule::SPAWN_JOIN_TICK - 2) return; if (_date_fract != LinkGraphSchedule::SPAWN_JOIN_TICK - 2) return;
if (_date % _settings_game.linkgraph.recalc_interval != (_settings_game.linkgraph.recalc_interval / SECONDS_PER_DAY) / 2) return; if (_date.base() % _settings_game.linkgraph.recalc_interval != (_settings_game.linkgraph.recalc_interval / SECONDS_PER_DAY) / 2) return;
} else { } else {
int date_ticks = ((_date * DAY_TICKS) + _date_fract - (LinkGraphSchedule::SPAWN_JOIN_TICK - 2)); int date_ticks = (NowDateTicks() - (LinkGraphSchedule::SPAWN_JOIN_TICK - 2)).base();
int interval = std::max<int>(2, (_settings_game.linkgraph.recalc_interval * DAY_TICKS / (SECONDS_PER_DAY * _settings_game.economy.day_length_factor))); int interval = std::max<int>(2, (_settings_game.linkgraph.recalc_interval * DAY_TICKS / (SECONDS_PER_DAY * _settings_game.economy.day_length_factor)));
if (date_ticks % interval != interval / 2) return; if (date_ticks % interval != interval / 2) return;
} }
@ -358,10 +358,10 @@ void OnTick_LinkGraph()
if (_settings_game.economy.day_length_factor == 1) { if (_settings_game.economy.day_length_factor == 1) {
if (_date_fract != LinkGraphSchedule::SPAWN_JOIN_TICK) return; if (_date_fract != LinkGraphSchedule::SPAWN_JOIN_TICK) return;
interval = _settings_game.linkgraph.recalc_interval / SECONDS_PER_DAY; interval = _settings_game.linkgraph.recalc_interval / SECONDS_PER_DAY;
offset = _date % interval; offset = _date.base() % interval;
} else { } else {
interval = std::max<int>(2, (_settings_game.linkgraph.recalc_interval * DAY_TICKS / (SECONDS_PER_DAY * _settings_game.economy.day_length_factor))); interval = std::max<int>(2, (_settings_game.linkgraph.recalc_interval * DAY_TICKS / (SECONDS_PER_DAY * _settings_game.economy.day_length_factor)));
offset = ((_date * DAY_TICKS) + _date_fract - LinkGraphSchedule::SPAWN_JOIN_TICK) % interval; offset = (NowDateTicks() - LinkGraphSchedule::SPAWN_JOIN_TICK).base() % interval;
} }
if (offset == 0) { if (offset == 0) {
LinkGraphSchedule::instance.SpawnNext(); LinkGraphSchedule::instance.SpawnNext();

@ -66,7 +66,7 @@ public:
bool IsJoinWithUnfinishedJobDue() const; bool IsJoinWithUnfinishedJobDue() const;
void JoinNext(); void JoinNext();
void SpawnAll(); void SpawnAll();
void ShiftDates(int interval); void ShiftDates(DateDelta interval);
/** /**
* Queue a link graph for execution. * Queue a link graph for execution.

@ -235,8 +235,8 @@ void SerializeNetworkGameInfo(Packet *p, const NetworkServerGameInfo *info, bool
} }
/* NETWORK_GAME_INFO_VERSION = 3 */ /* NETWORK_GAME_INFO_VERSION = 3 */
p->Send_uint32(info->game_date); p->Send_uint32(info->game_date.base());
p->Send_uint32(info->start_date); p->Send_uint32(info->start_date.base());
/* NETWORK_GAME_INFO_VERSION = 2 */ /* NETWORK_GAME_INFO_VERSION = 2 */
p->Send_uint8 (info->companies_max); p->Send_uint8 (info->companies_max);
@ -275,8 +275,8 @@ void SerializeNetworkGameInfoExtended(Packet *p, const NetworkServerGameInfo *in
p->Send_uint8(version); // version num p->Send_uint8(version); // version num
p->Send_uint32(info->game_date); p->Send_uint32(info->game_date.base());
p->Send_uint32(info->start_date); p->Send_uint32(info->start_date.base());
p->Send_uint8 (info->companies_max); p->Send_uint8 (info->companies_max);
p->Send_uint8 (info->companies_on); p->Send_uint8 (info->companies_on);
p->Send_uint8 (info->clients_max); // Used to be max-spectators p->Send_uint8 (info->clients_max); // Used to be max-spectators
@ -400,8 +400,8 @@ void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info, const GameInfo
} }
case 3: case 3:
info->game_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); info->game_date = Clamp(p->Recv_uint32(), 0, MAX_DATE.base());
info->start_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); info->start_date = Clamp(p->Recv_uint32(), 0, MAX_DATE.base());
FALLTHROUGH; FALLTHROUGH;
case 2: case 2:
@ -455,8 +455,8 @@ void DeserializeNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info)
NewGRFSerializationType newgrf_serialisation = NST_GRFID_MD5; NewGRFSerializationType newgrf_serialisation = NST_GRFID_MD5;
info->game_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); info->game_date = Clamp(p->Recv_uint32(), 0, MAX_DATE.base());
info->start_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); info->start_date = Clamp(p->Recv_uint32(), 0, MAX_DATE.base());
info->companies_max = p->Recv_uint8 (); info->companies_max = p->Recv_uint8 ();
info->companies_on = p->Recv_uint8 (); info->companies_on = p->Recv_uint8 ();
p->Recv_uint8(); // Used to contain max-spectators. p->Recv_uint8(); // Used to contain max-spectators.

@ -326,7 +326,7 @@ void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send,
char *msg_ptr = message + Utf8Encode(message, _current_text_dir == TD_LTR ? CHAR_TD_LRM : CHAR_TD_RLM); char *msg_ptr = message + Utf8Encode(message, _current_text_dir == TD_LTR ? CHAR_TD_LRM : CHAR_TD_RLM);
GetString(msg_ptr, strid, lastof(message)); GetString(msg_ptr, strid, lastof(message));
DEBUG(desync, 1, "msg: date{%08x; %02x; %02x}; %s", _date, _date_fract, _tick_skip_counter, message); DEBUG(desync, 1, "msg: date{%08x; %02x; %02x}; %s", _date.base(), _date_fract, _tick_skip_counter, message);
IConsolePrintF(colour, "%s", message); IConsolePrintF(colour, "%s", message);
NetworkAddChatMessage(colour, _settings_client.gui.network_chat_timeout, message); NetworkAddChatMessage(colour, _settings_client.gui.network_chat_timeout, message);
} }
@ -1162,7 +1162,7 @@ void NetworkGameLoop()
/* We don't want to log multiple times if paused. */ /* We don't want to log multiple times if paused. */
static Date last_log; static Date last_log;
if (last_log != _date) { if (last_log != _date) {
DEBUG(desync, 2, "sync: date{%08x; %02x; %02x}; %08x; %08x", _date, _date_fract, _tick_skip_counter, _random.state[0], _random.state[1]); DEBUG(desync, 2, "sync: date{%08x; %02x; %02x}; %08x; %08x", _date.base(), _date_fract, _tick_skip_counter, _random.state[0], _random.state[1]);
last_log = _date; last_log = _date;
} }
} }
@ -1185,15 +1185,15 @@ void NetworkGameLoop()
if (_date == next_date && _date_fract == next_date_fract) { if (_date == next_date && _date_fract == next_date_fract) {
if (cp != nullptr) { if (cp != nullptr) {
NetworkSendCommand(cp->tile, cp->p1, cp->p2, cp->p3, cp->cmd & ~CMD_FLAGS_MASK, nullptr, cp->text.c_str(), cp->company, cp->aux_data); NetworkSendCommand(cp->tile, cp->p1, cp->p2, cp->p3, cp->cmd & ~CMD_FLAGS_MASK, nullptr, cp->text.c_str(), cp->company, cp->aux_data);
DEBUG(net, 0, "injecting: date{%08x; %02x; %02x}; %02x; %06x; %08x; %08x; " OTTD_PRINTFHEX64PAD " %08x; \"%s\" (%x) (%s)", _date, _date_fract, _tick_skip_counter, (int)_current_company, cp->tile, cp->p1, cp->p2, cp->p3, cp->cmd, cp->text.c_str(), cp->binary_length, GetCommandName(cp->cmd)); DEBUG(net, 0, "injecting: date{%08x; %02x; %02x}; %02x; %06x; %08x; %08x; " OTTD_PRINTFHEX64PAD " %08x; \"%s\" (%x) (%s)", _date.base(), _date_fract, _tick_skip_counter, (int)_current_company, cp->tile, cp->p1, cp->p2, cp->p3, cp->cmd, cp->text.c_str(), cp->binary_length, GetCommandName(cp->cmd));
cp.reset(); cp.reset();
} }
if (check_sync_state) { if (check_sync_state) {
if (sync_state[0] == _random.state[0] && sync_state[1] == _random.state[1]) { if (sync_state[0] == _random.state[0] && sync_state[1] == _random.state[1]) {
DEBUG(net, 0, "sync check: date{%08x; %02x; %02x}; match", _date, _date_fract, _tick_skip_counter); DEBUG(net, 0, "sync check: date{%08x; %02x; %02x}; match", _date.base(), _date_fract, _tick_skip_counter);
} else { } else {
DEBUG(net, 0, "sync check: date{%08x; %02x; %02x}; mismatch expected {%08x, %08x}, got {%08x, %08x}", DEBUG(net, 0, "sync check: date{%08x; %02x; %02x}; mismatch expected {%08x, %08x}, got {%08x, %08x}",
_date, _date_fract, _tick_skip_counter, sync_state[0], sync_state[1], _random.state[0], _random.state[1]); _date.base(), _date_fract, _tick_skip_counter, sync_state[0], sync_state[1], _random.state[0], _random.state[1]);
NOT_REACHED(); NOT_REACHED();
} }
check_sync_state = false; check_sync_state = false;

@ -179,7 +179,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendWelcome()
p->Send_string(""); // Used to be map-name. p->Send_string(""); // Used to be map-name.
p->Send_uint32(_settings_game.game_creation.generation_seed); p->Send_uint32(_settings_game.game_creation.generation_seed);
p->Send_uint8 (_settings_game.game_creation.landscape); p->Send_uint8 (_settings_game.game_creation.landscape);
p->Send_uint32(ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1)); p->Send_uint32(ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1).base());
p->Send_uint16(MapSizeX()); p->Send_uint16(MapSizeX());
p->Send_uint16(MapSizeY()); p->Send_uint16(MapSizeY());
@ -209,7 +209,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendDate()
{ {
Packet *p = new Packet(ADMIN_PACKET_SERVER_DATE); Packet *p = new Packet(ADMIN_PACKET_SERVER_DATE);
p->Send_uint32(_date); p->Send_uint32(_date.base());
this->SendPacket(p); this->SendPacket(p);
return NETWORK_RECV_STATUS_OKAY; return NETWORK_RECV_STATUS_OKAY;
@ -245,7 +245,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientInfo(const NetworkC
p->Send_string(cs == nullptr ? "" : const_cast<NetworkAddress &>(cs->client_address).GetHostname()); p->Send_string(cs == nullptr ? "" : const_cast<NetworkAddress &>(cs->client_address).GetHostname());
p->Send_string(ci->client_name); p->Send_string(ci->client_name);
p->Send_uint8 (0); // Used to be language p->Send_uint8 (0); // Used to be language
p->Send_uint32(ci->join_date); p->Send_uint32(ci->join_date.base());
p->Send_uint8 (ci->client_playas); p->Send_uint8 (ci->client_playas);
this->SendPacket(p); this->SendPacket(p);

@ -331,7 +331,7 @@ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res)
ShowNetworkError(STR_NETWORK_ERROR_DESYNC); ShowNetworkError(STR_NETWORK_ERROR_DESYNC);
DEBUG(desync, 1, "sync_err: date{%08x; %02x; %02x} {%x, " OTTD_PRINTFHEX64 "} != {%x, " OTTD_PRINTFHEX64 "}" DEBUG(desync, 1, "sync_err: date{%08x; %02x; %02x} {%x, " OTTD_PRINTFHEX64 "} != {%x, " OTTD_PRINTFHEX64 "}"
, _date, _date_fract, _tick_skip_counter, _sync_seed_1, _sync_state_checksum, _random.state[0], _state_checksum.state); , _date.base(), _date_fract, _tick_skip_counter, _sync_seed_1, _sync_state_checksum, _random.state[0], _state_checksum.state);
DEBUG(net, 0, "Sync error detected!"); DEBUG(net, 0, "Sync error detected!");
std::string desync_log; std::string desync_log;
@ -632,7 +632,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendDesyncLog(const std::strin
NetworkRecvStatus ClientNetworkGameSocketHandler::SendDesyncMessage(const char *msg) NetworkRecvStatus ClientNetworkGameSocketHandler::SendDesyncMessage(const char *msg)
{ {
Packet *p = new Packet(PACKET_CLIENT_DESYNC_MSG, SHRT_MAX); Packet *p = new Packet(PACKET_CLIENT_DESYNC_MSG, SHRT_MAX);
p->Send_uint32(_date); p->Send_uint32(_date.base());
p->Send_uint16(_date_fract); p->Send_uint16(_date_fract);
p->Send_uint8(_tick_skip_counter); p->Send_uint8(_tick_skip_counter);
p->Send_string(msg); p->Send_string(msg);

@ -287,7 +287,7 @@ protected:
/** Sort servers by the number of days the game is running */ /** Sort servers by the number of days the game is running */
static bool NGameYearsSorter(NetworkGameList * const &a, NetworkGameList * const &b) static bool NGameYearsSorter(NetworkGameList * const &a, NetworkGameList * const &b)
{ {
auto r = a->info.game_date - a->info.start_date - b->info.game_date + b->info.start_date; auto r = a->info.game_date.base() - a->info.start_date.base() - b->info.game_date.base() + b->info.start_date.base();
return (r != 0) ? r < 0: NGameDateSorter(a, b); return (r != 0) ? r < 0: NGameDateSorter(a, b);
} }
@ -650,11 +650,11 @@ public:
DrawString(tr, invite_or_address); // server address / invite code DrawString(tr, invite_or_address); // server address / invite code
tr.top += GetCharacterHeight(FS_NORMAL); tr.top += GetCharacterHeight(FS_NORMAL);
SetDParam(0, sel->info.start_date); SetDParam(0, sel->info.start_date.base());
DrawString(tr, STR_NETWORK_SERVER_LIST_START_DATE); // start date DrawString(tr, STR_NETWORK_SERVER_LIST_START_DATE); // start date
tr.top += GetCharacterHeight(FS_NORMAL); tr.top += GetCharacterHeight(FS_NORMAL);
SetDParam(0, sel->info.game_date); SetDParam(0, sel->info.game_date.base());
DrawString(tr, STR_NETWORK_SERVER_LIST_CURRENT_DATE); // current date DrawString(tr, STR_NETWORK_SERVER_LIST_CURRENT_DATE); // current date
tr.top += GetCharacterHeight(FS_NORMAL); tr.top += GetCharacterHeight(FS_NORMAL);

@ -1020,7 +1020,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p)
ci->join_frame = _frame_counter; ci->join_frame = _frame_counter;
ci->client_name = client_name; ci->client_name = client_name;
ci->client_playas = playas; ci->client_playas = playas;
DEBUG(desync, 1, "client: date{%08x; %02x; %02x}; client: %02x; company: %02x", _date, _date_fract, _tick_skip_counter, (int)ci->index, (int)ci->client_playas); DEBUG(desync, 1, "client: date{%08x; %02x; %02x}; client: %02x; company: %02x", _date.base(), _date_fract, _tick_skip_counter, (int)ci->index, (int)ci->client_playas);
/* Make sure companies to which people try to join are not autocleaned */ /* Make sure companies to which people try to join are not autocleaned */
if (Company::IsValidID(playas)) _network_company_states[playas].months_empty = 0; if (Company::IsValidID(playas)) _network_company_states[playas].months_empty = 0;
@ -1812,7 +1812,7 @@ void NetworkUpdateClientInfo(ClientID client_id)
if (ci == nullptr) return; if (ci == nullptr) return;
DEBUG(desync, 1, "client: date{%08x; %02x; %02x}; client: %02x; company: %02x", _date, _date_fract, _tick_skip_counter, client_id, (int)ci->client_playas); DEBUG(desync, 1, "client: date{%08x; %02x; %02x}; client: %02x; company: %02x", _date.base(), _date_fract, _tick_skip_counter, client_id, (int)ci->client_playas);
for (NetworkClientSocket *cs : NetworkClientSocket::Iterate()) { for (NetworkClientSocket *cs : NetworkClientSocket::Iterate()) {
if (cs->status >= ServerNetworkGameSocketHandler::STATUS_AUTHORIZED) { if (cs->status >= ServerNetworkGameSocketHandler::STATUS_AUTHORIZED) {
@ -2169,7 +2169,7 @@ void NetworkServerMonthlyLoop()
void NetworkServerDailyLoop() void NetworkServerDailyLoop()
{ {
NetworkAdminUpdate(ADMIN_FREQUENCY_DAILY); NetworkAdminUpdate(ADMIN_FREQUENCY_DAILY);
if ((_date % 7) == 3) NetworkAdminUpdate(ADMIN_FREQUENCY_WEEKLY); if ((_date.base() % 7) == 3) NetworkAdminUpdate(ADMIN_FREQUENCY_WEEKLY);
} }
/** /**

@ -7403,7 +7403,7 @@ bool GetGlobalVariable(byte param, uint32 *value, const GRFFile *grffile)
switch (param) { switch (param) {
case 0x00: // current date case 0x00: // current date
*value = std::max(_date - DAYS_TILL_ORIGINAL_BASE_YEAR, 0); *value = std::max<DateDelta>(_date - DAYS_TILL_ORIGINAL_BASE_YEAR, 0).base();
return true; return true;
case 0x01: // current year case 0x01: // current year
@ -7412,7 +7412,7 @@ bool GetGlobalVariable(byte param, uint32 *value, const GRFFile *grffile)
case 0x02: { // detailed date information: month of year (bit 0-7), day of month (bit 8-12), leap year (bit 15), day of year (bit 16-24) case 0x02: { // detailed date information: month of year (bit 0-7), day of month (bit 8-12), leap year (bit 15), day of year (bit 16-24)
Date start_of_year = ConvertYMDToDate(_cur_date_ymd.year, 0, 1); Date start_of_year = ConvertYMDToDate(_cur_date_ymd.year, 0, 1);
*value = _cur_date_ymd.month | (_cur_date_ymd.day - 1) << 8 | (IsLeapYear(_cur_date_ymd.year) ? 1 << 15 : 0) | (_date - start_of_year) << 16; *value = _cur_date_ymd.month | (_cur_date_ymd.day - 1) << 8 | (IsLeapYear(_cur_date_ymd.year) ? 1 << 15 : 0) | (_date - start_of_year).base() << 16;
return true; return true;
} }
@ -7518,7 +7518,7 @@ bool GetGlobalVariable(byte param, uint32 *value, const GRFFile *grffile)
return true; return true;
case 0x23: // long format date case 0x23: // long format date
*value = _date; *value = _date.base();
return true; return true;
case 0x24: // long format year case 0x24: // long format year

@ -169,7 +169,7 @@ void AirportOverrideManager::SetEntitySpec(AirportSpec *as)
case 0x7C: return (this->st->airport.psa != nullptr) ? this->st->airport.psa->GetValue(parameter) : 0; case 0x7C: return (this->st->airport.psa != nullptr) ? this->st->airport.psa->GetValue(parameter) : 0;
case 0xF0: return this->st->facilities; case 0xF0: return this->st->facilities;
case 0xFA: return ClampTo<uint16_t>(this->st->build_date - DAYS_TILL_ORIGINAL_BASE_YEAR); case 0xFA: return ClampTo<uint16_t>((this->st->build_date - DAYS_TILL_ORIGINAL_BASE_YEAR).base());
} }
return this->st->GetNewGRFVariable(this->ro, variable, parameter, &(extra->available)); return this->st->GetNewGRFVariable(this->ro, variable, parameter, &(extra->available));

@ -729,7 +729,7 @@ static uint32 VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *object,
} }
case 0x4B: // Long date of last service case 0x4B: // Long date of last service
return v->date_of_last_service_newgrf; return v->date_of_last_service_newgrf.base();
case 0x4C: // Current maximum speed in NewGRF units case 0x4C: // Current maximum speed in NewGRF units
if (!v->IsPrimaryVehicle()) return 0; if (!v->IsPrimaryVehicle()) return 0;
@ -1118,7 +1118,7 @@ static uint32 VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *object,
} }
case 0x48: return Engine::Get(this->self_type)->flags; // Vehicle Type Info case 0x48: return Engine::Get(this->self_type)->flags; // Vehicle Type Info
case 0x49: return _cur_year; // 'Long' format build year case 0x49: return _cur_year; // 'Long' format build year
case 0x4B: return _date; // Long date of last service case 0x4B: return _date.base(); // Long date of last service
case 0x92: return ClampTo<uint16>(_date - DAYS_TILL_ORIGINAL_BASE_YEAR); // Date of last service case 0x92: return ClampTo<uint16>(_date - DAYS_TILL_ORIGINAL_BASE_YEAR); // Date of last service
case 0x93: return GB(ClampTo<uint16>(_date - DAYS_TILL_ORIGINAL_BASE_YEAR), 8, 8); case 0x93: return GB(ClampTo<uint16>(_date - DAYS_TILL_ORIGINAL_BASE_YEAR), 8, 8);
case 0xC4: return Clamp(_cur_year, ORIGINAL_BASE_YEAR, ORIGINAL_MAX_YEAR) - ORIGINAL_BASE_YEAR; // Build year case 0xC4: return Clamp(_cur_year, ORIGINAL_BASE_YEAR, ORIGINAL_MAX_YEAR) - ORIGINAL_BASE_YEAR; // Build year

@ -271,7 +271,7 @@ static uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout
return this->industry->founder | (is_ai ? 0x10000 : 0) | (colours << 24); return this->industry->founder | (is_ai ? 0x10000 : 0) | (colours << 24);
} }
case 0x46: return this->industry->construction_date; // Date when built - long format - (in days) case 0x46: return this->industry->construction_date.base(); // Date when built - long format - (in days)
/* Override flags from GS */ /* Override flags from GS */
case 0x47: return this->industry->ctlflags; case 0x47: return this->industry->ctlflags;
@ -362,7 +362,7 @@ static uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout
if (cargo == CT_INVALID) return 0; if (cargo == CT_INVALID) return 0;
int index = this->industry->GetCargoAcceptedIndex(cargo); int index = this->industry->GetCargoAcceptedIndex(cargo);
if (index < 0) return 0; // invalid cargo if (index < 0) return 0; // invalid cargo
if (variable == 0x6E) return this->industry->last_cargo_accepted_at[index]; if (variable == 0x6E) return this->industry->last_cargo_accepted_at[index].base();
if (variable == 0x6F) return this->industry->incoming_cargo_waiting[index]; if (variable == 0x6F) return this->industry->incoming_cargo_waiting[index];
NOT_REACHED(); NOT_REACHED();
} }

@ -281,7 +281,7 @@ static uint32 GetCountAndDistanceOfClosestInstance(uint32 local_id, uint32 grfid
break; break;
/* Construction date */ /* Construction date */
case 0x42: return _date; case 0x42: return _date.base();
/* Object founder information */ /* Object founder information */
case 0x44: return _current_company; case 0x44: return _current_company;
@ -325,7 +325,7 @@ static uint32 GetCountAndDistanceOfClosestInstance(uint32 local_id, uint32 grfid
case 0x41: return GetTileSlope(this->tile) << 8 | GetTerrainType(this->tile); case 0x41: return GetTileSlope(this->tile) << 8 | GetTerrainType(this->tile);
/* Construction date */ /* Construction date */
case 0x42: return this->obj->build_date; case 0x42: return this->obj->build_date.base();
/* Animation counter */ /* Animation counter */
case 0x43: return GetAnimationFrame(this->tile); case 0x43: return GetAnimationFrame(this->tile);

@ -34,7 +34,7 @@
case 0x40: return 0; case 0x40: return 0;
case 0x41: return 0; case 0x41: return 0;
case 0x42: return 0; case 0x42: return 0;
case 0x43: return _date; case 0x43: return _date.base();
case 0x44: return HZB_TOWN_EDGE; case 0x44: return HZB_TOWN_EDGE;
case A2VRI_RAILTYPE_SIGNAL_RESTRICTION_INFO: return 0; case A2VRI_RAILTYPE_SIGNAL_RESTRICTION_INFO: return 0;
case A2VRI_RAILTYPE_SIGNAL_CONTEXT: return this->signal_context; case A2VRI_RAILTYPE_SIGNAL_CONTEXT: return this->signal_context;
@ -49,8 +49,8 @@
case 0x41: return 0; case 0x41: return 0;
case 0x42: return IsLevelCrossingTile(this->tile) && IsCrossingBarred(this->tile); case 0x42: return IsLevelCrossingTile(this->tile) && IsCrossingBarred(this->tile);
case 0x43: case 0x43:
if (IsRailDepotTile(this->tile)) return Depot::GetByTile(this->tile)->build_date; if (IsRailDepotTile(this->tile)) return Depot::GetByTile(this->tile)->build_date.base();
return _date; return _date.base();
case 0x44: { case 0x44: {
const Town *t = nullptr; const Town *t = nullptr;
if (IsRailDepotTile(this->tile)) { if (IsRailDepotTile(this->tile)) {

@ -30,7 +30,7 @@
case 0x40: return 0; case 0x40: return 0;
case 0x41: return 0; case 0x41: return 0;
case 0x42: return 0; case 0x42: return 0;
case 0x43: return _date; case 0x43: return _date.base();
case 0x44: return HZB_TOWN_EDGE; case 0x44: return HZB_TOWN_EDGE;
} }
} }
@ -40,8 +40,8 @@
case 0x41: return 0; case 0x41: return 0;
case 0x42: return IsLevelCrossingTile(this->tile) && IsCrossingBarred(this->tile); case 0x42: return IsLevelCrossingTile(this->tile) && IsCrossingBarred(this->tile);
case 0x43: case 0x43:
if (IsRoadDepotTile(this->tile)) return Depot::GetByTile(this->tile)->build_date; if (IsRoadDepotTile(this->tile)) return Depot::GetByTile(this->tile)->build_date.base();
return _date; return _date.base();
case 0x44: { case 0x44: {
const Town *t = nullptr; const Town *t = nullptr;
if (IsRoadDepotTile(this->tile)) { if (IsRoadDepotTile(this->tile)) {

@ -1554,7 +1554,7 @@ void CheckCaches(bool force_check, std::function<void(const char *)> log, CheckC
desync_level = 1; desync_level = 1;
if (HasChickenBit(DCBF_DESYNC_CHECK_NO_GENERAL)) flags &= ~CHECK_CACHE_GENERAL; if (HasChickenBit(DCBF_DESYNC_CHECK_NO_GENERAL)) flags &= ~CHECK_CACHE_GENERAL;
} }
if (unlikely(HasChickenBit(DCBF_DESYNC_CHECK_PERIODIC_SIGNALS)) && desync_level < 2 && _scaled_date_ticks % 256 == 0) { if (unlikely(HasChickenBit(DCBF_DESYNC_CHECK_PERIODIC_SIGNALS)) && desync_level < 2 && _scaled_date_ticks.base() % 256 == 0) {
if (!SignalInfraTotalMatches()) desync_level = 2; if (!SignalInfraTotalMatches()) desync_level = 2;
} }
@ -1562,7 +1562,7 @@ void CheckCaches(bool force_check, std::function<void(const char *)> log, CheckC
* always to aid testing of caches. */ * always to aid testing of caches. */
if (desync_level < 1) return; if (desync_level < 1) return;
if (desync_level == 1 && _scaled_date_ticks % 500 != 0) return; if (desync_level == 1 && _scaled_date_ticks.base() % 500 != 0) return;
} }
SCOPE_INFO_FMT([flags], "CheckCaches: %X", flags); SCOPE_INFO_FMT([flags], "CheckCaches: %X", flags);
@ -2103,10 +2103,10 @@ void StateGameLoop()
CallWindowGameTickEvent(); CallWindowGameTickEvent();
NewsLoop(); NewsLoop();
} else { } else {
if (_debug_desync_level > 2 && _tick_skip_counter == 0 && _date_fract == 0 && (_date & 0x1F) == 0) { if (_debug_desync_level > 2 && _tick_skip_counter == 0 && _date_fract == 0 && (_date.base() & 0x1F) == 0) {
/* Save the desync savegame if needed. */ /* Save the desync savegame if needed. */
char name[MAX_PATH]; char name[MAX_PATH];
seprintf(name, lastof(name), "dmp_cmds_%08x_%08x.sav", _settings_game.game_creation.generation_seed, _date); seprintf(name, lastof(name), "dmp_cmds_%08x_%08x.sav", _settings_game.game_creation.generation_seed, _date.base());
SaveOrLoad(name, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false); SaveOrLoad(name, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false);
} }
@ -2124,7 +2124,7 @@ void StateGameLoop()
} }
if (!(_game_mode == GM_MENU || _game_mode == GM_BOOTSTRAP) && !_settings_client.gui.autosave_realtime && if (!(_game_mode == GM_MENU || _game_mode == GM_BOOTSTRAP) && !_settings_client.gui.autosave_realtime &&
(_scaled_date_ticks % (_settings_client.gui.autosave_interval * (_settings_game.economy.tick_rate == TRM_MODERN ? (60000 / 27) : (60000 / 30)))) == 0) { (_scaled_date_ticks.base() % (_settings_client.gui.autosave_interval * (_settings_game.economy.tick_rate == TRM_MODERN ? (60000 / 27) : (60000 / 30)))) == 0) {
_do_autosave = true; _do_autosave = true;
_check_special_modes = true; _check_special_modes = true;
SetWindowDirty(WC_STATUS_BAR, 0); SetWindowDirty(WC_STATUS_BAR, 0);

@ -979,6 +979,6 @@ public:
inline const DispatchSchedule &GetDispatchScheduleByIndex(uint index) const { return this->dispatch_schedules[index]; } inline const DispatchSchedule &GetDispatchScheduleByIndex(uint index) const { return this->dispatch_schedules[index]; }
}; };
void ShiftOrderDates(int interval); void ShiftOrderDates(DateDelta interval);
#endif /* ORDER_BASE_H */ #endif /* ORDER_BASE_H */

@ -3042,7 +3042,7 @@ bool EvaluateDispatchSlotConditionalOrder(const Order *order, const Vehicle *v,
} else { } else {
extern DateTicksScaled GetScheduledDispatchTime(const DispatchSchedule &ds, DateTicksScaled leave_time); extern DateTicksScaled GetScheduledDispatchTime(const DispatchSchedule &ds, DateTicksScaled leave_time);
DateTicksScaled slot = GetScheduledDispatchTime(sched, _scaled_date_ticks); DateTicksScaled slot = GetScheduledDispatchTime(sched, _scaled_date_ticks);
offset = (slot - sched.GetScheduledDispatchStartTick()) % sched.GetScheduledDispatchDuration(); offset = (slot - sched.GetScheduledDispatchStartTick()).base() % sched.GetScheduledDispatchDuration();
} }
bool value; bool value;
@ -3082,7 +3082,7 @@ VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v, Pro
case OCV_RELIABILITY: skip_order = OrderConditionCompare(occ, ToPercent16(v->reliability), value); break; case OCV_RELIABILITY: skip_order = OrderConditionCompare(occ, ToPercent16(v->reliability), value); break;
case OCV_MAX_RELIABILITY: skip_order = OrderConditionCompare(occ, ToPercent16(v->GetEngine()->reliability), value); break; case OCV_MAX_RELIABILITY: skip_order = OrderConditionCompare(occ, ToPercent16(v->GetEngine()->reliability), value); break;
case OCV_MAX_SPEED: skip_order = OrderConditionCompare(occ, v->GetDisplayMaxSpeed() * 10 / 16, value); break; case OCV_MAX_SPEED: skip_order = OrderConditionCompare(occ, v->GetDisplayMaxSpeed() * 10 / 16, value); break;
case OCV_AGE: skip_order = OrderConditionCompare(occ, DateToYear(v->age), value); break; case OCV_AGE: skip_order = OrderConditionCompare(occ, DateDeltaToYears(v->age), value); break;
case OCV_REQUIRES_SERVICE: skip_order = OrderConditionCompare(occ, v->NeedsServicing(), value); break; case OCV_REQUIRES_SERVICE: skip_order = OrderConditionCompare(occ, v->NeedsServicing(), value); break;
case OCV_UNCONDITIONALLY: skip_order = true; break; case OCV_UNCONDITIONALLY: skip_order = true; break;
case OCV_CARGO_WAITING: { case OCV_CARGO_WAITING: {
@ -3172,7 +3172,7 @@ VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v, Pro
skip_order = ord->UpdateJumpCounter((byte)value, mode == PCO_DRY_RUN); skip_order = ord->UpdateJumpCounter((byte)value, mode == PCO_DRY_RUN);
break; break;
} }
case OCV_REMAINING_LIFETIME: skip_order = OrderConditionCompare(occ, std::max(DateToYear(v->max_age - v->age + DAYS_IN_LEAP_YEAR - 1), 0), value); break; case OCV_REMAINING_LIFETIME: skip_order = OrderConditionCompare(occ, std::max(DateDeltaToYears(v->max_age - v->age + DAYS_IN_LEAP_YEAR - 1), 0), value); break;
case OCV_COUNTER_VALUE: { case OCV_COUNTER_VALUE: {
const TraceRestrictCounter* ctr = TraceRestrictCounter::GetIfValid(GB(order->GetXData(), 16, 16)); const TraceRestrictCounter* ctr = TraceRestrictCounter::GetIfValid(GB(order->GetXData(), 16, 16));
if (ctr != nullptr) { if (ctr != nullptr) {
@ -3690,7 +3690,7 @@ CommandCost CmdMassChangeOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, u
return CommandCost(); return CommandCost();
} }
void ShiftOrderDates(int interval) void ShiftOrderDates(DateDelta interval)
{ {
SetWindowClassesDirty(WC_VEHICLE_ORDERS); SetWindowClassesDirty(WC_VEHICLE_ORDERS);
SetWindowClassesDirty(WC_VEHICLE_TIMETABLE); SetWindowClassesDirty(WC_VEHICLE_TIMETABLE);

@ -325,7 +325,7 @@ RailTypes AddDateIntroducedRailTypes(RailTypes current, Date date)
if (rti->label == 0) continue; if (rti->label == 0) continue;
/* Not date introduced. */ /* Not date introduced. */
if (!IsInsideMM(rti->introduction_date, 0, MAX_DAY)) continue; if (!IsInsideMM(rti->introduction_date, 0, MAX_DATE.base())) continue;
/* Not yet introduced at this date. */ /* Not yet introduced at this date. */
if (rti->introduction_date > date) continue; if (rti->introduction_date > date) continue;
@ -403,7 +403,7 @@ RailTypes GetRailTypes(bool introduces)
} }
} }
if (introduces) return AddDateIntroducedRailTypes(rts, MAX_DAY); if (introduces) return AddDateIntroducedRailTypes(rts, MAX_DATE);
return rts; return rts;
} }

@ -147,7 +147,7 @@ bool HasRoadTypeAvail(const CompanyID company, RoadType roadtype)
if (rti->label == 0) return false; if (rti->label == 0) return false;
/* Not yet introduced at this date. */ /* Not yet introduced at this date. */
if (IsInsideMM(rti->introduction_date, 0, MAX_DAY) && rti->introduction_date > _date) return false; if (IsInsideMM(rti->introduction_date, 0, MAX_DATE.base()) && rti->introduction_date > _date) return false;
/* /*
* Do not allow building hidden road types, except when a town may build it. * Do not allow building hidden road types, except when a town may build it.
@ -209,7 +209,7 @@ RoadTypes AddDateIntroducedRoadTypes(RoadTypes current, Date date)
if (rti->label == 0) continue; if (rti->label == 0) continue;
/* Not date introduced. */ /* Not date introduced. */
if (!IsInsideMM(rti->introduction_date, 0, MAX_DAY)) continue; if (!IsInsideMM(rti->introduction_date, 0, MAX_DATE.base())) continue;
/* Not yet introduced at this date. */ /* Not yet introduced at this date. */
if (rti->introduction_date > date) continue; if (rti->introduction_date > date) continue;
@ -277,7 +277,7 @@ RoadTypes GetRoadTypes(bool introduces)
} }
} }
if (introduces) return AddDateIntroducedRoadTypes(rts, MAX_DAY); if (introduces) return AddDateIntroducedRoadTypes(rts, MAX_DATE);
return rts; return rts;
} }

@ -1790,19 +1790,19 @@ bool AfterLoadGame()
/* Time starts at 0 instead of 1920. /* Time starts at 0 instead of 1920.
* Account for this in older games by adding an offset */ * Account for this in older games by adding an offset */
if (IsSavegameVersionBefore(SLV_31)) { if (IsSavegameVersionBefore(SLV_31)) {
_date += DAYS_TILL_ORIGINAL_BASE_YEAR; _date += DAYS_TILL_ORIGINAL_BASE_YEAR.AsDelta();
SetScaledTickVariables(); SetScaledTickVariables();
ConvertDateToYMD(_date, &_cur_date_ymd); ConvertDateToYMD(_date, &_cur_date_ymd);
UpdateCachedSnowLine(); UpdateCachedSnowLine();
for (Station *st : Station::Iterate()) st->build_date += DAYS_TILL_ORIGINAL_BASE_YEAR; for (Station *st : Station::Iterate()) st->build_date += DAYS_TILL_ORIGINAL_BASE_YEAR.AsDelta();
for (Waypoint *wp : Waypoint::Iterate()) wp->build_date += DAYS_TILL_ORIGINAL_BASE_YEAR; for (Waypoint *wp : Waypoint::Iterate()) wp->build_date += DAYS_TILL_ORIGINAL_BASE_YEAR.AsDelta();
for (Engine *e : Engine::Iterate()) e->intro_date += DAYS_TILL_ORIGINAL_BASE_YEAR; for (Engine *e : Engine::Iterate()) e->intro_date += DAYS_TILL_ORIGINAL_BASE_YEAR.AsDelta();
for (Company *c : Company::Iterate()) c->inaugurated_year += ORIGINAL_BASE_YEAR; for (Company *c : Company::Iterate()) c->inaugurated_year += ORIGINAL_BASE_YEAR;
for (Industry *i : Industry::Iterate()) i->last_prod_year += ORIGINAL_BASE_YEAR; for (Industry *i : Industry::Iterate()) i->last_prod_year += ORIGINAL_BASE_YEAR;
for (Vehicle *v : Vehicle::Iterate()) { for (Vehicle *v : Vehicle::Iterate()) {
v->date_of_last_service += DAYS_TILL_ORIGINAL_BASE_YEAR; v->date_of_last_service += DAYS_TILL_ORIGINAL_BASE_YEAR.AsDelta();
v->build_year += ORIGINAL_BASE_YEAR; v->build_year += ORIGINAL_BASE_YEAR;
} }
} }
@ -3603,7 +3603,7 @@ bool AfterLoadGame()
/* If the start date is 0, the vehicle is not waiting to start and can be ignored. */ /* If the start date is 0, the vehicle is not waiting to start and can be ignored. */
if (v->timetable_start == 0) continue; if (v->timetable_start == 0) continue;
v->timetable_start += _scaled_date_ticks - _tick_counter; v->timetable_start += _scaled_date_ticks.base() - _tick_counter;
} }
} else if (!SlXvIsFeaturePresent(XSLFI_TIMETABLES_START_TICKS, 3)) { } else if (!SlXvIsFeaturePresent(XSLFI_TIMETABLES_START_TICKS, 3)) {
extern btree::btree_map<VehicleID, uint16> _old_timetable_start_subticks_map; extern btree::btree_map<VehicleID, uint16> _old_timetable_start_subticks_map;
@ -3612,10 +3612,10 @@ bool AfterLoadGame()
if (v->timetable_start == 0) continue; if (v->timetable_start == 0) continue;
if (SlXvIsFeatureMissing(XSLFI_TIMETABLES_START_TICKS)) { if (SlXvIsFeatureMissing(XSLFI_TIMETABLES_START_TICKS)) {
v->timetable_start *= DAY_TICKS; v->timetable_start.edit_base() *= DAY_TICKS;
} }
v->timetable_start = DateTicksToScaledDateTicks(v->timetable_start); v->timetable_start = DateTicksToScaledDateTicks(v->timetable_start.base());
if (SlXvIsFeaturePresent(XSLFI_TIMETABLES_START_TICKS, 2, 2)) { if (SlXvIsFeaturePresent(XSLFI_TIMETABLES_START_TICKS, 2, 2)) {
v->timetable_start += _old_timetable_start_subticks_map[v->index]; v->timetable_start += _old_timetable_start_subticks_map[v->index];
@ -4194,7 +4194,7 @@ bool AfterLoadGame()
for (OrderList *order_list : OrderList::Iterate()) { for (OrderList *order_list : OrderList::Iterate()) {
for (DispatchSchedule &ds : order_list->GetScheduledDispatchScheduleSet()) { for (DispatchSchedule &ds : order_list->GetScheduledDispatchScheduleSet()) {
DateTicksScaled start_tick = DateToScaledDateTicks(ds.GetScheduledDispatchStartTick()) + _old_scheduled_dispatch_start_full_date_fract_map[&ds]; DateTicksScaled start_tick = DateToScaledDateTicks(ds.GetScheduledDispatchStartTick().base()) + _old_scheduled_dispatch_start_full_date_fract_map[&ds];
ds.SetScheduledDispatchStartTick(start_tick); ds.SetScheduledDispatchStartTick(start_tick);
} }
} }

@ -104,7 +104,7 @@ struct DATEChunkHandler : ChunkHandler {
this->LoadCommon(_date_check_desc, _date_check_sl_compat); this->LoadCommon(_date_check_desc, _date_check_sl_compat);
if (IsSavegameVersionBefore(SLV_31)) { if (IsSavegameVersionBefore(SLV_31)) {
_load_check_data.current_date += DAYS_TILL_ORIGINAL_BASE_YEAR; _load_check_data.current_date += DAYS_TILL_ORIGINAL_BASE_YEAR.AsDelta();
} }
} }
}; };

@ -674,9 +674,9 @@ bool DispatchSchedule::UpdateScheduledDispatchToDate(DateTicksScaled now)
{ {
bool update_windows = false; bool update_windows = false;
if (this->GetScheduledDispatchStartTick() == 0) { if (this->GetScheduledDispatchStartTick() == 0) {
DateTicksScaled start = now - (now % this->GetScheduledDispatchDuration()); DateTicksScaled start = now - (now.base() % this->GetScheduledDispatchDuration());
this->SetScheduledDispatchStartTick(start); this->SetScheduledDispatchStartTick(start);
int64 last_dispatch = -start; int64 last_dispatch = -(start.base());
if (last_dispatch < INT_MIN && _settings_game.game_time.time_in_minutes) { if (last_dispatch < INT_MIN && _settings_game.game_time.time_in_minutes) {
/* Advance by multiples of 24 hours */ /* Advance by multiples of 24 hours */
const int64 day = 24 * 60 * _settings_game.game_time.ticks_per_minute; const int64 day = 24 * 60 * _settings_game.game_time.ticks_per_minute;

@ -67,7 +67,7 @@ enum SchdispatchWidgets {
*/ */
static void SetScheduleStartDateIntl(uint32 p1, DateTicksScaled date) static void SetScheduleStartDateIntl(uint32 p1, DateTicksScaled date)
{ {
DoCommandPEx(0, p1, 0, (uint64)date, CMD_SCHEDULED_DISPATCH_SET_START_DATE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), nullptr, nullptr, 0); DoCommandPEx(0, p1, 0, (uint64)date.base(), CMD_SCHEDULED_DISPATCH_SET_START_DATE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), nullptr, nullptr, 0);
} }
/** /**
@ -102,12 +102,12 @@ static void ScheduleAddIntl(uint32 p1, DateTicksScaled date, uint extra_slots, u
if (extra_slots > 0 && offset > 0 && !wrap_mode) { if (extra_slots > 0 && offset > 0 && !wrap_mode) {
DateTicksScaled end_tick = start_tick + duration; DateTicksScaled end_tick = start_tick + duration;
DateTicksScaled max_extra_slots = (end_tick - 1 - date) / offset; int64 max_extra_slots = (end_tick - 1 - date).base() / offset;
if (max_extra_slots < extra_slots) extra_slots = static_cast<uint>(std::max<DateTicksScaled>(0, max_extra_slots)); if (max_extra_slots < extra_slots) extra_slots = static_cast<uint>(std::max<int64>(0, max_extra_slots));
extra_slots = std::min<uint>(extra_slots, UINT16_MAX); extra_slots = std::min<uint>(extra_slots, UINT16_MAX);
} }
DoCommandPEx(0, p1, (uint32)(date - start_tick), (((uint64)extra_slots) << 32) | offset, CMD_SCHEDULED_DISPATCH_ADD | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), nullptr, nullptr, 0); DoCommandPEx(0, p1, (uint32)(date - start_tick).base(), (((uint64)extra_slots) << 32) | offset, CMD_SCHEDULED_DISPATCH_ADD | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), nullptr, nullptr, 0);
} }
/** /**
@ -163,18 +163,16 @@ static void AddNewScheduledDispatchSchedule(VehicleID vindex)
if (_settings_time.time_in_minutes) { if (_settings_time.time_in_minutes) {
/* Set to 00:00 of today, and 1 day */ /* Set to 00:00 of today, and 1 day */
start_tick = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), 0, 0); start_tick = _settings_time.FromTickMinutes(_settings_time.NowInTickMinutes().ToSameDayClockTime(0, 0));
start_tick -= _settings_time.clock_offset;
start_tick *= _settings_time.ticks_per_minute;
duration = 24 * 60 * _settings_time.ticks_per_minute; duration = 24 * 60 * _settings_time.ticks_per_minute;
} else { } else {
/* Set Jan 1st and 365 day */ /* Set Jan 1st and 365 day */
start_tick = DateToScaledDateTicks(DAYS_TILL(_cur_year)); start_tick = DateToScaledDateTicks(DateAtStartOfYear(_cur_year));
duration = 365 * DAY_TICKS; duration = 365 * DAY_TICKS;
} }
DoCommandPEx(0, vindex, duration, (uint64)start_tick, CMD_SCHEDULED_DISPATCH_ADD_NEW_SCHEDULE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), CcAddNewSchDispatchSchedule, nullptr, 0); DoCommandPEx(0, vindex, duration, (uint64)start_tick.base(), CMD_SCHEDULED_DISPATCH_ADD_NEW_SCHEDULE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), CcAddNewSchDispatchSchedule, nullptr, 0);
} }
struct SchdispatchWindow : GeneralVehicleWindow { struct SchdispatchWindow : GeneralVehicleWindow {
@ -700,12 +698,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
case WID_SCHDISPATCH_SET_START_DATE: { case WID_SCHDISPATCH_SET_START_DATE: {
if (!this->IsScheduleSelected()) break; if (!this->IsScheduleSelected()) break;
if (_settings_time.time_in_minutes && _settings_client.gui.timetable_start_text_entry) { if (_settings_time.time_in_minutes && _settings_client.gui.timetable_start_text_entry) {
uint64 time = _scaled_date_ticks; SetDParam(0, _settings_time.NowInTickMinutes().ClockHHMM());
time /= _settings_time.ticks_per_minute;
time += _settings_time.clock_offset;
time %= (24 * 60);
time = (time % 60) + (((time / 60) % 24) * 100);
SetDParam(0, time);
ShowQueryString(STR_JUST_INT, STR_SCHDISPATCH_START_CAPTION_MINUTE, 31, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED); ShowQueryString(STR_JUST_INT, STR_SCHDISPATCH_START_CAPTION_MINUTE, 31, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED);
} else { } else {
ShowSetDateWindow(this, v->index | (this->schedule_index << 20), _scaled_date_ticks, _cur_year, _cur_year + 15, SetScheduleStartDateCallback, STR_SCHDISPATCH_SET_START, STR_SCHDISPATCH_START_TOOLTIP); ShowSetDateWindow(this, v->index | (this->schedule_index << 20), _scaled_date_ticks, _cur_year, _cur_year + 15, SetScheduleStartDateCallback, STR_SCHDISPATCH_SET_START, STR_SCHDISPATCH_START_TOOLTIP);
@ -867,9 +860,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
if (val >= 0 && end != nullptr && *end == 0) { if (val >= 0 && end != nullptr && *end == 0) {
uint minutes = (val % 100) % 60; uint minutes = (val % 100) % 60;
uint hours = (val / 100) % 24; uint hours = (val / 100) % 24;
DateTicksScaled slot = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), hours, minutes); DateTicksScaled slot = _settings_time.FromTickMinutes(_settings_time.NowInTickMinutes().ToSameDayClockTime(hours, minutes));
slot -= _settings_time.clock_offset;
slot *= _settings_time.ticks_per_minute;
ScheduleAddIntl(v->index | (this->schedule_index << 20), slot, 0, 0); ScheduleAddIntl(v->index | (this->schedule_index << 20), slot, 0, 0);
} }
break; break;
@ -885,9 +876,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
if (val >= 0 && end != nullptr && *end == 0) { if (val >= 0 && end != nullptr && *end == 0) {
uint minutes = (val % 100) % 60; uint minutes = (val % 100) % 60;
uint hours = (val / 100) % 24; uint hours = (val / 100) % 24;
DateTicksScaled start = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), hours, minutes); DateTicksScaled start = _settings_time.FromTickMinutes(_settings_time.NowInTickMinutes().ToSameDayClockTime(hours, minutes));
start -= _settings_time.clock_offset;
start *= _settings_time.ticks_per_minute;
SetScheduleStartDateIntl(v->index | (this->schedule_index << 20), start); SetScheduleStartDateIntl(v->index | (this->schedule_index << 20), start);
} }
break; break;
@ -974,9 +963,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
} }
if (end < start || step == 0 || !this->IsScheduleSelected()) return; if (end < start || step == 0 || !this->IsScheduleSelected()) return;
DateTicksScaled slot = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), 0, start); DateTicksScaled slot = _settings_time.FromTickMinutes(_settings_time.NowInTickMinutes().ToSameDayClockTime(0, start));
slot -= _settings_time.clock_offset;
slot *= _settings_time.ticks_per_minute;
ScheduleAddIntl(this->vehicle->index | (this->schedule_index << 20), slot, (end - start) / step, step * _settings_time.ticks_per_minute, wrap_mode); ScheduleAddIntl(this->vehicle->index | (this->schedule_index << 20), slot, (end - start) / step, step * _settings_time.ticks_per_minute, wrap_mode);
} }
}; };
@ -1075,14 +1062,14 @@ enum ScheduledDispatchAddSlotsWindowWidgets {
}; };
struct ScheduledDispatchAddSlotsWindow : Window { struct ScheduledDispatchAddSlotsWindow : Window {
uint start; ClockFaceMinutes start;
uint step; ClockFaceMinutes step;
uint end; ClockFaceMinutes end;
ScheduledDispatchAddSlotsWindow(WindowDesc *desc, WindowNumber window_number, SchdispatchWindow *parent) : ScheduledDispatchAddSlotsWindow(WindowDesc *desc, WindowNumber window_number, SchdispatchWindow *parent) :
Window(desc) Window(desc)
{ {
this->start = (_scaled_date_ticks / _settings_time.ticks_per_minute) % (60 * 24); this->start = _settings_time.NowInTickMinutes().ToClockFaceMinutes();
this->step = 30; this->step = 30;
this->end = this->start + 60; this->end = this->start + 60;
this->parent = parent; this->parent = parent;
@ -1137,33 +1124,33 @@ struct ScheduledDispatchAddSlotsWindow : Window {
virtual void SetStringParameters(int widget) const override virtual void SetStringParameters(int widget) const override
{ {
switch (widget) { switch (widget) {
case WID_SCHDISPATCH_ADD_SLOT_START_HOUR: SetDParam(0, MINUTES_HOUR(start)); break; case WID_SCHDISPATCH_ADD_SLOT_START_HOUR: SetDParam(0, start.ClockHour()); break;
case WID_SCHDISPATCH_ADD_SLOT_START_MINUTE: SetDParam(0, MINUTES_MINUTE(start)); break; case WID_SCHDISPATCH_ADD_SLOT_START_MINUTE: SetDParam(0, start.ClockMinute()); break;
case WID_SCHDISPATCH_ADD_SLOT_STEP_HOUR: SetDParam(0, MINUTES_HOUR(step)); break; case WID_SCHDISPATCH_ADD_SLOT_STEP_HOUR: SetDParam(0, step.ClockHour()); break;
case WID_SCHDISPATCH_ADD_SLOT_STEP_MINUTE: SetDParam(0, MINUTES_MINUTE(step)); break; case WID_SCHDISPATCH_ADD_SLOT_STEP_MINUTE: SetDParam(0, step.ClockMinute()); break;
case WID_SCHDISPATCH_ADD_SLOT_END_HOUR: SetDParam(0, MINUTES_HOUR(end)); break; case WID_SCHDISPATCH_ADD_SLOT_END_HOUR: SetDParam(0, end.ClockHour()); break;
case WID_SCHDISPATCH_ADD_SLOT_END_MINUTE: SetDParam(0, MINUTES_MINUTE(end)); break; case WID_SCHDISPATCH_ADD_SLOT_END_MINUTE: SetDParam(0, end.ClockMinute()); break;
} }
} }
virtual void OnClick(Point pt, int widget, int click_count) override virtual void OnClick(Point pt, int widget, int click_count) override
{ {
auto handle_hours_dropdown = [&](uint current) { auto handle_hours_dropdown = [&](ClockFaceMinutes current) {
DropDownList list; DropDownList list;
for (uint i = 0; i < 24; i++) { for (uint i = 0; i < 24; i++) {
SetDParam(0, i); SetDParam(0, i);
list.emplace_back(new DropDownListStringItem(STR_JUST_INT, i, false)); list.emplace_back(new DropDownListStringItem(STR_JUST_INT, i, false));
} }
ShowDropDownList(this, std::move(list), MINUTES_HOUR(current), widget); ShowDropDownList(this, std::move(list), current.ClockHour(), widget);
}; };
auto handle_minutes_dropdown = [&](uint current) { auto handle_minutes_dropdown = [&](ClockFaceMinutes current) {
DropDownList list; DropDownList list;
for (uint i = 0; i < 60; i++) { for (uint i = 0; i < 60; i++) {
SetDParam(0, i); SetDParam(0, i);
list.emplace_back(new DropDownListStringItem(STR_JUST_INT, i, false)); list.emplace_back(new DropDownListStringItem(STR_JUST_INT, i, false));
} }
ShowDropDownList(this, std::move(list), MINUTES_MINUTE(current), widget); ShowDropDownList(this, std::move(list), current.ClockMinute(), widget);
}; };
switch (widget) { switch (widget) {
@ -1187,7 +1174,7 @@ struct ScheduledDispatchAddSlotsWindow : Window {
break; break;
case WID_SCHDISPATCH_ADD_SLOT_ADD_BUTTON: case WID_SCHDISPATCH_ADD_SLOT_ADD_BUTTON:
static_cast<SchdispatchWindow *>(this->parent)->AddMultipleDepartureSlots(this->start, this->step, this->end); static_cast<SchdispatchWindow *>(this->parent)->AddMultipleDepartureSlots(this->start.base(), this->step.base(), this->end.base());
this->Close(); this->Close();
break; break;
} }
@ -1197,22 +1184,22 @@ struct ScheduledDispatchAddSlotsWindow : Window {
{ {
switch (widget) { switch (widget) {
case WID_SCHDISPATCH_ADD_SLOT_START_HOUR: case WID_SCHDISPATCH_ADD_SLOT_START_HOUR:
this->start = MINUTES_DATE(0, index, MINUTES_MINUTE(this->start)); this->start = ClockFaceMinutes::FromClockFace(index, this->start.ClockMinute());
break; break;
case WID_SCHDISPATCH_ADD_SLOT_START_MINUTE: case WID_SCHDISPATCH_ADD_SLOT_START_MINUTE:
this->start = MINUTES_DATE(0, MINUTES_HOUR(this->start), index); this->start = ClockFaceMinutes::FromClockFace(this->start.ClockHour(), index);
break; break;
case WID_SCHDISPATCH_ADD_SLOT_STEP_HOUR: case WID_SCHDISPATCH_ADD_SLOT_STEP_HOUR:
this->step = MINUTES_DATE(0, index, MINUTES_MINUTE(this->step)); this->step = ClockFaceMinutes::FromClockFace(index, this->step.ClockMinute());
break; break;
case WID_SCHDISPATCH_ADD_SLOT_STEP_MINUTE: case WID_SCHDISPATCH_ADD_SLOT_STEP_MINUTE:
this->step = MINUTES_DATE(0, MINUTES_HOUR(this->step), index); this->step = ClockFaceMinutes::FromClockFace(this->step.ClockHour(), index);
break; break;
case WID_SCHDISPATCH_ADD_SLOT_END_HOUR: case WID_SCHDISPATCH_ADD_SLOT_END_HOUR:
this->end = MINUTES_DATE(0, index, MINUTES_MINUTE(this->end)); this->end = ClockFaceMinutes::FromClockFace(index, this->end.ClockMinute());
break; break;
case WID_SCHDISPATCH_ADD_SLOT_END_MINUTE: case WID_SCHDISPATCH_ADD_SLOT_END_MINUTE:
this->end = MINUTES_DATE(0, MINUTES_HOUR(this->end), index); this->end = ClockFaceMinutes::FromClockFace(this->end.ClockHour(), index);
break; break;
} }

@ -57,5 +57,5 @@
{ {
if (!IsValidBaseStation(station_id)) return ScriptDate::DATE_INVALID; if (!IsValidBaseStation(station_id)) return ScriptDate::DATE_INVALID;
return (ScriptDate::Date)::BaseStation::Get(station_id)->build_date; return (ScriptDate::Date)::BaseStation::Get(station_id)->build_date.base();
} }

@ -50,5 +50,5 @@ static NetworkClientInfo *FindClientInfo(ScriptClient::ClientID client)
{ {
NetworkClientInfo *ci = FindClientInfo(client); NetworkClientInfo *ci = FindClientInfo(client);
if (ci == nullptr) return ScriptDate::DATE_INVALID; if (ci == nullptr) return ScriptDate::DATE_INVALID;
return (ScriptDate::Date)ci->join_date; return (ScriptDate::Date)ci->join_date.base();
} }

@ -25,7 +25,7 @@
/* static */ ScriptDate::Date ScriptDate::GetCurrentDate() /* static */ ScriptDate::Date ScriptDate::GetCurrentDate()
{ {
return (ScriptDate::Date)_date; return (ScriptDate::Date)_date.base();
} }
/* static */ SQInteger ScriptDate::GetDayLengthFactor() /* static */ SQInteger ScriptDate::GetDayLengthFactor()
@ -66,7 +66,7 @@
if (day_of_month < 1 || day_of_month > 31) return DATE_INVALID; if (day_of_month < 1 || day_of_month > 31) return DATE_INVALID;
if (year < 0 || year > MAX_YEAR) return DATE_INVALID; if (year < 0 || year > MAX_YEAR) return DATE_INVALID;
return (ScriptDate::Date)::ConvertYMDToDate(year, month - 1, day_of_month); return (ScriptDate::Date)::ConvertYMDToDate(year, month - 1, day_of_month).base();
} }
/* static */ SQInteger ScriptDate::GetSystemTime() /* static */ SQInteger ScriptDate::GetSystemTime()
@ -88,17 +88,17 @@
/* static */ SQInteger ScriptDate::GetCurrentScaledDateTicks() /* static */ SQInteger ScriptDate::GetCurrentScaledDateTicks()
{ {
return _scaled_date_ticks; return _scaled_date_ticks.base();
} }
/* static */ SQInteger ScriptDate::GetHour(DateTicksScaled ticks) /* static */ SQInteger ScriptDate::GetHour(SQInteger ticks)
{ {
Minutes minutes = (ticks / _settings_game.game_time.ticks_per_minute) + _settings_game.game_time.clock_offset; TickMinutes minutes = _settings_game.game_time.ToTickMinutes(DateTicksScaled(ticks));
return MINUTES_HOUR(minutes); return minutes.ClockHour();
} }
/* static */ SQInteger ScriptDate::GetMinute(DateTicksScaled ticks) /* static */ SQInteger ScriptDate::GetMinute(SQInteger ticks)
{ {
Minutes minutes = (ticks / _settings_game.game_time.ticks_per_minute) + _settings_game.game_time.clock_offset; TickMinutes minutes = _settings_game.game_time.ToTickMinutes(DateTicksScaled(ticks));
return MINUTES_MINUTE(minutes); return minutes.ClockMinute();
} }

@ -31,7 +31,7 @@ public:
* compose valid date values for a known year, month and day. * compose valid date values for a known year, month and day.
*/ */
enum Date { enum Date {
DATE_INVALID = ::INVALID_DATE, ///< A value representing an invalid date. DATE_INVALID = ::INVALID_DATE.base(), ///< A value representing an invalid date.
}; };
/** /**
@ -96,9 +96,9 @@ public:
static SQInteger GetCurrentScaledDateTicks(); static SQInteger GetCurrentScaledDateTicks();
static SQInteger GetHour(DateTicksScaled ticks); static SQInteger GetHour(SQInteger ticks);
static SQInteger GetMinute(DateTicksScaled ticks); static SQInteger GetMinute(SQInteger ticks);
}; };
#endif /* SCRIPT_DATE_HPP */ #endif /* SCRIPT_DATE_HPP */

@ -131,7 +131,7 @@
if (!IsValidEngine(engine_id)) return -1; if (!IsValidEngine(engine_id)) return -1;
if (GetVehicleType(engine_id) == ScriptVehicle::VT_RAIL && IsWagon(engine_id)) return -1; if (GetVehicleType(engine_id) == ScriptVehicle::VT_RAIL && IsWagon(engine_id)) return -1;
return ::Engine::Get(engine_id)->GetLifeLengthInDays(); return ::Engine::Get(engine_id)->GetLifeLengthInDays().base();
} }
/* static */ Money ScriptEngine::GetRunningCost(EngineID engine_id) /* static */ Money ScriptEngine::GetRunningCost(EngineID engine_id)
@ -171,7 +171,7 @@
{ {
if (!IsValidEngine(engine_id)) return ScriptDate::DATE_INVALID; if (!IsValidEngine(engine_id)) return ScriptDate::DATE_INVALID;
return (ScriptDate::Date)::Engine::Get(engine_id)->intro_date; return (ScriptDate::Date)::Engine::Get(engine_id)->intro_date.base();
} }
/* static */ ScriptVehicle::VehicleType ScriptEngine::GetVehicleType(EngineID engine_id) /* static */ ScriptVehicle::VehicleType ScriptEngine::GetVehicleType(EngineID engine_id)

@ -52,7 +52,7 @@
{ {
Industry *i = Industry::GetIfValid(industry_id); Industry *i = Industry::GetIfValid(industry_id);
if (i == nullptr) return ScriptDate::DATE_INVALID; if (i == nullptr) return ScriptDate::DATE_INVALID;
return (ScriptDate::Date)i->construction_date; return (ScriptDate::Date)i->construction_date.base();
} }
/* static */ bool ScriptIndustry::SetText(IndustryID industry_id, Text *text) /* static */ bool ScriptIndustry::SetText(IndustryID industry_id, Text *text)
@ -230,11 +230,11 @@
if (i == nullptr) return ScriptDate::DATE_INVALID; if (i == nullptr) return ScriptDate::DATE_INVALID;
if (cargo_type == CT_INVALID) { if (cargo_type == CT_INVALID) {
return (ScriptDate::Date)std::accumulate(std::begin(i->last_cargo_accepted_at), std::end(i->last_cargo_accepted_at), 0, [](Date a, Date b) { return std::max(a, b); }); return (ScriptDate::Date)std::accumulate(std::begin(i->last_cargo_accepted_at), std::end(i->last_cargo_accepted_at), Date(0), [](Date a, Date b) { return std::max(a, b); }).base();
} else { } else {
int index = i->GetCargoAcceptedIndex(cargo_type); int index = i->GetCargoAcceptedIndex(cargo_type);
if (index < 0) return ScriptDate::DATE_INVALID; if (index < 0) return ScriptDate::DATE_INVALID;
return (ScriptDate::Date)i->last_cargo_accepted_at[index]; return (ScriptDate::Date)i->last_cargo_accepted_at[index].base();
} }
} }

@ -190,7 +190,7 @@ static inline bool StoryPageElementTypeRequiresText(StoryPageElementType type)
EnforcePrecondition(ScriptDate::DATE_INVALID, IsValidStoryPage(story_page_id)); EnforcePrecondition(ScriptDate::DATE_INVALID, IsValidStoryPage(story_page_id));
EnforceDeityMode(ScriptDate::DATE_INVALID); EnforceDeityMode(ScriptDate::DATE_INVALID);
return (ScriptDate::Date)StoryPage::Get(story_page_id)->date; return (ScriptDate::Date)StoryPage::Get(story_page_id)->date.base();
} }
/* static */ bool ScriptStoryPage::SetDate(StoryPageID story_page_id, ScriptDate::Date date) /* static */ bool ScriptStoryPage::SetDate(StoryPageID story_page_id, ScriptDate::Date date)

@ -310,7 +310,7 @@
{ {
if (!IsValidVehicle(vehicle_id)) return -1; if (!IsValidVehicle(vehicle_id)) return -1;
return ::Vehicle::Get(vehicle_id)->age; return ::Vehicle::Get(vehicle_id)->age.base();
} }
/* static */ SQInteger ScriptVehicle::GetWagonAge(VehicleID vehicle_id, SQInteger wagon) /* static */ SQInteger ScriptVehicle::GetWagonAge(VehicleID vehicle_id, SQInteger wagon)
@ -322,21 +322,21 @@
if (v->type == VEH_TRAIN) { if (v->type == VEH_TRAIN) {
while (wagon-- > 0) v = ::Train::From(v)->GetNextUnit(); while (wagon-- > 0) v = ::Train::From(v)->GetNextUnit();
} }
return v->age; return v->age.base();
} }
/* static */ SQInteger ScriptVehicle::GetMaxAge(VehicleID vehicle_id) /* static */ SQInteger ScriptVehicle::GetMaxAge(VehicleID vehicle_id)
{ {
if (!IsPrimaryVehicle(vehicle_id)) return -1; if (!IsPrimaryVehicle(vehicle_id)) return -1;
return ::Vehicle::Get(vehicle_id)->max_age; return ::Vehicle::Get(vehicle_id)->max_age.base();
} }
/* static */ SQInteger ScriptVehicle::GetAgeLeft(VehicleID vehicle_id) /* static */ SQInteger ScriptVehicle::GetAgeLeft(VehicleID vehicle_id)
{ {
if (!IsPrimaryVehicle(vehicle_id)) return -1; if (!IsPrimaryVehicle(vehicle_id)) return -1;
return ::Vehicle::Get(vehicle_id)->max_age - ::Vehicle::Get(vehicle_id)->age; return ::Vehicle::Get(vehicle_id)->max_age.base() - ::Vehicle::Get(vehicle_id)->age.base();
} }
/* static */ SQInteger ScriptVehicle::GetCurrentSpeed(VehicleID vehicle_id) /* static */ SQInteger ScriptVehicle::GetCurrentSpeed(VehicleID vehicle_id)

@ -135,6 +135,22 @@ struct TimeSettings {
bool time_in_minutes; ///< whether to use the hh:mm conversion when printing dates bool time_in_minutes; ///< whether to use the hh:mm conversion when printing dates
uint16 ticks_per_minute; ///< how many ticks per minute uint16 ticks_per_minute; ///< how many ticks per minute
uint16 clock_offset; ///< clock offset in minutes uint16 clock_offset; ///< clock offset in minutes
TickMinutes ToTickMinutes(DateTicksScaled ticks) const
{
return (ticks.base() / this->ticks_per_minute) + this->clock_offset;
}
TickMinutes NowInTickMinutes() const
{
extern DateTicksScaled _scaled_date_ticks;
return this->ToTickMinutes(_scaled_date_ticks);
}
DateTicksScaled FromTickMinutes(TickMinutes minutes) const
{
return (minutes.base() - this->clock_offset) * this->ticks_per_minute;
}
}; };
/** Settings related to the GUI and other stuff that is not saved in the savegame. */ /** Settings related to the GUI and other stuff that is not saved in the savegame. */

@ -39,7 +39,7 @@ SaveLoadTable GetLinkGraphDesc()
void GetLinkGraphJobDayLengthScaleAfterLoad(LinkGraphJob *lgj) void GetLinkGraphJobDayLengthScaleAfterLoad(LinkGraphJob *lgj)
{ {
lgj->join_date_ticks *= DAY_TICKS; lgj->join_date_ticks.edit_base() *= DAY_TICKS;
lgj->join_date_ticks += LinkGraphSchedule::SPAWN_JOIN_TICK; lgj->join_date_ticks += LinkGraphSchedule::SPAWN_JOIN_TICK;
uint recalc_scale; uint recalc_scale;

@ -162,7 +162,7 @@ static void Check_DATE()
{ {
SlGlobList(_date_check_desc); SlGlobList(_date_check_desc);
if (IsSavegameVersionBefore(SLV_31)) { if (IsSavegameVersionBefore(SLV_31)) {
_load_check_data.current_date += DAYS_TILL_ORIGINAL_BASE_YEAR; _load_check_data.current_date += DAYS_TILL_ORIGINAL_BASE_YEAR.AsDelta();
} }
} }

@ -395,7 +395,7 @@ static bool FixTTOEngines()
for (uint i = 0; i < lengthof(_orig_aircraft_vehicle_info); i++, j++) new (GetTempDataEngine(j)) Engine(VEH_AIRCRAFT, i); for (uint i = 0; i < lengthof(_orig_aircraft_vehicle_info); i++, j++) new (GetTempDataEngine(j)) Engine(VEH_AIRCRAFT, i);
} }
Date aging_date = std::min(_date + DAYS_TILL_ORIGINAL_BASE_YEAR, ConvertYMDToDate(2050, 0, 1)); Date aging_date = std::min(_date + DAYS_TILL_ORIGINAL_BASE_YEAR.AsDelta(), ConvertYMDToDate(2050, 0, 1));
for (EngineID i = 0; i < 256; i++) { for (EngineID i = 0; i < 256; i++) {
int oi = ttd_to_tto[i]; int oi = ttd_to_tto[i];
@ -403,17 +403,17 @@ static bool FixTTOEngines()
if (oi == 255) { if (oi == 255) {
/* Default engine is used */ /* Default engine is used */
_date += DAYS_TILL_ORIGINAL_BASE_YEAR; _date += DAYS_TILL_ORIGINAL_BASE_YEAR.AsDelta();
StartupOneEngine(e, aging_date, 0, INT_MAX); StartupOneEngine(e, aging_date, 0, INT_MAX);
CalcEngineReliability(e, false); CalcEngineReliability(e, false);
e->intro_date -= DAYS_TILL_ORIGINAL_BASE_YEAR; e->intro_date -= DAYS_TILL_ORIGINAL_BASE_YEAR.AsDelta();
_date -= DAYS_TILL_ORIGINAL_BASE_YEAR; _date -= DAYS_TILL_ORIGINAL_BASE_YEAR.AsDelta();
/* Make sure for example monorail and maglev are available when they should be */ /* Make sure for example monorail and maglev are available when they should be */
if (_date >= e->intro_date && HasBit(e->info.climates, 0)) { if (_date >= e->intro_date && HasBit(e->info.climates, 0)) {
e->flags |= ENGINE_AVAILABLE; e->flags |= ENGINE_AVAILABLE;
e->company_avail = MAX_UVALUE(CompanyMask); e->company_avail = MAX_UVALUE(CompanyMask);
e->age = _date > e->intro_date ? (_date - e->intro_date) / 30 : 0; e->age = _date > e->intro_date ? (_date - e->intro_date).base() / 30 : 0;
} }
} else { } else {
/* Using data from TTO savegame */ /* Using data from TTO savegame */

@ -3726,7 +3726,7 @@ SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop,
} }
if (fop == SLO_SAVE) { // SAVE game if (fop == SLO_SAVE) { // SAVE game
DEBUG(desync, 1, "save: date{%08x; %02x; %02x}; %s", _date, _date_fract, _tick_skip_counter, filename.c_str()); DEBUG(desync, 1, "save: date{%08x; %02x; %02x}; %s", _date.base(), _date_fract, _tick_skip_counter, filename.c_str());
if (!_settings_client.gui.threaded_saves) threaded = false; if (!_settings_client.gui.threaded_saves) threaded = false;
return DoSave(new FileWriter(fh), threaded); return DoSave(new FileWriter(fh), threaded);

@ -17,6 +17,7 @@
#include "../scope.h" #include "../scope.h"
#include "../core/ring_buffer.hpp" #include "../core/ring_buffer.hpp"
#include "../core/tinystring_type.hpp" #include "../core/tinystring_type.hpp"
#include "../core/strong_typedef_type.hpp"
#include <stdarg.h> #include <stdarg.h>
#include <vector> #include <vector>
@ -338,7 +339,7 @@ static inline constexpr bool SlCheckPrimitiveTypeVar(VarType type)
if (GetVarMemType(type) == SLE_VAR_CNAME) { if (GetVarMemType(type) == SLE_VAR_CNAME) {
return std::is_same_v<T, char *> || std::is_same_v<T, const char *> || std::is_same_v<T, TinyString>; return std::is_same_v<T, char *> || std::is_same_v<T, const char *> || std::is_same_v<T, TinyString>;
} }
if (!std::is_integral_v<T> && !std::is_enum_v<T> && !sl_is_instance<T, OverflowSafeInt>{}) return false; if (!std::is_integral_v<T> && !std::is_enum_v<T> && !sl_is_instance<T, OverflowSafeInt>{} && !std::is_base_of_v<StrongTypedefBase, T>) return false;
return sizeof(T) == SlVarSize(type); return sizeof(T) == SlVarSize(type);
} }

@ -4452,11 +4452,11 @@ void DeleteStaleLinks(Station *from)
Station *to = Station::Get((*lg)[to_id].Station()); Station *to = Station::Get((*lg)[to_id].Station());
assert(to->goods[c].node == to_id); assert(to->goods[c].node == to_id);
assert(_date >= edge.LastUpdate()); assert(_date >= edge.LastUpdate());
uint timeout = std::max<uint>((LinkGraph::MIN_TIMEOUT_DISTANCE + (DistanceManhattan(from->xy, to->xy) >> 3)) / _settings_game.economy.day_length_factor, 1); DateDelta timeout = std::max<uint>((LinkGraph::MIN_TIMEOUT_DISTANCE + (DistanceManhattan(from->xy, to->xy) >> 3)) / _settings_game.economy.day_length_factor, 1);
if (edge.LastAircraftUpdate() != INVALID_DATE && (uint)(_date - edge.LastAircraftUpdate()) > timeout) { if (edge.LastAircraftUpdate() != INVALID_DATE && (_date - edge.LastAircraftUpdate()) > timeout) {
edge.ClearAircraft(); edge.ClearAircraft();
} }
if ((uint)(_date - edge.LastUpdate()) > timeout) { if ((_date - edge.LastUpdate()) > timeout) {
bool updated = false; bool updated = false;
if (auto_distributed) { if (auto_distributed) {
@ -4488,7 +4488,7 @@ void DeleteStaleLinks(Station *from)
// Only run LinkRefresher if vehicle was not already in the cache // Only run LinkRefresher if vehicle was not already in the cache
if (res.second) { if (res.second) {
/* Do not refresh links of vehicles that have been stopped in depot for a long time. */ /* Do not refresh links of vehicles that have been stopped in depot for a long time. */
if (!v->IsStoppedInDepot() || static_cast<uint>(_date - v->date_of_last_service) <= if (!v->IsStoppedInDepot() || (_date - v->date_of_last_service) <=
LinkGraph::STALE_LINK_DEPOT_TIMEOUT) { LinkGraph::STALE_LINK_DEPOT_TIMEOUT) {
edge_helper.RecordSize(); edge_helper.RecordSize();
LinkRefresher::Run(v, false); // Don't allow merging. Otherwise lg might get deleted. LinkRefresher::Run(v, false); // Don't allow merging. Otherwise lg might get deleted.
@ -4520,11 +4520,11 @@ void DeleteStaleLinks(Station *from)
if (ge.data != nullptr) ge.data->flows.DeleteFlows(to->index); if (ge.data != nullptr) ge.data->flows.DeleteFlows(to->index);
RerouteCargo(from, c, to->index, from->index); RerouteCargo(from, c, to->index, from->index);
} }
} else if (edge.LastUnrestrictedUpdate() != INVALID_DATE && (uint)(_date - edge.LastUnrestrictedUpdate()) > timeout) { } else if (edge.LastUnrestrictedUpdate() != INVALID_DATE && (_date - edge.LastUnrestrictedUpdate()) > timeout) {
edge.Restrict(); edge.Restrict();
if (ge.data != nullptr) ge.data->flows.RestrictFlows(to->index); if (ge.data != nullptr) ge.data->flows.RestrictFlows(to->index);
RerouteCargo(from, c, to->index, from->index); RerouteCargo(from, c, to->index, from->index);
} else if (edge.LastRestrictedUpdate() != INVALID_DATE && (uint)(_date - edge.LastRestrictedUpdate()) > timeout) { } else if (edge.LastRestrictedUpdate() != INVALID_DATE && (_date - edge.LastRestrictedUpdate()) > timeout) {
edge.Release(); edge.Release();
} }
@ -4643,7 +4643,7 @@ void OnTick_Station()
void StationDailyLoop() void StationDailyLoop()
{ {
// Only record cargo history every second day. // Only record cargo history every second day.
if (_date % 2 != 0) { if (_date.base() % 2 != 0) {
for (Station *st : Station::Iterate()) { for (Station *st : Station::Iterate()) {
st->UpdateCargoHistory(); st->UpdateCargoHistory();
} }

@ -59,7 +59,7 @@ struct StatusBarWindow : Window {
int ticker_scroll; int ticker_scroll;
GUITimer ticker_timer; GUITimer ticker_timer;
GUITimer reminder_timeout; GUITimer reminder_timeout;
int64 last_minute = 0; TickMinutes last_minute = 0;
static const int TICKER_STOP = 1640; ///< scrolling is finished when counter reaches this value static const int TICKER_STOP = 1640; ///< scrolling is finished when counter reaches this value
static const int REMINDER_START = 1350; ///< time in ms for reminder notification (red dot on the right) to stay static const int REMINDER_START = 1350; ///< time in ms for reminder notification (red dot on the right) to stay
@ -211,9 +211,12 @@ struct StatusBarWindow : Window {
{ {
if (_pause_mode != PM_UNPAUSED) return; if (_pause_mode != PM_UNPAUSED) return;
if (_settings_time.time_in_minutes && this->last_minute != CURRENT_MINUTE) { if (_settings_time.time_in_minutes) {
this->last_minute = CURRENT_MINUTE; const TickMinutes now = _settings_time.NowInTickMinutes();
this->SetWidgetDirty(WID_S_LEFT); if (this->last_minute != now) {
this->last_minute = now;
this->SetWidgetDirty(WID_S_LEFT);
}
} }
if (this->ticker_scroll < TICKER_STOP) { // Scrolling text if (this->ticker_scroll < TICKER_STOP) { // Scrolling text

@ -413,6 +413,7 @@ static_assert(SIZE_MAX >= UINT32_MAX);
# define GetString OTTD_GetString # define GetString OTTD_GetString
# define DrawString OTTD_DrawString # define DrawString OTTD_DrawString
# define CloseConnection OTTD_CloseConnection # define CloseConnection OTTD_CloseConnection
# define DateDelta OTTD_DateDelta
#endif /* __APPLE__ */ #endif /* __APPLE__ */
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)

@ -454,16 +454,19 @@ static char *FormatBytes(char *buff, int64 number, const char *last)
static char *FormatWallClockString(char *buff, DateTicksScaled ticks, const char *last, bool show_date, uint case_index) static char *FormatWallClockString(char *buff, DateTicksScaled ticks, const char *last, bool show_date, uint case_index)
{ {
Minutes minutes = ticks / _settings_time.ticks_per_minute + _settings_time.clock_offset; TickMinutes minutes = _settings_time.ToTickMinutes(ticks);
char hour[3], minute[3]; char hour[3], minute[3];
seprintf(hour, lastof(hour), "%02i", (int) MINUTES_HOUR(minutes) ); seprintf(hour, lastof(hour), "%02i", minutes.ClockHour());
seprintf(minute, lastof(minute), "%02i", (int) MINUTES_MINUTE(minutes)); seprintf(minute, lastof(minute), "%02i", minutes.ClockMinute());
if (show_date) { if (show_date) {
int64 final_arg = ScaledDateTicksToDate(ticks); Date date = ScaledDateTicksToDate(ticks);
int64 final_arg;
if (_settings_client.gui.date_with_time == 1) { if (_settings_client.gui.date_with_time == 1) {
YearMonthDay ymd; YearMonthDay ymd;
ConvertDateToYMD(final_arg, &ymd); ConvertDateToYMD(date, &ymd);
final_arg = ymd.year; final_arg = ymd.year;
} else {
final_arg = date.base();
} }
auto tmp_params = MakeParameters(hour, minute, final_arg); auto tmp_params = MakeParameters(hour, minute, final_arg);
return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_MINUTES + _settings_client.gui.date_with_time), tmp_params, last, case_index); return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_MINUTES + _settings_client.gui.date_with_time), tmp_params, last, case_index);

@ -94,9 +94,9 @@ WChar GetDecimalSeparatorChar();
* @param n Index of the string parameter. * @param n Index of the string parameter.
* @param v Value of the string parameter. * @param v Value of the string parameter.
*/ */
static inline void SetDParam(size_t n, uint64 v) template <typename T>
{ static inline void SetDParam(size_t n, T &&v) {
_global_string_params.SetParam(n, v); _global_string_params.SetParam(n, std::forward<T>(v));
} }
void SetDParamMaxValue(size_t n, uint64 max_value, uint min_count = 0, FontSize size = FS_NORMAL); void SetDParamMaxValue(size_t n, uint64 max_value, uint min_count = 0, FontSize size = FS_NORMAL);

@ -13,6 +13,7 @@
#include "strings_func.h" #include "strings_func.h"
#include "string_func.h" #include "string_func.h"
#include "core/span_type.hpp" #include "core/span_type.hpp"
#include "core/strong_typedef_type.hpp"
#include <array> #include <array>
@ -155,11 +156,11 @@ public:
this->parameters[n].string_view = nullptr; this->parameters[n].string_view = nullptr;
} }
//template <typename T, std::enable_if_t<std::is_base_of<StrongTypedefBase, T>::value, int> = 0> template <typename T, std::enable_if_t<std::is_base_of<StrongTypedefBase, T>::value, int> = 0>
//void SetParam(size_t n, T v) void SetParam(size_t n, T v)
//{ {
// SetParam(n, v.base()); SetParam(n, v.base());
//} }
void SetParam(size_t n, const char *str) void SetParam(size_t n, const char *str)
{ {

@ -1428,7 +1428,7 @@ class NIHSignals : public NIHelper {
if (it.second.IsOutOfDate()) { if (it.second.IsOutOfDate()) {
b += seprintf(b, lastof(buffer), ", expired"); b += seprintf(b, lastof(buffer), ", expired");
} else { } else {
b += seprintf(b, lastof(buffer), ", expires in %u ticks", (uint)(it.second.time_stamp - _scaled_date_ticks)); b += seprintf(b, lastof(buffer), ", expires in %u ticks", (uint)(it.second.time_stamp - _scaled_date_ticks).base());
} }
output.print(buffer); output.print(buffer);
} }
@ -1532,7 +1532,7 @@ class NIHObject : public NIHelper {
ConvertDateToYMD(spec->introduction_date, &ymd); ConvertDateToYMD(spec->introduction_date, &ymd);
char *b = buffer + seprintf(buffer, lastof(buffer), " intro: %4i-%02i-%02i", char *b = buffer + seprintf(buffer, lastof(buffer), " intro: %4i-%02i-%02i",
ymd.year, ymd.month + 1, ymd.day); ymd.year, ymd.month + 1, ymd.day);
if (spec->end_of_life_date < MAX_DAY) { if (spec->end_of_life_date < MAX_DATE) {
ConvertDateToYMD(spec->end_of_life_date, &ymd); ConvertDateToYMD(spec->end_of_life_date, &ymd);
seprintf(b, lastof(buffer), ", end of life: %4i-%02i-%02i", seprintf(b, lastof(buffer), ", end of life: %4i-%02i-%02i",
ymd.year, ymd.month + 1, ymd.day); ymd.year, ymd.month + 1, ymd.day);

@ -121,7 +121,7 @@ static const DrawTileSprites _object_hq[] = {
#undef TILE_SPRITE_LINE #undef TILE_SPRITE_LINE
#define M(name, size, build_cost_multiplier, clear_cost_multiplier, height, climate, gen_amount, flags) { GRFFilePropsBase<2>(), {0, 0, 0, 0}, INVALID_OBJECT_CLASS, name, climate, size, build_cost_multiplier, clear_cost_multiplier, 0, MAX_DAY + 1, flags, OBJECT_CTRL_FLAG_NONE, {0, 0, 0, 0}, 0, height, 1, gen_amount, OVMT_DEFAULT, 0 } #define M(name, size, build_cost_multiplier, clear_cost_multiplier, height, climate, gen_amount, flags) { GRFFilePropsBase<2>(), {0, 0, 0, 0}, INVALID_OBJECT_CLASS, name, climate, size, build_cost_multiplier, clear_cost_multiplier, 0, MAX_DATE + 1, flags, OBJECT_CTRL_FLAG_NONE, {0, 0, 0, 0}, 0, height, 1, gen_amount, OVMT_DEFAULT, 0 }
/* Climates /* Climates
* T = Temperate * T = Temperate

@ -863,8 +863,8 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling)
if (slot > -1) { if (slot > -1) {
just_started = !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED); just_started = !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED); SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
v->lateness_counter = _scaled_date_ticks - slot + wait_offset; v->lateness_counter = (_scaled_date_ticks - slot + wait_offset).AsTicks();
ds.SetScheduledDispatchLastDispatch(slot - ds.GetScheduledDispatchStartTick()); ds.SetScheduledDispatchLastDispatch((slot - ds.GetScheduledDispatchStartTick()).AsTicks());
set_scheduled_dispatch = true; set_scheduled_dispatch = true;
} }
} }
@ -890,7 +890,7 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling)
if (!set_scheduled_dispatch) just_started = !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED); if (!set_scheduled_dispatch) just_started = !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
if (v->timetable_start != 0) { if (v->timetable_start != 0) {
v->lateness_counter = (int32)(_scaled_date_ticks - v->timetable_start); v->lateness_counter = (_scaled_date_ticks - v->timetable_start).AsTicks();
v->timetable_start = 0; v->timetable_start = 0;
} }

@ -198,7 +198,7 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID
DateTicksScaled slot = GetScheduledDispatchTime(predicted_ds, _scaled_date_ticks + sum + order->GetTimetabledWait()); DateTicksScaled slot = GetScheduledDispatchTime(predicted_ds, _scaled_date_ticks + sum + order->GetTimetabledWait());
predicted_ds.ReturnSchedule(ds); predicted_ds.ReturnSchedule(ds);
if (slot <= -1) return; if (slot <= -1) return;
sum = slot - _scaled_date_ticks; sum = (slot - _scaled_date_ticks).AsTicks();
predicted = true; predicted = true;
no_offset = true; no_offset = true;
} else { } else {
@ -239,7 +239,7 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID
*/ */
static void ChangeTimetableStartIntl(uint32 p1, DateTicksScaled date) static void ChangeTimetableStartIntl(uint32 p1, DateTicksScaled date)
{ {
DoCommandPEx(0, p1, 0, (uint64)date, CMD_SET_TIMETABLE_START | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), nullptr, nullptr, 0); DoCommandPEx(0, p1, 0, (uint64)date.base(), CMD_SET_TIMETABLE_START | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), nullptr, nullptr, 0);
} }
/** /**
@ -906,12 +906,7 @@ struct TimetableWindow : GeneralVehicleWindow {
if (_settings_time.time_in_minutes && _settings_client.gui.timetable_start_text_entry) { if (_settings_time.time_in_minutes && _settings_client.gui.timetable_start_text_entry) {
this->set_start_date_all = v->orders->IsCompleteTimetable() && _ctrl_pressed; this->set_start_date_all = v->orders->IsCompleteTimetable() && _ctrl_pressed;
StringID str = STR_JUST_INT; StringID str = STR_JUST_INT;
uint64 time = _scaled_date_ticks; SetDParam(0, _settings_time.NowInTickMinutes().ClockHHMM());
time /= _settings_time.ticks_per_minute;
time += _settings_time.clock_offset;
time %= (24 * 60);
time = (time % 60) + (((time / 60) % 24) * 100);
SetDParam(0, time);
ShowQueryString(str, STR_TIMETABLE_STARTING_DATE, 31, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED); ShowQueryString(str, STR_TIMETABLE_STARTING_DATE, 31, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED);
} else { } else {
ShowSetDateWindow(this, v->index | (_ctrl_pressed ? 1U << 20 : 0), ShowSetDateWindow(this, v->index | (_ctrl_pressed ? 1U << 20 : 0),
@ -1123,12 +1118,12 @@ struct TimetableWindow : GeneralVehicleWindow {
if (val >= 0 && end && *end == 0) { if (val >= 0 && end && *end == 0) {
uint minutes = (val % 100) % 60; uint minutes = (val % 100) % 60;
uint hours = (val / 100) % 24; uint hours = (val / 100) % 24;
DateTicksScaled time = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), hours, minutes); const TickMinutes now = _settings_time.NowInTickMinutes();
time -= _settings_time.clock_offset; TickMinutes time = now.ToSameDayClockTime(hours, minutes);
if (time < (CURRENT_MINUTE - 60)) time += 60 * 24; if (time < (now - 60)) time += 60 * 24;
time *= _settings_time.ticks_per_minute;
ChangeTimetableStartIntl(v->index | (this->set_start_date_all ? 1 << 20 : 0), time); ChangeTimetableStartIntl(v->index | (this->set_start_date_all ? 1 << 20 : 0), _settings_time.FromTickMinutes(time));
} }
break; break;
} }

@ -1033,7 +1033,7 @@ RoadType GetTownRoadType()
if (HasBit(rti->extra_flags, RXTF_NO_TOWN_MODIFICATION)) continue; if (HasBit(rti->extra_flags, RXTF_NO_TOWN_MODIFICATION)) continue;
/* Not yet introduced at this date. */ /* Not yet introduced at this date. */
if (IsInsideMM(rti->introduction_date, 0, MAX_DAY) && rti->introduction_date > _date) continue; if (IsInsideMM(rti->introduction_date, 0, MAX_DATE.base()) && rti->introduction_date > _date) continue;
if (best != nullptr) { if (best != nullptr) {
if ((rti->max_speed == 0 ? assume_max_speed : rti->max_speed) < (best->max_speed == 0 ? assume_max_speed : best->max_speed)) continue; if ((rti->max_speed == 0 ? assume_max_speed : rti->max_speed) < (best->max_speed == 0 ? assume_max_speed : best->max_speed)) continue;

@ -2371,17 +2371,17 @@ CommandCost CmdProgramSignalTraceRestrictProgMgmt(TileIndex tile, DoCommandFlag
int GetTraceRestrictTimeDateValue(TraceRestrictTimeDateValueField type) int GetTraceRestrictTimeDateValue(TraceRestrictTimeDateValueField type)
{ {
Minutes minutes = (_scaled_date_ticks / _settings_game.game_time.ticks_per_minute) + _settings_game.game_time.clock_offset; const TickMinutes now = _settings_game.game_time.NowInTickMinutes();
switch (type) { switch (type) {
case TRTDVF_MINUTE: case TRTDVF_MINUTE:
return MINUTES_MINUTE(minutes); return now.ClockMinute();
case TRTDVF_HOUR: case TRTDVF_HOUR:
return MINUTES_HOUR(minutes); return now.ClockHour();
case TRTDVF_HOUR_MINUTE: case TRTDVF_HOUR_MINUTE:
return (MINUTES_HOUR(minutes) * 100) + MINUTES_MINUTE(minutes); return now.ClockHHMM();
case TRTDVF_DAY: case TRTDVF_DAY:
return _cur_date_ymd.day; return _cur_date_ymd.day;
@ -2396,17 +2396,17 @@ int GetTraceRestrictTimeDateValue(TraceRestrictTimeDateValueField type)
int GetTraceRestrictTimeDateValueFromDate(TraceRestrictTimeDateValueField type, DateTicksScaled scaled_date_ticks) int GetTraceRestrictTimeDateValueFromDate(TraceRestrictTimeDateValueField type, DateTicksScaled scaled_date_ticks)
{ {
Minutes minutes = (scaled_date_ticks / _settings_game.game_time.ticks_per_minute) + _settings_game.game_time.clock_offset; const TickMinutes minutes = _settings_game.game_time.ToTickMinutes(scaled_date_ticks);
switch (type) { switch (type) {
case TRTDVF_MINUTE: case TRTDVF_MINUTE:
return MINUTES_MINUTE(minutes); return minutes.ClockMinute();
case TRTDVF_HOUR: case TRTDVF_HOUR:
return MINUTES_HOUR(minutes); return minutes.ClockHour();
case TRTDVF_HOUR_MINUTE: case TRTDVF_HOUR_MINUTE:
return (MINUTES_HOUR(minutes) * 100) + MINUTES_MINUTE(minutes); return minutes.ClockHHMM();
case TRTDVF_DAY: { case TRTDVF_DAY: {
YearMonthDay ymd; YearMonthDay ymd;

@ -120,7 +120,7 @@ void ClearAllSignalSpeedRestrictions()
_signal_speeds.clear(); _signal_speeds.clear();
} }
void AdjustAllSignalSpeedRestrictionTickValues(DateTicksScaled delta) void AdjustAllSignalSpeedRestrictionTickValues(DateTicksScaledDelta delta)
{ {
for (auto &it : _signal_speeds) { for (auto &it : _signal_speeds) {
it.second.time_stamp += delta; it.second.time_stamp += delta;
@ -7558,15 +7558,15 @@ int GetTrainEstimatedMaxAchievableSpeed(const Train *train, int mass, const int
void SetSignalTrainAdaptationSpeed(const Train *v, TileIndex tile, uint16 track) void SetSignalTrainAdaptationSpeed(const Train *v, TileIndex tile, uint16 track)
{ {
SignalSpeedKey speed_key = { SignalSpeedKey speed_key = {};
speed_key.signal_tile = tile, speed_key.signal_tile = tile;
speed_key.signal_track = track, speed_key.signal_track = track;
speed_key.last_passing_train_dir = v->GetVehicleTrackdir() speed_key.last_passing_train_dir = v->GetVehicleTrackdir();
};
SignalSpeedValue speed_value = { SignalSpeedValue speed_value = {};
speed_value.train_speed = v->First()->cur_speed, speed_value.train_speed = v->First()->cur_speed;
speed_value.time_stamp = GetSpeedRestrictionTimeout(v->First()) speed_value.time_stamp = GetSpeedRestrictionTimeout(v->First());
};
_signal_speeds[speed_key] = speed_value; _signal_speeds[speed_key] = speed_value;
} }

@ -2418,16 +2418,16 @@ void AgeVehicle(Vehicle *v)
/* Stop if a virtual vehicle */ /* Stop if a virtual vehicle */
if (HasBit(v->subtype, GVSF_VIRTUAL)) return; if (HasBit(v->subtype, GVSF_VIRTUAL)) return;
if (v->age < MAX_DAY) { if (v->age < MAX_DATE.AsDelta()) {
v->age++; v->age++;
if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedMinAge(v); if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedMinAge(v);
} }
if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return; if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
int age = v->age - v->max_age; DateDelta age = v->age - v->max_age;
for (int i = 0; i <= 4; i++) { for (int i = 0; i <= 4; i++) {
if (age == DateAtStartOfYear(i)) { if (age == DateAtStartOfYear(i).AsDelta()) {
v->reliability_spd_dec <<= 1; v->reliability_spd_dec <<= 1;
break; break;
} }
@ -4649,7 +4649,7 @@ void DumpVehicleStats(char *buffer, const char *last)
buffer += seprintf(buffer, last, " %10s: %5u\n", "total", (uint)Vehicle::GetNumItems()); buffer += seprintf(buffer, last, " %10s: %5u\n", "total", (uint)Vehicle::GetNumItems());
} }
void AdjustVehicleScaledTickBase(int64 delta) void AdjustVehicleScaledTickBase(DateTicksScaledDelta delta)
{ {
for (Vehicle *v : Vehicle::Iterate()) { for (Vehicle *v : Vehicle::Iterate()) {
if (v->timetable_start != 0) v->timetable_start += delta; if (v->timetable_start != 0) v->timetable_start += delta;
@ -4662,10 +4662,10 @@ void AdjustVehicleScaledTickBase(int64 delta)
} }
} }
void ShiftVehicleDates(int interval) void ShiftVehicleDates(DateDelta interval)
{ {
for (Vehicle *v : Vehicle::Iterate()) { for (Vehicle *v : Vehicle::Iterate()) {
v->date_of_last_service = std::max(v->date_of_last_service + interval, 0); v->date_of_last_service = std::max<Date>(v->date_of_last_service + interval, 0);
} }
/* date_of_last_service_newgrf is not updated here as it must stay stable /* date_of_last_service_newgrf is not updated here as it must stay stable
* for vehicles outside of a depot. */ * for vehicles outside of a depot. */

@ -317,8 +317,8 @@ public:
/* Related to age and service time */ /* Related to age and service time */
Year build_year; ///< Year the vehicle has been built. Year build_year; ///< Year the vehicle has been built.
Date age; ///< Age in days DateDelta age; ///< Age in days
Date max_age; ///< Maximum age DateDelta max_age; ///< Maximum age
Date date_of_last_service; ///< Last date the vehicle had a service at a depot. Date date_of_last_service; ///< Last date the vehicle had a service at a depot.
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. 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.
uint16 reliability; ///< Reliability. uint16 reliability; ///< Reliability.
@ -1536,6 +1536,6 @@ void ClearVehicleTickCaches();
void RemoveFromOtherVehicleTickCache(const Vehicle *v); void RemoveFromOtherVehicleTickCache(const Vehicle *v);
void UpdateAllVehiclesIsDrawn(); void UpdateAllVehiclesIsDrawn();
void ShiftVehicleDates(int interval); void ShiftVehicleDates(DateDelta interval);
#endif /* VEHICLE_BASE_H */ #endif /* VEHICLE_BASE_H */

@ -26,7 +26,7 @@
#define IS_CUSTOM_FIRSTHEAD_SPRITE(x) (x == 0xFD) #define IS_CUSTOM_FIRSTHEAD_SPRITE(x) (x == 0xFD)
#define IS_CUSTOM_SECONDHEAD_SPRITE(x) (x == 0xFE) #define IS_CUSTOM_SECONDHEAD_SPRITE(x) (x == 0xFE)
static const int VEHICLE_PROFIT_MIN_AGE = DAYS_IN_YEAR * 2; ///< Only vehicles older than this have a meaningful profit. static constexpr DateDelta VEHICLE_PROFIT_MIN_AGE = DAYS_IN_YEAR * 2; ///< Only vehicles older than this have a meaningful profit.
static const Money VEHICLE_PROFIT_THRESHOLD = 10000; ///< Threshold for a vehicle to be considered making good profit. static const Money VEHICLE_PROFIT_THRESHOLD = 10000; ///< Threshold for a vehicle to be considered making good profit.
struct Viewport; struct Viewport;

@ -457,7 +457,7 @@ void DepotSortList(VehicleList *list)
} }
/** draw the vehicle profit button in the vehicle list window. */ /** draw the vehicle profit button in the vehicle list window. */
static void DrawVehicleProfitButton(Date age, Money display_profit_last_year, uint num_vehicles, int x, int y) static void DrawVehicleProfitButton(DateDelta age, Money display_profit_last_year, uint num_vehicles, int x, int y)
{ {
SpriteID spr; SpriteID spr;
@ -1882,8 +1882,8 @@ void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int
switch (this->vehgroups.SortType()) { switch (this->vehgroups.SortType()) {
case VST_AGE: { case VST_AGE: {
str = (v->age + DAYS_IN_YEAR < v->max_age) ? STR_VEHICLE_LIST_AGE : STR_VEHICLE_LIST_AGE_RED; str = (v->age + DAYS_IN_YEAR < v->max_age) ? STR_VEHICLE_LIST_AGE : STR_VEHICLE_LIST_AGE_RED;
SetDParam(3, v->age / DAYS_IN_LEAP_YEAR); SetDParam(3, DateDeltaToYears(v->age));
SetDParam(4, v->max_age / DAYS_IN_LEAP_YEAR); SetDParam(4, DateDeltaToYears(v->max_age));
break; break;
} }
@ -1938,7 +1938,7 @@ void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int
case VST_TIME_TO_LIVE: { case VST_TIME_TO_LIVE: {
auto years_remaining = (v->max_age / DAYS_IN_LEAP_YEAR) - (v->age / DAYS_IN_LEAP_YEAR); auto years_remaining = (v->max_age / DAYS_IN_LEAP_YEAR) - (v->age / DAYS_IN_LEAP_YEAR);
str = (years_remaining > 1) ? STR_VEHICLE_LIST_TIME_TO_LIVE : ((years_remaining < 0) ? STR_VEHICLE_LIST_TIME_TO_LIVE_OVERDUE : STR_VEHICLE_LIST_TIME_TO_LIVE_RED); str = (years_remaining > 1) ? STR_VEHICLE_LIST_TIME_TO_LIVE : ((years_remaining < 0) ? STR_VEHICLE_LIST_TIME_TO_LIVE_OVERDUE : STR_VEHICLE_LIST_TIME_TO_LIVE_RED);
SetDParam(3, std::abs(years_remaining)); SetDParam(3, std::abs(years_remaining.base()));
break; break;
} }
@ -3079,9 +3079,9 @@ struct VehicleDetailsWindow : Window {
Rect tr = r.Shrink(WidgetDimensions::scaled.framerect); Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
/* Draw running cost */ /* Draw running cost */
SetDParam(1, DateToYear(v->age)); SetDParam(1, DateDeltaToYears(v->age));
SetDParam(0, (v->age + DAYS_IN_YEAR < v->max_age) ? STR_VEHICLE_INFO_AGE : STR_VEHICLE_INFO_AGE_RED); SetDParam(0, (v->age + DAYS_IN_YEAR < v->max_age) ? STR_VEHICLE_INFO_AGE : STR_VEHICLE_INFO_AGE_RED);
SetDParam(2, DateToYear(v->max_age)); SetDParam(2, DateDeltaToYears(v->max_age));
SetDParam(3, v->GetDisplayRunningCost()); SetDParam(3, v->GetDisplayRunningCost());
DrawString(tr, STR_VEHICLE_INFO_AGE_RUNNING_COST_YR); DrawString(tr, STR_VEHICLE_INFO_AGE_RUNNING_COST_YR);
tr.top += GetCharacterHeight(FS_NORMAL); tr.top += GetCharacterHeight(FS_NORMAL);

@ -55,7 +55,7 @@ struct GUIVehicleGroup {
}); });
} }
Date GetOldestVehicleAge() const DateDelta GetOldestVehicleAge() const
{ {
const Vehicle *oldest = *std::max_element(this->vehicles_begin, this->vehicles_end, [](const Vehicle *v_a, const Vehicle *v_b) { const Vehicle *oldest = *std::max_element(this->vehicles_begin, this->vehicles_end, [](const Vehicle *v_a, const Vehicle *v_b) {
return v_a->age < v_b->age; return v_a->age < v_b->age;

Loading…
Cancel
Save