From c4c7c00e85df3137d57cc899302a160ff94fa71b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Mon, 28 Sep 2026 03:39:31 +0200 Subject: aoc: Manager is ksys, the DLC managers stay game-side aoc::Manager is created during engine start-up and called by gdt, map and StarterPackMgr; only game code uses HardModeManager and ChampionBalladManager. (AI assisted using Claude Fable 5.1 and Claude Opus 5.5) --- src/Game/AOC/CMakeLists.txt | 6 + src/Game/AOC/aocChampionBalladManager.cpp | 32 ++ src/Game/AOC/aocChampionBalladManager.h | 26 + src/Game/AOC/aocHardModeManager.cpp | 335 +++++++++++++ src/Game/AOC/aocHardModeManager.h | 81 +++ src/Game/Actor/AI/aiCookPotRoot.cpp | 2 +- src/Game/Actor/Action/actionCurseRRematchCount.cpp | 8 +- src/Game/Actor/Damage/dmgDamageManagerBase.cpp | 2 +- .../Actor/Query/queryCheckCurseRRetryEverOnce.cpp | 8 +- src/Game/CMakeLists.txt | 2 +- src/Game/DLC/CMakeLists.txt | 8 - src/Game/DLC/aocChampionBalladManager.cpp | 32 -- src/Game/DLC/aocChampionBalladManager.h | 26 - src/Game/DLC/aocHardModeManager.cpp | 335 ------------- src/Game/DLC/aocHardModeManager.h | 81 --- src/Game/DLC/aocManager.cpp | 546 --------------------- src/Game/DLC/aocManager.h | 132 ----- src/Game/UI/uiPauseMenuDataMgr.cpp | 19 +- src/Game/UI/uiUtils.cpp | 2 +- src/KingSystem/AOC/CMakeLists.txt | 4 + src/KingSystem/AOC/aocManager.cpp | 546 +++++++++++++++++++++ src/KingSystem/AOC/aocManager.h | 132 +++++ src/KingSystem/CMakeLists.txt | 1 + src/KingSystem/GameData/gdtManager.cpp | 4 +- src/KingSystem/Map/mapLazyTraverseList.cpp | 8 +- src/KingSystem/Map/mapPlacementMap.cpp | 10 +- src/KingSystem/System/StarterPackMgr.cpp | 12 +- src/KingSystem/System/StarterPackMgr.h | 2 +- 28 files changed, 1204 insertions(+), 1198 deletions(-) create mode 100644 src/Game/AOC/CMakeLists.txt create mode 100644 src/Game/AOC/aocChampionBalladManager.cpp create mode 100644 src/Game/AOC/aocChampionBalladManager.h create mode 100644 src/Game/AOC/aocHardModeManager.cpp create mode 100644 src/Game/AOC/aocHardModeManager.h delete mode 100644 src/Game/DLC/CMakeLists.txt delete mode 100644 src/Game/DLC/aocChampionBalladManager.cpp delete mode 100644 src/Game/DLC/aocChampionBalladManager.h delete mode 100644 src/Game/DLC/aocHardModeManager.cpp delete mode 100644 src/Game/DLC/aocHardModeManager.h delete mode 100644 src/Game/DLC/aocManager.cpp delete mode 100644 src/Game/DLC/aocManager.h create mode 100644 src/KingSystem/AOC/CMakeLists.txt create mode 100644 src/KingSystem/AOC/aocManager.cpp create mode 100644 src/KingSystem/AOC/aocManager.h (limited to 'src') diff --git a/src/Game/AOC/CMakeLists.txt b/src/Game/AOC/CMakeLists.txt new file mode 100644 index 00000000..20388b21 --- /dev/null +++ b/src/Game/AOC/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources(uking PRIVATE + aocChampionBalladManager.cpp + aocChampionBalladManager.h + aocHardModeManager.cpp + aocHardModeManager.h +) diff --git a/src/Game/AOC/aocChampionBalladManager.cpp b/src/Game/AOC/aocChampionBalladManager.cpp new file mode 100644 index 00000000..1600a6f9 --- /dev/null +++ b/src/Game/AOC/aocChampionBalladManager.cpp @@ -0,0 +1,32 @@ +#include "Game/AOC/aocChampionBalladManager.h" + +namespace uking::aoc { + +SEAD_SINGLETON_DISPOSER_IMPL(ChampionBalladManager) + +void ChampionBalladManager::init() { + mBlightCounts = {}; +} + +void ChampionBalladManager::setBlightRematchCount(s8 count, BlightType blight_type) { + if (count >= 0 && blight_type <= BlightType::Water) { + if (count >= 30) { + count = 30; + } + mBlightCounts[u32(blight_type)] = count; + } +} + +void ChampionBalladManager::incrementBlightRematchCount(BlightType blight_type) { + if (blight_type <= BlightType::Water) { + s8 count = mBlightCounts[u32(blight_type)] + 1; + setBlightRematchCount(count, blight_type); + } +} + +s8 ChampionBalladManager::getBlightRematchCount(BlightType blight_type) const { + if (blight_type <= BlightType::Water) + return mBlightCounts[u32(blight_type)]; + return 0; +} +} // namespace uking::aoc diff --git a/src/Game/AOC/aocChampionBalladManager.h b/src/Game/AOC/aocChampionBalladManager.h new file mode 100644 index 00000000..4fa8e268 --- /dev/null +++ b/src/Game/AOC/aocChampionBalladManager.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +namespace uking::aoc { + +enum class BlightType : u32 { Wind, Electric, Fire, Water }; + +class ChampionBalladManager { + SEAD_SINGLETON_DISPOSER(ChampionBalladManager) + ChampionBalladManager() = default; + virtual ~ChampionBalladManager() = default; + +public: + void init(); + void setBlightRematchCount(s8 count, BlightType blight_type); + void incrementBlightRematchCount(BlightType blight_type); + s8 getBlightRematchCount(BlightType blight_type) const; + +private: + sead::SafeArray mBlightCounts; +}; + +} // namespace uking::aoc diff --git a/src/Game/AOC/aocHardModeManager.cpp b/src/Game/AOC/aocHardModeManager.cpp new file mode 100644 index 00000000..479fef42 --- /dev/null +++ b/src/Game/AOC/aocHardModeManager.cpp @@ -0,0 +1,335 @@ +#include "Game/AOC/aocHardModeManager.h" +#include +#include "KingSystem/ActorSystem/actActor.h" +#include "KingSystem/ActorSystem/actActorUtil.h" +#include "KingSystem/ActorSystem/actTag.h" +#include "KingSystem/Utils/InitTimeInfo.h" + +namespace uking::aoc { + +SEAD_SINGLETON_DISPOSER_IMPL(HardModeManager) + +namespace { +struct aoc2StaticData { + ksys::util::InitConstants init_constants; + sead::SafeString flag_name_HardMode_HighScore{"HardMode_HighScore"}; + sead::SafeString flag_name_AoC_HardMode_Enabled{"AoC_HardMode_Enabled"}; + sead::SafeString flag_name_IsLastPlayHardMode{"IsLastPlayHardMode"}; +}; + +aoc2StaticData sData; +sead::FixedSafeString<64> sStr{""}; +} // namespace + +HardModeManager::HardModeManager() + : mGdtResetSlot(this, &HardModeManager::setHardModeEnabledFlag), + mGdtReinitSlot(this, &HardModeManager::initFlagHandles) { + mMultipliers.fill(0.5); + + setHardModeChange(HardModeChange::IsLastPlayHardMode, true); + setHardModeChange(HardModeChange::EnableShorterEnemyNotice, true); + setHardModeChange(HardModeChange::EnableLifeRegen, true); + setHardModeChange(HardModeChange::DisableNoDeathDamage, true); + setHardModeChange(HardModeChange::PatchGanonStunLock, true); + setHardModeChange(HardModeChange::RandomizeGuardianChargeBeam, true); + setHardModeChange(HardModeChange::ApplyDamageMultiplier, true); +} + +void HardModeManager::setHardModeEnabledFlag(ksys::gdt::Manager::ResetEvent*) { + ksys::gdt::Manager::instance()->setBool(true, mAoCHardModeEnabledFlag); + mGdtResetSlot.release(); +} + +void HardModeManager::initFlagHandles(ksys::gdt::Manager::ReinitEvent*) { + auto* gdm = ksys::gdt::Manager::instance(); + mHardModeHighScoreFlag = gdm->getS32Handle(sData.flag_name_HardMode_HighScore); + mAoCHardModeEnabledFlag = gdm->getBoolHandle(sData.flag_name_AoC_HardMode_Enabled); + mIsLastPlayHardModeFlag = gdm->getBoolHandle(sData.flag_name_IsLastPlayHardMode); +} + +HardModeManager::~HardModeManager() { + mFileHandle.requestUnload2(); + if (_120) { + // TODO: use the normal operator delete once we figure out what _120 is + ::operator delete(_120); + _120 = nullptr; + } +} + +void HardModeManager::init(sead::Heap*) { + init_(); +} + +void HardModeManager::init_() { + initFlagHandles(); + ksys::gdt::Manager::instance()->addReinitCallback(mGdtReinitSlot); +} + +void HardModeManager::nerfHpRestore(f32* hp) const { + *hp = sead::Mathf::clampMin(*hp * getMultiplier(MultiplierType::HpRestore), 1.0f); +} + +void HardModeManager::nerfHpRestore(s32* hp) const { + *hp = sead::Mathi::clampMin(*hp * getMultiplier(MultiplierType::HpRestore), 1); +} + +void HardModeManager::modifyEnemyNoticeDuration(f32* value) const { + *value = sead::Mathf::clampMin(*value * getMultiplier(MultiplierType::EnemyNoticeDuration), 0); +} + +bool HardModeManager::shouldCreateLifeRecoverInfo(ksys::act::Actor* actor) { + // Health regen should only apply to enemy actors. + if (!ksys::act::isEnemyProfile(actor)) + return false; + + // But not to wolves or bears... + if (ksys::act::isWolfOrBear(actor)) + return false; + + // and not to Dark Beast Ganon... + if (actor->getName() == "Enemy_GanonBeast") + return false; + + // and not to enemy swarms... + if (actor->getProfile() == "EnemySwarm") + return false; + + return actor->getMaxLife() > 1; +} + +bool HardModeManager::shouldApplyMasterModeDamageMultiplier( + const ksys::act::ActorConstDataAccess& accessor) { + if (!accessor.hasProc()) + return false; + + ksys::act::ActorConstDataAccess parent; + if (accessor.hasConnectedCalcParent() && accessor.acquireConnectedCalcParent(&parent)) + return shouldApplyMasterModeDamageMultiplier(parent); + + if (accessor.hasTag(ksys::act::tags::IsMasterModeDamageMultiplierActor) || + (HardModeManager::instance() && HardModeManager::instance()->isTestOfStrengthShrine() && + accessor.hasTag(ksys::act::tags::AncientGuardTarget))) { + return true; + } + + const sead::SafeString& profile = accessor.getProfile(); + const sead::SafeString& name = accessor.getName(); + + if (profile == "LastBoss" || profile == "SiteBoss") + return true; + + if (name == "Enemy_GanonBeast" || name == "GanonShockWave" || name == "EnemyGanonShockWave" || + name == "GanonSeaOfFlame" || name == "GanonFlameBall" || name == "GanonPillarOfFlame" || + name == "GanonNormalArrow" || name == "GanonSpearForThrowing" || name == "CurseGanonBeam" || + name == "GanonBeam" || name == "GanonIceBullet" || name == "GanonThunder" || + name == "GanonIronPile" || name == "GanonTornado" || name == "GanonBeastBeam" || + name == "SiteBossSeaOfFlame" || name == "SiteBossSeaOfFlameRotate" || + name == "SiteBossFlameBall" || name == "SiteBossBigFlameBall" || + name == "SiteBossPillarOfFlame" || name == "SiteBossWearFlame" || + name == "SiteBossDrawingFlameTornado" || name == "SiteBossGaleArrow" || + name == "SiteBossNormalArrow" || name == "SiteBossSpearForThrowing" || + name == "SiteBossReflectArrow" || name == "ArrowRainChild" || + name == "SiteBossSpearIceBullet" || name == "SiteBossTornado" || + name == "LastBossThunder" || name == "Enemy_Assassin_Senior" || + name == "AssassinRockBall" || name == "AssassinIronBall") { + return true; + } + + return false; +} + +void HardModeManager::buffDamage(s32& damage) { + damage = damage * 1.5f; + if (damage == 1) { + damage = 2; + } +} + +void HardModeManager::loadIsLastPlayHardModeFlag() { + bool value{}; + ksys::gdt::Manager::instance()->getBool(mIsLastPlayHardModeFlag, &value); + const bool x = value; +#ifdef MATCHING_HACK_NX_CLANG + asm(""); +#endif + setFlag(Flag::EnableHardMode, x); +} + +void HardModeManager::loadIsHardModeFlag() { + bool value{}; + ksys::gdt::Manager::instance()->getBool(mAoCHardModeEnabledFlag, &value); + const bool x = value; +#ifdef MATCHING_HACK_NX_CLANG + asm(""); +#endif + setFlag(Flag::EnableHardMode, x); +} + +void HardModeManager::storeIsLastPlayHardModeFlag() { + ksys::gdt::Manager::instance()->setBool(checkFlag(Flag::EnableHardMode), + mIsLastPlayHardModeFlag); +} + +void HardModeManager::resetIsLastPlayHardModeFlag() { + ksys::gdt::Manager::instance()->setBool(false, "IsLastPlayHardMode"); +} + +bool HardModeManager::isTestOfStrengthShrine() const { + if (mMapType != "CDungeon") + return false; + + // There are 20 ToS shrines in the base game (070-089). + if (mMapName.startsWith("Dungeon07") || mMapName.startsWith("Dungeon08")) + return true; + + // Ruvo Korbah + if (mMapName == "Dungeon135") + return true; + + return false; +} + +void HardModeManager::calc() { + volatile u32 unused = 0; + static_cast(unused); +} + +bool HardModeManager::rankUpEnemy(const sead::SafeString& actor_name, const ksys::map::Object& obj, + const char** new_name) { + if (obj.getFlags().isOn(ksys::map::Object::Flag::HasUniqueName) || + obj.getHardModeFlags().isOn(ksys::map::Object::HardModeFlag::DisableRankup)) { + return false; + } + + if (!actor_name.startsWith("Enemy")) + return false; + + if (isTestOfStrengthShrine()) + return false; + + sead::SafeString next = ""; + + /// @bug Yes, this is duplicated and shouldn't exist. + if (!actor_name.startsWith("Enemy")) + return false; + + if (actor_name == "Enemy_Assassin_Junior") + next = "Enemy_Assassin_Middle"; + else if (actor_name == "Enemy_Assassin_Middle") + next = "Enemy_Assassin_Senior"; + else if (actor_name == "Enemy_Assassin_Shooter_Junior") + next = "Enemy_Assassin_Shooter_Azito_Junior"; + + else if (actor_name == "Enemy_Bokoblin_Junior") + next = "Enemy_Bokoblin_Middle"; + else if (actor_name == "Enemy_Bokoblin_Middle") + next = "Enemy_Bokoblin_Senior"; + else if (actor_name == "Enemy_Bokoblin_Senior") + next = "Enemy_Bokoblin_Dark"; + else if (actor_name == "Enemy_Bokoblin_Dark") + next = "Enemy_Bokoblin_Gold"; + + else if (actor_name == "Enemy_Bokoblin_Guard_Junior") + next = "Enemy_Bokoblin_Guard_Middle"; + + else if (actor_name == "Enemy_Bokoblin_Guard_Junior_Ambush") + next = "Enemy_Bokoblin_Guard_Middle_Ambush"; + + else if (actor_name == "Enemy_Bokoblin_Guard_Junior_TreeHouseTop") + next = "Enemy_Bokoblin_Guard_Middle_TreeHouseTop"; + + /// @bug Yes, this is duplicated and shouldn't exist. + else if (actor_name == "Enemy_Bokoblin_Guard_Junior_Ambush") + next = "Enemy_Bokoblin_Guard_Middle_Ambush"; + + else if (actor_name == "Enemy_Chuchu_Electric_Junior") + next = "Enemy_Chuchu_Electric_Middle"; + else if (actor_name == "Enemy_Chuchu_Electric_Middle") + next = "Enemy_Chuchu_Electric_Senior"; + + else if (actor_name == "Enemy_Chuchu_Fire_Junior") + next = "Enemy_Chuchu_Fire_Middle"; + else if (actor_name == "Enemy_Chuchu_Fire_Middle") + next = "Enemy_Chuchu_Fire_Senior"; + + else if (actor_name == "Enemy_Chuchu_Ice_Junior") + next = "Enemy_Chuchu_Ice_Middle"; + else if (actor_name == "Enemy_Chuchu_Ice_Middle") + next = "Enemy_Chuchu_Ice_Senior"; + + else if (actor_name == "Enemy_Chuchu_Junior") + next = "Enemy_Chuchu_Middle"; + else if (actor_name == "Enemy_Chuchu_Middle") + next = "Enemy_Chuchu_Senior"; + + else if (actor_name == "Enemy_Giant_Junior") + next = "Enemy_Giant_Middle"; + else if (actor_name == "Enemy_Giant_Middle") + next = "Enemy_Giant_Senior"; + + else if (actor_name == "Enemy_Golem_Junior") + next = "Enemy_Golem_Middle"; + else if (actor_name == "Enemy_Golem_Middle") + next = "Enemy_Golem_Senior"; + + else if (actor_name == "Enemy_Guardian_Mini_Baby") + next = "Enemy_Guardian_Mini_Junior"; + else if (actor_name == "Enemy_Guardian_Mini_Junior") + next = "Enemy_Guardian_Mini_Middle"; + else if (actor_name == "Enemy_Guardian_Mini_Middle") + next = "Enemy_Guardian_Mini_Senior"; + + else if (actor_name == "Enemy_Guardian_Mini_Junior_DetachLineBeam") + next = "Enemy_Guardian_Mini_Middle_DetachLineBeam"; + + else if (actor_name == "Enemy_Lizalfos_Junior") + next = "Enemy_Lizalfos_Middle"; + else if (actor_name == "Enemy_Lizalfos_Middle") + next = "Enemy_Lizalfos_Senior"; + else if (actor_name == "Enemy_Lizalfos_Senior") + next = "Enemy_Lizalfos_Dark"; + else if (actor_name == "Enemy_Lizalfos_Dark") + next = "Enemy_Lizalfos_Gold"; + + else if (actor_name == "Enemy_Lizalfos_Guard_Junior") + next = "Enemy_Lizalfos_Guard_Middle"; + + else if (actor_name == "Enemy_Lizalfos_Guard_Junior_LongVisibility") + next = "Enemy_Lizalfos_Guard_Middle_LongVisibility"; + + else if (actor_name == "Enemy_Lizalfos_Junior_Guard_Ambush") + next = "Enemy_Lizalfos_Middle_Guard_Ambush"; + + else if (actor_name == "Enemy_Lynel_Junior") + next = "Enemy_Lynel_Middle"; + else if (actor_name == "Enemy_Lynel_Middle") + next = "Enemy_Lynel_Senior"; + else if (actor_name == "Enemy_Lynel_Senior") + next = "Enemy_Lynel_Dark"; + else if (actor_name == "Enemy_Lynel_Dark") + next = "Enemy_Lynel_Gold"; + + else if (actor_name == "Enemy_Moriblin_Junior") + next = "Enemy_Moriblin_Middle"; + else if (actor_name == "Enemy_Moriblin_Middle") + next = "Enemy_Moriblin_Senior"; + else if (actor_name == "Enemy_Moriblin_Senior") + next = "Enemy_Moriblin_Dark"; + else if (actor_name == "Enemy_Moriblin_Dark") + next = "Enemy_Moriblin_Gold"; + + else if (actor_name == "Enemy_Wizzrobe_Electric") + next = "Enemy_Wizzrobe_Electric_Senior"; + else if (actor_name == "Enemy_Wizzrobe_Fire") + next = "Enemy_Wizzrobe_Fire_Senior"; + else if (actor_name == "Enemy_Wizzrobe_Ice") + next = "Enemy_Wizzrobe_Ice_Senior"; + + if (next.isEmpty()) + return false; + + *new_name = next.cstr(); + return true; +} + +} // namespace uking::aoc diff --git a/src/Game/AOC/aocHardModeManager.h b/src/Game/AOC/aocHardModeManager.h new file mode 100644 index 00000000..375438a2 --- /dev/null +++ b/src/Game/AOC/aocHardModeManager.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include +#include +#include "KingSystem/ActorSystem/actActorConstDataAccess.h" +#include "KingSystem/GameData/gdtManager.h" +#include "KingSystem/Map/mapObject.h" +#include "KingSystem/Resource/resHandle.h" +#include "KingSystem/Utils/Types.h" + +namespace uking::aoc { + +class HardModeManager final { + SEAD_SINGLETON_DISPOSER(HardModeManager) + + HardModeManager(); + virtual ~HardModeManager(); + +public: + SEAD_ENUM(HardModeChange, IsLastPlayHardMode = 0, NerfHpRestore = 1, _2 = 2, + DisableNoDeathDamage = 3, PatchGanonStunLock = 4, EnableLifeRegen = 5, + RandomizeGuardianChargeBeam = 6, ApplyDamageMultiplier = 7, + EnableShorterEnemyNotice = 8) + SEAD_ENUM(Flag, EnableHardMode = 0) + SEAD_ENUM(MultiplierType, HpRestore = 0, _1 = 1, EnemyNoticeDuration = 2) + + void init(sead::Heap* heap); + + void loadIsLastPlayHardModeFlag(); + void loadIsHardModeFlag(); + + void storeIsLastPlayHardModeFlag(); + void resetIsLastPlayHardModeFlag(); + + void calc(); + + void nerfHpRestore(f32* hp) const; + void nerfHpRestore(s32* hp) const; + void modifyEnemyNoticeDuration(f32* value) const; + + static bool shouldCreateLifeRecoverInfo(ksys::act::Actor* actor); + static bool + shouldApplyMasterModeDamageMultiplier(const ksys::act::ActorConstDataAccess& accessor); + static void buffDamage(s32& damage); + + bool isTestOfStrengthShrine() const; + + bool rankUpEnemy(const sead::SafeString& actor_name, const ksys::map::Object& obj, + const char** new_name); + + void setHardModeChange(HardModeChange flag, bool on) { mHardModeChanges.changeBit(flag, on); } + bool isHardModeChangeOn(HardModeChange flag) const { return mHardModeChanges.isOnBit(flag); } + + void setFlag(Flag flag, bool on) { mFlags.changeBit(flag, on); } + bool checkFlag(Flag flag) const { return mFlags.isOnBit(flag); } + + f32 getMultiplier(MultiplierType type) const { return mMultipliers[type]; } + +private: + void setHardModeEnabledFlag(ksys::gdt::Manager::ResetEvent* event = nullptr); + void initFlagHandles(ksys::gdt::Manager::ReinitEvent* event = nullptr); + void init_(); + + sead::BitFlag32 mHardModeChanges; + sead::BitFlag32 mFlags; + sead::SafeArray mMultipliers; + ksys::gdt::Manager::ResetSignal::Slot mGdtResetSlot; + ksys::gdt::Manager::ReinitSignal::Slot mGdtReinitSlot; + void* _120 = nullptr; + ksys::res::Handle mFileHandle; + ksys::gdt::FlagHandle mHardModeHighScoreFlag = ksys::gdt::InvalidHandle; + ksys::gdt::FlagHandle mAoCHardModeEnabledFlag = ksys::gdt::InvalidHandle; + ksys::gdt::FlagHandle mIsLastPlayHardModeFlag = ksys::gdt::InvalidHandle; + sead::FixedSafeString<256> mMapType; + sead::FixedSafeString<256> mMapName; +}; +KSYS_CHECK_SIZE_NX150(HardModeManager, 0x3b8); + +} // namespace uking::aoc diff --git a/src/Game/Actor/AI/aiCookPotRoot.cpp b/src/Game/Actor/AI/aiCookPotRoot.cpp index da09eec4..0482daf6 100644 --- a/src/Game/Actor/AI/aiCookPotRoot.cpp +++ b/src/Game/Actor/AI/aiCookPotRoot.cpp @@ -1,5 +1,5 @@ #include "Game/Actor/AI/aiCookPotRoot.h" -#include "Game/DLC/aocHardModeManager.h" +#include "Game/AOC/aocHardModeManager.h" #include "Game/UI/uiPauseMenuDataMgr.h" #include "KingSystem/ActorSystem/Attention/actAttention.h" #include "KingSystem/ActorSystem/actActor.h" diff --git a/src/Game/Actor/Action/actionCurseRRematchCount.cpp b/src/Game/Actor/Action/actionCurseRRematchCount.cpp index 2838007a..a1d70ca1 100644 --- a/src/Game/Actor/Action/actionCurseRRematchCount.cpp +++ b/src/Game/Actor/Action/actionCurseRRematchCount.cpp @@ -1,5 +1,5 @@ #include "Game/Actor/Action/actionCurseRRematchCount.h" -#include "Game/DLC/aocChampionBalladManager.h" +#include "Game/AOC/aocChampionBalladManager.h" #include "KingSystem/GameData/gdtManager.h" namespace uking::action { @@ -19,12 +19,12 @@ void CurseRRematchCount::loadParams_() { } bool CurseRRematchCount::oneShot_() { - auto* manager = ChampionBalladManager::instance(); + auto* manager = aoc::ChampionBalladManager::instance(); if (!manager) return false; - auto blight = BlightType(*mCurseRType_d); - if (blight > BlightType::Water) + auto blight = aoc::BlightType(*mCurseRType_d); + if (blight > aoc::BlightType::Water) return false; if (*mRematchCount_d == 0) diff --git a/src/Game/Actor/Damage/dmgDamageManagerBase.cpp b/src/Game/Actor/Damage/dmgDamageManagerBase.cpp index b99cac50..08557ba6 100644 --- a/src/Game/Actor/Damage/dmgDamageManagerBase.cpp +++ b/src/Game/Actor/Damage/dmgDamageManagerBase.cpp @@ -1,6 +1,6 @@ #include "Game/Actor/Damage/dmgDamageManagerBase.h" +#include "Game/AOC/aocHardModeManager.h" #include "Game/Actor/Damage/dmgDamageCallback.h" -#include "Game/DLC/aocHardModeManager.h" #include "KingSystem/ActorSystem/actActor.h" #include "KingSystem/ActorSystem/actActorConstDataAccess.h" #include "KingSystem/ActorSystem/actActorParam.h" diff --git a/src/Game/Actor/Query/queryCheckCurseRRetryEverOnce.cpp b/src/Game/Actor/Query/queryCheckCurseRRetryEverOnce.cpp index 57f4377d..c66c959a 100644 --- a/src/Game/Actor/Query/queryCheckCurseRRetryEverOnce.cpp +++ b/src/Game/Actor/Query/queryCheckCurseRRetryEverOnce.cpp @@ -1,6 +1,6 @@ #include "Game/Actor/Query/queryCheckCurseRRetryEverOnce.h" #include -#include "Game/DLC/aocChampionBalladManager.h" +#include "Game/AOC/aocChampionBalladManager.h" namespace uking::query { @@ -10,12 +10,12 @@ CheckCurseRRetryEverOnce::CheckCurseRRetryEverOnce(const InitArg& arg) CheckCurseRRetryEverOnce::~CheckCurseRRetryEverOnce() = default; int CheckCurseRRetryEverOnce::doQuery() { - auto* manager = ChampionBalladManager::instance(); + auto* manager = aoc::ChampionBalladManager::instance(); if (!manager) return 0; - auto blight = BlightType(*mCurseRType); - if (blight <= BlightType::Water) + auto blight = aoc::BlightType(*mCurseRType); + if (blight <= aoc::BlightType::Water) return manager->getBlightRematchCount(blight) > 0; return 0; } diff --git a/src/Game/CMakeLists.txt b/src/Game/CMakeLists.txt index 702f626f..e7a19dc2 100644 --- a/src/Game/CMakeLists.txt +++ b/src/Game/CMakeLists.txt @@ -1,7 +1,7 @@ add_subdirectory(Actor) +add_subdirectory(AOC) add_subdirectory(AutoPlacement) add_subdirectory(Cooking) -add_subdirectory(DLC) add_subdirectory(Scene) add_subdirectory(UI) diff --git a/src/Game/DLC/CMakeLists.txt b/src/Game/DLC/CMakeLists.txt deleted file mode 100644 index 0b0579b0..00000000 --- a/src/Game/DLC/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -target_sources(uking PRIVATE - aocChampionBalladManager.cpp - aocChampionBalladManager.h - aocHardModeManager.cpp - aocHardModeManager.h - aocManager.cpp - aocManager.h -) diff --git a/src/Game/DLC/aocChampionBalladManager.cpp b/src/Game/DLC/aocChampionBalladManager.cpp deleted file mode 100644 index 0a0833a0..00000000 --- a/src/Game/DLC/aocChampionBalladManager.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "Game/DLC/aocChampionBalladManager.h" - -namespace uking { - -SEAD_SINGLETON_DISPOSER_IMPL(ChampionBalladManager) - -void ChampionBalladManager::init() { - mBlightCounts = {}; -} - -void ChampionBalladManager::setBlightRematchCount(s8 count, BlightType blight_type) { - if (count >= 0 && blight_type <= BlightType::Water) { - if (count >= 30) { - count = 30; - } - mBlightCounts[u32(blight_type)] = count; - } -} - -void ChampionBalladManager::incrementBlightRematchCount(BlightType blight_type) { - if (blight_type <= BlightType::Water) { - s8 count = mBlightCounts[u32(blight_type)] + 1; - setBlightRematchCount(count, blight_type); - } -} - -s8 ChampionBalladManager::getBlightRematchCount(BlightType blight_type) const { - if (blight_type <= BlightType::Water) - return mBlightCounts[u32(blight_type)]; - return 0; -} -} // namespace uking diff --git a/src/Game/DLC/aocChampionBalladManager.h b/src/Game/DLC/aocChampionBalladManager.h deleted file mode 100644 index 22c07b60..00000000 --- a/src/Game/DLC/aocChampionBalladManager.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace uking { - -enum class BlightType : u32 { Wind, Electric, Fire, Water }; - -class ChampionBalladManager { - SEAD_SINGLETON_DISPOSER(ChampionBalladManager) - ChampionBalladManager() = default; - virtual ~ChampionBalladManager() = default; - -public: - void init(); - void setBlightRematchCount(s8 count, BlightType blight_type); - void incrementBlightRematchCount(BlightType blight_type); - s8 getBlightRematchCount(BlightType blight_type) const; - -private: - sead::SafeArray mBlightCounts; -}; - -} // namespace uking diff --git a/src/Game/DLC/aocHardModeManager.cpp b/src/Game/DLC/aocHardModeManager.cpp deleted file mode 100644 index 9d8faef9..00000000 --- a/src/Game/DLC/aocHardModeManager.cpp +++ /dev/null @@ -1,335 +0,0 @@ -#include "Game/DLC/aocHardModeManager.h" -#include -#include "KingSystem/ActorSystem/actActor.h" -#include "KingSystem/ActorSystem/actActorUtil.h" -#include "KingSystem/ActorSystem/actTag.h" -#include "KingSystem/Utils/InitTimeInfo.h" - -namespace uking::aoc { - -SEAD_SINGLETON_DISPOSER_IMPL(HardModeManager) - -namespace { -struct aoc2StaticData { - ksys::util::InitConstants init_constants; - sead::SafeString flag_name_HardMode_HighScore{"HardMode_HighScore"}; - sead::SafeString flag_name_AoC_HardMode_Enabled{"AoC_HardMode_Enabled"}; - sead::SafeString flag_name_IsLastPlayHardMode{"IsLastPlayHardMode"}; -}; - -aoc2StaticData sData; -sead::FixedSafeString<64> sStr{""}; -} // namespace - -HardModeManager::HardModeManager() - : mGdtResetSlot(this, &HardModeManager::setHardModeEnabledFlag), - mGdtReinitSlot(this, &HardModeManager::initFlagHandles) { - mMultipliers.fill(0.5); - - setHardModeChange(HardModeChange::IsLastPlayHardMode, true); - setHardModeChange(HardModeChange::EnableShorterEnemyNotice, true); - setHardModeChange(HardModeChange::EnableLifeRegen, true); - setHardModeChange(HardModeChange::DisableNoDeathDamage, true); - setHardModeChange(HardModeChange::PatchGanonStunLock, true); - setHardModeChange(HardModeChange::RandomizeGuardianChargeBeam, true); - setHardModeChange(HardModeChange::ApplyDamageMultiplier, true); -} - -void HardModeManager::setHardModeEnabledFlag(ksys::gdt::Manager::ResetEvent*) { - ksys::gdt::Manager::instance()->setBool(true, mAoCHardModeEnabledFlag); - mGdtResetSlot.release(); -} - -void HardModeManager::initFlagHandles(ksys::gdt::Manager::ReinitEvent*) { - auto* gdm = ksys::gdt::Manager::instance(); - mHardModeHighScoreFlag = gdm->getS32Handle(sData.flag_name_HardMode_HighScore); - mAoCHardModeEnabledFlag = gdm->getBoolHandle(sData.flag_name_AoC_HardMode_Enabled); - mIsLastPlayHardModeFlag = gdm->getBoolHandle(sData.flag_name_IsLastPlayHardMode); -} - -HardModeManager::~HardModeManager() { - mFileHandle.requestUnload2(); - if (_120) { - // TODO: use the normal operator delete once we figure out what _120 is - ::operator delete(_120); - _120 = nullptr; - } -} - -void HardModeManager::init(sead::Heap*) { - init_(); -} - -void HardModeManager::init_() { - initFlagHandles(); - ksys::gdt::Manager::instance()->addReinitCallback(mGdtReinitSlot); -} - -void HardModeManager::nerfHpRestore(f32* hp) const { - *hp = sead::Mathf::clampMin(*hp * getMultiplier(MultiplierType::HpRestore), 1.0f); -} - -void HardModeManager::nerfHpRestore(s32* hp) const { - *hp = sead::Mathi::clampMin(*hp * getMultiplier(MultiplierType::HpRestore), 1); -} - -void HardModeManager::modifyEnemyNoticeDuration(f32* value) const { - *value = sead::Mathf::clampMin(*value * getMultiplier(MultiplierType::EnemyNoticeDuration), 0); -} - -bool HardModeManager::shouldCreateLifeRecoverInfo(ksys::act::Actor* actor) { - // Health regen should only apply to enemy actors. - if (!ksys::act::isEnemyProfile(actor)) - return false; - - // But not to wolves or bears... - if (ksys::act::isWolfOrBear(actor)) - return false; - - // and not to Dark Beast Ganon... - if (actor->getName() == "Enemy_GanonBeast") - return false; - - // and not to enemy swarms... - if (actor->getProfile() == "EnemySwarm") - return false; - - return actor->getMaxLife() > 1; -} - -bool HardModeManager::shouldApplyMasterModeDamageMultiplier( - const ksys::act::ActorConstDataAccess& accessor) { - if (!accessor.hasProc()) - return false; - - ksys::act::ActorConstDataAccess parent; - if (accessor.hasConnectedCalcParent() && accessor.acquireConnectedCalcParent(&parent)) - return shouldApplyMasterModeDamageMultiplier(parent); - - if (accessor.hasTag(ksys::act::tags::IsMasterModeDamageMultiplierActor) || - (HardModeManager::instance() && HardModeManager::instance()->isTestOfStrengthShrine() && - accessor.hasTag(ksys::act::tags::AncientGuardTarget))) { - return true; - } - - const sead::SafeString& profile = accessor.getProfile(); - const sead::SafeString& name = accessor.getName(); - - if (profile == "LastBoss" || profile == "SiteBoss") - return true; - - if (name == "Enemy_GanonBeast" || name == "GanonShockWave" || name == "EnemyGanonShockWave" || - name == "GanonSeaOfFlame" || name == "GanonFlameBall" || name == "GanonPillarOfFlame" || - name == "GanonNormalArrow" || name == "GanonSpearForThrowing" || name == "CurseGanonBeam" || - name == "GanonBeam" || name == "GanonIceBullet" || name == "GanonThunder" || - name == "GanonIronPile" || name == "GanonTornado" || name == "GanonBeastBeam" || - name == "SiteBossSeaOfFlame" || name == "SiteBossSeaOfFlameRotate" || - name == "SiteBossFlameBall" || name == "SiteBossBigFlameBall" || - name == "SiteBossPillarOfFlame" || name == "SiteBossWearFlame" || - name == "SiteBossDrawingFlameTornado" || name == "SiteBossGaleArrow" || - name == "SiteBossNormalArrow" || name == "SiteBossSpearForThrowing" || - name == "SiteBossReflectArrow" || name == "ArrowRainChild" || - name == "SiteBossSpearIceBullet" || name == "SiteBossTornado" || - name == "LastBossThunder" || name == "Enemy_Assassin_Senior" || - name == "AssassinRockBall" || name == "AssassinIronBall") { - return true; - } - - return false; -} - -void HardModeManager::buffDamage(s32& damage) { - damage = damage * 1.5f; - if (damage == 1) { - damage = 2; - } -} - -void HardModeManager::loadIsLastPlayHardModeFlag() { - bool value{}; - ksys::gdt::Manager::instance()->getBool(mIsLastPlayHardModeFlag, &value); - const bool x = value; -#ifdef MATCHING_HACK_NX_CLANG - asm(""); -#endif - setFlag(Flag::EnableHardMode, x); -} - -void HardModeManager::loadIsHardModeFlag() { - bool value{}; - ksys::gdt::Manager::instance()->getBool(mAoCHardModeEnabledFlag, &value); - const bool x = value; -#ifdef MATCHING_HACK_NX_CLANG - asm(""); -#endif - setFlag(Flag::EnableHardMode, x); -} - -void HardModeManager::storeIsLastPlayHardModeFlag() { - ksys::gdt::Manager::instance()->setBool(checkFlag(Flag::EnableHardMode), - mIsLastPlayHardModeFlag); -} - -void HardModeManager::resetIsLastPlayHardModeFlag() { - ksys::gdt::Manager::instance()->setBool(false, "IsLastPlayHardMode"); -} - -bool HardModeManager::isTestOfStrengthShrine() const { - if (mMapType != "CDungeon") - return false; - - // There are 20 ToS shrines in the base game (070-089). - if (mMapName.startsWith("Dungeon07") || mMapName.startsWith("Dungeon08")) - return true; - - // Ruvo Korbah - if (mMapName == "Dungeon135") - return true; - - return false; -} - -void HardModeManager::calc() { - volatile u32 unused = 0; - static_cast(unused); -} - -bool HardModeManager::rankUpEnemy(const sead::SafeString& actor_name, const ksys::map::Object& obj, - const char** new_name) { - if (obj.getFlags().isOn(ksys::map::Object::Flag::HasUniqueName) || - obj.getHardModeFlags().isOn(ksys::map::Object::HardModeFlag::DisableRankup)) { - return false; - } - - if (!actor_name.startsWith("Enemy")) - return false; - - if (isTestOfStrengthShrine()) - return false; - - sead::SafeString next = ""; - - /// @bug Yes, this is duplicated and shouldn't exist. - if (!actor_name.startsWith("Enemy")) - return false; - - if (actor_name == "Enemy_Assassin_Junior") - next = "Enemy_Assassin_Middle"; - else if (actor_name == "Enemy_Assassin_Middle") - next = "Enemy_Assassin_Senior"; - else if (actor_name == "Enemy_Assassin_Shooter_Junior") - next = "Enemy_Assassin_Shooter_Azito_Junior"; - - else if (actor_name == "Enemy_Bokoblin_Junior") - next = "Enemy_Bokoblin_Middle"; - else if (actor_name == "Enemy_Bokoblin_Middle") - next = "Enemy_Bokoblin_Senior"; - else if (actor_name == "Enemy_Bokoblin_Senior") - next = "Enemy_Bokoblin_Dark"; - else if (actor_name == "Enemy_Bokoblin_Dark") - next = "Enemy_Bokoblin_Gold"; - - else if (actor_name == "Enemy_Bokoblin_Guard_Junior") - next = "Enemy_Bokoblin_Guard_Middle"; - - else if (actor_name == "Enemy_Bokoblin_Guard_Junior_Ambush") - next = "Enemy_Bokoblin_Guard_Middle_Ambush"; - - else if (actor_name == "Enemy_Bokoblin_Guard_Junior_TreeHouseTop") - next = "Enemy_Bokoblin_Guard_Middle_TreeHouseTop"; - - /// @bug Yes, this is duplicated and shouldn't exist. - else if (actor_name == "Enemy_Bokoblin_Guard_Junior_Ambush") - next = "Enemy_Bokoblin_Guard_Middle_Ambush"; - - else if (actor_name == "Enemy_Chuchu_Electric_Junior") - next = "Enemy_Chuchu_Electric_Middle"; - else if (actor_name == "Enemy_Chuchu_Electric_Middle") - next = "Enemy_Chuchu_Electric_Senior"; - - else if (actor_name == "Enemy_Chuchu_Fire_Junior") - next = "Enemy_Chuchu_Fire_Middle"; - else if (actor_name == "Enemy_Chuchu_Fire_Middle") - next = "Enemy_Chuchu_Fire_Senior"; - - else if (actor_name == "Enemy_Chuchu_Ice_Junior") - next = "Enemy_Chuchu_Ice_Middle"; - else if (actor_name == "Enemy_Chuchu_Ice_Middle") - next = "Enemy_Chuchu_Ice_Senior"; - - else if (actor_name == "Enemy_Chuchu_Junior") - next = "Enemy_Chuchu_Middle"; - else if (actor_name == "Enemy_Chuchu_Middle") - next = "Enemy_Chuchu_Senior"; - - else if (actor_name == "Enemy_Giant_Junior") - next = "Enemy_Giant_Middle"; - else if (actor_name == "Enemy_Giant_Middle") - next = "Enemy_Giant_Senior"; - - else if (actor_name == "Enemy_Golem_Junior") - next = "Enemy_Golem_Middle"; - else if (actor_name == "Enemy_Golem_Middle") - next = "Enemy_Golem_Senior"; - - else if (actor_name == "Enemy_Guardian_Mini_Baby") - next = "Enemy_Guardian_Mini_Junior"; - else if (actor_name == "Enemy_Guardian_Mini_Junior") - next = "Enemy_Guardian_Mini_Middle"; - else if (actor_name == "Enemy_Guardian_Mini_Middle") - next = "Enemy_Guardian_Mini_Senior"; - - else if (actor_name == "Enemy_Guardian_Mini_Junior_DetachLineBeam") - next = "Enemy_Guardian_Mini_Middle_DetachLineBeam"; - - else if (actor_name == "Enemy_Lizalfos_Junior") - next = "Enemy_Lizalfos_Middle"; - else if (actor_name == "Enemy_Lizalfos_Middle") - next = "Enemy_Lizalfos_Senior"; - else if (actor_name == "Enemy_Lizalfos_Senior") - next = "Enemy_Lizalfos_Dark"; - else if (actor_name == "Enemy_Lizalfos_Dark") - next = "Enemy_Lizalfos_Gold"; - - else if (actor_name == "Enemy_Lizalfos_Guard_Junior") - next = "Enemy_Lizalfos_Guard_Middle"; - - else if (actor_name == "Enemy_Lizalfos_Guard_Junior_LongVisibility") - next = "Enemy_Lizalfos_Guard_Middle_LongVisibility"; - - else if (actor_name == "Enemy_Lizalfos_Junior_Guard_Ambush") - next = "Enemy_Lizalfos_Middle_Guard_Ambush"; - - else if (actor_name == "Enemy_Lynel_Junior") - next = "Enemy_Lynel_Middle"; - else if (actor_name == "Enemy_Lynel_Middle") - next = "Enemy_Lynel_Senior"; - else if (actor_name == "Enemy_Lynel_Senior") - next = "Enemy_Lynel_Dark"; - else if (actor_name == "Enemy_Lynel_Dark") - next = "Enemy_Lynel_Gold"; - - else if (actor_name == "Enemy_Moriblin_Junior") - next = "Enemy_Moriblin_Middle"; - else if (actor_name == "Enemy_Moriblin_Middle") - next = "Enemy_Moriblin_Senior"; - else if (actor_name == "Enemy_Moriblin_Senior") - next = "Enemy_Moriblin_Dark"; - else if (actor_name == "Enemy_Moriblin_Dark") - next = "Enemy_Moriblin_Gold"; - - else if (actor_name == "Enemy_Wizzrobe_Electric") - next = "Enemy_Wizzrobe_Electric_Senior"; - else if (actor_name == "Enemy_Wizzrobe_Fire") - next = "Enemy_Wizzrobe_Fire_Senior"; - else if (actor_name == "Enemy_Wizzrobe_Ice") - next = "Enemy_Wizzrobe_Ice_Senior"; - - if (next.isEmpty()) - return false; - - *new_name = next.cstr(); - return true; -} - -} // namespace uking::aoc diff --git a/src/Game/DLC/aocHardModeManager.h b/src/Game/DLC/aocHardModeManager.h deleted file mode 100644 index 375438a2..00000000 --- a/src/Game/DLC/aocHardModeManager.h +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include "KingSystem/ActorSystem/actActorConstDataAccess.h" -#include "KingSystem/GameData/gdtManager.h" -#include "KingSystem/Map/mapObject.h" -#include "KingSystem/Resource/resHandle.h" -#include "KingSystem/Utils/Types.h" - -namespace uking::aoc { - -class HardModeManager final { - SEAD_SINGLETON_DISPOSER(HardModeManager) - - HardModeManager(); - virtual ~HardModeManager(); - -public: - SEAD_ENUM(HardModeChange, IsLastPlayHardMode = 0, NerfHpRestore = 1, _2 = 2, - DisableNoDeathDamage = 3, PatchGanonStunLock = 4, EnableLifeRegen = 5, - RandomizeGuardianChargeBeam = 6, ApplyDamageMultiplier = 7, - EnableShorterEnemyNotice = 8) - SEAD_ENUM(Flag, EnableHardMode = 0) - SEAD_ENUM(MultiplierType, HpRestore = 0, _1 = 1, EnemyNoticeDuration = 2) - - void init(sead::Heap* heap); - - void loadIsLastPlayHardModeFlag(); - void loadIsHardModeFlag(); - - void storeIsLastPlayHardModeFlag(); - void resetIsLastPlayHardModeFlag(); - - void calc(); - - void nerfHpRestore(f32* hp) const; - void nerfHpRestore(s32* hp) const; - void modifyEnemyNoticeDuration(f32* value) const; - - static bool shouldCreateLifeRecoverInfo(ksys::act::Actor* actor); - static bool - shouldApplyMasterModeDamageMultiplier(const ksys::act::ActorConstDataAccess& accessor); - static void buffDamage(s32& damage); - - bool isTestOfStrengthShrine() const; - - bool rankUpEnemy(const sead::SafeString& actor_name, const ksys::map::Object& obj, - const char** new_name); - - void setHardModeChange(HardModeChange flag, bool on) { mHardModeChanges.changeBit(flag, on); } - bool isHardModeChangeOn(HardModeChange flag) const { return mHardModeChanges.isOnBit(flag); } - - void setFlag(Flag flag, bool on) { mFlags.changeBit(flag, on); } - bool checkFlag(Flag flag) const { return mFlags.isOnBit(flag); } - - f32 getMultiplier(MultiplierType type) const { return mMultipliers[type]; } - -private: - void setHardModeEnabledFlag(ksys::gdt::Manager::ResetEvent* event = nullptr); - void initFlagHandles(ksys::gdt::Manager::ReinitEvent* event = nullptr); - void init_(); - - sead::BitFlag32 mHardModeChanges; - sead::BitFlag32 mFlags; - sead::SafeArray mMultipliers; - ksys::gdt::Manager::ResetSignal::Slot mGdtResetSlot; - ksys::gdt::Manager::ReinitSignal::Slot mGdtReinitSlot; - void* _120 = nullptr; - ksys::res::Handle mFileHandle; - ksys::gdt::FlagHandle mHardModeHighScoreFlag = ksys::gdt::InvalidHandle; - ksys::gdt::FlagHandle mAoCHardModeEnabledFlag = ksys::gdt::InvalidHandle; - ksys::gdt::FlagHandle mIsLastPlayHardModeFlag = ksys::gdt::InvalidHandle; - sead::FixedSafeString<256> mMapType; - sead::FixedSafeString<256> mMapName; -}; -KSYS_CHECK_SIZE_NX150(HardModeManager, 0x3b8); - -} // namespace uking::aoc diff --git a/src/Game/DLC/aocManager.cpp b/src/Game/DLC/aocManager.cpp deleted file mode 100644 index bb7ca50c..00000000 --- a/src/Game/DLC/aocManager.cpp +++ /dev/null @@ -1,546 +0,0 @@ -#include "Game/DLC/aocManager.h" -#include -#include -#include -#include -#include "KingSystem/Resource/resLoadRequest.h" -#include "KingSystem/Resource/resResourceMgrTask.h" -#include "KingSystem/Utils/InitTimeInfo.h" -#include "KingSystem/Utils/SafeDelete.h" - -#ifdef NNSDK -#include -#include -#include -#include -#endif - -namespace uking::aoc { - -namespace { - -struct DungeonInfo { - int number; - sead::SafeString flag; -}; - -ksys::util::InitConstants sInitConstants; -DungeonInfo sDungeonInfoData[] = { - {120, "Defeat_OneHitDungeon001"}, - {121, "BalladOfHeroGerudo_AppearDungeon03"}, - {122, "Defeat_OneHitDungeon002"}, - {123, "Defeat_OneHitDungeon003"}, - {124, "BalladOfHeroZora_AppearDungeon01"}, - {125, "BalladOfHeroZora_AppearDungeon02"}, - {126, "BalladOfHeroZora_AppearDungeon03"}, - {127, "BalladOfHeroRito_TargetHittingSuccess"}, - {128, "BalladOfHeroRito_AppearDungeon03"}, - {129, "BalladOfHeroRito_AppearDungeon02"}, - {130, "BalladOfHeroGoron_FirstKillGolemR"}, - {131, "BalladOfHeroGoron_AppearDungeon01"}, - {132, "BalladOfHeroGoron_AppearDungeon03"}, - {133, "BalladOfHeroGerudo_AppearDungeon02"}, - {134, "BalladOfHeroGerudo_AppearDungeon01"}, - {135, "Defeat_OneHitDungeon004"}, -}; -sead::Buffer sDungeonInfo{sDungeonInfoData}; -constexpr int NumDungeons = 16; - -} // namespace - -SEAD_SINGLETON_DISPOSER_IMPL(Manager) - -Manager::Manager() : mVersionFile(), mGdtReinitSlot{this, &Manager::onGdtReinit} { - resetFlags(); -} - -Manager::~Manager() { - if (auto* gdm = ksys::gdt::Manager::instance()) - gdm->removeReinitCallback(mGdtReinitSlot); - - mAocMainFieldPackPrefix.deregister(); - mAocPackPrefix.deregister(); - mVersionFileDevPrefix.deregister(); - - if (mFileDevice) { - sead::FileDeviceMgr::instance()->unmount("aoc"); - ksys::util::safeDelete(mFileDevice); - } - -#ifdef NNSDK - if (mAocFsCache) { - nn::fs::Unmount("aoc"); - ksys::util::safeDelete(mAocFsCache); - } -#endif -} - -void Manager::resetFlags() { - mFlagAocVerAtLastPlay = ksys::gdt::InvalidHandle; - mFlagLatestAocVerPlayed = ksys::gdt::InvalidHandle; - mFlagHasAocVer1 = ksys::gdt::InvalidHandle; - mFlagHasAocVer2 = ksys::gdt::InvalidHandle; - mFlagHasAocVer3 = ksys::gdt::InvalidHandle; -} - -void Manager::init(sead::Heap* heap) { -#ifdef NNSDK - std::array dlc_info; - if (nn::aoc::ListAddOnContent(dlc_info.data(), 0, dlc_info.size()) == dlc_info.size() && - (dlc_info[0] == 1 || dlc_info[1] == 1)) { - size_t cache_size = 0; - nn::fs::QueryMountAddOnContentCacheSize(&cache_size, 1); - const size_t cache_size_ = cache_size; - mAocFsCache = static_cast(heap->tryAlloc(cache_size_, sizeof(void*))); - - nn::fs::MountAddOnContent("aoc", 1, mAocFsCache, cache_size); - - mFileDevice = new (heap) sead::NinAocFileDevice("aoc"); - sead::FileDeviceMgr::instance()->mount(mFileDevice, "aoc"); - loadVersionFile(); - } -#endif -} - -void Manager::loadVersionFile() { - if (!mFileDevice) - return; - - mVersionFileDevPrefix.registerPrefix("Aoc/0010/", mFileDevice, false); - - ksys::res::LoadRequest req; - req.mRequester = "aocManager"; - req._26 = false; - req.mAocFileDevice = mFileDevice; - const sead::SafeString path = "System/AocVersion.txt"; - mVersionFile.file_handle.requestLoad(path, &req); -} - -void Manager::loadAocMainFieldPack(ksys::OverlayArena* arena) { - if (!hasAoc3()) - return; - - auto* device = mFileDevice; - if (!device) - return; - - ksys::res::LoadRequest req; - req.mRequester = "aocManager"; - req._8 = true; - req._26 = false; - req.mAocFileDevice = device; - req.mArena = arena; - mAocMainFieldPack.requestLoad("Pack/AocMainField.pack", &req); -} - -void Manager::registerAocMainFieldPack() { - mAocMainFieldPack.waitForReady(); - mAocMainFieldPack.parseResource(nullptr); - if (mAocMainFieldPack.isSuccess()) - mAocMainFieldPackPrefix.registerPrefix("Aoc/0010/", mAocMainFieldPack.getResource(), false); -} - -void Manager::unloadAocMainFieldPack() { - mAocMainFieldPackPrefix.deregister(); - mAocMainFieldPack.requestUnload(); -} - -sead::FileDevice* Manager::getFileDeviceForMapFile(const sead::SafeString& path) const { - if (!hasAoc2()) - return nullptr; - - auto* device = mFileDevice; - - if (aocPackHasFile(path)) - return nullptr; - - if (path.startsWith("Map/MainField/")) - return device; - - if (hasAoc3() && path.startsWith("Map/MainFieldDungeon/")) - return device; - - if (isAocFile(path, "Map/", "Map/AocField/", "Map/MainFieldDungeon/", "Map/CDungeon/")) - return device; - - return nullptr; -} - -bool Manager::getFileDeviceForMap(sead::FileDevice** p_file_device, ksys::res::Handle** p_handle, - const sead::SafeString& path) { - if (!hasAoc2()) - return false; - - auto* device = mFileDevice; - - if (path.startsWith("Map/MainField/")) { - if (mAocMainFieldPack.isSuccess()) - *p_handle = &mAocMainFieldPack; - else - *p_file_device = device; - return true; - } - - if (aocPackHasFile(path)) - return false; - - if (hasAoc3() && path.startsWith("Map/MainFieldDungeon/")) { - *p_file_device = device; - return true; - } - - if (isAocFile(path, "Map/", "Map/AocField/", "Map/MainFieldDungeon/", "Map/CDungeon/")) { - *p_file_device = device; - return true; - } - - return false; -} - -sead::FileDevice* Manager::getFileDeviceForStaticCompound(const sead::SafeString& path) const { - if (!hasAoc2()) - return nullptr; - - auto* device = mFileDevice; - - if (aocPackHasFile(path)) - return nullptr; - - if (isAocFile(path, "Physics/StaticCompound/", "Physics/StaticCompound/AocField/", - "Physics/StaticCompound/MainFieldDungeon/", "Physics/StaticCompound/CDungeon/")) { - return device; - } - - return nullptr; -} - -sead::FileDevice* Manager::getFileDeviceForTeraMesh(const sead::SafeString& path) const { - if (!hasAoc2()) - return nullptr; - - if (path.startsWith("Physics/TeraMeshRigidBody/AocField/")) - return mFileDevice; - - return nullptr; -} - -sead::FileDevice* Manager::getFileDeviceForNavMesh(const sead::SafeString& path) const { - if (!hasAoc2()) - return nullptr; - - auto* device = mFileDevice; - - if (aocPackHasFile(path)) - return nullptr; - - if (isAocFile(path, "NavMesh/", "NavMesh/AocField/", "NavMesh/MainFieldDungeon/", - "NavMesh/CDungeon/")) { - return device; - } - - return nullptr; -} - -sead::FileDevice* Manager::getFileDeviceForTerrain(const sead::SafeString& path) const { - if (!hasAoc2()) - return nullptr; - - if (path.startsWith("Terrain/A/AocField")) - return mFileDevice; - - return nullptr; -} - -sead::FileDevice* Manager::getFileDeviceForGame(const sead::SafeString& path) const { - if (!hasAoc2()) - return nullptr; - - if (path.startsWith("Game/AocField/")) - return mFileDevice; - - return nullptr; -} - -sead::FileDevice* Manager::getFileDeviceForUI(const sead::SafeString& path) const { - if (!hasAoc3()) - return nullptr; - - if (path.startsWith("UI/StaffRollDLC/")) - return mFileDevice; - - return nullptr; -} - -bool Manager::isAocFile(const sead::SafeString& path, const sead::SafeString& dir, - const sead::SafeString& dir_aoc_field, - const sead::SafeString& dir_main_field_dungeon, - const sead::SafeString& dir_cdungeon) const { - if (!path.startsWith(dir)) - return false; - - if (path.startsWith(dir_aoc_field)) - return true; - - if (path.startsWith(dir_main_field_dungeon)) { - const auto rel_path = path.getPart(dir_main_field_dungeon.calcLength()); - if (rel_path.startsWith("RemainsElectric")) - return false; - if (rel_path.startsWith("RemainsFire")) - return false; - if (rel_path.startsWith("RemainsWater")) - return false; - if (rel_path.startsWith("RemainsWind")) - return false; - return true; - } - - if (path.startsWith(dir_cdungeon)) { - const auto rel_path = path.getPart(dir_cdungeon.calcLength()); - return isAocDungeon(rel_path); - } - - return false; -} - -static bool isAocDungeonNumber(const sead::SafeString& number) { - int num; - sead::FixedSafeString<4> buffer; - buffer.copy(number, 3); - return sead::StringUtil::tryParseS32(&num, buffer, sead::StringUtil::CardinalNumber::Base10) && - num >= 120; -} - -bool Manager::isAocDungeon(const sead::SafeString& map_name) { - if (!map_name.startsWith("Dungeon")) - return true; - const auto number = map_name.getPart(sead::SafeString("Dungeon").calcLength()); - return isAocDungeonNumber(number); -} - -bool Manager::isAocMap(const sead::SafeString& map_type, const sead::SafeString& map_name) { - if (isAocField(map_type)) - return true; - - if (map_type == "MainFieldDungeon") { - if (map_name == "RemainsElectric") - return false; - if (map_name == "RemainsFire") - return false; - if (map_name == "RemainsWater") - return false; - if (map_name == "RemainsWind") - return false; - return true; - } - - if (map_type == "CDungeon") - return isAocDungeon(map_name); - - return false; -} - -bool Manager::isAocField(const sead::SafeString& map_type) { - return map_type == "AocField"; -} - -sead::FileDevice* Manager::getFileDeviceForDungeonPack(const sead::SafeString& path) const { - if (!hasAoc3()) - return nullptr; - - auto* device = mFileDevice; - - const sead::SafeString prefix = "Pack/"; - const auto prefix_len = prefix.calcLength(); - - if (!path.startsWith(prefix)) - return nullptr; - - const auto rel_path = path.getPart(prefix_len); - - if (rel_path.startsWith("Dungeon") && !isAocDungeon(rel_path)) - device = nullptr; - - return device; -} - -void Manager::registerAocPack(ksys::res::Handle* pack) { - mAocPack = pack; - if (pack) { - mAocPackPrefix.registerPrefix("Aoc/0010/", pack->getResource(), true); - } else { - mAocPackPrefix.deregister(); - } -} - -bool Manager::aocPackHasFile(const sead::SafeString& path) const { - if (!mAocPack) - return false; - - const auto* res = sead::DynamicCast(mAocPack->getResource()); - if (!res) - return false; - - sead::FixedStringBuilder<0x81> builder; - builder.copy(path.cstr()); - ksys::res::ResourceMgrTask::instance()->addSExtensionPrefix(builder); - return res->isExistFile(builder); -} - -bool Manager::changeMoviePath(sead::BufferedSafeString& path) const { - if (!hasAoc3()) - return false; - - const sead::SafeString prefix = "Movie/"; - const auto prefix_len = prefix.calcLength(); - if (!path.startsWith(prefix)) - return false; - - const auto rel_path = path.getPart(prefix_len); - if (!rel_path.startsWith("Demo6")) - return false; - - path.prepend("aoc://"); - return true; -} - -bool Manager::parseVersion() { - if (!mVersionFile.readVersion()) - return false; - - if (mVersionFile.string.isEmpty()) { - mVersion = 0; - return true; - } - - { - const int dot_index = mVersionFile.string.findIndex("."); - const int minor_index = dot_index + 1; - if (dot_index <= 0 || minor_index >= mVersionFile.string.calcLength()) { - mVersionFile.string.clear(); - mVersion = 0; - return true; - } - - u32 major, minor; - const auto parse = [&] { - sead::FixedSafeString<16> major_str; - major_str.copy(mVersionFile.string, dot_index); - - constexpr auto base = sead::StringUtil::CardinalNumber::Base10; - if (!sead::StringUtil::tryParseU32(&major, major_str, base)) - return false; - - const auto minor_str = mVersionFile.string.getPart(minor_index); - if (!sead::StringUtil::tryParseU32(&minor, minor_str, base)) - return false; - - return true; - }; - - if (!parse()) { - mVersionFile.string.clear(); - mVersion = 0; - return true; - } - - mVersion = (major << 8) + minor; - } - - checkVersion(); - return true; -} - -// NON_MATCHING: stack and duplicated branch -- volatile variables are painful -void Manager::checkVersion() { - if (mVersion == 0) - return; - - const auto fail = [this](VersionError error) { - mVersionFlags.setBit(error); - mVersion = 0; - }; - - if (mVersion < 0x300) { - fail(VersionError::TooOld); - } else if (mVersion >= 0x400) { - fail(VersionError::TooNew); - } - - mVersionFlags.isOnBit(VersionError(VersionError::TooOld)); - mVersionFlags.isOnBit(VersionError(VersionError::TooNew)); -} - -bool Manager::VersionFile::readVersion() { - if (!file_handle.requestedLoad()) - return true; - - if (file_handle.isSuccess()) { - auto* res = sead::DynamicCast(file_handle.getResource()); - string.copy(reinterpret_cast(res->getRawData()), res->getRawSize()); - } else { - if (!file_handle.checkLoadStatus()) - return false; - string.clear(); - } - - file_handle.requestUnload(); - return true; -} - -void Manager::initGameData() { - reinitFlags(); - ksys::gdt::Manager::instance()->addReinitCallback(mGdtReinitSlot); -} - -static const sead::SafeString& getDungeonFlag(int number) { - for (auto it = sDungeonInfo.begin(); it != sDungeonInfo.end(); ++it) { - if (it->number == number) - return it->flag; - } - return sead::SafeString::cEmptyString; -} - -void Manager::reinitFlags() { - mFlagAocVerAtLastPlay = ksys::gdt::Manager::instance()->getS32Handle("AoCVerAtLastPlay"); - mFlagLatestAocVerPlayed = ksys::gdt::Manager::instance()->getS32Handle("LatestAoCVerPlayed"); - mFlagHasAocVer1 = - ksys::gdt::Manager::instance()->getBoolHandle(GameDataFlag::text(GameDataFlag::HasAoCVer1)); - mFlagHasAocVer2 = - ksys::gdt::Manager::instance()->getBoolHandle(GameDataFlag::text(GameDataFlag::HasAoCVer2)); - mFlagHasAocVer3 = - ksys::gdt::Manager::instance()->getBoolHandle(GameDataFlag::text(GameDataFlag::HasAoCVer3)); - - for (int i = 0; i < mDLCPositions.size(); ++i) { - ksys::gdt::FlagHandle handle = ksys::gdt::InvalidHandle; - - if (i < NumDungeons) { - if (mDLCPositions[i].flag_handle == ksys::gdt::InvalidHandle) - continue; - - handle = ksys::gdt::Manager::instance()->getBoolHandle(getDungeonFlag(i + 120)); - } - - mDLCPositions[i].flag_handle = handle; - } -} - -void Manager::setGameDataFlags() const { - ksys::gdt::Manager::instance()->setS32(mVersion, mFlagAocVerAtLastPlay); - - s32 latest_ver; - if (ksys::gdt::Manager::instance()->getS32(mFlagLatestAocVerPlayed, &latest_ver)) { - if (u32(latest_ver) < mVersion) - ksys::gdt::Manager::instance()->setS32(mVersion, mFlagLatestAocVerPlayed); - } - - const auto ver = mVersion; - ksys::gdt::Manager::instance()->setBool(ver >= 0x100, mFlagHasAocVer1); - ksys::gdt::Manager::instance()->setBool(ver >= 0x200, mFlagHasAocVer2); - ksys::gdt::Manager::instance()->setBool(ver >= 0x300, mFlagHasAocVer3); -} - -void Manager::onGdtReinit(ksys::gdt::Manager::ReinitEvent* event) { - reinitFlags(); -} - -} // namespace uking::aoc diff --git a/src/Game/DLC/aocManager.h b/src/Game/DLC/aocManager.h deleted file mode 100644 index 9dd19636..00000000 --- a/src/Game/DLC/aocManager.h +++ /dev/null @@ -1,132 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include "KingSystem/GameData/gdtManager.h" -#include "KingSystem/Resource/resHandle.h" -#include "KingSystem/Resource/resResourceMgrTask.h" -#include "KingSystem/Utils/Types.h" - -namespace ksys { -class OverlayArena; -} - -namespace sead { -class FileDevice; -} - -namespace uking::aoc { - -// TODO -class Manager { - SEAD_SINGLETON_DISPOSER(Manager) - Manager(); - ~Manager(); - -public: - void init(sead::Heap* heap); - - u32 getVersion() const { return mVersion; } - - /// @return Whether the Master Trials DLC is supported. - bool hasAoc2() const { return mVersion >= 0x200; } - - /// @return Whether the Champion's Ballad DLC is supported. - bool hasAoc3() const { return mVersion >= 0x300; } - - void loadAocMainFieldPack(ksys::OverlayArena* arena); - void registerAocMainFieldPack(); - void unloadAocMainFieldPack(); - - sead::FileDevice* getFileDeviceForMapFile(const sead::SafeString& path) const; - bool getFileDeviceForMap(sead::FileDevice** p_file_device, ksys::res::Handle** p_handle, - const sead::SafeString& path); - sead::FileDevice* getFileDeviceForStaticCompound(const sead::SafeString& path) const; - sead::FileDevice* getFileDeviceForTeraMesh(const sead::SafeString& path) const; - sead::FileDevice* getFileDeviceForNavMesh(const sead::SafeString& path) const; - sead::FileDevice* getFileDeviceForTerrain(const sead::SafeString& path) const; - sead::FileDevice* getFileDeviceForGame(const sead::SafeString& path) const; - sead::FileDevice* getFileDeviceForUI(const sead::SafeString& path) const; - - bool isAocFile(const sead::SafeString& path, const sead::SafeString& dir, - const sead::SafeString& dir_aoc_field, - const sead::SafeString& dir_main_field_dungeon, - const sead::SafeString& dir_cdungeon) const; - static bool isAocDungeon(const sead::SafeString& map_name); - static bool isAocMap(const sead::SafeString& map_type, const sead::SafeString& map_name); - static bool isAocField(const sead::SafeString& map_type); - - sead::FileDevice* getFileDeviceForDungeonPack(const sead::SafeString& path) const; - void registerAocPack(ksys::res::Handle* pack); - bool aocPackHasFile(const sead::SafeString& path) const; - - bool changeMoviePath(sead::BufferedSafeString& path) const; - - void parseAocMainFieldStaticInfo(const al::ByamlIter& iter); - // TODO: figure out what this does - bool getSomePos(sead::Vector3f* translate, u32* p_mask, u32* mask); - - bool parseVersion(); - void setGameDataFlags() const; - -private: - SEAD_ENUM(GameDataFlag, AoCVerAtLastPlay ,LatestAoCVerPlayed ,HasAoCVer1 ,HasAoCVer2 ,HasAoCVer3) - SEAD_ENUM(VersionError, TooNew, TooOld) - - struct DLCPosition { - sead::Vector3f translate = sead::Vector3f::zero; - sead::Vector3f rotate = sead::Vector3f::zero; - sead::Vector3f translate2 = sead::Vector3f::zero; - float scale = 0.0; - ksys::gdt::FlagHandle flag_handle = ksys::gdt::InvalidHandle; - }; - KSYS_CHECK_SIZE_NX150(DLCPosition, 0x2c); - - struct VersionFile { - bool readVersion(); - - ksys::res::Handle file_handle; - sead::FixedSafeString<16> string; - }; - KSYS_CHECK_SIZE_NX150(VersionFile, 0x78); - - void loadVersionFile(); - void checkVersion(); - - void resetFlags(); - void initGameData(); - void reinitFlags(); - void onGdtReinit(ksys::gdt::Manager::ReinitEvent* event); - - sead::FileDevice* mFileDevice{}; - - ksys::res::FileDevicePrefix mVersionFileDevPrefix; - VersionFile mVersionFile{}; - u32 mVersion{}; - - ksys::res::Handle mAocMainFieldPack; - ksys::res::FileDevicePrefix mAocMainFieldPackPrefix; - ksys::res::Handle* mAocPack{}; - ksys::res::FileDevicePrefix mAocPackPrefix; - - sead::SafeArray mDLCPositions; - - ksys::gdt::FlagHandle mFlagAocVerAtLastPlay{}; - ksys::gdt::FlagHandle mFlagLatestAocVerPlayed{}; - ksys::gdt::FlagHandle mFlagHasAocVer1{}; - ksys::gdt::FlagHandle mFlagHasAocVer2{}; - ksys::gdt::FlagHandle mFlagHasAocVer3{}; - ksys::gdt::Manager::ReinitSignal::Slot mGdtReinitSlot; - - sead::BitFlag8 mVersionFlags{}; -#ifdef NNSDK - u8* mAocFsCache{}; -#endif -}; -KSYS_CHECK_SIZE_NX150(Manager, 0x598); - -} // namespace uking::aoc diff --git a/src/Game/UI/uiPauseMenuDataMgr.cpp b/src/Game/UI/uiPauseMenuDataMgr.cpp index 53a87310..ec352d31 100644 --- a/src/Game/UI/uiPauseMenuDataMgr.cpp +++ b/src/Game/UI/uiPauseMenuDataMgr.cpp @@ -10,10 +10,10 @@ #include "Game/Actor/actPlayerCreateUtils.h" #include "Game/Actor/actWeapon.h" #include "Game/Cooking/cookManager.h" -#include "Game/DLC/aocManager.h" #include "Game/Scene/gameScene.h" #include "Game/UI/uiUtils.h" #include "Game/gameItemUtils.h" +#include "KingSystem/AOC/aocManager.h" #include "KingSystem/ActorSystem/actActorConstDataAccess.h" #include "KingSystem/ActorSystem/actActorHeapUtil.h" #include "KingSystem/ActorSystem/actActorUtil.h" @@ -450,7 +450,7 @@ void PauseMenuDataMgr::doLoadFromGameData() { } // Add the Travel Medallion (Obj_WarpDLC) to the inventory if it is missing for some reason - if (aoc::Manager::instance()->hasAoc2() && + if (ksys::aoc::Manager::instance()->hasAoc2() && !(found_travel_medallion | !gdt::getFlag_IsGet_Obj_WarpDLC())) { addToPouch(sValues.Obj_WarpDLC.cstr(), PouchItemType::KeyItem, lists, 1, false, nullptr, true); @@ -986,12 +986,12 @@ void PauseMenuDataMgr::doAddToPouch(PouchItemType type, const sead::SafeString& } if (item_name_to_add == sValues.Obj_WarpDLC) { - if (!aoc::Manager::instance()->hasAoc2()) { + if (!ksys::aoc::Manager::instance()->hasAoc2()) { return; } } - if (!aoc::Manager::instance()->hasAoc3()) { + if (!ksys::aoc::Manager::instance()->hasAoc3()) { if (item_name_to_add == sValues.Obj_DLC_HeroSoul_Zora) { item_name_to_add = sValues.Obj_HeroSoul_Zora; } else if (item_name_to_add == sValues.Obj_DLC_HeroSoul_Rito) { @@ -2621,23 +2621,26 @@ bool PauseMenuDataMgr::isHeroSoulEnabled(const sead::SafeString& name) const { // NOLINTNEXTLINE(readability-convert-member-functions-to-static) bool PauseMenuDataMgr::hasRitoSoulPlus() const { - return ksys::gdt::getFlag_IsGet_Obj_DLC_HeroSoul_Rito() && aoc::Manager::instance()->hasAoc3(); + return ksys::gdt::getFlag_IsGet_Obj_DLC_HeroSoul_Rito() && + ksys::aoc::Manager::instance()->hasAoc3(); } // NOLINTNEXTLINE(readability-convert-member-functions-to-static) bool PauseMenuDataMgr::hasGoronSoulPlus() const { - return ksys::gdt::getFlag_IsGet_Obj_DLC_HeroSoul_Goron() && aoc::Manager::instance()->hasAoc3(); + return ksys::gdt::getFlag_IsGet_Obj_DLC_HeroSoul_Goron() && + ksys::aoc::Manager::instance()->hasAoc3(); } // NOLINTNEXTLINE(readability-convert-member-functions-to-static) bool PauseMenuDataMgr::hasGerudoSoulPlus() const { return ksys::gdt::getFlag_IsGet_Obj_DLC_HeroSoul_Gerudo() && - aoc::Manager::instance()->hasAoc3(); + ksys::aoc::Manager::instance()->hasAoc3(); } // NOLINTNEXTLINE(readability-convert-member-functions-to-static) bool PauseMenuDataMgr::hasZoraSoulPlus() const { - return ksys::gdt::getFlag_IsGet_Obj_DLC_HeroSoul_Zora() && aoc::Manager::instance()->hasAoc3(); + return ksys::gdt::getFlag_IsGet_Obj_DLC_HeroSoul_Zora() && + ksys::aoc::Manager::instance()->hasAoc3(); } int PauseMenuDataMgr::countItemsWithCategoryByType(PouchCategory category) const { diff --git a/src/Game/UI/uiUtils.cpp b/src/Game/UI/uiUtils.cpp index 06462c7b..3d4e4979 100644 --- a/src/Game/UI/uiUtils.cpp +++ b/src/Game/UI/uiUtils.cpp @@ -1,8 +1,8 @@ #include "Game/UI/uiUtils.h" +#include "Game/AOC/aocHardModeManager.h" #include "Game/Actor/Damage/dmgInfoManager.h" #include "Game/Actor/actPlayerCreateMgr.h" #include "Game/Actor/actWeapon.h" -#include "Game/DLC/aocHardModeManager.h" #include "Game/UI/uiPauseMenuDataMgr.h" #include "KingSystem/ActorSystem/actInfoCommon.h" #include "KingSystem/ActorSystem/actInfoData.h" diff --git a/src/KingSystem/AOC/CMakeLists.txt b/src/KingSystem/AOC/CMakeLists.txt new file mode 100644 index 00000000..4badc13f --- /dev/null +++ b/src/KingSystem/AOC/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(uking PRIVATE + aocManager.cpp + aocManager.h +) diff --git a/src/KingSystem/AOC/aocManager.cpp b/src/KingSystem/AOC/aocManager.cpp new file mode 100644 index 00000000..c0c13f77 --- /dev/null +++ b/src/KingSystem/AOC/aocManager.cpp @@ -0,0 +1,546 @@ +#include "KingSystem/AOC/aocManager.h" +#include +#include +#include +#include +#include "KingSystem/Resource/resLoadRequest.h" +#include "KingSystem/Resource/resResourceMgrTask.h" +#include "KingSystem/Utils/InitTimeInfo.h" +#include "KingSystem/Utils/SafeDelete.h" + +#ifdef NNSDK +#include +#include +#include +#include +#endif + +namespace ksys::aoc { + +namespace { + +struct DungeonInfo { + int number; + sead::SafeString flag; +}; + +util::InitConstants sInitConstants; +DungeonInfo sDungeonInfoData[] = { + {120, "Defeat_OneHitDungeon001"}, + {121, "BalladOfHeroGerudo_AppearDungeon03"}, + {122, "Defeat_OneHitDungeon002"}, + {123, "Defeat_OneHitDungeon003"}, + {124, "BalladOfHeroZora_AppearDungeon01"}, + {125, "BalladOfHeroZora_AppearDungeon02"}, + {126, "BalladOfHeroZora_AppearDungeon03"}, + {127, "BalladOfHeroRito_TargetHittingSuccess"}, + {128, "BalladOfHeroRito_AppearDungeon03"}, + {129, "BalladOfHeroRito_AppearDungeon02"}, + {130, "BalladOfHeroGoron_FirstKillGolemR"}, + {131, "BalladOfHeroGoron_AppearDungeon01"}, + {132, "BalladOfHeroGoron_AppearDungeon03"}, + {133, "BalladOfHeroGerudo_AppearDungeon02"}, + {134, "BalladOfHeroGerudo_AppearDungeon01"}, + {135, "Defeat_OneHitDungeon004"}, +}; +sead::Buffer sDungeonInfo{sDungeonInfoData}; +constexpr int NumDungeons = 16; + +} // namespace + +SEAD_SINGLETON_DISPOSER_IMPL(Manager) + +Manager::Manager() : mVersionFile(), mGdtReinitSlot{this, &Manager::onGdtReinit} { + resetFlags(); +} + +Manager::~Manager() { + if (auto* gdm = gdt::Manager::instance()) + gdm->removeReinitCallback(mGdtReinitSlot); + + mAocMainFieldPackPrefix.deregister(); + mAocPackPrefix.deregister(); + mVersionFileDevPrefix.deregister(); + + if (mFileDevice) { + sead::FileDeviceMgr::instance()->unmount("aoc"); + util::safeDelete(mFileDevice); + } + +#ifdef NNSDK + if (mAocFsCache) { + nn::fs::Unmount("aoc"); + util::safeDelete(mAocFsCache); + } +#endif +} + +void Manager::resetFlags() { + mFlagAocVerAtLastPlay = gdt::InvalidHandle; + mFlagLatestAocVerPlayed = gdt::InvalidHandle; + mFlagHasAocVer1 = gdt::InvalidHandle; + mFlagHasAocVer2 = gdt::InvalidHandle; + mFlagHasAocVer3 = gdt::InvalidHandle; +} + +void Manager::init(sead::Heap* heap) { +#ifdef NNSDK + std::array dlc_info; + if (nn::aoc::ListAddOnContent(dlc_info.data(), 0, dlc_info.size()) == dlc_info.size() && + (dlc_info[0] == 1 || dlc_info[1] == 1)) { + size_t cache_size = 0; + nn::fs::QueryMountAddOnContentCacheSize(&cache_size, 1); + const size_t cache_size_ = cache_size; + mAocFsCache = static_cast(heap->tryAlloc(cache_size_, sizeof(void*))); + + nn::fs::MountAddOnContent("aoc", 1, mAocFsCache, cache_size); + + mFileDevice = new (heap) sead::NinAocFileDevice("aoc"); + sead::FileDeviceMgr::instance()->mount(mFileDevice, "aoc"); + loadVersionFile(); + } +#endif +} + +void Manager::loadVersionFile() { + if (!mFileDevice) + return; + + mVersionFileDevPrefix.registerPrefix("Aoc/0010/", mFileDevice, false); + + res::LoadRequest req; + req.mRequester = "aocManager"; + req._26 = false; + req.mAocFileDevice = mFileDevice; + const sead::SafeString path = "System/AocVersion.txt"; + mVersionFile.file_handle.requestLoad(path, &req); +} + +void Manager::loadAocMainFieldPack(OverlayArena* arena) { + if (!hasAoc3()) + return; + + auto* device = mFileDevice; + if (!device) + return; + + res::LoadRequest req; + req.mRequester = "aocManager"; + req._8 = true; + req._26 = false; + req.mAocFileDevice = device; + req.mArena = arena; + mAocMainFieldPack.requestLoad("Pack/AocMainField.pack", &req); +} + +void Manager::registerAocMainFieldPack() { + mAocMainFieldPack.waitForReady(); + mAocMainFieldPack.parseResource(nullptr); + if (mAocMainFieldPack.isSuccess()) + mAocMainFieldPackPrefix.registerPrefix("Aoc/0010/", mAocMainFieldPack.getResource(), false); +} + +void Manager::unloadAocMainFieldPack() { + mAocMainFieldPackPrefix.deregister(); + mAocMainFieldPack.requestUnload(); +} + +sead::FileDevice* Manager::getFileDeviceForMapFile(const sead::SafeString& path) const { + if (!hasAoc2()) + return nullptr; + + auto* device = mFileDevice; + + if (aocPackHasFile(path)) + return nullptr; + + if (path.startsWith("Map/MainField/")) + return device; + + if (hasAoc3() && path.startsWith("Map/MainFieldDungeon/")) + return device; + + if (isAocFile(path, "Map/", "Map/AocField/", "Map/MainFieldDungeon/", "Map/CDungeon/")) + return device; + + return nullptr; +} + +bool Manager::getFileDeviceForMap(sead::FileDevice** p_file_device, res::Handle** p_handle, + const sead::SafeString& path) { + if (!hasAoc2()) + return false; + + auto* device = mFileDevice; + + if (path.startsWith("Map/MainField/")) { + if (mAocMainFieldPack.isSuccess()) + *p_handle = &mAocMainFieldPack; + else + *p_file_device = device; + return true; + } + + if (aocPackHasFile(path)) + return false; + + if (hasAoc3() && path.startsWith("Map/MainFieldDungeon/")) { + *p_file_device = device; + return true; + } + + if (isAocFile(path, "Map/", "Map/AocField/", "Map/MainFieldDungeon/", "Map/CDungeon/")) { + *p_file_device = device; + return true; + } + + return false; +} + +sead::FileDevice* Manager::getFileDeviceForStaticCompound(const sead::SafeString& path) const { + if (!hasAoc2()) + return nullptr; + + auto* device = mFileDevice; + + if (aocPackHasFile(path)) + return nullptr; + + if (isAocFile(path, "Physics/StaticCompound/", "Physics/StaticCompound/AocField/", + "Physics/StaticCompound/MainFieldDungeon/", "Physics/StaticCompound/CDungeon/")) { + return device; + } + + return nullptr; +} + +sead::FileDevice* Manager::getFileDeviceForTeraMesh(const sead::SafeString& path) const { + if (!hasAoc2()) + return nullptr; + + if (path.startsWith("Physics/TeraMeshRigidBody/AocField/")) + return mFileDevice; + + return nullptr; +} + +sead::FileDevice* Manager::getFileDeviceForNavMesh(const sead::SafeString& path) const { + if (!hasAoc2()) + return nullptr; + + auto* device = mFileDevice; + + if (aocPackHasFile(path)) + return nullptr; + + if (isAocFile(path, "NavMesh/", "NavMesh/AocField/", "NavMesh/MainFieldDungeon/", + "NavMesh/CDungeon/")) { + return device; + } + + return nullptr; +} + +sead::FileDevice* Manager::getFileDeviceForTerrain(const sead::SafeString& path) const { + if (!hasAoc2()) + return nullptr; + + if (path.startsWith("Terrain/A/AocField")) + return mFileDevice; + + return nullptr; +} + +sead::FileDevice* Manager::getFileDeviceForGame(const sead::SafeString& path) const { + if (!hasAoc2()) + return nullptr; + + if (path.startsWith("Game/AocField/")) + return mFileDevice; + + return nullptr; +} + +sead::FileDevice* Manager::getFileDeviceForUI(const sead::SafeString& path) const { + if (!hasAoc3()) + return nullptr; + + if (path.startsWith("UI/StaffRollDLC/")) + return mFileDevice; + + return nullptr; +} + +bool Manager::isAocFile(const sead::SafeString& path, const sead::SafeString& dir, + const sead::SafeString& dir_aoc_field, + const sead::SafeString& dir_main_field_dungeon, + const sead::SafeString& dir_cdungeon) const { + if (!path.startsWith(dir)) + return false; + + if (path.startsWith(dir_aoc_field)) + return true; + + if (path.startsWith(dir_main_field_dungeon)) { + const auto rel_path = path.getPart(dir_main_field_dungeon.calcLength()); + if (rel_path.startsWith("RemainsElectric")) + return false; + if (rel_path.startsWith("RemainsFire")) + return false; + if (rel_path.startsWith("RemainsWater")) + return false; + if (rel_path.startsWith("RemainsWind")) + return false; + return true; + } + + if (path.startsWith(dir_cdungeon)) { + const auto rel_path = path.getPart(dir_cdungeon.calcLength()); + return isAocDungeon(rel_path); + } + + return false; +} + +static bool isAocDungeonNumber(const sead::SafeString& number) { + int num; + sead::FixedSafeString<4> buffer; + buffer.copy(number, 3); + return sead::StringUtil::tryParseS32(&num, buffer, sead::StringUtil::CardinalNumber::Base10) && + num >= 120; +} + +bool Manager::isAocDungeon(const sead::SafeString& map_name) { + if (!map_name.startsWith("Dungeon")) + return true; + const auto number = map_name.getPart(sead::SafeString("Dungeon").calcLength()); + return isAocDungeonNumber(number); +} + +bool Manager::isAocMap(const sead::SafeString& map_type, const sead::SafeString& map_name) { + if (isAocField(map_type)) + return true; + + if (map_type == "MainFieldDungeon") { + if (map_name == "RemainsElectric") + return false; + if (map_name == "RemainsFire") + return false; + if (map_name == "RemainsWater") + return false; + if (map_name == "RemainsWind") + return false; + return true; + } + + if (map_type == "CDungeon") + return isAocDungeon(map_name); + + return false; +} + +bool Manager::isAocField(const sead::SafeString& map_type) { + return map_type == "AocField"; +} + +sead::FileDevice* Manager::getFileDeviceForDungeonPack(const sead::SafeString& path) const { + if (!hasAoc3()) + return nullptr; + + auto* device = mFileDevice; + + const sead::SafeString prefix = "Pack/"; + const auto prefix_len = prefix.calcLength(); + + if (!path.startsWith(prefix)) + return nullptr; + + const auto rel_path = path.getPart(prefix_len); + + if (rel_path.startsWith("Dungeon") && !isAocDungeon(rel_path)) + device = nullptr; + + return device; +} + +void Manager::registerAocPack(res::Handle* pack) { + mAocPack = pack; + if (pack) { + mAocPackPrefix.registerPrefix("Aoc/0010/", pack->getResource(), true); + } else { + mAocPackPrefix.deregister(); + } +} + +bool Manager::aocPackHasFile(const sead::SafeString& path) const { + if (!mAocPack) + return false; + + const auto* res = sead::DynamicCast(mAocPack->getResource()); + if (!res) + return false; + + sead::FixedStringBuilder<0x81> builder; + builder.copy(path.cstr()); + res::ResourceMgrTask::instance()->addSExtensionPrefix(builder); + return res->isExistFile(builder); +} + +bool Manager::changeMoviePath(sead::BufferedSafeString& path) const { + if (!hasAoc3()) + return false; + + const sead::SafeString prefix = "Movie/"; + const auto prefix_len = prefix.calcLength(); + if (!path.startsWith(prefix)) + return false; + + const auto rel_path = path.getPart(prefix_len); + if (!rel_path.startsWith("Demo6")) + return false; + + path.prepend("aoc://"); + return true; +} + +bool Manager::parseVersion() { + if (!mVersionFile.readVersion()) + return false; + + if (mVersionFile.string.isEmpty()) { + mVersion = 0; + return true; + } + + { + const int dot_index = mVersionFile.string.findIndex("."); + const int minor_index = dot_index + 1; + if (dot_index <= 0 || minor_index >= mVersionFile.string.calcLength()) { + mVersionFile.string.clear(); + mVersion = 0; + return true; + } + + u32 major, minor; + const auto parse = [&] { + sead::FixedSafeString<16> major_str; + major_str.copy(mVersionFile.string, dot_index); + + constexpr auto base = sead::StringUtil::CardinalNumber::Base10; + if (!sead::StringUtil::tryParseU32(&major, major_str, base)) + return false; + + const auto minor_str = mVersionFile.string.getPart(minor_index); + if (!sead::StringUtil::tryParseU32(&minor, minor_str, base)) + return false; + + return true; + }; + + if (!parse()) { + mVersionFile.string.clear(); + mVersion = 0; + return true; + } + + mVersion = (major << 8) + minor; + } + + checkVersion(); + return true; +} + +// NON_MATCHING: stack and duplicated branch -- volatile variables are painful +void Manager::checkVersion() { + if (mVersion == 0) + return; + + const auto fail = [this](VersionError error) { + mVersionFlags.setBit(error); + mVersion = 0; + }; + + if (mVersion < 0x300) { + fail(VersionError::TooOld); + } else if (mVersion >= 0x400) { + fail(VersionError::TooNew); + } + + mVersionFlags.isOnBit(VersionError(VersionError::TooOld)); + mVersionFlags.isOnBit(VersionError(VersionError::TooNew)); +} + +bool Manager::VersionFile::readVersion() { + if (!file_handle.requestedLoad()) + return true; + + if (file_handle.isSuccess()) { + auto* res = sead::DynamicCast(file_handle.getResource()); + string.copy(reinterpret_cast(res->getRawData()), res->getRawSize()); + } else { + if (!file_handle.checkLoadStatus()) + return false; + string.clear(); + } + + file_handle.requestUnload(); + return true; +} + +void Manager::initGameData() { + reinitFlags(); + gdt::Manager::instance()->addReinitCallback(mGdtReinitSlot); +} + +static const sead::SafeString& getDungeonFlag(int number) { + for (auto it = sDungeonInfo.begin(); it != sDungeonInfo.end(); ++it) { + if (it->number == number) + return it->flag; + } + return sead::SafeString::cEmptyString; +} + +void Manager::reinitFlags() { + mFlagAocVerAtLastPlay = gdt::Manager::instance()->getS32Handle("AoCVerAtLastPlay"); + mFlagLatestAocVerPlayed = gdt::Manager::instance()->getS32Handle("LatestAoCVerPlayed"); + mFlagHasAocVer1 = + gdt::Manager::instance()->getBoolHandle(GameDataFlag::text(GameDataFlag::HasAoCVer1)); + mFlagHasAocVer2 = + gdt::Manager::instance()->getBoolHandle(GameDataFlag::text(GameDataFlag::HasAoCVer2)); + mFlagHasAocVer3 = + gdt::Manager::instance()->getBoolHandle(GameDataFlag::text(GameDataFlag::HasAoCVer3)); + + for (int i = 0; i < mDLCPositions.size(); ++i) { + gdt::FlagHandle handle = gdt::InvalidHandle; + + if (i < NumDungeons) { + if (mDLCPositions[i].flag_handle == gdt::InvalidHandle) + continue; + + handle = gdt::Manager::instance()->getBoolHandle(getDungeonFlag(i + 120)); + } + + mDLCPositions[i].flag_handle = handle; + } +} + +void Manager::setGameDataFlags() const { + gdt::Manager::instance()->setS32(mVersion, mFlagAocVerAtLastPlay); + + s32 latest_ver; + if (gdt::Manager::instance()->getS32(mFlagLatestAocVerPlayed, &latest_ver)) { + if (u32(latest_ver) < mVersion) + gdt::Manager::instance()->setS32(mVersion, mFlagLatestAocVerPlayed); + } + + const auto ver = mVersion; + gdt::Manager::instance()->setBool(ver >= 0x100, mFlagHasAocVer1); + gdt::Manager::instance()->setBool(ver >= 0x200, mFlagHasAocVer2); + gdt::Manager::instance()->setBool(ver >= 0x300, mFlagHasAocVer3); +} + +void Manager::onGdtReinit(gdt::Manager::ReinitEvent* event) { + reinitFlags(); +} + +} // namespace ksys::aoc diff --git a/src/KingSystem/AOC/aocManager.h b/src/KingSystem/AOC/aocManager.h new file mode 100644 index 00000000..9f1623f3 --- /dev/null +++ b/src/KingSystem/AOC/aocManager.h @@ -0,0 +1,132 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "KingSystem/GameData/gdtManager.h" +#include "KingSystem/Resource/resHandle.h" +#include "KingSystem/Resource/resResourceMgrTask.h" +#include "KingSystem/Utils/Types.h" + +namespace sead { +class FileDevice; +} + +namespace ksys { +class OverlayArena; +} + +namespace ksys::aoc { + +// TODO +class Manager { + SEAD_SINGLETON_DISPOSER(Manager) + Manager(); + ~Manager(); + +public: + void init(sead::Heap* heap); + + u32 getVersion() const { return mVersion; } + + /// @return Whether the Master Trials DLC is supported. + bool hasAoc2() const { return mVersion >= 0x200; } + + /// @return Whether the Champion's Ballad DLC is supported. + bool hasAoc3() const { return mVersion >= 0x300; } + + void loadAocMainFieldPack(OverlayArena* arena); + void registerAocMainFieldPack(); + void unloadAocMainFieldPack(); + + sead::FileDevice* getFileDeviceForMapFile(const sead::SafeString& path) const; + bool getFileDeviceForMap(sead::FileDevice** p_file_device, res::Handle** p_handle, + const sead::SafeString& path); + sead::FileDevice* getFileDeviceForStaticCompound(const sead::SafeString& path) const; + sead::FileDevice* getFileDeviceForTeraMesh(const sead::SafeString& path) const; + sead::FileDevice* getFileDeviceForNavMesh(const sead::SafeString& path) const; + sead::FileDevice* getFileDeviceForTerrain(const sead::SafeString& path) const; + sead::FileDevice* getFileDeviceForGame(const sead::SafeString& path) const; + sead::FileDevice* getFileDeviceForUI(const sead::SafeString& path) const; + + bool isAocFile(const sead::SafeString& path, const sead::SafeString& dir, + const sead::SafeString& dir_aoc_field, + const sead::SafeString& dir_main_field_dungeon, + const sead::SafeString& dir_cdungeon) const; + static bool isAocDungeon(const sead::SafeString& map_name); + static bool isAocMap(const sead::SafeString& map_type, const sead::SafeString& map_name); + static bool isAocField(const sead::SafeString& map_type); + + sead::FileDevice* getFileDeviceForDungeonPack(const sead::SafeString& path) const; + void registerAocPack(res::Handle* pack); + bool aocPackHasFile(const sead::SafeString& path) const; + + bool changeMoviePath(sead::BufferedSafeString& path) const; + + void parseAocMainFieldStaticInfo(const al::ByamlIter& iter); + // TODO: figure out what this does + bool getSomePos(sead::Vector3f* translate, u32* p_mask, u32* mask); + + bool parseVersion(); + void setGameDataFlags() const; + +private: + SEAD_ENUM(GameDataFlag, AoCVerAtLastPlay ,LatestAoCVerPlayed ,HasAoCVer1 ,HasAoCVer2 ,HasAoCVer3) + SEAD_ENUM(VersionError, TooNew, TooOld) + + struct DLCPosition { + sead::Vector3f translate = sead::Vector3f::zero; + sead::Vector3f rotate = sead::Vector3f::zero; + sead::Vector3f translate2 = sead::Vector3f::zero; + float scale = 0.0; + gdt::FlagHandle flag_handle = gdt::InvalidHandle; + }; + KSYS_CHECK_SIZE_NX150(DLCPosition, 0x2c); + + struct VersionFile { + bool readVersion(); + + res::Handle file_handle; + sead::FixedSafeString<16> string; + }; + KSYS_CHECK_SIZE_NX150(VersionFile, 0x78); + + void loadVersionFile(); + void checkVersion(); + + void resetFlags(); + void initGameData(); + void reinitFlags(); + void onGdtReinit(gdt::Manager::ReinitEvent* event); + + sead::FileDevice* mFileDevice{}; + + res::FileDevicePrefix mVersionFileDevPrefix; + VersionFile mVersionFile{}; + u32 mVersion{}; + + res::Handle mAocMainFieldPack; + res::FileDevicePrefix mAocMainFieldPackPrefix; + res::Handle* mAocPack{}; + res::FileDevicePrefix mAocPackPrefix; + + sead::SafeArray mDLCPositions; + + gdt::FlagHandle mFlagAocVerAtLastPlay{}; + gdt::FlagHandle mFlagLatestAocVerPlayed{}; + gdt::FlagHandle mFlagHasAocVer1{}; + gdt::FlagHandle mFlagHasAocVer2{}; + gdt::FlagHandle mFlagHasAocVer3{}; + gdt::Manager::ReinitSignal::Slot mGdtReinitSlot; + + sead::BitFlag8 mVersionFlags{}; +#ifdef NNSDK + u8* mAocFsCache{}; +#endif +}; +KSYS_CHECK_SIZE_NX150(Manager, 0x598); + +} // namespace ksys::aoc diff --git a/src/KingSystem/CMakeLists.txt b/src/KingSystem/CMakeLists.txt index f1d8fddc..93a1d8db 100644 --- a/src/KingSystem/CMakeLists.txt +++ b/src/KingSystem/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(GameData) add_subdirectory(Resource) add_subdirectory(ActorSystem) +add_subdirectory(AOC) add_subdirectory(Camera) add_subdirectory(Chemical) add_subdirectory(Ecosystem) diff --git a/src/KingSystem/GameData/gdtManager.cpp b/src/KingSystem/GameData/gdtManager.cpp index c067801d..ff628407 100644 --- a/src/KingSystem/GameData/gdtManager.cpp +++ b/src/KingSystem/GameData/gdtManager.cpp @@ -8,7 +8,7 @@ #include #include