diff --git a/data/uking_functions.csv b/data/uking_functions.csv index ee32db97..371ace74 100644 --- a/data/uking_functions.csv +++ b/data/uking_functions.csv @@ -89363,15 +89363,15 @@ 0x00000071010e6278,wm::TimeMgr::loadInfo,388,_ZN4ksys5world7TimeMgr8loadInfoEv 0x00000071010e63fc,wm::TimeMgr::loadFlags,1632,_ZN4ksys5world7TimeMgr9loadFlagsEv 0x00000071010e6a5c,wm::TimeMgr::dtor,172,_ZN4ksys5world7TimeMgrD1Ev -0x00000071010e6b08,sub_71010E6B08,132,_ZN4sead13DelegateEventIN4ksys5world7TimeMgr11NewDayEventEED2Ev +0x00000071010e6b08,sub_71010E6B08,132,_ZN4sead13DelegateEventIRKN4ksys5world7TimeMgr11NewDayEventEED2Ev 0x00000071010e6b8c,wm::TimeMgr::dtorDelete,180,_ZN4ksys5world7TimeMgrD0Ev -0x00000071010e6c40,wm::TimeMgr::callBloodMoonDemo,104, +0x00000071010e6c40,wm::TimeMgr::callBloodMoonDemo,104,_ZN4ksys5world7TimeMgr17callBloodMoonDemoEv 0x00000071010e6ca8,wm::TimeMgr::isBloodyMoonProhibited,364,_ZNK4ksys5world7TimeMgr21isBloodMoonProhibitedEv 0x00000071010e6e14,wm::TimeMgr::isBloodyMoonProhibitionFlagSet,120,_ZNK4ksys5world7TimeMgr29isBloodMoonProhibitionFlagSetEv 0x00000071010e6e8c,wm::TimeMgr::isInRelicBattle,372,_ZNK4ksys5world7TimeMgr15isInRelicBattleEv -0x00000071010e7000,wm::TimeMgr::handleNewDay,608, -0x00000071010e7260,nullsub_4540,4, -0x00000071010e7264,wm::TimeMgr::calc,2676, +0x00000071010e7000,wm::TimeMgr::handleNewDay,608,_ZN4ksys5world7TimeMgr12handleNewDayENS1_16HandleNewDayModeE +0x00000071010e7260,nullsub_4540,4,_ZN4ksys5world7TimeMgr11NewDayEventD2Ev +0x00000071010e7264,wm::TimeMgr::calc,2676,_ZN4ksys5world7TimeMgr5calc_Ev 0x00000071010e7cd8,wm::TimeMgr::AnimalMasterFlags::calc,772,_ZN4ksys5world7TimeMgr22AnimalMasterController4calcEv 0x00000071010e7fdc,wm::TimeMgr::m7_null,4,_ZN4ksys5world7TimeMgr10calcType1_Ev 0x00000071010e7fe0,wm::TimeMgr::m8_null,4,_ZN4ksys5world7TimeMgr10calcType2_Ev @@ -89388,8 +89388,8 @@ 0x00000071010e8424,wm::TimeMgr::rtti1,204,_ZNK4ksys5world7TimeMgr27checkDerivedRuntimeTypeInfoEPKN4sead15RuntimeTypeInfo9InterfaceE 0x00000071010e84f0,wm::TimeMgr::rtti2,92,_ZNK4ksys5world7TimeMgr18getRuntimeTypeInfoEv 0x00000071010e854c,wm::TimeMgr::getType,8,_ZNK4ksys5world7TimeMgr7getTypeEv -0x00000071010e8554,j__ZdlPv_1202,4, -0x00000071010e8558,sub_71010E8558,148,_ZN4sead13DelegateEventIN4ksys5world7TimeMgr11NewDayEventEED0Ev +0x00000071010e8554,j__ZdlPv_1202,4,_ZN4ksys5world7TimeMgr11NewDayEventD0Ev +0x00000071010e8558,sub_71010E8558,148,_ZN4sead13DelegateEventIRKN4ksys5world7TimeMgr11NewDayEventEED0Ev 0x00000071010e85ec,wm::getWeatherMgr,56, 0x00000071010e8624,sub_71010E8624,12, 0x00000071010e8630,sub_71010E8630,12, diff --git a/lib/sead b/lib/sead index 2105e3e5..fb5b8c49 160000 --- a/lib/sead +++ b/lib/sead @@ -1 +1 @@ -Subproject commit 2105e3e5591796112deef0f2ecce0a1e26e5004b +Subproject commit fb5b8c490f2ae4393a43c972a69f9c3c11907915 diff --git a/src/KingSystem/Event/CMakeLists.txt b/src/KingSystem/Event/CMakeLists.txt index 2ba90f45..c5680ef8 100644 --- a/src/KingSystem/Event/CMakeLists.txt +++ b/src/KingSystem/Event/CMakeLists.txt @@ -5,6 +5,8 @@ target_sources(uking PRIVATE evtInfoData.h evtManager.cpp evtManager.h + evtMetadata.cpp + evtMetadata.h evtOrderParam.cpp evtOrderParam.h ) diff --git a/src/KingSystem/Event/evtManager.h b/src/KingSystem/Event/evtManager.h index 662bd429..fcd72078 100644 --- a/src/KingSystem/Event/evtManager.h +++ b/src/KingSystem/Event/evtManager.h @@ -2,9 +2,14 @@ #include +namespace ksys::act { +class Actor; +} + namespace ksys::evt { class Event; +class Metadata; // TODO class Manager { @@ -18,6 +23,8 @@ public: Event* getActiveEvent() const; bool hasActiveEvent() const; + bool callEvent(const Metadata& metadata, act::Actor* actor = nullptr, void* x = nullptr); + sead::Heap* mEventHeap; // 0x1d180 }; diff --git a/src/KingSystem/Event/evtMetadata.cpp b/src/KingSystem/Event/evtMetadata.cpp new file mode 100644 index 00000000..b5a6e735 --- /dev/null +++ b/src/KingSystem/Event/evtMetadata.cpp @@ -0,0 +1 @@ +#include "KingSystem/Event/evtMetadata.h" diff --git a/src/KingSystem/Event/evtMetadata.h b/src/KingSystem/Event/evtMetadata.h new file mode 100644 index 00000000..08639ed4 --- /dev/null +++ b/src/KingSystem/Event/evtMetadata.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include "KingSystem/Utils/Types.h" + +namespace ksys::evt { + +// FIXME: incomplete +class Metadata { +public: + Metadata(); + Metadata(const char* event, const char* entry_point, const char* type = ""); + explicit Metadata(const char* event) : Metadata(event, event) {} + virtual ~Metadata(); + +private: + u8 pad_0x8[0x158 - 0x8]; +}; +KSYS_CHECK_SIZE_NX150(Metadata, 0x158); + +} // namespace ksys::evt diff --git a/src/KingSystem/World/worldCloudMgr.h b/src/KingSystem/World/worldCloudMgr.h index f868839d..2249d546 100644 --- a/src/KingSystem/World/worldCloudMgr.h +++ b/src/KingSystem/World/worldCloudMgr.h @@ -12,6 +12,8 @@ public: JobType getType() const override { return JobType::Cloud; } + void onTimeUpdate(); + u8 _20[0x3fb8 - 0x20]; }; KSYS_CHECK_SIZE_NX150(CloudMgr, 0x3fb8); diff --git a/src/KingSystem/World/worldManager.h b/src/KingSystem/World/worldManager.h index 2a772691..661f5247 100644 --- a/src/KingSystem/World/worldManager.h +++ b/src/KingSystem/World/worldManager.h @@ -272,6 +272,8 @@ public: DofMgr* getDofMgr() const { return static_cast(mMgrs[7]); } ChemicalMgr* getChemicalMgr() const { return static_cast(mMgrs[8]); } + bool worldInfoLoaded() const { return mWorldInfoLoadStatus != WorldInfoLoadStatus::NotLoaded; } + private: enum class WorldInfoLoadStatus : u8 { NotLoaded, @@ -279,8 +281,6 @@ private: Unloaded, }; - bool worldInfoLoaded() const { return mWorldInfoLoadStatus != WorldInfoLoadStatus::NotLoaded; } - void overrideWindSpeed(float* wind_speed) const; void calcManagers(sead::WorkerMgr* worker_mgr); diff --git a/src/KingSystem/World/worldSkyMgr.h b/src/KingSystem/World/worldSkyMgr.h index f77def72..6f3b5481 100644 --- a/src/KingSystem/World/worldSkyMgr.h +++ b/src/KingSystem/World/worldSkyMgr.h @@ -15,6 +15,7 @@ public: void resetForStageUnload(); bool checkConcentrationDarkness() const; + bool isBloodMoonNight() const; u8 _20[0x6b618 - 0x20]; }; diff --git a/src/KingSystem/World/worldTimeMgr.cpp b/src/KingSystem/World/worldTimeMgr.cpp index d2330291..7615202d 100644 --- a/src/KingSystem/World/worldTimeMgr.cpp +++ b/src/KingSystem/World/worldTimeMgr.cpp @@ -3,7 +3,11 @@ #include "KingSystem/ActorSystem/actActorConstDataAccess.h" #include "KingSystem/ActorSystem/actActorSystem.h" #include "KingSystem/Ecosystem/ecoUtil.h" +#include "KingSystem/Event/evtManager.h" +#include "KingSystem/Event/evtMetadata.h" #include "KingSystem/GameData/gdtManager.h" +#include "KingSystem/System/PlayReportMgr.h" +#include "KingSystem/System/VFR.h" #include "KingSystem/World/worldManager.h" namespace ksys::world { @@ -18,7 +22,7 @@ TimeMgr::TimeMgr() { _21 = true; _22 = 0; _24 = false; - mForceBloodyDay = false; + mBloodMoonForceMode = {}; _14b = false; _cc = {}; mMoonType = MoonType::Auto; @@ -52,7 +56,7 @@ void TimeMgr::resetForStageUnload() { void TimeMgr::loadInfo() { mTimeStep = DefaultTimeStep; mTimeUpdateMode = TimeUpdateMode::Normal; - mForceBloodyDay = false; + mBloodMoonForceMode = {}; _14b = false; _cc = 0.0; mMoonType = MoonType::Auto; @@ -126,6 +130,14 @@ void TimeMgr::loadFlags() { TimeMgr::~TimeMgr() = default; +void TimeMgr::callBloodMoonDemo() { + auto* evt_mgr = evt::Manager::instance(); + if (!evt_mgr) + return; + + evt_mgr->callEvent(evt::Metadata("Demo011_0")); +} + bool TimeMgr::isBloodMoonProhibited() const { bool prohibited = !Manager::instance()->isMainField(); @@ -182,6 +194,379 @@ bool TimeMgr::isInRelicBattle() const { return in_battle; } +static bool playerHasLeftPlateau() { + bool left_plateau = false; + gdt::Manager::instance()->getParamBypassPerm().get().getBool(&left_plateau, "FirstTouchdown"); + return left_plateau; +} + +void TimeMgr::handleNewDay(HandleNewDayMode mode) { + auto* wm = Manager::instance(); + auto* gdm = gdt::Manager::instance(); + + ++mNumberOfDays; + + if (mode == HandleNewDayMode::Normal && wm->getSkyMgr()->isBloodMoonNight()) { + if (isBloodMoonProhibited() || isInRelicBattle()) { + mBloodMoonTimer = 8_days; + } else { + callBloodMoonDemo(); + mBloodMoonTimer = 0.0; + if (PlayReportMgr::instance()) + PlayReportMgr::instance()->reportDebug("BloodyMoon", "Schedule"); + } + mBloodMoonForceMode = {}; + } + + if (gdm) { + gdt::Manager::instance()->getBool(mBloodyDayFlag, &mWasBloodyDay); + + if (playerHasLeftPlateau() && mBloodMoonTimer > 7_days) { + mBloodMoonTimer = 0.0; + gdm->setBool(true, mBloodyDayFlag); + } else { + mBloodyEndReserveTimer = 150; + } + } + + NewDayEvent event; + if (mNewDaySignal.getNumSlots() > 0) + mNewDaySignal.emit(event); +} + +void TimeMgr::calc_() { + bool is_daytime_flag_set = true; + + auto* gdm = gdt::Manager::instance(); + if (!gdm) + return; + + mIsTimeFlowingNormally = false; + float& time = mTime; + gdm->getF32(mTimeFlag, &time); + gdm->getS32(mNumberOfDaysFlag, &mNumberOfDays); + gdm->getS32(mTimeDivisionFlag, &mTimeDivision); + gdm->getBool(mIsDaytimeFlag, &is_daytime_flag_set); + gdm->getF32(mBloodyMoonTimerFlag, &mBloodMoonTimer); + if (mBloodyEndReserveTimerFlag != gdt::InvalidHandle) + gdm->getS32(mBloodyEndReserveTimerFlag, &mBloodyEndReserveTimer); + + if (!mPlayedDemo103Or997) { + mTime = DefaultTime; + gdm->getBool(mIsPlayedDemo103Flag, &mPlayedDemo103Or997, true); + if (!mPlayedDemo103Or997) + gdm->getBool(mDemo997Flag, &mPlayedDemo103Or997, true); + } + + if (!mFindDungeonActivated) { + gdm->getBool(mFindDungeonActivatedFlag, &mFindDungeonActivated, true); + } + + auto* wm = Manager::instance(); + if (!wm->worldInfoLoaded()) + return; + + if (mNewTime >= 0.0 && + (mTimeUpdateMode == TimeUpdateMode::Forced || mTimeUpdateMode == TimeUpdateMode::Normal)) { + mTime = mNewTime; + mNewTime = -1.0; + wm->getCloudMgr()->onTimeUpdate(); + } + + if (mNeedToHandleNewDay) { + handleNewDay(HandleNewDayMode::Forced); + mNeedToHandleNewDay = false; + } + + switch (mBloodMoonForceMode) { + case BloodMoonForceMode::Disabled: + break; + case BloodMoonForceMode::Force23h: + mBloodMoonForceMode = {}; + mTime = timeToFloat(23, 29) + 0.1; + mBloodMoonTimer = 0.0; + gdm->setBool(true, mBloodyDayFlag); + break; + case BloodMoonForceMode::Force21h: + mBloodMoonForceMode = {}; + mTime = timeToFloat(21, 30); + mBloodMoonTimer = 0.0; + gdm->setBool(true, mBloodyDayFlag); + break; + case BloodMoonForceMode::Immediate: + mTimeUpdateMode = TimeUpdateMode::Forced; + mTime = 0.0; + _cc = 1.0; + gdm->setBool(true, mBloodyDayFlag); + mWasBloodyDay = true; + if (!evt::Manager::instance()->hasActiveEvent()) { + mBloodMoonForceMode = {}; + mTimeUpdateMode = TimeUpdateMode::Normal; + _cc = 0.0; + } + break; + } + + if (mWasBloodyDay && mTime > 12_h) + mWasBloodyDay = false; + + _d0 = 1.0; + + switch (mTimeUpdateMode) { + case TimeUpdateMode::Normal: { + if (!mPlayedDemo103Or997 || evt::Manager::instance()->hasActiveEvent()) + break; + + const auto delta = mTimeStep * VFR::instance()->getDeltaTime(); + mIsTimeFlowingNormally = true; + mTime += delta; + if (mTime >= 24_h) { + mTime -= 24_h; + handleNewDay(HandleNewDayMode::Normal); + } + if (!mFindDungeonActivated && mTime >= 11_h) + mTime = 11_h; + + _d0 = sead::Mathf::max(mTimeStep / DefaultTimeStep, 1.0); + mBloodMoonTimer += delta; + break; + } + + case TimeUpdateMode::Force0400_b: + mTime = 4_h; + break; + case TimeUpdateMode::Force0500_b: + mTime = 5_h; + break; + case TimeUpdateMode::Force0700_b: + mTime = 7_h; + break; + case TimeUpdateMode::Force1000_b: + mTime = 10_h; + break; + case TimeUpdateMode::Force1700_b: + mTime = 17_h; + break; + case TimeUpdateMode::Force1900_b: + mTime = 19_h; + break; + case TimeUpdateMode::Force2100_b: + mTime = 21_h; + break; + case TimeUpdateMode::Force0200_b: + mTime = 2_h; + break; + + case TimeUpdateMode::Force0000: + mTime = 0_h; + break; + case TimeUpdateMode::Force0100: + mTime = 1_h; + break; + case TimeUpdateMode::Force0200: + mTime = 2_h; + break; + case TimeUpdateMode::Force0300: + mTime = 3_h; + break; + case TimeUpdateMode::Force0400: + mTime = 4_h; + break; + case TimeUpdateMode::Force0500: + mTime = 5_h; + break; + case TimeUpdateMode::Force0600: + mTime = 6_h; + break; + case TimeUpdateMode::Force0700: + mTime = 7_h; + break; + case TimeUpdateMode::Force0800: + mTime = 8_h; + break; + case TimeUpdateMode::Force0900: + mTime = 9_h; + break; + case TimeUpdateMode::Force1000: + mTime = 10_h; + break; + case TimeUpdateMode::Force1100: + mTime = 11_h; + break; + case TimeUpdateMode::Force1200: + mTime = 12_h; + break; + case TimeUpdateMode::Force1300: + mTime = 13_h; + break; + case TimeUpdateMode::Force1400: + mTime = 14_h; + break; + case TimeUpdateMode::Force1500: + mTime = 15_h; + break; + case TimeUpdateMode::Force1600: + mTime = 16_h; + break; + case TimeUpdateMode::Force1700: + mTime = 17_h; + break; + case TimeUpdateMode::Force1800: + mTime = 18_h; + break; + case TimeUpdateMode::Force1900: + mTime = 19_h; + break; + case TimeUpdateMode::Force2000: + mTime = 20_h; + break; + case TimeUpdateMode::Force2100: + mTime = 21_h; + break; + case TimeUpdateMode::Force2200: + mTime = 22_h; + break; + case TimeUpdateMode::Force2300: + mTime = 23_h; + break; + + case TimeUpdateMode::OnlyUpdateTimeOfDay: + mTime += mTimeStep * VFR::instance()->getDeltaTime(); + if (mTime >= 24_h) + mTime -= 24_h; + break; + + case TimeUpdateMode::Force0400_c: + mTime = 4_h; + break; + case TimeUpdateMode::Force0700_c: + mTime = 7_h; + break; + case TimeUpdateMode::Force1000_c: + mTime = 10_h; + break; + case TimeUpdateMode::Force1300_c: + mTime = 13_h; + break; + case TimeUpdateMode::Force1700_c: + mTime = 17_h; + break; + case TimeUpdateMode::Force1900_c: + mTime = 19_h; + break; + case TimeUpdateMode::Force2100_c: + mTime = 21_h; + break; + case TimeUpdateMode::Force0000_c: + mTime = 0_h; + break; + + case TimeUpdateMode::Forced: + break; + } + + const auto update_daytime_flags = [&] { + if (mTime > 6_h && mTime < 18_h) { + if (!is_daytime_flag_set) { + gdm->setBool(true, mIsDaytimeFlag); + gdm->setBool(false, mIsNighttimeFlag); + } + } else { + if (is_daytime_flag_set) { + gdm->setBool(false, mIsDaytimeFlag); + gdm->setBool(true, mIsNighttimeFlag); + } + } + }; + update_daytime_flags(); + + bool update_division = false; + bool division_flag_set = false; + + if (mTime >= 4_h && mTime < 7_h) { + mTimeDivision = TimeDivision::Morning_A; + u8 ok = 0; + if (gdm->getBool(mIsMorningAFlag, &division_flag_set)) + ok = !division_flag_set; + if (mTime < 5_h) { + mTimeType = TimeType::Morning_A1; + if (ok) + update_division = true; + } else { + mTimeType = TimeType::Morning_A2; + if (ok) + [&] { update_division = true; }(); + } + } else if (mTime >= 7_h && mTime < 10_h) { + mTimeDivision = TimeDivision::Morning_B; + mTimeType = TimeType::Morning_B; + if (gdm->getBool(mIsMorningBFlag, &division_flag_set) && !division_flag_set) + update_division = true; + } else if (mTime >= 10_h && mTime < 13_h) { + mTimeDivision = TimeDivision::Noon_A; + mTimeType = TimeType::Noon_A; + if (gdm->getBool(mIsNoonAFlag, &division_flag_set) && !division_flag_set) + update_division = true; + } else if (mTime >= 13_h && mTime < 17_h) { + mTimeDivision = TimeDivision::Noon_B; + mTimeType = TimeType::Noon_B; + if (gdm->getBool(mIsNoonBFlag, &division_flag_set) && !division_flag_set) + update_division = true; + } else if (mTime >= 17_h && mTime < 19_h) { + mTimeDivision = TimeDivision::Evening_A; + mTimeType = TimeType::Evening_A; + if (gdm->getBool(mIsEveningAFlag, &division_flag_set) && !division_flag_set) + update_division = true; + } else if (mTime >= 19_h && mTime < 21_h) { + mTimeDivision = TimeDivision::Evening_B; + mTimeType = TimeType::Evening_B; + if (gdm->getBool(mIsEveningBFlag, &division_flag_set) && !division_flag_set) + update_division = true; + } else if (mTime >= 21_h && mTime < 24_h) { + mTimeDivision = TimeDivision::Night_A; + mTimeType = TimeType::Night_A; + if (gdm->getBool(mIsNightAFlag, &division_flag_set) && !division_flag_set) + update_division = true; + } else { + mTimeDivision = TimeDivision::Night_B; + mTimeType = TimeType::Night_B; + if (gdm->getBool(mIsNightBFlag, &division_flag_set) && !division_flag_set) + update_division = true; + } + + if (update_division) { + const bool is_morning_a = mTimeDivision == TimeDivision::Morning_A; + const bool is_morning_b = mTimeDivision == TimeDivision::Morning_B; + const bool is_noon_a = mTimeDivision == TimeDivision::Noon_A; + const bool is_noon_b = mTimeDivision == TimeDivision::Noon_B; + const bool is_evening_a = mTimeDivision == TimeDivision::Evening_A; + const bool is_evening_b = mTimeDivision == TimeDivision::Evening_B; + const bool is_night_a = mTimeDivision == TimeDivision::Night_A; + const bool is_night_b = mTimeDivision == TimeDivision::Night_B; + gdm->setBool(is_morning_a, mIsMorningAFlag); + gdm->setBool(is_morning_b, mIsMorningBFlag); + gdm->setBool(is_noon_a, mIsNoonAFlag); + gdm->setBool(is_noon_b, mIsNoonBFlag); + gdm->setBool(is_evening_a, mIsEveningAFlag); + gdm->setBool(is_evening_b, mIsEveningBFlag); + gdm->setBool(is_night_a, mIsNightAFlag); + gdm->setBool(is_night_b, mIsNightBFlag); + gdm->setS32(mTimeDivision, mTimeDivisionFlag); + } + + if (mBloodyEndReserveTimer != 0) { + --mBloodyEndReserveTimer; + if (mBloodyEndReserveTimer <= 0) + gdm->setBool(false, mBloodyDayFlag); + } + + gdm->setF32(mTime, mTimeFlag); + gdm->setS32(mNumberOfDays, mNumberOfDaysFlag); + gdm->setF32(mBloodMoonTimer, mBloodyMoonTimerFlag); + gdm->setS32(mBloodyEndReserveTimer, mBloodyEndReserveTimerFlag); + mAnimalMasterCtrl.calc(); +} + void TimeMgr::AnimalMasterController::calc() { const auto num_days = Manager::instance()->getTimeMgr()->getNumberOfDays(); diff --git a/src/KingSystem/World/worldTimeMgr.h b/src/KingSystem/World/worldTimeMgr.h index c2719280..cb156938 100644 --- a/src/KingSystem/World/worldTimeMgr.h +++ b/src/KingSystem/World/worldTimeMgr.h @@ -11,6 +11,7 @@ namespace ksys::world { SEAD_ENUM(TimeDivision, Morning_A,Morning_B,Noon_A,Noon_B,Evening_A,Evening_B,Night_A,Night_B) +SEAD_ENUM(TimeType, Morning_A1,Morning_A2,Morning_B,Noon_A,Noon_B,Evening_A,Evening_B,Night_A,Night_B) enum class MoonType : u8 { FullMoon = 0, @@ -32,6 +33,10 @@ constexpr float durationToFloat(int h, int m) { return timeToFloat(h, m); } +constexpr float operator""_days(unsigned long long hours) { + return timeToFloat(static_cast(24 * hours), 0); +} + constexpr float operator""_h(unsigned long long hours) { return timeToFloat(static_cast(hours), 0); } @@ -44,6 +49,16 @@ constexpr float operator""_m(unsigned long long minutes) { class TimeMgr : public Job { SEAD_RTTI_OVERRIDE(TimeMgr, Job) public: + enum class BloodMoonForceMode { + Disabled = 0, + /// Force a blood moon to happen tonight and set the time to 23:29. + Force23h = 1, + /// Force a blood moon to happen tonight and set the time to 21:30. + Force21h = 2, + /// Force a blood moon to happen tonight and set the time to 00:00. + Immediate = 99, + }; + struct NewDayEvent { virtual ~NewDayEvent() = default; }; @@ -76,11 +91,11 @@ public: bool isTimeFlowingNormally() const; int getTimeDivision() const { return mTimeDivision; } - sead::DelegateEvent& getNewDaySignal() { return mNewDaySignal; } + sead::DelegateEvent& getNewDaySignal() { return mNewDaySignal; } float getTimeStep() const { return mTimeStep; } float getBloodMoonTimer() const { return mBloodMoonTimer; } int getNumberOfDays() const { return mNumberOfDays; } - bool isForceBloodyDay() const { return mForceBloodyDay; } + BloodMoonForceMode getBloodMoonForceMode() const { return mBloodMoonForceMode; } bool isPlayedDemo103Or997() const { return mPlayedDemo103Or997; } bool isFindDungeonActivated() const { return mFindDungeonActivated; } bool isResetGdtOnNextSceneUnloadForBloodMoon() const { @@ -97,6 +112,58 @@ protected: private: enum class TimeUpdateMode { Normal = 0, + + Force0400_b = 1, + Force0500_b = 2, + Force0700_b = 3, + Force1000_b = 4, + Force1700_b = 5, + Force1900_b = 6, + Force2100_b = 7, + Force0200_b = 8, + + Force0000 = 9, + Force0100 = 10, + Force0200 = 11, + Force0300 = 12, + Force0400 = 13, + Force0500 = 14, + Force0600 = 15, + Force0700 = 16, + Force0800 = 17, + Force0900 = 18, + Force1000 = 19, + Force1100 = 20, + Force1200 = 21, + Force1300 = 22, + Force1400 = 23, + Force1500 = 24, + Force1600 = 25, + Force1700 = 26, + Force1800 = 27, + Force1900 = 28, + Force2000 = 29, + Force2100 = 30, + Force2200 = 31, + Force2300 = 32, + + OnlyUpdateTimeOfDay = 34, + + Force0400_c = 35, + Force0700_c = 36, + Force1000_c = 37, + Force1300_c = 38, + Force1700_c = 39, + Force1900_c = 40, + Force2100_c = 41, + Force0000_c = 42, + + Forced = 0xff, + }; + + enum class HandleNewDayMode { + Normal, + Forced, }; struct AnimalMasterController { @@ -122,18 +189,19 @@ private: static constexpr float DefaultTimeStep = durationToFloat(0, 1) / 30.0; void loadFlags(); + void handleNewDay(HandleNewDayMode mode); bool _20; bool _21; u16 _22; bool _24; int mTimeDivision{}; - int mTimeDivision2{}; + int mTimeType{}; struct { std::array _0; int _4; } _30[10]; - sead::DelegateEvent mNewDaySignal; + sead::DelegateEvent mNewDaySignal; AnimalMasterController mAnimalMasterCtrl; float mTime = DefaultTime; float mTimeForAocFieldSkyEnv = DefaultTime; @@ -173,7 +241,7 @@ private: int mBloodyEndReserveTimer{}; bool mNeedToHandleNewDay{}; sead::SizedEnum mTimeUpdateMode = TimeUpdateMode::Normal; - bool mForceBloodyDay; + sead::SizedEnum mBloodMoonForceMode; bool _14b; MoonType mMoonType; bool mPlayedDemo103Or997{};