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 4 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) {
/* Design date - Life length */
SetDParam(0, ymd.year);
SetDParam(1, DateToYear(e->GetLifeLengthInDays()));
SetDParam(1, DateDeltaToYears(e->GetLifeLengthInDays()));
DrawString(left, right, y, STR_PURCHASE_INFO_DESIGNED_LIFE);
y += GetCharacterHeight(FS_NORMAL);

@ -1173,7 +1173,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint64 p3,
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)",
(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));
}
@ -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
* 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)",
_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();
return_dcpi(res);
@ -1212,7 +1212,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint64 p3,
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)",
_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
* use the construction one */

@ -949,7 +949,7 @@ CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
}
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;
}
@ -967,7 +967,7 @@ CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
Company *c = DoStartupNewCompany(DSNC_AI, company_id);
if (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;
}
@ -984,7 +984,7 @@ CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
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);

@ -1876,7 +1876,7 @@ struct CompanyInfrastructureWindow : Window
}
/* 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. */
for (const Engine *e : Engine::IterateType(VEH_ROAD)) {
@ -1886,7 +1886,7 @@ struct CompanyInfrastructureWindow : Window
}
/* 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;
}

@ -2490,7 +2490,7 @@ DEF_CONSOLE_CMD(ConMergeLinkgraphJobsAsap)
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;
}
@ -2739,14 +2739,14 @@ DEF_CONSOLE_CMD(ConDumpLinkgraphJobs)
IConsolePrintF(CC_DEFAULT, PRINTF_SIZE " link graph jobs", LinkGraphJob::GetNumItems());
for (const LinkGraphJob *lgj : LinkGraphJob::Iterate()) {
YearMonthDay start_ymd;
ConvertDateToYMD(lgj->StartDateTicks() / DAY_TICKS, &start_ymd);
ConvertDateToYMD(lgj->StartDateTicks().ToDate(), &start_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",
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->JoinDateTicks(), join_ymd.year, join_ymd.month + 1, join_ymd.day, lgj->JoinDateTicks() % DAY_TICKS,
lgj->JoinDateTicks() - lgj->StartDateTicks());
lgj->StartDateTicks().base(), start_ymd.year, start_ymd.month + 1, start_ymd.day, lgj->StartDateTicks().ToDateFractRemainder(),
lgj->JoinDateTicks().base(), join_ymd.year, join_ymd.month + 1, join_ymd.day, lgj->JoinDateTicks().ToDateFractRemainder(),
(lgj->JoinDateTicks() - lgj->StartDateTicks()).base());
}
return true;
}
@ -3686,20 +3686,20 @@ DEF_CONSOLE_CMD(ConIfDay)
DEF_CONSOLE_CMD(ConIfHour)
{
Minutes minutes = _scaled_date_ticks / _settings_time.ticks_per_minute + _settings_time.clock_offset;
return ConConditionalCommon(argc, argv, MINUTES_HOUR(minutes), "the current hour (in game, assuming time is in minutes)", "if_hour");
TickMinutes minutes = _settings_time.NowInTickMinutes();
return ConConditionalCommon(argc, argv, minutes.ClockHour(), "the current hour (in game, assuming time is in minutes)", "if_hour");
}
DEF_CONSOLE_CMD(ConIfMinute)
{
Minutes minutes = _scaled_date_ticks / _settings_time.ticks_per_minute + _settings_time.clock_offset;
return ConConditionalCommon(argc, argv, MINUTES_MINUTE(minutes), "the current minute (in game, assuming time is in minutes)", "if_minute");
TickMinutes minutes = _settings_time.NowInTickMinutes();
return ConConditionalCommon(argc, argv, minutes.ClockMinute(), "the current minute (in game, assuming time is in minutes)", "if_minute");
}
DEF_CONSOLE_CMD(ConIfHourMinute)
{
Minutes minutes = _scaled_date_ticks / _settings_time.ticks_per_minute + _settings_time.clock_offset;
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");
TickMinutes minutes = _settings_time.NowInTickMinutes();
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

@ -44,7 +44,7 @@ inline bool ShouldLogUpdateStateChecksum()
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, \
_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
# define DEBUG_UPDATESTATECHECKSUM(str, ...)
#endif /* RANDOM_DEBUG */

@ -72,7 +72,7 @@ void SetRandomSeed(uint32 seed)
uint32 DoRandom(int line, const char *file)
{
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();

@ -44,15 +44,15 @@ extern void ClearOutOfDateSignalSpeedRestrictions();
void CheckScaledDateTicksWrap()
{
DateTicksScaled tick_adjust = 0;
DateTicksScaledDelta tick_adjust = 0;
auto get_tick_adjust = [&](DateTicksScaled target) {
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)) {
tick_adjust = get_tick_adjust(_scaled_date_ticks);
} 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 {
return;
}
@ -60,13 +60,13 @@ void CheckScaledDateTicksWrap()
_scaled_date_ticks_offset -= tick_adjust;
_scaled_date_ticks -= tick_adjust;
extern void AdjustAllSignalSpeedRestrictionTickValues(DateTicksScaled delta);
extern void AdjustAllSignalSpeedRestrictionTickValues(DateTicksScaledDelta delta);
AdjustAllSignalSpeedRestrictionTickValues(-tick_adjust);
extern void AdjustVehicleScaledTickBase(int64 delta);
extern void AdjustVehicleScaledTickBase(DateTicksScaledDelta delta);
AdjustVehicleScaledTickBase(-tick_adjust);
extern void AdjustLinkGraphScaledTickBase(int64 delta);
extern void AdjustLinkGraphScaledTickBase(DateTicksScaledDelta delta);
AdjustLinkGraphScaledTickBase(-tick_adjust);
}
@ -106,7 +106,7 @@ void SetDate(Date date, DateFract fract, bool preserve_scaled_ticks)
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)
@ -160,8 +160,8 @@ void ConvertDateToYMD(Date date, YearMonthDay *ymd)
*/
/* There are 97 leap years in 400 years */
Year yr = 400 * (date / (DAYS_IN_YEAR * 400 + 97));
int rem = date % (DAYS_IN_YEAR * 400 + 97);
Year yr = 400 * (date.base() / (DAYS_IN_YEAR * 400 + 97));
int rem = date.base() % (DAYS_IN_YEAR * 400 + 97);
uint16 x;
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 */
if (!IsLeapYear(year) && days >= ACCUM_MAR) days--;
return DAYS_TILL(year) + days;
return DateAtStartOfYear(year) + days;
}
/** Functions used by the IncreaseDate function */

