Refactor and clean up date handling (#19666)

* Refactor and clean up date handling

* Remove gDate, remove direct access to days_in_month

* Adjust the MultiLaunch test

* Bump network version

---------

Co-authored-by: ζeh Matt <5415177+ZehMatt@users.noreply.github.com>
This commit is contained in:
Michael Steenbeek 2023-04-02 10:25:41 +02:00 committed by GitHub
parent 9e4099fcdf
commit fd80b4c822
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 241 additions and 302 deletions

View File

@ -9,6 +9,7 @@
#include <openrct2-ui/interface/Graph.h>
#include <openrct2/Context.h>
#include <openrct2/Date.h>
#include <openrct2/localisation/Date.h>
#include <openrct2/localisation/Formatter.h>
#include <openrct2/localisation/Localisation.h>
@ -17,8 +18,9 @@ namespace Graph
{
static void DrawMonths(DrawPixelInfo* dpi, const uint8_t* history, int32_t count, const ScreenCoordsXY& origCoords)
{
int32_t currentMonth = DateGetMonth(gDateMonthsElapsed);
int32_t currentDay = gDateMonthTicks;
auto& date = GetDate();
int32_t currentMonth = date.GetMonth();
int32_t currentDay = date.GetMonthTicks();
int32_t yearOver32 = (currentMonth * 4) + (currentDay >> 14) - 31;
auto screenCoords = origCoords;
for (int32_t i = count - 1; i >= 0; i--)
@ -152,13 +154,12 @@ namespace Graph
{
static void DrawMonths(DrawPixelInfo* dpi, const money64* history, int32_t count, const ScreenCoordsXY& origCoords)
{
int32_t i, yearOver32, currentMonth, currentDay;
currentMonth = DateGetMonth(gDateMonthsElapsed);
currentDay = gDateMonthTicks;
yearOver32 = (currentMonth * 4) + (currentDay >> 14) - 31;
auto& date = GetDate();
int32_t currentMonth = date.GetMonth();
int32_t currentDay = date.GetMonthTicks();
int32_t yearOver32 = (currentMonth * 4) + (currentDay >> 14) - 31;
auto screenCoords = origCoords;
for (i = count - 1; i >= 0; i--)
for (int32_t i = count - 1; i >= 0; i--)
{
if (history[i] != MONEY64_UNDEFINED && yearOver32 % 4 == 0)
{

View File

@ -31,6 +31,8 @@
constexpr auto CHEATS_MONEY_DEFAULT = 10000.00_GBP;
constexpr auto CHEATS_MONEY_INCREMENT_DIV = 5000.00_GBP;
using OpenRCT2::Date;
// clang-format off
enum
{
@ -724,37 +726,41 @@ private:
case WIDX_MONTH_UP:
_monthSpinnerValue++;
_monthSpinnerValue = std::clamp(_monthSpinnerValue, 1, static_cast<int32_t>(MONTH_COUNT));
_daySpinnerValue = std::clamp(_daySpinnerValue, 1, static_cast<int32_t>(days_in_month[_monthSpinnerValue - 1]));
_daySpinnerValue = std::clamp(
_daySpinnerValue, 1, static_cast<int32_t>(Date::GetDaysInMonth(_monthSpinnerValue - 1)));
InvalidateWidget(WIDX_MONTH_BOX);
InvalidateWidget(WIDX_DAY_BOX);
break;
case WIDX_MONTH_DOWN:
_monthSpinnerValue--;
_monthSpinnerValue = std::clamp(_monthSpinnerValue, 1, static_cast<int32_t>(MONTH_COUNT));
_daySpinnerValue = std::clamp(_daySpinnerValue, 1, static_cast<int32_t>(days_in_month[_monthSpinnerValue - 1]));
_daySpinnerValue = std::clamp(
_daySpinnerValue, 1, static_cast<int32_t>(Date::GetDaysInMonth(_monthSpinnerValue - 1)));
InvalidateWidget(WIDX_MONTH_BOX);
InvalidateWidget(WIDX_DAY_BOX);
break;
case WIDX_DAY_UP:
_daySpinnerValue++;
_daySpinnerValue = std::clamp(_daySpinnerValue, 1, static_cast<int32_t>(days_in_month[_monthSpinnerValue - 1]));
_daySpinnerValue = std::clamp(
_daySpinnerValue, 1, static_cast<int32_t>(Date::GetDaysInMonth(_monthSpinnerValue - 1)));
InvalidateWidget(WIDX_DAY_BOX);
break;
case WIDX_DAY_DOWN:
_daySpinnerValue--;
_daySpinnerValue = std::clamp(_daySpinnerValue, 1, static_cast<int32_t>(days_in_month[_monthSpinnerValue - 1]));
_daySpinnerValue = std::clamp(
_daySpinnerValue, 1, static_cast<int32_t>(Date::GetDaysInMonth(_monthSpinnerValue - 1)));
InvalidateWidget(WIDX_DAY_BOX);
break;
case WIDX_DATE_SET:
{
auto setDateAction = ParkSetDateAction(_yearSpinnerValue, _monthSpinnerValue, _daySpinnerValue);
auto setDateAction = ParkSetDateAction(_yearSpinnerValue - 1, _monthSpinnerValue - 1, _daySpinnerValue - 1);
GameActions::Execute(&setDateAction);
WindowInvalidateByClass(WindowClass::BottomToolbar);
break;
}
case WIDX_DATE_RESET:
{
auto setDateAction = ParkSetDateAction(1, 1, 1);
auto setDateAction = ParkSetDateAction(0, 0, 0);
GameActions::Execute(&setDateAction);
WindowInvalidateByClass(WindowClass::BottomToolbar);
InvalidateWidget(WIDX_YEAR_BOX);

View File

@ -224,13 +224,13 @@ static_assert(std::size(_windowFinancesPageHoldDownWidgets) == WINDOW_FINANCES_P
class FinancesWindow final : public Window
{
private:
int32_t _lastPaintedMonth;
uint32_t _lastPaintedMonth;
public:
void OnOpen() override
{
SetPage(WINDOW_FINANCES_PAGE_SUMMARY);
_lastPaintedMonth = std::numeric_limits<int32_t>::max();
_lastPaintedMonth = std::numeric_limits<uint32_t>::max();
ResearchUpdateUncompletedTypes();
}
@ -381,7 +381,7 @@ public:
}
// Expenditure / Income values for each month
uint16_t currentMonthYear = static_cast<uint16_t>(gDateMonthsElapsed);
auto currentMonthYear = GetDate().GetMonthsElapsed();
for (int32_t i = SummaryMaxAvailableMonth(); i >= 0; i--)
{
screenCoords.y = 0;
@ -522,7 +522,7 @@ public:
ft.Add<money64>(gBankLoan);
// Keep up with new months being added in the first two years.
if (gDateMonthsElapsed != _lastPaintedMonth)
if (GetDate().GetMonthsElapsed() != _lastPaintedMonth)
InitialiseScrollPosition(WIDX_SUMMARY_SCROLL, 0);
}
@ -593,7 +593,7 @@ public:
uint16_t SummaryMaxAvailableMonth()
{
return std::min<uint16_t>(gDateMonthsElapsed, EXPENDITURE_TABLE_MONTH_COUNT - 1);
return std::min<uint16_t>(GetDate().GetMonthsElapsed(), EXPENDITURE_TABLE_MONTH_COUNT - 1);
}
#pragma endregion

View File

@ -190,7 +190,6 @@ static void WindowGameBottomToolbarMouseup(WindowBase* w, WidgetIndex widgetInde
static OpenRCT2String WindowGameBottomToolbarTooltip(WindowBase* w, const WidgetIndex widgetIndex, const StringId fallback)
{
int32_t month, day;
auto ft = Formatter();
switch (widgetIndex)
@ -202,13 +201,6 @@ static OpenRCT2String WindowGameBottomToolbarTooltip(WindowBase* w, const Widget
case WIDX_PARK_RATING:
ft.Add<int16_t>(gParkRating);
break;
case WIDX_DATE:
month = DateGetMonth(gDateMonthsElapsed);
day = ((gDateMonthTicks * days_in_month[month]) >> 16) & 0xFF;
ft.Add<StringId>(DateDayNames[day]);
ft.Add<StringId>(DateGameMonthNames[month]);
break;
}
return { fallback, ft };
}
@ -496,9 +488,10 @@ static void WindowGameBottomToolbarDrawRightPanel(DrawPixelInfo* dpi, WindowBase
window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].top + w->windowPos.y + 2 };
// Date
int32_t year = DateGetYear(gDateMonthsElapsed) + 1;
int32_t month = DateGetMonth(gDateMonthsElapsed);
int32_t day = ((gDateMonthTicks * days_in_month[month]) >> 16) & 0xFF;
auto& date = GetDate();
int32_t year = date.GetYear() + 1;
int32_t month = date.GetMonth();
int32_t day = date.GetDay();
colour_t colour
= (gHoverWidget.window_classification == WindowClass::BottomToolbar && gHoverWidget.widget_index == WIDX_DATE

View File

@ -9,7 +9,10 @@
#include "localisation/Date.h"
#include "Context.h"
#include "Date.h"
#include "Game.h"
#include "GameState.h"
#include "core/Guard.hpp"
#include <algorithm>
@ -21,6 +24,13 @@ constexpr int32_t MASK_WEEK_TICKS = 0x3FFF;
constexpr int32_t MASK_FORTNIGHT_TICKS = 0x7FFF;
constexpr int32_t MASK_MONTH_TICKS = 0xFFFF;
// rct2: 0x00993988
static const int16_t days_in_month[MONTH_COUNT] = {
31, 30, 31, 30, 31, 31, 30, 31,
};
RealWorldTime gRealTimeOfDay;
Date::Date(uint32_t monthsElapsed, uint16_t monthTicks)
: _monthTicks(monthTicks)
, _monthsElapsed(monthsElapsed)
@ -29,17 +39,17 @@ Date::Date(uint32_t monthsElapsed, uint16_t monthTicks)
Date Date::FromYMD(int32_t year, int32_t month, int32_t day)
{
// Year, months
Guard::ArgumentInRange(month, 0, MONTH_COUNT - 1);
int32_t monthsElapsed = (year * MONTH_COUNT) + month;
year = std::clamp(year, 0, MAX_YEAR - 1);
month = std::clamp(month, 0, static_cast<int>(MONTH_COUNT) - 1);
auto daysInMonth = days_in_month[month];
day = std::clamp(day, 0, daysInMonth - 1);
int32_t monthsElapsed = (year * MONTH_COUNT) + month;
// Day
int32_t monthTicks = 0;
if (day != 0)
{
auto daysInMonth = GetDaysInMonth(month);
day = std::clamp(day, 0, daysInMonth - 1);
monthTicks = (day << 16) / daysInMonth;
monthTicks = ((day << 16) / daysInMonth) + MONTH_TICKS_INCREMENT;
}
return Date(monthsElapsed, monthTicks);
@ -119,3 +129,33 @@ int32_t Date::GetDaysInMonth(int32_t month)
return days_in_month[month];
}
int32_t DateGetMonth(int32_t months)
{
return months % MONTH_COUNT;
}
int32_t DateGetYear(int32_t months)
{
return months / MONTH_COUNT;
}
int32_t DateGetTotalMonths(int32_t month, int32_t year)
{
return (year - 1) * MONTH_COUNT + month;
}
void DateUpdateRealTimeOfDay()
{
time_t timestamp = time(nullptr);
struct tm* now = localtime(&timestamp);
gRealTimeOfDay.second = now->tm_sec;
gRealTimeOfDay.minute = now->tm_min;
gRealTimeOfDay.hour = now->tm_hour;
}
Date& GetDate()
{
return OpenRCT2::GetContext()->GetGameState()->GetDate();
}

View File

@ -11,6 +11,31 @@
#include "common.h"
constexpr int32_t MAX_YEAR = 8192;
constexpr int32_t TICKS_PER_MONTH = 0x10000;
enum
{
MONTH_MARCH,
MONTH_APRIL,
MONTH_MAY,
MONTH_JUNE,
MONTH_JULY,
MONTH_AUGUST,
MONTH_SEPTEMBER,
MONTH_OCTOBER,
MONTH_COUNT
};
enum
{
DATE_FORMAT_DAY_MONTH_YEAR,
DATE_FORMAT_MONTH_DAY_YEAR,
DATE_FORMAT_YEAR_MONTH_DAY,
DATE_FORMAT_YEAR_DAY_MONTH
};
namespace OpenRCT2
{
/**
@ -44,3 +69,26 @@ namespace OpenRCT2
static int32_t GetDaysInMonth(int32_t month);
};
} // namespace OpenRCT2
struct RealWorldDate
{
uint8_t day;
uint8_t month;
int16_t year;
uint8_t day_of_week;
};
struct RealWorldTime
{
uint8_t second;
uint8_t minute;
uint8_t hour;
};
OpenRCT2::Date& GetDate();
extern RealWorldTime gRealTimeOfDay;
int32_t DateGetMonth(int32_t months);
int32_t DateGetYear(int32_t months);
int32_t DateGetTotalMonths(int32_t month, int32_t year);
void DateUpdateRealTimeOfDay();

View File

@ -11,6 +11,7 @@
#include "./peep/GuestPathfinding.h"
#include "Context.h"
#include "Date.h"
#include "Editor.h"
#include "Game.h"
#include "GameState.h"
@ -70,7 +71,7 @@ void GameState::InitAll(const TileCoordsXY& mapSize)
RideInitAll();
ResetAllEntities();
UpdateConsolidatedPatrolAreas();
DateReset();
ResetDate();
ClimateReset(ClimateType::CoolAndWet);
News::InitQueue();
@ -313,8 +314,7 @@ void GameState::UpdateLogic(LogicTimings* timings)
auto day = _date.GetDay();
#endif
DateUpdate();
_date = Date(static_cast<uint32_t>(gDateMonthsElapsed), gDateMonthTicks);
_date.Update();
report_time(LogicTimePart::Date);
ScenarioUpdate();
@ -408,3 +408,18 @@ void GameState::CreateStateSnapshot()
snapshots->Capture(snapshot);
snapshots->LinkSnapshot(snapshot, gCurrentTicks, ScenarioRandState().s0);
}
void GameState::SetDate(Date newDate)
{
_date = newDate;
}
/**
*
* rct2: 0x006C4494
*/
void GameState::ResetDate()
{
_date = OpenRCT2::Date();
gCurrentRealTimeTicks = 0;
}

View File

@ -86,6 +86,8 @@ namespace OpenRCT2
void InitAll(const TileCoordsXY& mapSize);
void Tick();
void UpdateLogic(LogicTimings* timings = nullptr);
void SetDate(Date newDate);
void ResetDate();
private:
void CreateStateSnapshot();

View File

@ -10,6 +10,7 @@
#include "ParkSetDateAction.h"
#include "../Context.h"
#include "../GameState.h"
#include "../core/MemoryStream.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
@ -44,7 +45,7 @@ void ParkSetDateAction::Serialise(DataSerialiser& stream)
GameActions::Result ParkSetDateAction::Query() const
{
if (_year <= 0 || _year > MAX_YEAR || _month <= 0 || _month > MONTH_COUNT || _day <= 0 || _day > 31)
if (_year < 0 || _year >= MAX_YEAR || _month < 0 || _month >= MONTH_COUNT || _day < 0 || _day >= 31)
{
return GameActions::Result(GameActions::Status::InvalidParameters, STR_NONE, STR_NONE);
}
@ -54,6 +55,6 @@ GameActions::Result ParkSetDateAction::Query() const
GameActions::Result ParkSetDateAction::Execute() const
{
DateSet(_year, _month, _day);
OpenRCT2::GetContext()->GetGameState()->SetDate(OpenRCT2::Date::FromYMD(_year, _month, _day));
return GameActions::Result();
}

View File

@ -286,7 +286,7 @@ GameActions::Result RideCreateAction::Execute() const
ride->num_riders = 0;
ride->slide_in_use = 0;
ride->maze_tiles = 0;
ride->build_date = gDateMonthsElapsed;
ride->build_date = GetDate().GetMonthsElapsed();
ride->music_tune_id = TUNE_ID_NULL;
ride->breakdown_reason = 255;

View File

@ -190,7 +190,7 @@ GameActions::Result StaffHireNewAction::QueryExecute(bool execute) const
}
// Staff uses this
newPeep->As<Staff>()->SetHireDate(gDateMonthsElapsed);
newPeep->As<Staff>()->SetHireDate(GetDate().GetMonthsElapsed());
newPeep->PathfindGoal.x = 0xFF;
newPeep->PathfindGoal.y = 0xFF;
newPeep->PathfindGoal.z = 0xFF;

View File

@ -171,7 +171,7 @@ void Duck::UpdateSwim()
}
else
{
int32_t currentMonth = DateGetMonth(gDateMonthsElapsed);
int32_t currentMonth = GetDate().GetMonth();
if (currentMonth >= MONTH_SEPTEMBER && (randomNumber >> 16) < 218)
{
state = DuckState::FlyAway;

View File

@ -265,7 +265,7 @@ void Staff::ResetStats()
{
for (auto peep : EntityList<Staff>())
{
peep->SetHireDate(gDateMonthsElapsed);
peep->SetHireDate(GetDate().GetMonthsElapsed());
peep->StaffLawnsMown = 0;
peep->StaffRidesFixed = 0;
peep->StaffGardensWatered = 0;

View File

@ -10,6 +10,7 @@
#include "InteractiveConsole.h"
#include "../Context.h"
#include "../Date.h"
#include "../EditorObjectSelectionSession.h"
#include "../Game.h"
#include "../OpenRCT2.h"
@ -18,6 +19,7 @@
#include "../Version.h"
#include "../actions/CheatSetAction.h"
#include "../actions/ClimateSetAction.h"
#include "../actions/ParkSetDateAction.h"
#include "../actions/ParkSetParameterAction.h"
#include "../actions/RideFreezeRatingAction.h"
#include "../actions/RideSetPriceAction.h"
@ -76,6 +78,7 @@
#endif
using arguments_t = std::vector<std::string>;
using OpenRCT2::Date;
static constexpr const char* ClimateNames[] = {
"cool_and_wet",
@ -1452,7 +1455,7 @@ static int32_t ConsoleCommandForceDate([[maybe_unused]] InteractiveConsole& cons
// YYYY (no month provided, preserve existing month)
if (argv.size() == 1)
{
month = gDateMonthsElapsed % MONTH_COUNT + 1;
month = GetDate().GetMonth() + 1;
}
// YYYY MM or YYYY MM DD (month provided)
@ -1469,21 +1472,21 @@ static int32_t ConsoleCommandForceDate([[maybe_unused]] InteractiveConsole& cons
// YYYY OR YYYY MM (no day provided, preserve existing day)
if (argv.size() <= 2)
{
day = std::clamp(
gDateMonthTicks / (TICKS_PER_MONTH / days_in_month[month - 1]) + 1, 1, static_cast<int>(days_in_month[month - 1]));
day = std::clamp(GetDate().GetDay() + 1, 1, static_cast<int>(Date::GetDaysInMonth(month - 1)));
}
// YYYY MM DD (year, month, and day provided)
if (argv.size() == 3)
{
day = atoi(argv[2].c_str());
if (day < 1 || day > days_in_month[month - 1])
if (day < 1 || day > Date::GetDaysInMonth(month - 1))
{
return -1;
}
}
DateSet(year, month, day);
auto setDateAction = ParkSetDateAction(year - 1, month - 1, day - 1);
GameActions::Execute(&setDateAction);
WindowInvalidateByClass(WindowClass::BottomToolbar);
return 1;

View File

@ -9,57 +9,12 @@
#pragma once
#include "../Date.h"
#include "../common.h"
constexpr int32_t MAX_YEAR = 8192;
constexpr int32_t TICKS_PER_MONTH = 0x10000;
enum
{
MONTH_MARCH,
MONTH_APRIL,
MONTH_MAY,
MONTH_JUNE,
MONTH_JULY,
MONTH_AUGUST,
MONTH_SEPTEMBER,
MONTH_OCTOBER,
MONTH_COUNT
};
enum
{
DATE_FORMAT_DAY_MONTH_YEAR,
DATE_FORMAT_MONTH_DAY_YEAR,
DATE_FORMAT_YEAR_MONTH_DAY,
DATE_FORMAT_YEAR_DAY_MONTH
};
struct RealWorldTime
{
uint8_t second;
uint8_t minute;
uint8_t hour;
};
extern const int16_t days_in_month[MONTH_COUNT];
extern const StringId DateFormatStringIDs[];
extern const StringId DateFormatStringFormatIds[];
extern uint16_t gDateMonthTicks;
extern int32_t gDateMonthsElapsed;
extern RealWorldTime gRealTimeOfDay;
int32_t DateGetMonth(int32_t months);
int32_t DateGetYear(int32_t months);
int32_t DateGetTotalMonths(int32_t month, int32_t year);
void DateReset();
void DateUpdate();
void DateSet(int32_t year, int32_t month, int32_t day);
void DateUpdateRealTimeOfDay();
bool DateIsDayStart(int32_t monthTicks);
bool DateIsWeekStart(int32_t monthTicks);
bool DateIsFortnightStart(int32_t monthTicks);
bool DateIsMonthStart(int32_t monthTicks);
extern const StringId DateDayNames[31];
extern const StringId DateGameMonthNames[MONTH_COUNT];
extern const StringId DateGameShortMonthNames[MONTH_COUNT];

View File

@ -7,21 +7,26 @@
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
#include "../Game.h"
#include "../profiling/Profiling.h"
#include "../util/Math.hpp"
#include "../common.h"
#include "Date.h"
#include "StringIds.h"
#include <algorithm>
#include <time.h>
const StringId DateDayNames[] = {
STR_DATE_DAY_1, STR_DATE_DAY_2, STR_DATE_DAY_3, STR_DATE_DAY_4, STR_DATE_DAY_5, STR_DATE_DAY_6, STR_DATE_DAY_7,
STR_DATE_DAY_8, STR_DATE_DAY_9, STR_DATE_DAY_10, STR_DATE_DAY_11, STR_DATE_DAY_12, STR_DATE_DAY_13, STR_DATE_DAY_14,
STR_DATE_DAY_15, STR_DATE_DAY_16, STR_DATE_DAY_17, STR_DATE_DAY_18, STR_DATE_DAY_19, STR_DATE_DAY_20, STR_DATE_DAY_21,
STR_DATE_DAY_22, STR_DATE_DAY_23, STR_DATE_DAY_24, STR_DATE_DAY_25, STR_DATE_DAY_26, STR_DATE_DAY_27, STR_DATE_DAY_28,
STR_DATE_DAY_29, STR_DATE_DAY_30, STR_DATE_DAY_31,
};
uint16_t gDateMonthTicks;
int32_t gDateMonthsElapsed;
const StringId DateGameMonthNames[MONTH_COUNT] = {
STR_MONTH_MARCH, STR_MONTH_APRIL, STR_MONTH_MAY, STR_MONTH_JUNE,
STR_MONTH_JULY, STR_MONTH_AUGUST, STR_MONTH_SEPTEMBER, STR_MONTH_OCTOBER,
};
// rct2: 0x00993988
const int16_t days_in_month[MONTH_COUNT] = {
31, 30, 31, 30, 31, 31, 30, 31,
const StringId DateGameShortMonthNames[MONTH_COUNT] = {
STR_MONTH_SHORT_MAR, STR_MONTH_SHORT_APR, STR_MONTH_SHORT_MAY, STR_MONTH_SHORT_JUN,
STR_MONTH_SHORT_JUL, STR_MONTH_SHORT_AUG, STR_MONTH_SHORT_SEP, STR_MONTH_SHORT_OCT,
};
const StringId DateFormatStringIDs[] = {
@ -37,93 +42,3 @@ const StringId DateFormatStringFormatIds[] = {
STR_DATE_FORMAT_YMD,
STR_DATE_FORMAT_YDM,
};
RealWorldTime gRealTimeOfDay;
int32_t DateGetMonth(int32_t months)
{
return months % MONTH_COUNT;
}
int32_t DateGetYear(int32_t months)
{
return months / MONTH_COUNT;
}
int32_t DateGetTotalMonths(int32_t month, int32_t year)
{
return (year - 1) * MONTH_COUNT + month;
}
/**
*
* rct2: 0x006C4494
*/
void DateReset()
{
gDateMonthsElapsed = 0;
gDateMonthTicks = 0;
gCurrentRealTimeTicks = 0;
}
void DateSet(int32_t year, int32_t month, int32_t day)
{
year = std::clamp(year, 1, MAX_YEAR);
month = std::clamp(month, 1, static_cast<int>(MONTH_COUNT));
day = std::clamp(day, 1, static_cast<int>(days_in_month[month - 1]));
gDateMonthsElapsed = (year - 1) * MONTH_COUNT + month - 1;
gDateMonthTicks = TICKS_PER_MONTH / days_in_month[month - 1] * (day - 1) + 4;
}
void DateUpdate()
{
PROFILED_FUNCTION();
int32_t monthTicks = gDateMonthTicks + 4;
if (monthTicks >= TICKS_PER_MONTH)
{
gDateMonthTicks = 0;
gDateMonthsElapsed++;
}
else
{
gDateMonthTicks = Floor2(static_cast<uint16_t>(monthTicks), 4);
}
}
void DateUpdateRealTimeOfDay()
{
time_t timestamp = time(nullptr);
struct tm* now = localtime(&timestamp);
gRealTimeOfDay.second = now->tm_sec;
gRealTimeOfDay.minute = now->tm_min;
gRealTimeOfDay.hour = now->tm_hour;
}
bool DateIsDayStart(int32_t monthTicks)
{
if (monthTicks < 4)
{
return false;
}
int32_t prevMonthTick = monthTicks - 4;
int32_t currentMonth = DateGetMonth(gDateMonthsElapsed);
int32_t currentDaysInMonth = days_in_month[currentMonth];
return ((currentDaysInMonth * monthTicks) >> 16 != (currentDaysInMonth * prevMonthTick) >> 16);
}
bool DateIsWeekStart(int32_t monthTicks)
{
return (monthTicks & 0x3FFF) == 0;
}
bool DateIsFortnightStart(int32_t monthTicks)
{
return (monthTicks & 0x7FFF) == 0;
}
bool DateIsMonthStart(int32_t monthTicks)
{
return (monthTicks == 0);
}

View File

@ -263,62 +263,6 @@ const StringId PeepThoughts[] = {
STR_PEEP_THOUGHT_TYPE_EXCITED_DEPRECATED,
STR_PEEP_THOUGHT_TYPE_HERE_WE_ARE,
};
const StringId DateDayNames[] = {
STR_DATE_DAY_1,
STR_DATE_DAY_2,
STR_DATE_DAY_3,
STR_DATE_DAY_4,
STR_DATE_DAY_5,
STR_DATE_DAY_6,
STR_DATE_DAY_7,
STR_DATE_DAY_8,
STR_DATE_DAY_9,
STR_DATE_DAY_10,
STR_DATE_DAY_11,
STR_DATE_DAY_12,
STR_DATE_DAY_13,
STR_DATE_DAY_14,
STR_DATE_DAY_15,
STR_DATE_DAY_16,
STR_DATE_DAY_17,
STR_DATE_DAY_18,
STR_DATE_DAY_19,
STR_DATE_DAY_20,
STR_DATE_DAY_21,
STR_DATE_DAY_22,
STR_DATE_DAY_23,
STR_DATE_DAY_24,
STR_DATE_DAY_25,
STR_DATE_DAY_26,
STR_DATE_DAY_27,
STR_DATE_DAY_28,
STR_DATE_DAY_29,
STR_DATE_DAY_30,
STR_DATE_DAY_31,
};
const StringId DateGameMonthNames[MONTH_COUNT] = {
STR_MONTH_MARCH,
STR_MONTH_APRIL,
STR_MONTH_MAY,
STR_MONTH_JUNE,
STR_MONTH_JULY,
STR_MONTH_AUGUST,
STR_MONTH_SEPTEMBER,
STR_MONTH_OCTOBER,
};
const StringId DateGameShortMonthNames[MONTH_COUNT] = {
STR_MONTH_SHORT_MAR,
STR_MONTH_SHORT_APR,
STR_MONTH_SHORT_MAY,
STR_MONTH_SHORT_JUN,
STR_MONTH_SHORT_JUL,
STR_MONTH_SHORT_AUG,
STR_MONTH_SHORT_SEP,
STR_MONTH_SHORT_OCT,
};
// clang-format on
std::string FormatStringID(StringId format, const void* args)

View File

@ -63,6 +63,3 @@ extern const StringId ResearchFundingLevelNames[4];
extern const StringId MarketingCampaignNames[ADVERTISING_CAMPAIGN_COUNT][3];
extern const StringId RideInspectionIntervalNames[];
extern const StringId PeepThoughts[174];
extern const StringId DateDayNames[31];
extern const StringId DateGameMonthNames[MONTH_COUNT];
extern const StringId DateGameShortMonthNames[MONTH_COUNT];

View File

@ -320,7 +320,7 @@ money64 FinanceGetCurrentCash()
void FinanceShiftExpenditureTable()
{
// If EXPENDITURE_TABLE_MONTH_COUNT months have passed then is full, sum the oldest month
if (gDateMonthsElapsed >= EXPENDITURE_TABLE_MONTH_COUNT)
if (GetDate().GetMonthsElapsed() >= EXPENDITURE_TABLE_MONTH_COUNT)
{
money64 sum = 0;
for (uint32_t i = 0; i < static_cast<int32_t>(ExpenditureType::Count); i++)
@ -363,7 +363,7 @@ void FinanceResetCashToInitial()
money64 FinanceGetLastMonthShopProfit()
{
money64 profit = 0;
if (gDateMonthsElapsed != 0)
if (GetDate().GetMonthsElapsed() != 0)
{
const auto* lastMonthExpenditure = gExpenditureTable[1];

View File

@ -323,13 +323,14 @@ News::Item* News::AddItemToQueue(ItemType type, StringId string_id, EntityId ass
News::Item* News::AddItemToQueue(News::ItemType type, const utf8* text, uint32_t assoc)
{
auto& date = GetDate();
News::Item* newsItem = gNewsItems.FirstOpenOrNewSlot();
newsItem->Type = type;
newsItem->Flags = 0;
newsItem->Assoc = assoc; // Make optional for Award, Money, Graph and Null
newsItem->Ticks = 0;
newsItem->MonthYear = static_cast<uint16_t>(gDateMonthsElapsed);
newsItem->Day = ((days_in_month[DateGetMonth(newsItem->MonthYear)] * gDateMonthTicks) >> 16) + 1;
newsItem->MonthYear = static_cast<uint16_t>(date.GetMonthsElapsed());
newsItem->Day = date.GetDay() + 1;
newsItem->Text = text;
return newsItem;

View File

@ -9,6 +9,7 @@
#include "Research.h"
#include "../Date.h"
#include "../Game.h"
#include "../OpenRCT2.h"
#include "../actions/ParkSetResearchFundingAction.h"
@ -108,16 +109,18 @@ static void ResearchCalculateExpectedDate()
}
else
{
auto& date = GetDate();
int32_t progressRemaining = gResearchProgressStage == RESEARCH_STAGE_COMPLETING_DESIGN ? 0x10000 : 0x20000;
progressRemaining -= gResearchProgress;
int32_t daysRemaining = (progressRemaining / _researchRate[gResearchFundingLevel]) * 128;
int32_t expectedDay = gDateMonthTicks + (daysRemaining & 0xFFFF);
int32_t expectedDay = date.GetMonthTicks() + (daysRemaining & 0xFFFF);
int32_t dayQuotient = expectedDay / 0x10000;
int32_t dayRemainder = expectedDay % 0x10000;
int32_t expectedMonth = DateGetMonth(gDateMonthsElapsed + dayQuotient + (daysRemaining >> 16));
expectedDay = (dayRemainder * days_in_month[expectedMonth]) >> 16;
int32_t expectedMonth = DateGetMonth(date.GetMonthsElapsed() + dayQuotient + (daysRemaining >> 16));
expectedDay = (dayRemainder * Date::GetDaysInMonth(expectedMonth)) >> 16;
gResearchExpectedDay = expectedDay;
gResearchExpectedMonth = expectedMonth;

View File

@ -43,7 +43,7 @@
// It is used for making sure only compatible builds get connected, even within
// single OpenRCT2 version.
#define NETWORK_STREAM_VERSION "0"
#define NETWORK_STREAM_VERSION "1"
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION

View File

@ -297,9 +297,10 @@ private:
{ "players", numPlayers },
};
auto& date = GetDate();
json_t mapSize = { { "x", gMapSize.x - 2 }, { "y", gMapSize.y - 2 } };
json_t gameInfo = {
{ "mapSize", mapSize }, { "day", gDateMonthTicks }, { "month", gDateMonthsElapsed },
{ "mapSize", mapSize }, { "day", date.GetMonthTicks() }, { "month", date.GetMonthsElapsed() },
{ "guests", gNumGuestsInPark }, { "parkValue", gParkValue },
};
if (!(gParkFlags & PARK_FLAGS_NO_MONEY))

View File

@ -479,8 +479,20 @@ namespace OpenRCT2
cs.Write(isPaused);
}
cs.ReadWrite(gCurrentTicks);
cs.ReadWrite(gDateMonthTicks);
cs.ReadWrite(gDateMonthsElapsed);
if (cs.GetMode() == OrcaStream::Mode::READING)
{
uint16_t monthTicks;
uint32_t monthsElapsed;
cs.ReadWrite(monthTicks);
cs.ReadWrite(monthsElapsed);
GetContext()->GetGameState()->SetDate(Date(monthsElapsed, monthTicks));
}
else
{
auto& date = GetDate();
cs.Write(date.GetMonthTicks());
cs.Write(date.GetMonthsElapsed());
}
if (cs.GetMode() == OrcaStream::Mode::READING)
{

View File

@ -28,6 +28,7 @@
# include <fontconfig/fontconfig.h>
# endif // NO_TTF
# include "../Date.h"
# include "../OpenRCT2.h"
# include "../core/Path.hpp"
# include "../localisation/Language.h"

View File

@ -11,10 +11,10 @@
# include "Platform.h"
# include "../Date.h"
# include "../core/Memory.hpp"
# include "../core/Path.hpp"
# include "../core/String.hpp"
# include "../localisation/Date.h"
# include "../util/Util.h"
# include <cerrno>

View File

@ -21,6 +21,7 @@
# include <shlobj.h>
# undef GetEnvironmentVariable
# include "../Date.h"
# include "../OpenRCT2.h"
# include "../common.h"
# include "../core/Path.hpp"

View File

@ -40,13 +40,7 @@ enum class SPECIAL_FOLDER
RCT2_DISCORD,
};
struct RealWorldDate
{
uint8_t day;
uint8_t month;
int16_t year;
uint8_t day_of_week;
};
struct RealWorldDate;
struct RealWorldTime;
namespace Platform

View File

@ -9,11 +9,13 @@
#if defined(__APPLE__) && defined(__MACH__)
# include "Platform.h"
# include "../Date.h"
# include "../OpenRCT2.h"
# include "../core/Path.hpp"
# include "../core/String.hpp"
# include "../localisation/Language.h"
# include "Platform.h"
// undefine `interface` and `abstract`, because it's causing conflicts with Objective-C's keywords
# undef interface

View File

@ -7,6 +7,7 @@
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
#include "../Date.h"
#include "../common.h"
#ifdef _WIN32

View File

@ -2121,8 +2121,7 @@ namespace RCT1
// Date and srand
gCurrentTicks = _s4.Ticks;
ScenarioRandSeed(_s4.RandomA, _s4.RandomB);
gDateMonthsElapsed = static_cast<int32_t>(_s4.Month);
gDateMonthTicks = _s4.Day;
GetContext()->GetGameState()->SetDate(Date(_s4.Month, _s4.Day));
// Park rating
gParkRating = _s4.ParkRating;

View File

@ -249,8 +249,7 @@ namespace RCT2
gScenarioDetails = loadMaybeUTF8(_s6.ScenarioDescription);
}
gDateMonthsElapsed = static_cast<int32_t>(_s6.ElapsedMonths);
gDateMonthTicks = _s6.CurrentDay;
OpenRCT2::GetContext()->GetGameState()->SetDate(OpenRCT2::Date(_s6.ElapsedMonths, _s6.CurrentDay));
gCurrentTicks = _s6.GameTicks1;
ScenarioRandSeed(_s6.ScenarioSrand0, _s6.ScenarioSrand1);

View File

@ -307,7 +307,7 @@ size_t Ride::GetNumPrices() const
int32_t Ride::GetAge() const
{
return gDateMonthsElapsed - build_date;
return GetDate().GetMonthsElapsed() - build_date;
}
int32_t Ride::GetTotalQueueLength() const
@ -953,7 +953,7 @@ void ResetAllRideBuildDates()
{
for (auto& ride : GetRideManager())
{
ride.build_date -= gDateMonthsElapsed;
ride.build_date -= GetDate().GetMonthsElapsed();
}
}
@ -5162,7 +5162,7 @@ void Ride::Delete()
void Ride::Renew()
{
// Set build date to current date (so the ride is brand new)
build_date = gDateMonthsElapsed;
build_date = GetDate().GetMonthsElapsed();
reliability = RIDE_INITIAL_RELIABILITY;
}

View File

@ -11,6 +11,7 @@
#include "../Cheats.h"
#include "../Context.h"
#include "../Date.h"
#include "../FileClassifier.h"
#include "../Game.h"
#include "../GameState.h"
@ -158,7 +159,7 @@ void ScenarioReset()
FinanceResetHistory();
AwardReset();
ResetAllRideBuildDates();
DateReset();
GetContext()->GetGameState()->ResetDate();
Duck::RemoveAll();
ParkCalculateSize();
MapCountRemainingLandRights();
@ -326,7 +327,7 @@ static void ScenarioDayUpdate()
static void ScenarioWeekUpdate()
{
int32_t month = DateGetMonth(gDateMonthsElapsed);
int32_t month = GetDate().GetMonth();
FinancePayWages();
FinancePayResearch();
@ -369,7 +370,7 @@ static void ScenarioUpdateDayNightCycle()
if (gScreenFlags == SCREEN_FLAGS_PLAYING && gConfigGeneral.DayNightCycle)
{
float monthFraction = gDateMonthTicks / static_cast<float>(TICKS_PER_MONTH);
float monthFraction = GetDate().GetMonthTicks() / static_cast<float>(TICKS_PER_MONTH);
if (monthFraction < (1 / 8.0f))
{
gDayNightCycle = 0.0f;
@ -409,19 +410,20 @@ void ScenarioUpdate()
if (gScreenFlags == SCREEN_FLAGS_PLAYING)
{
if (DateIsDayStart(gDateMonthTicks))
auto& date = GetDate();
if (date.IsDayStart())
{
ScenarioDayUpdate();
}
if (DateIsWeekStart(gDateMonthTicks))
if (date.IsWeekStart())
{
ScenarioWeekUpdate();
}
if (DateIsFortnightStart(gDateMonthTicks))
if (date.IsFortnightStart())
{
ScenarioFortnightUpdate();
}
if (DateIsMonthStart(gDateMonthTicks))
if (date.IsMonthStart())
{
ScenarioMonthUpdate();
}
@ -617,7 +619,7 @@ ResultWithMessage ScenarioPrepareForSave()
ObjectiveStatus Objective::CheckGuestsBy() const
{
auto parkRating = gParkRating;
auto currentMonthYear = gDateMonthsElapsed;
int32_t currentMonthYear = GetDate().GetMonthsElapsed();
if (currentMonthYear == MONTH_COUNT * Year || AllowEarlyCompletion())
{
@ -637,7 +639,7 @@ ObjectiveStatus Objective::CheckGuestsBy() const
ObjectiveStatus Objective::CheckParkValueBy() const
{
int32_t currentMonthYear = gDateMonthsElapsed;
int32_t currentMonthYear = GetDate().GetMonthsElapsed();
money64 objectiveParkValue = Currency;
money64 parkValue = gParkValue;
@ -695,7 +697,7 @@ ObjectiveStatus Objective::Check10RollerCoasters() const
*/
ObjectiveStatus Objective::CheckGuestsAndRating() const
{
if (gParkRating < 700 && gDateMonthsElapsed >= 1)
if (gParkRating < 700 && GetDate().GetMonthsElapsed() >= 1)
{
gScenarioParkRatingWarningDays++;
if (gScenarioParkRatingWarningDays == 1)

View File

@ -46,7 +46,7 @@ namespace OpenRCT2::Scripting
void monthsElapsed_set(int32_t value)
{
ThrowIfGameStateNotMutable();
gDateMonthsElapsed = static_cast<int32_t>(value);
GetContext()->GetGameState()->SetDate(Date(value, GetDate().GetMonthTicks()));
}
uint32_t monthProgress_get() const
@ -58,13 +58,13 @@ namespace OpenRCT2::Scripting
void monthProgress_set(int32_t value)
{
ThrowIfGameStateNotMutable();
gDateMonthTicks = value;
GetContext()->GetGameState()->SetDate(Date(GetDate().GetMonthsElapsed(), value));
}
uint32_t yearsElapsed_get() const
{
const auto& date = GetDate();
return date.GetMonthsElapsed() / 8;
return date.GetYear();
}
uint32_t ticksElapsed_get() const

View File

@ -91,7 +91,7 @@ int32_t ClimateCelsiusToFahrenheit(int32_t celsius)
void ClimateReset(ClimateType climate)
{
auto weather = WeatherType::PartiallyCloudy;
int32_t month = DateGetMonth(gDateMonthsElapsed);
int32_t month = GetDate().GetMonth();
const WeatherTransition* transition = &ClimateTransitions[static_cast<uint8_t>(climate)][month];
const WeatherState* weatherState = &ClimateWeatherData[EnumValue(weather)];
@ -197,7 +197,7 @@ void ClimateUpdate()
void ClimateForceWeather(WeatherType weather)
{
int32_t month = DateGetMonth(gDateMonthsElapsed);
int32_t month = GetDate().GetMonth();
const WeatherTransition* transition = &ClimateTransitions[static_cast<uint8_t>(gClimate)][month];
const auto weatherState = &ClimateWeatherData[EnumValue(weather)];
@ -295,7 +295,7 @@ static int8_t ClimateStepWeatherLevel(int8_t currentWeatherLevel, int8_t nextWea
*/
static void ClimateDetermineFutureWeather(int32_t randomDistribution)
{
int32_t month = DateGetMonth(gDateMonthsElapsed);
int32_t month = GetDate().GetMonth();
// Generate a random variable with values 0 up to DistributionSize-1 and chose weather from the distribution table
// accordingly

View File

@ -48,8 +48,11 @@ TEST(MultiLaunchTest, all)
ASSERT_EQ(RideGetCount(), 134);
auto gs = context->GetGameState();
ASSERT_NE(gs, nullptr);
auto& date = gs->GetDate();
ASSERT_EQ(date.GetMonthTicks(), 0);
// NOTE: This value is saved in the SV6 file, after the import this will be the current state.
// In case the save file gets replaced this needs to be adjusted.
ASSERT_EQ(date.GetMonthTicks(), 0x1e98);
for (int j = 0; j < updatesToTest; j++)
{