@ -38,7 +38,7 @@ inline Date ConvertYMDToDate(const YearMonthDay &ymd)
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.
@ -52,22 +52,22 @@ static inline bool IsLeapYear(Year yr)
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)
{
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)
{
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)
{
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)
{
return date / DAYS_IN_LEAP_YEAR;
return date.base() / DAYS_IN_LEAP_YEAR;
}
/**
* Calculate the date of the first day of a given year.
* @param year the year to get the first day of.
* @return the date.
*/
static constexpr Date DateAtStartOfYear(Year year)
static constexpr Year DateDeltaToYears(DateDelta date)
{
return date.base() / DAYS_IN_LEAP_YEAR;
}
static constexpr DateTicks DateToDateTicks(Date date, DateFract fract = 0)
{
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);
return (date.base() * DAY_TICKS) + fract;
}
/* Hardcode the number of days in a year because we can't access CalendarTime from here. */
return (365 * year_as_int) + number_of_leap_years;
static constexpr DateTicksDelta DateDeltaToDateTicksDelta(DateDelta date, DateFract fract = 0)
{
return (date.base() * DAY_TICKS) + fract;
}
static inline DateTicks NowDateTicks()
{
return DateToDateTicks(_date, _date_fract);
}
#endif /* DATE_FUNC_H */

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

@ -10,16 +10,8 @@
#ifndef DATE_TYPE_H
#define DATE_TYPE_H
typedef int32 Date; ///< The type to store our dates in
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 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.
#include "core/strong_typedef_type.hpp"
#include "core/math_func.hpp"
/**
* 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
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))
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 */
static const Year ORIGINAL_BASE_YEAR = 1920;
static constexpr Year ORIGINAL_BASE_YEAR = 1920;
/** 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 */
static const 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)
static constexpr Year ORIGINAL_MAX_YEAR = 2090;
/**
* Calculate the date of the first day of a given year.
* @param year the year to get the first day of.
* @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
* '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 */
static const Year MIN_YEAR = 0;
static constexpr Year MIN_YEAR = 0;
/** The default starting year */
static const Year DEF_START_YEAR = 1950;
static constexpr Year DEF_START_YEAR = 1950;
/** 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
@ -102,22 +185,7 @@ static const Year DEF_END_YEAR = ORIGINAL_END_YEAR - 1;
static const Year MAX_YEAR = 5000000;
/** The number of days till the last day */
#define MAX_DAY (DAYS_TILL(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)
static constexpr Date MAX_DATE = DateAtStartOfYear(MAX_YEAR + 1) - 1;
/**
* Data structure to convert between Date and triplet (year, month, and day).
@ -129,8 +197,9 @@ struct YearMonthDay {
Day day; ///< Day (1..31)
};
static const Year INVALID_YEAR = -1; ///< Representation of an invalid year
static const Date INVALID_DATE = -1; ///< Representation of an invalid date
static const Ticks INVALID_TICKS = -1; ///< Representation of an invalid number of ticks
static constexpr Year INVALID_YEAR = -1; ///< Representation of an invalid year
static constexpr Date INVALID_DATE = -1; ///< Representation of an invalid date
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 */

@ -43,15 +43,23 @@
typedef btree::btree_map<const DispatchSchedule *, btree::btree_set<DateTicksScaled>> schdispatch_cache_t;
/** A scheduled order. */
typedef struct OrderDate
{
struct OrderDate {
const Order *order; ///< 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
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
} OrderDate;
Ticks scheduled_waiting_time; ///< Scheduled waiting time if scheduled dispatch is used
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) {
return (order->GetType() == OT_GOTO_STATION &&
@ -93,7 +101,7 @@ static uint8 GetDepartureConditionalOrderMode(const Order *order, const Vehicle
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)) {
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();
/* 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) {
earliest_departure = ready_to_depart_time - max_delay - 1;
/* -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();
*previous_departure = actual_departure - date_only_scaled;
*waiting_time = (actual_departure - date_ticks_base).AsTicks() - *previous_departure - order->GetTravelTime();
*previous_departure = (actual_departure - date_ticks_base).AsTicks();
slot_cache.insert(actual_departure);
/* 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 */
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_b = b->scheduled_date - (b->scheduled_waiting_time > 0 ? b->scheduled_waiting_time : b->order->GetWaitTime());
DateTicksScaled arr_a = a->scheduled_date - a->EffectiveWaitingTime();
DateTicksScaled arr_b = b->scheduled_date - b->EffectiveWaitingTime();
return arr_a < arr_b;
});
/* Re-assign them sequentially */
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());
d_list[i]->scheduled_waiting_time = departure_time_list[i] - arrival;
const DateTicksScaled arrival = d_list[i]->scheduled_date - d_list[i]->EffectiveWaitingTime();
d_list[i]->scheduled_waiting_time = (departure_time_list[i] - arrival).AsTicks();
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;
}
}
@ -237,12 +245,11 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
std::vector<OrderDate*> next_orders;
/* 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);
DateTicksScaled date_fract_scaled = ((DateTicksScaled)_date_fract * _settings_game.economy.day_length_factor) + _tick_skip_counter;
const DateTicksScaled date_ticks_base = _scaled_date_ticks;
/* 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;
/* 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());
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) {
/* 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_timetable_order = v->GetOrder(v->cur_timetable_order_index);
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 {
/* This can also occur with implicit orders, when there are no real orders, do nothing */
}
}
DepartureStatus status = D_TRAVELLING;
bool should_reset_lateness = false;
uint waiting_time = 0;
Ticks waiting_time = 0;
/* If the vehicle is stopped in a depot, ignore it. */
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)) {
/* Account for the vehicle having reached the current order and being in the loading phase. */
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;
}
/* 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. */
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;
}
/* If the order is a conditional branch, handle it. */
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: {
/* Give up */
break;
@ -329,7 +336,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
break;
}
start_date -= order->GetTravelTime();
start_ticks -= order->GetTravelTime();
require_travel_time = false;
continue;
}
@ -338,7 +345,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
if (status != D_CANCELLED) {
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;
require_travel_time = true;
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 (start_date - v->lateness_counter > max_date) {
if (start_ticks - v->lateness_counter > max_ticks) {
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_ARRIVAL && IsArrival(order, station))) {
/* 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;
}
@ -371,7 +378,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
od->order = order;
od->v = v;
/* 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->status = status;
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 (!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. */
if (least_order == nullptr) {
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))) {
/* Somehow my compiler perform an unsigned comparition above so integer cast is required */
least_order = od;
} else if (type == D_ARRIVAL) {
if ((least_order->expected_tick - least_order->lateness - least_order->EffectiveWaitingTime()) > (od->expected_tick - od->lateness - od->EffectiveWaitingTime())) {
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);
@ -420,22 +432,22 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
for(int i = 10000; i > 0; --i) {
/* I should probably try to convince you that this loop always terminates regardless of the safeguard. */
/* 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.) */
/* 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. */
/* least_order is the best candidate for the next departure. */
/* First, we check if we can stop looking for departures yet. */
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;
}
/* We already know the least order and that it's a suitable departure, so make it into a 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->status = least_order->status;
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. */
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;
}
@ -762,7 +774,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
for (int i = least_order->v->GetNumOrders(); i > 0; --i) {
/* If the order is a conditional branch, handle it. */
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: {
/* Give up */
break;
@ -774,8 +786,8 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
break;
}
least_order->expected_date -= 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)) {
least_order->expected_tick -= order->GetTravelTime(); /* Added in next VehicleSetNextDepartureTime */
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;
}
require_travel_time = false;
@ -783,9 +795,9 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
}
case 2: {
/* 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;
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;
}
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 (least_order->expected_date - least_order->lateness > max_date) {
if (least_order->expected_tick - least_order->lateness > max_ticks) {
break;
}
@ -815,7 +827,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
}
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;
}
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. */
/* 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. */
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. */
@ -838,15 +850,15 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
for (uint i = 0; i < next_orders.size(); ++i) {
OrderDate *od = next_orders[i];
DateTicks lod = least_order->expected_date - least_order->lateness;
DateTicks odd = od->expected_date - od->lateness;
DateTicks lod = least_order->expected_tick - least_order->lateness;
DateTicks odd = od->expected_tick - od->lateness;
if (type == D_ARRIVAL) {
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();
}
if (lod > odd && od->expected_date - od->lateness < max_date) {
if (lod > odd && od->expected_tick - od->lateness < max_ticks) {
least_order = od;
}
}
@ -864,7 +876,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
return result;
}
DateTicksScaled GetDeparturesMaxTicksAhead()
Ticks GetDeparturesMaxTicksAhead()
{
if (_settings_time.time_in_minutes) {
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,
bool show_vehicles_via = false, bool show_pax = true, bool show_freight = true);
DateTicksScaled GetDeparturesMaxTicksAhead();
Ticks GetDeparturesMaxTicksAhead();
#endif /* DEPARTURES_FUNC_H */

@ -32,10 +32,10 @@ typedef enum {
struct CallAt {
StationID station;
DateTicks scheduled_date;
DateTicksScaled scheduled_date;
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) { }
inline bool operator==(const CallAt& c) const {
@ -82,7 +82,7 @@ struct Departure {
DepartureType type; ///< The type of the departure (departure or arrival)
const Vehicle *vehicle; ///< The vehicle performing 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) { }
inline bool operator==(const Departure& d) const {
@ -93,13 +93,22 @@ struct Departure {
}
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->via == d.via &&
this->via2 == d.via2 &&
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;

@ -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);
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;
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));

@ -484,7 +484,7 @@ uint Engine::GetDisplayMaxTractiveEffort() const
* Returns the vehicle's (not model's!) life length in days.
* @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. */
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;
SaveRandomSeeds(&saved_seeds);
SetRandomSeed(_settings_game.game_creation.generation_seed ^ seed ^
ei->base_intro ^
ei->base_intro.base() ^
e->type ^
e->GetGRFID());
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
* of engines in early starting games.
* 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) {
e->age = (aging_date - e->intro_date) >> 5;
e->age = (aging_date - e->intro_date).base() >> 5;
e->company_avail = MAX_UVALUE(CompanyMask);
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 ^
(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 ^
e->type ^
e->GetGRFID());

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

@ -204,7 +204,7 @@ static void _GenerateWorld()
if (_debug_desync_level > 0) {
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);
}
} 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
* division by 0 if spawn date == last compression date. This matches
* 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) {
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.
* @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) {
BaseNode &source = this->nodes[node1];
@ -54,7 +54,7 @@ void LinkGraph::ShiftDates(int interval)
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) {
this->nodes[node1].supply /= 2;
}
@ -79,8 +79,8 @@ void LinkGraph::Compress()
*/
void LinkGraph::Merge(LinkGraph *other)
{
uint32 age = ClampTo<uint32>(CeilDivT<DateTicksScaled>(_scaled_date_ticks - this->last_compression + 1, DAY_TICKS));
uint32 other_age = ClampTo<uint32>(CeilDivT<DateTicksScaled>(_scaled_date_ticks - other->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<int64>(_scaled_date_ticks.base() - other->last_compression.base() + 1, DAY_TICKS));
NodeID first = this->Size();
this->nodes.reserve(first + other->Size());
for (NodeID node1 = 0; node1 < other->Size(); ++node1) {
@ -266,7 +266,7 @@ void LinkGraph::Init(uint size)
this->nodes.resize(size);
}
void AdjustLinkGraphScaledTickBase(int64 delta)
void AdjustLinkGraphScaledTickBase(DateTicksScaledDelta delta)
{
for (LinkGraph *lg : LinkGraph::Iterate()) lg->last_compression += delta;
@ -279,10 +279,10 @@ void AdjustLinkGraphScaledTickBase(int64 delta)
void LinkGraphFixupLastCompressionAfterLoad()
{
/* 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()) {
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;
/** 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. */
static const uint COMPRESSION_INTERVAL = 256 * DAY_TICKS;
/** Minimum number of ticks between subsequent compressions of a LG. */
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
@ -314,7 +314,7 @@ public:
LinkGraph(CargoID cargo) : cargo(cargo), last_compression(_scaled_date_ticks) {}
void Init(uint size);
void ShiftDates(int interval);
void ShiftDates(DateDelta interval);
void Compress();
void Merge(LinkGraph *other);
@ -365,7 +365,7 @@ public:
*/
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);
@ -392,7 +392,7 @@ protected:
friend upstream_sl::SlLinkgraphNode;
friend upstream_sl::SlLinkgraphEdge;
friend void AdjustLinkGraphScaledTickBase(int64 delta);
friend void AdjustLinkGraphScaledTickBase(DateTicksScaledDelta delta);
friend void LinkGraphFixupLastCompressionAfterLoad();
CargoID cargo; ///< Cargo of this component's link graph.

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

@ -262,7 +262,7 @@ public:
* settings have to be brutally const-casted in order to populate them.
*/
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();
@ -297,7 +297,7 @@ public:
* @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.
*/
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.
@ -315,10 +315,10 @@ public:
* Change the start and join dates on date cheating.
* @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->start_date_ticks += interval * DAY_TICKS;
this->join_date_ticks += DateDeltaToDateTicksDelta(interval);
this->start_date_ticks += DateDeltaToDateTicksDelta(interval);
}
/**

@ -200,7 +200,7 @@ void LinkGraphSchedule::SpawnAll()
* graph jobs by the number of days given.
* @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 (LinkGraphJob *lgj : LinkGraphJob::Iterate()) lgj->ShiftJoinDate(interval);
@ -285,7 +285,7 @@ void LinkGraphJobGroup::JoinThread()
auto flush_bucket = [&]() {
if (!bucket_cost) return;
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));
group->SpawnThread();
bucket_cost = 0;
@ -321,9 +321,9 @@ void StateGameLoop_LinkGraphPauseControl()
} else if (_pause_mode == PM_UNPAUSED && _tick_skip_counter == 0) {
if (_settings_game.economy.day_length_factor == 1) {
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 {
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)));
if (date_ticks % interval != interval / 2) return;
}
@ -358,10 +358,10 @@ void OnTick_LinkGraph()
if (_settings_game.economy.day_length_factor == 1) {
if (_date_fract != LinkGraphSchedule::SPAWN_JOIN_TICK) return;
interval = _settings_game.linkgraph.recalc_interval / SECONDS_PER_DAY;
offset = _date % interval;
offset = _date.base() % interval;
} else {
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) {
LinkGraphSchedule::instance.SpawnNext();

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

@ -235,8 +235,8 @@ void SerializeNetworkGameInfo(Packet *p, const NetworkServerGameInfo *info, bool
}
/* NETWORK_GAME_INFO_VERSION = 3 */
p->Send_uint32(info->game_date);
p->Send_uint32(info->start_date);
p->Send_uint32(info->game_date.base());
p->Send_uint32(info->start_date.base());
/* NETWORK_GAME_INFO_VERSION = 2 */
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_uint32(info->game_date);
p->Send_uint32(info->start_date);
p->Send_uint32(info->game_date.base());
p->Send_uint32(info->start_date.base());
p->Send_uint8 (info->companies_max);
p->Send_uint8 (info->companies_on);
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:
info->game_date = Clamp(p->Recv_uint32(), 0, MAX_DATE);
info->start_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.base());
FALLTHROUGH;
case 2:
@ -455,8 +455,8 @@ void DeserializeNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info)
NewGRFSerializationType newgrf_serialisation = NST_GRFID_MD5;
info->game_date = Clamp(p->Recv_uint32(), 0, MAX_DATE);
info->start_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.base());
info->companies_max = p->Recv_uint8 ();
info->companies_on = p->Recv_uint8 ();
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);
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);
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. */
static Date last_log;
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;
}
}
@ -1185,15 +1185,15 @@ void NetworkGameLoop()
if (_date == next_date && _date_fract == next_date_fract) {
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);
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();
}
if (check_sync_state) {
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 {
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();
}
check_sync_state = false;

@ -179,7 +179,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendWelcome()
p->Send_string(""); // Used to be map-name.
p->Send_uint32(_settings_game.game_creation.generation_seed);
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(MapSizeY());
@ -209,7 +209,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendDate()
{
Packet *p = new Packet(ADMIN_PACKET_SERVER_DATE);
p->Send_uint32(_date);
p->Send_uint32(_date.base());
this->SendPacket(p);
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(ci->client_name);
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);
this->SendPacket(p);

@ -331,7 +331,7 @@ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res)
ShowNetworkError(STR_NETWORK_ERROR_DESYNC);
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!");
std::string desync_log;
@ -632,7 +632,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendDesyncLog(const std::strin
NetworkRecvStatus ClientNetworkGameSocketHandler::SendDesyncMessage(const char *msg)
{
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_uint8(_tick_skip_counter);
p->Send_string(msg);

@ -287,7 +287,7 @@ protected:
/** Sort servers by the number of days the game is running */
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);
}
@ -650,11 +650,11 @@ public:
DrawString(tr, invite_or_address); // server address / invite code
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
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
tr.top += GetCharacterHeight(FS_NORMAL);

@ -1020,7 +1020,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p)
ci->join_frame = _frame_counter;
ci->client_name = client_name;
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 */
if (Company::IsValidID(playas)) _network_company_states[playas].months_empty = 0;
@ -1812,7 +1812,7 @@ void NetworkUpdateClientInfo(ClientID client_id)
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()) {
if (cs->status >= ServerNetworkGameSocketHandler::STATUS_AUTHORIZED) {
@ -2169,7 +2169,7 @@ void NetworkServerMonthlyLoop()
void NetworkServerDailyLoop()
{
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) {
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;
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)
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;
}
@ -7518,7 +7518,7 @@ bool GetGlobalVariable(byte param, uint32 *value, const GRFFile *grffile)
return true;
case 0x23: // long format date
*value = _date;
*value = _date.base();
return true;
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 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));

@ -729,7 +729,7 @@ static uint32 VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *object,
}
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
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 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 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

@ -271,7 +271,7 @@ static uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout
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 */
case 0x47: return this->industry->ctlflags;
@ -362,7 +362,7 @@ static uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout
if (cargo == CT_INVALID) return 0;
int index = this->industry->GetCargoAcceptedIndex(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];
NOT_REACHED();
}

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

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

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

@ -1554,7 +1554,7 @@ void CheckCaches(bool force_check, std::function<void(const char *)> log, CheckC
desync_level = 1;
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;
}
@ -1562,7 +1562,7 @@ void CheckCaches(bool force_check, std::function<void(const char *)> log, CheckC
* always to aid testing of caches. */
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);
@ -2103,10 +2103,10 @@ void StateGameLoop()
CallWindowGameTickEvent();
NewsLoop();
} 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. */
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);
}
@ -2124,7 +2124,7 @@ void StateGameLoop()
}
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;
_check_special_modes = true;
SetWindowDirty(WC_STATUS_BAR, 0);

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

@ -3042,7 +3042,7 @@ bool EvaluateDispatchSlotConditionalOrder(const Order *order, const Vehicle *v,
} else {
extern DateTicksScaled GetScheduledDispatchTime(const DispatchSchedule &ds, DateTicksScaled leave_time);
DateTicksScaled slot = GetScheduledDispatchTime(sched, _scaled_date_ticks);
offset = (slot - sched.GetScheduledDispatchStartTick()) % sched.GetScheduledDispatchDuration();
offset = (slot - sched.GetScheduledDispatchStartTick()).base() % sched.GetScheduledDispatchDuration();
}
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_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_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_UNCONDITIONALLY: skip_order = true; break;
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);
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: {
const TraceRestrictCounter* ctr = TraceRestrictCounter::GetIfValid(GB(order->GetXData(), 16, 16));
if (ctr != nullptr) {
@ -3690,7 +3690,7 @@ CommandCost CmdMassChangeOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, u
return CommandCost();
}
void ShiftOrderDates(int interval)
void ShiftOrderDates(DateDelta interval)
{
SetWindowClassesDirty(WC_VEHICLE_ORDERS);
SetWindowClassesDirty(WC_VEHICLE_TIMETABLE);

@ -325,7 +325,7 @@ RailTypes AddDateIntroducedRailTypes(RailTypes current, Date date)
if (rti->label == 0) continue;
/* 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. */
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;
}

@ -147,7 +147,7 @@ bool HasRoadTypeAvail(const CompanyID company, RoadType roadtype)
if (rti->label == 0) return false;
/* 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.
@ -209,7 +209,7 @@ RoadTypes AddDateIntroducedRoadTypes(RoadTypes current, Date date)
if (rti->label == 0) continue;
/* 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. */
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;
}

@ -1790,19 +1790,19 @@ bool AfterLoadGame()
/* Time starts at 0 instead of 1920.
* Account for this in older games by adding an offset */
if (IsSavegameVersionBefore(SLV_31)) {
_date += DAYS_TILL_ORIGINAL_BASE_YEAR;
_date += DAYS_TILL_ORIGINAL_BASE_YEAR.AsDelta();
SetScaledTickVariables();
ConvertDateToYMD(_date, &_cur_date_ymd);
UpdateCachedSnowLine();
for (Station *st : Station::Iterate()) st->build_date += DAYS_TILL_ORIGINAL_BASE_YEAR;
for (Waypoint *wp : Waypoint::Iterate()) wp->build_date += DAYS_TILL_ORIGINAL_BASE_YEAR;
for (Engine *e : Engine::Iterate()) e->intro_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.AsDelta();
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 (Industry *i : Industry::Iterate()) i->last_prod_year += ORIGINAL_BASE_YEAR;
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;
}
}
@ -3603,7 +3603,7 @@ bool AfterLoadGame()
/* If the start date is 0, the vehicle is not waiting to start and can be ignored. */
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)) {
extern btree::btree_map<VehicleID, uint16> _old_timetable_start_subticks_map;
@ -3612,10 +3612,10 @@ bool AfterLoadGame()
if (v->timetable_start == 0) continue;
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)) {
v->timetable_start += _old_timetable_start_subticks_map[v->index];
@ -4194,7 +4194,7 @@ bool AfterLoadGame()
for (OrderList *order_list : OrderList::Iterate()) {
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);
}
}

@ -104,7 +104,7 @@ struct DATEChunkHandler : ChunkHandler {
this->LoadCommon(_date_check_desc, _date_check_sl_compat);
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;
if (this->GetScheduledDispatchStartTick() == 0) {
DateTicksScaled start = now - (now % this->GetScheduledDispatchDuration());
DateTicksScaled start = now - (now.base() % this->GetScheduledDispatchDuration());
this->SetScheduledDispatchStartTick(start);
int64 last_dispatch = -start;
int64 last_dispatch = -(start.base());
if (last_dispatch < INT_MIN && _settings_game.game_time.time_in_minutes) {
/* Advance by multiples of 24 hours */
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)
{
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) {
DateTicksScaled end_tick = start_tick + duration;
DateTicksScaled max_extra_slots = (end_tick - 1 - date) / offset;
if (max_extra_slots < extra_slots) extra_slots = static_cast<uint>(std::max<DateTicksScaled>(0, max_extra_slots));
int64 max_extra_slots = (end_tick - 1 - date).base() / offset;
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);
}
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) {
/* Set to 00:00 of today, and 1 day */
start_tick = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), 0, 0);
start_tick -= _settings_time.clock_offset;
start_tick *= _settings_time.ticks_per_minute;
start_tick = _settings_time.FromTickMinutes(_settings_time.NowInTickMinutes().ToSameDayClockTime(0, 0));
duration = 24 * 60 * _settings_time.ticks_per_minute;
} else {
/* Set Jan 1st and 365 day */
start_tick = DateToScaledDateTicks(DAYS_TILL(_cur_year));
start_tick = DateToScaledDateTicks(DateAtStartOfYear(_cur_year));
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 {
@ -700,12 +698,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
case WID_SCHDISPATCH_SET_START_DATE: {
if (!this->IsScheduleSelected()) break;
if (_settings_time.time_in_minutes && _settings_client.gui.timetable_start_text_entry) {
uint64 time = _scaled_date_ticks;
time /= _settings_time.ticks_per_minute;
time += _settings_time.clock_offset;
time %= (24 * 60);
time = (time % 60) + (((time / 60) % 24) * 100);
SetDParam(0, time);
SetDParam(0, _settings_time.NowInTickMinutes().ClockHHMM());
ShowQueryString(STR_JUST_INT, STR_SCHDISPATCH_START_CAPTION_MINUTE, 31, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED);
} 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);
@ -867,9 +860,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
if (val >= 0 && end != nullptr && *end == 0) {
uint minutes = (val % 100) % 60;
uint hours = (val / 100) % 24;
DateTicksScaled slot = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), hours, minutes);
slot -= _settings_time.clock_offset;
slot *= _settings_time.ticks_per_minute;
DateTicksScaled slot = _settings_time.FromTickMinutes(_settings_time.NowInTickMinutes().ToSameDayClockTime(hours, minutes));
ScheduleAddIntl(v->index | (this->schedule_index << 20), slot, 0, 0);
}
break;
@ -885,9 +876,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
if (val >= 0 && end != nullptr && *end == 0) {
uint minutes = (val % 100) % 60;
uint hours = (val / 100) % 24;
DateTicksScaled start = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), hours, minutes);
start -= _settings_time.clock_offset;
start *= _settings_time.ticks_per_minute;
DateTicksScaled start = _settings_time.FromTickMinutes(_settings_time.NowInTickMinutes().ToSameDayClockTime(hours, minutes));
SetScheduleStartDateIntl(v->index | (this->schedule_index << 20), start);
}
break;
@ -974,9 +963,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
}
if (end < start || step == 0 || !this->IsScheduleSelected()) return;
DateTicksScaled slot = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), 0, start);
slot -= _settings_time.clock_offset;
slot *= _settings_time.ticks_per_minute;
DateTicksScaled slot = _settings_time.FromTickMinutes(_settings_time.NowInTickMinutes().ToSameDayClockTime(0, start));
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 {
uint start;
uint step;
uint end;
ClockFaceMinutes start;
ClockFaceMinutes step;
ClockFaceMinutes end;
ScheduledDispatchAddSlotsWindow(WindowDesc *desc, WindowNumber window_number, SchdispatchWindow *parent) :
Window(desc)
{
this->start = (_scaled_date_ticks / _settings_time.ticks_per_minute) % (60 * 24);
this->start = _settings_time.NowInTickMinutes().ToClockFaceMinutes();
this->step = 30;
this->end = this->start + 60;
this->parent = parent;
@ -1137,33 +1124,33 @@ struct ScheduledDispatchAddSlotsWindow : Window {
virtual void SetStringParameters(int widget) const override
{
switch (widget) {
case WID_SCHDISPATCH_ADD_SLOT_START_HOUR: SetDParam(0, MINUTES_HOUR(start)); break;
case WID_SCHDISPATCH_ADD_SLOT_START_MINUTE: SetDParam(0, MINUTES_MINUTE(start)); break;
case WID_SCHDISPATCH_ADD_SLOT_STEP_HOUR: SetDParam(0, MINUTES_HOUR(step)); break;
case WID_SCHDISPATCH_ADD_SLOT_STEP_MINUTE: SetDParam(0, MINUTES_MINUTE(step)); break;
case WID_SCHDISPATCH_ADD_SLOT_END_HOUR: SetDParam(0, MINUTES_HOUR(end)); break;
case WID_SCHDISPATCH_ADD_SLOT_END_MINUTE: SetDParam(0, MINUTES_MINUTE(end)); break;
case WID_SCHDISPATCH_ADD_SLOT_START_HOUR: SetDParam(0, start.ClockHour()); break;
case WID_SCHDISPATCH_ADD_SLOT_START_MINUTE: SetDParam(0, start.ClockMinute()); break;
case WID_SCHDISPATCH_ADD_SLOT_STEP_HOUR: SetDParam(0, step.ClockHour()); break;
case WID_SCHDISPATCH_ADD_SLOT_STEP_MINUTE: SetDParam(0, step.ClockMinute()); break;
case WID_SCHDISPATCH_ADD_SLOT_END_HOUR: SetDParam(0, end.ClockHour()); break;
case WID_SCHDISPATCH_ADD_SLOT_END_MINUTE: SetDParam(0, end.ClockMinute()); break;
}
}
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;
for (uint i = 0; i < 24; i++) {
SetDParam(0, i);
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;
for (uint i = 0; i < 60; i++) {
SetDParam(0, i);
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) {
@ -1187,7 +1174,7 @@ struct ScheduledDispatchAddSlotsWindow : Window {
break;
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();
break;
}
@ -1197,22 +1184,22 @@ struct ScheduledDispatchAddSlotsWindow : Window {
{
switch (widget) {
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;
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;
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;
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;
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;
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;
}

@ -57,5 +57,5 @@
{
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);
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()
{
return (ScriptDate::Date)_date;
return (ScriptDate::Date)_date.base();
}
/* static */ SQInteger ScriptDate::GetDayLengthFactor()
@ -66,7 +66,7 @@
if (day_of_month < 1 || day_of_month > 31) 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()
@ -88,17 +88,17 @@
/* 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;
return MINUTES_HOUR(minutes);
TickMinutes minutes = _settings_game.game_time.ToTickMinutes(DateTicksScaled(ticks));
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;
return MINUTES_MINUTE(minutes);
TickMinutes minutes = _settings_game.game_time.ToTickMinutes(DateTicksScaled(ticks));
return minutes.ClockMinute();
}

@ -31,7 +31,7 @@ public:
* compose valid date values for a known year, month and day.
*/
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 GetHour(DateTicksScaled ticks);
static SQInteger GetHour(SQInteger ticks);
static SQInteger GetMinute(DateTicksScaled ticks);
static SQInteger GetMinute(SQInteger ticks);
};
#endif /* SCRIPT_DATE_HPP */

@ -131,7 +131,7 @@
if (!IsValidEngine(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)
@ -171,7 +171,7 @@
{
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)

@ -52,7 +52,7 @@
{
Industry *i = Industry::GetIfValid(industry_id);
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)
@ -230,11 +230,11 @@
if (i == nullptr) return ScriptDate::DATE_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 {
int index = i->GetCargoAcceptedIndex(cargo_type);
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));
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)

@ -310,7 +310,7 @@
{
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)
@ -322,21 +322,21 @@
if (v->type == VEH_TRAIN) {
while (wagon-- > 0) v = ::Train::From(v)->GetNextUnit();
}
return v->age;
return v->age.base();
}
/* static */ SQInteger ScriptVehicle::GetMaxAge(VehicleID vehicle_id)
{
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)
{
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)

@ -135,6 +135,22 @@ struct TimeSettings {
bool time_in_minutes; ///< whether to use the hh:mm conversion when printing dates
uint16 ticks_per_minute; ///< how many ticks per minute
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. */

@ -39,7 +39,7 @@ SaveLoadTable GetLinkGraphDesc()
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;
uint recalc_scale;

@ -162,7 +162,7 @@ static void Check_DATE()
{
SlGlobList(_date_check_desc);
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);
}
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++) {
int oi = ttd_to_tto[i];
@ -403,17 +403,17 @@ static bool FixTTOEngines()
if (oi == 255) {
/* Default engine is used */
_date += DAYS_TILL_ORIGINAL_BASE_YEAR;
_date += DAYS_TILL_ORIGINAL_BASE_YEAR.AsDelta();
StartupOneEngine(e, aging_date, 0, INT_MAX);
CalcEngineReliability(e, false);
e->intro_date -= DAYS_TILL_ORIGINAL_BASE_YEAR;
_date -= DAYS_TILL_ORIGINAL_BASE_YEAR;
e->intro_date -= DAYS_TILL_ORIGINAL_BASE_YEAR.AsDelta();
_date -= DAYS_TILL_ORIGINAL_BASE_YEAR.AsDelta();
/* Make sure for example monorail and maglev are available when they should be */
if (_date >= e->intro_date && HasBit(e->info.climates, 0)) {
e->flags |= ENGINE_AVAILABLE;
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 {
/* Using data from TTO savegame */

@ -3726,7 +3726,7 @@ SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop,
}
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;
return DoSave(new FileWriter(fh), threaded);

@ -17,6 +17,7 @@
#include "../scope.h"
#include "../core/ring_buffer.hpp"
#include "../core/tinystring_type.hpp"
#include "../core/strong_typedef_type.hpp"
#include <stdarg.h>
#include <vector>
@ -338,7 +339,7 @@ static inline constexpr bool SlCheckPrimitiveTypeVar(VarType type)
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>;
}
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);
}

@ -4452,11 +4452,11 @@ void DeleteStaleLinks(Station *from)
Station *to = Station::Get((*lg)[to_id].Station());
assert(to->goods[c].node == to_id);
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);
if (edge.LastAircraftUpdate() != INVALID_DATE && (uint)(_date - edge.LastAircraftUpdate()) > timeout) {
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 && (_date - edge.LastAircraftUpdate()) > timeout) {
edge.ClearAircraft();
}
if ((uint)(_date - edge.LastUpdate()) > timeout) {
if ((_date - edge.LastUpdate()) > timeout) {
bool updated = false;
if (auto_distributed) {
@ -4488,7 +4488,7 @@ void DeleteStaleLinks(Station *from)
// Only run LinkRefresher if vehicle was not already in the cache
if (res.second) {
/* 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) {
edge_helper.RecordSize();
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);
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();
if (ge.data != nullptr) ge.data->flows.RestrictFlows(to->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();
}
@ -4643,7 +4643,7 @@ void OnTick_Station()
void StationDailyLoop()
{
// Only record cargo history every second day.
if (_date % 2 != 0) {
if (_date.base() % 2 != 0) {
for (Station *st : Station::Iterate()) {
st->UpdateCargoHistory();
}

@ -59,7 +59,7 @@ struct StatusBarWindow : Window {
int ticker_scroll;
GUITimer ticker_timer;
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 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 (_settings_time.time_in_minutes && this->last_minute != CURRENT_MINUTE) {
this->last_minute = CURRENT_MINUTE;
this->SetWidgetDirty(WID_S_LEFT);
if (_settings_time.time_in_minutes) {
const TickMinutes now = _settings_time.NowInTickMinutes();
if (this->last_minute != now) {
this->last_minute = now;
this->SetWidgetDirty(WID_S_LEFT);
}
}
if (this->ticker_scroll < TICKER_STOP) { // Scrolling text

@ -413,6 +413,7 @@ static_assert(SIZE_MAX >= UINT32_MAX);
# define GetString OTTD_GetString
# define DrawString OTTD_DrawString
# define CloseConnection OTTD_CloseConnection
# define DateDelta OTTD_DateDelta
#endif /* __APPLE__ */
#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)
{
Minutes minutes = ticks / _settings_time.ticks_per_minute + _settings_time.clock_offset;
TickMinutes minutes = _settings_time.ToTickMinutes(ticks);
char hour[3], minute[3];
seprintf(hour, lastof(hour), "%02i", (int) MINUTES_HOUR(minutes) );
seprintf(minute, lastof(minute), "%02i", (int) MINUTES_MINUTE(minutes));
seprintf(hour, lastof(hour), "%02i", minutes.ClockHour());
seprintf(minute, lastof(minute), "%02i", minutes.ClockMinute());
if (show_date) {
int64 final_arg = ScaledDateTicksToDate(ticks);
Date date = ScaledDateTicksToDate(ticks);
int64 final_arg;
if (_settings_client.gui.date_with_time == 1) {
YearMonthDay ymd;
ConvertDateToYMD(final_arg, &ymd);
ConvertDateToYMD(date, &ymd);
final_arg = ymd.year;
} else {
final_arg = date.base();
}
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);

@ -94,9 +94,9 @@ WChar GetDecimalSeparatorChar();
* @param n Index of the string parameter.
* @param v Value of the string parameter.
*/
static inline void SetDParam(size_t n, uint64 v)
{
_global_string_params.SetParam(n, v);
template <typename T>
static inline void SetDParam(size_t n, T &&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);

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

@ -1428,7 +1428,7 @@ class NIHSignals : public NIHelper {
if (it.second.IsOutOfDate()) {
b += seprintf(b, lastof(buffer), ", expired");
} 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);
}
@ -1532,7 +1532,7 @@ class NIHObject : public NIHelper {
ConvertDateToYMD(spec->introduction_date, &ymd);
char *b = buffer + seprintf(buffer, lastof(buffer), " intro: %4i-%02i-%02i",
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);
seprintf(b, lastof(buffer), ", end of life: %4i-%02i-%02i",
ymd.year, ymd.month + 1, ymd.day);

@ -121,7 +121,7 @@ static const DrawTileSprites _object_hq[] = {
#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
* T = Temperate

@ -863,8 +863,8 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling)
if (slot > -1) {
just_started = !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
v->lateness_counter = _scaled_date_ticks - slot + wait_offset;
ds.SetScheduledDispatchLastDispatch(slot - ds.GetScheduledDispatchStartTick());
v->lateness_counter = (_scaled_date_ticks - slot + wait_offset).AsTicks();
ds.SetScheduledDispatchLastDispatch((slot - ds.GetScheduledDispatchStartTick()).AsTicks());
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 (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;
}

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

@ -1033,7 +1033,7 @@ RoadType GetTownRoadType()
if (HasBit(rti->extra_flags, RXTF_NO_TOWN_MODIFICATION)) continue;
/* 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 ((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)
{
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) {
case TRTDVF_MINUTE:
return MINUTES_MINUTE(minutes);
return now.ClockMinute();
case TRTDVF_HOUR:
return MINUTES_HOUR(minutes);
return now.ClockHour();
case TRTDVF_HOUR_MINUTE:
return (MINUTES_HOUR(minutes) * 100) + MINUTES_MINUTE(minutes);
return now.ClockHHMM();
case TRTDVF_DAY:
return _cur_date_ymd.day;
@ -2396,17 +2396,17 @@ int GetTraceRestrictTimeDateValue(TraceRestrictTimeDateValueField type)
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) {
case TRTDVF_MINUTE:
return MINUTES_MINUTE(minutes);
return minutes.ClockMinute();
case TRTDVF_HOUR:
return MINUTES_HOUR(minutes);
return minutes.ClockHour();
case TRTDVF_HOUR_MINUTE:
return (MINUTES_HOUR(minutes) * 100) + MINUTES_MINUTE(minutes);
return minutes.ClockHHMM();
case TRTDVF_DAY: {
YearMonthDay ymd;

@ -120,7 +120,7 @@ void ClearAllSignalSpeedRestrictions()
_signal_speeds.clear();
}
void AdjustAllSignalSpeedRestrictionTickValues(DateTicksScaled delta)
void AdjustAllSignalSpeedRestrictionTickValues(DateTicksScaledDelta delta)
{
for (auto &it : _signal_speeds) {
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)
{
SignalSpeedKey speed_key = {
speed_key.signal_tile = tile,
speed_key.signal_track = track,
speed_key.last_passing_train_dir = v->GetVehicleTrackdir()
};
SignalSpeedValue speed_value = {
speed_value.train_speed = v->First()->cur_speed,
speed_value.time_stamp = GetSpeedRestrictionTimeout(v->First())
};
SignalSpeedKey speed_key = {};
speed_key.signal_tile = tile;
speed_key.signal_track = track;
speed_key.last_passing_train_dir = v->GetVehicleTrackdir();
SignalSpeedValue speed_value = {};
speed_value.train_speed = v->First()->cur_speed;
speed_value.time_stamp = GetSpeedRestrictionTimeout(v->First());
_signal_speeds[speed_key] = speed_value;
}

@ -2418,16 +2418,16 @@ void AgeVehicle(Vehicle *v)
/* Stop if a virtual vehicle */
if (HasBit(v->subtype, GVSF_VIRTUAL)) return;
if (v->age < MAX_DAY) {
if (v->age < MAX_DATE.AsDelta()) {
v->age++;
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;
int age = v->age - v->max_age;
DateDelta age = v->age - v->max_age;
for (int i = 0; i <= 4; i++) {
if (age == DateAtStartOfYear(i)) {
if (age == DateAtStartOfYear(i).AsDelta()) {
v->reliability_spd_dec <<= 1;
break;
}
@ -4649,7 +4649,7 @@ void DumpVehicleStats(char *buffer, const char *last)
buffer += seprintf(buffer, last, " %10s: %5u\n", "total", (uint)Vehicle::GetNumItems());
}
void AdjustVehicleScaledTickBase(int64 delta)
void AdjustVehicleScaledTickBase(DateTicksScaledDelta delta)
{
for (Vehicle *v : Vehicle::Iterate()) {
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()) {
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
* for vehicles outside of a depot. */

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

@ -26,7 +26,7 @@
#define IS_CUSTOM_FIRSTHEAD_SPRITE(x) (x == 0xFD)
#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.
struct Viewport;

@ -457,7 +457,7 @@ void DepotSortList(VehicleList *list)
}
/** 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;
@ -1882,8 +1882,8 @@ void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int
switch (this->vehgroups.SortType()) {
case VST_AGE: {
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(4, v->max_age / DAYS_IN_LEAP_YEAR);
SetDParam(3, DateDeltaToYears(v->age));
SetDParam(4, DateDeltaToYears(v->max_age));
break;
}
@ -1938,7 +1938,7 @@ void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int
case VST_TIME_TO_LIVE: {
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);
SetDParam(3, std::abs(years_remaining));
SetDParam(3, std::abs(years_remaining.base()));
break;
}
@ -3079,9 +3079,9 @@ struct VehicleDetailsWindow : Window {
Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
/* 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(2, DateToYear(v->max_age));
SetDParam(2, DateDeltaToYears(v->max_age));
SetDParam(3, v->GetDisplayRunningCost());
DrawString(tr, STR_VEHICLE_INFO_AGE_RUNNING_COST_YR);
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) {
return v_a->age < v_b->age;

Loading…
Cancel
Save