From 199c7d25526c2852a4b4a2bc4fb18a0fd2db6ad5 Mon Sep 17 00:00:00 2001 From: ThePixelGamer Date: Sun, 6 Feb 2022 13:16:17 -0600 Subject: [PATCH] ksys/map: Implement PlacementAreaMgr --- data/uking_functions.csv | 58 +- .../ActorSystem/LOD/actLodState.cpp | 5 +- src/KingSystem/Map/CMakeLists.txt | 2 + src/KingSystem/Map/mapLazyTraverseList.h | 2 + src/KingSystem/Map/mapPlacementActors.h | 10 +- src/KingSystem/Map/mapPlacementAreaMgr.cpp | 640 ++++++++++++++++++ src/KingSystem/Map/mapPlacementAreaMgr.h | 244 +++++++ src/KingSystem/Map/mapStagePreActorCache.h | 5 + src/KingSystem/Utils/MathUtil.h | 12 + 9 files changed, 939 insertions(+), 39 deletions(-) create mode 100644 src/KingSystem/Map/mapPlacementAreaMgr.cpp create mode 100644 src/KingSystem/Map/mapPlacementAreaMgr.h create mode 100644 src/KingSystem/Utils/MathUtil.h diff --git a/data/uking_functions.csv b/data/uking_functions.csv index 26e1fde1..a60f5cba 100644 --- a/data/uking_functions.csv +++ b/data/uking_functions.csv @@ -73108,35 +73108,35 @@ Address,Quality,Size,Name 0x0000007100d1c81c,U,000156,ActorQuestLink::x_2 0x0000007100d1c8b8,U,000004,nullsub_3740 0x0000007100d1c8bc,U,000004,j__ZdlPv_840 -0x0000007100d1c8c0,U,001536,PlacementStruct1::ctor -0x0000007100d1cec0,U,000012,PlacementStruct1::dtor -0x0000007100d1cecc,U,005804,PlacementStruct1::parseObj -0x0000007100d1e578,U,001080,PlacementStruct1::x -0x0000007100d1e9b0,U,000372,PlacementStruct1::x_1 -0x0000007100d1eb24,U,002484,PlacementStruct1::postPlaceActorsUpdateFlagsAndLazyTraverse -0x0000007100d1f4d8,U,000244,placement::getIsPlayedDemo145AndFirstInCastleBossRoom -0x0000007100d1f5cc,U,000360,PlacementStruct1::x_8 -0x0000007100d1f734,U,000460,PlacementStruct1::x_7 -0x0000007100d1f900,U,000460,PlacementStruct1::x_5 -0x0000007100d1facc,U,000460,PlacementStruct1::x_6 -0x0000007100d1fc98,U,000388,PlacementStruct1::x_10 -0x0000007100d1fe1c,U,000092, -0x0000007100d1fe78,U,003412,PlacementStruct1::shouldSkipSpawnOneHitChallengeActor -0x0000007100d20bcc,U,003772,PlacementStruct1::shouldSkipSpawn -0x0000007100d21a88,U,000436,PlacementStruct1::x_3 -0x0000007100d21c3c,U,000416,PlacementStruct1::x_2 -0x0000007100d21ddc,U,001820,PlacementStruct1::x_9 -0x0000007100d224f8,U,000436,PlacementStruct1::x_12 -0x0000007100d226ac,U,000700, -0x0000007100d22968,U,000724, -0x0000007100d22c3c,U,000700, -0x0000007100d22ef8,U,000724, -0x0000007100d231cc,U,001728,PlacementStruct1::x_11 -0x0000007100d2388c,U,002640,PlacementStruct1::x_4 -0x0000007100d242dc,U,000292,PlacementStruct1::x_13 -0x0000007100d24400,U,000156,PlacementStruct1::postPlaceActors1 -0x0000007100d2449c,U,000412,PlacementStruct1::x_0 -0x0000007100d24638,U,000684,sinitPlacementStruct1 +0x0000007100d1c8c0,W,001536,_ZN4ksys3map16PlacementAreaMgrC1Ev +0x0000007100d1cec0,O,000012,_ZN4ksys3map16PlacementAreaMgrD1Ev +0x0000007100d1cecc,W,005804,_ZN4ksys3map16PlacementAreaMgr10parseAreasERKN4sead14SafeStringBaseIcEERKNS0_9MubinIterE +0x0000007100d1e578,U,001080,_ZN4ksys3map16PlacementAreaMgr1xEv +0x0000007100d1e9b0,O,000372,_ZN4ksys3map16PlacementAreaMgr11addLinkPairERKiS3_ +0x0000007100d1eb24,U,002484,PlacementAreaMgr::postPlaceActorsUpdateFlagsAndLazyTraverse +0x0000007100d1f4d8,U,000244,PlacementAreaMgr::getIsPlayedDemo145AndFirstInCastleBossRoom +0x0000007100d1f5cc,O,000360,_ZN4ksys3map16PlacementAreaMgr20insideInnerHideTransERKi +0x0000007100d1f734,O,000460,_ZN4ksys3map16PlacementAreaMgr19insideInnerHideBaseERKi +0x0000007100d1f900,O,000460,_ZN4ksys3map16PlacementAreaMgr19insideInnerHideCalcERKi +0x0000007100d1facc,O,000460,_ZN4ksys3map16PlacementAreaMgr19insideInnerHideLoadERKi +0x0000007100d1fc98,O,000388,_ZN4ksys3map16PlacementAreaMgr15loadDemoCullingERKN4sead14SafeStringBaseIcEE +0x0000007100d1fe1c,O,000092,_ZN4ksys3map16PlacementAreaMgr17unloadDemoCullingEv +0x0000007100d1fe78,U,003412,PlacementAreaMgr::shouldSkipSpawnOneHitChallengeActor +0x0000007100d20bcc,U,003772,PlacementAreaMgr::shouldSkipSpawn +0x0000007100d21a88,O,000436,_ZN4ksys3map16PlacementAreaMgr19insideInnerHideBaseERKN4sead7Vector3IfEERKfRKi +0x0000007100d21c3c,O,000416,_ZN4ksys3map16PlacementAreaMgr17isPlayerInsideNpcERKN4sead7Vector3IfEE +0x0000007100d21ddc,U,001820,PlacementAreaMgr::boundsChecking +0x0000007100d224f8,O,000436,_ZN4ksys3map16PlacementAreaMgr19insideInnerHideCalcERKN4sead7Vector3IfEERKfRKi +0x0000007100d226ac,O,000700,_ZN4ksys3map16PlacementAreaMgr15insideAlphaBaseERKN4sead7Vector3IfEERKf +0x0000007100d22968,O,000724,_ZN4ksys3map16PlacementAreaMgr15insideOmegaBaseEv +0x0000007100d22c3c,O,000700,_ZN4ksys3map16PlacementAreaMgr15insideOmegaBaseERKN4sead7Vector3IfEERKf +0x0000007100d22ef8,O,000724,_ZN4ksys3map16PlacementAreaMgr15insideAlphaBaseEv +0x0000007100d231cc,U,001728,PlacementAreaMgr::boundsChecking_1 +0x0000007100d2388c,U,002640,PlacementAreaMgr::weirdSetup +0x0000007100d242dc,O,000292,_ZN4ksys3map16PlacementAreaMgr11isInsideNpcERKN4sead7Vector3IfEE +0x0000007100d24400,U,000156,PlacementAreaMgr::pushFarModels +0x0000007100d2449c,U,000412,PlacementAreaMgr::x_0 +0x0000007100d24638,U,000684,_GLOBAL__sub_I_mapPlacementAreaMgr.cpp 0x0000007100d248e4,O,000044,_ZN4ksys3act2ai8BehaviorC1ERKNS2_7InitArgE 0x0000007100d24910,U,000256,AI_BehaviorBase::init 0x0000007100d24a10,U,000176, diff --git a/src/KingSystem/ActorSystem/LOD/actLodState.cpp b/src/KingSystem/ActorSystem/LOD/actLodState.cpp index 3dff3e29..03f01885 100644 --- a/src/KingSystem/ActorSystem/LOD/actLodState.cpp +++ b/src/KingSystem/ActorSystem/LOD/actLodState.cpp @@ -3,6 +3,7 @@ #include "KingSystem/ActorSystem/actActorParam.h" #include "KingSystem/ActorSystem/actActorUtil.h" #include "KingSystem/Map/mapObject.h" +#include "KingSystem/Map/mapPlacementAreaMgr.h" #include "KingSystem/Map/mapPlacementMgr.h" #include "KingSystem/Resource/Actor/resResourceLod.h" #include "KingSystem/System/OcclusionQueryCylinder.h" @@ -156,10 +157,10 @@ LodState::LodState(sead::Heap* heap, sead::BitFlag32 flags, Actor* actor, } auto* pm = map::PlacementMgr::instance(); - map::PlacementStruct1* ps1 = nullptr; + map::PlacementAreaMgr* ps1 = nullptr; if (pm && pm->mPlacementActors && (ps1 = pm->mPlacementActors->mStruct1)) { if (!ps1->mIsOneHitChallengeActive) { - if (ps1->mFlags.isOnBit(15)) { + if (ps1->mFlags.isOn(map::PlacementAreaMgr::Flag::FinalTrial)) { if (actor->getName() == "DgnObj_DLC_IbutsuEx_Candle_A_01" || actor->getName() == "TBox_Dungeon_Stone" || actor->getName() == "DgnObj_DLC_SwordLight_A_01") { diff --git a/src/KingSystem/Map/CMakeLists.txt b/src/KingSystem/Map/CMakeLists.txt index 4c612d59..b201b701 100644 --- a/src/KingSystem/Map/CMakeLists.txt +++ b/src/KingSystem/Map/CMakeLists.txt @@ -14,6 +14,8 @@ target_sources(uking PRIVATE mapObjectLink.h mapPlacementActors.cpp mapPlacementActors.h + mapPlacementAreaMgr.cpp + mapPlacementAreaMgr.h mapPlacementMap.cpp mapPlacementMap.h mapPlacementMapMgr.cpp diff --git a/src/KingSystem/Map/mapLazyTraverseList.h b/src/KingSystem/Map/mapLazyTraverseList.h index 12afcd7e..99bb6dd4 100644 --- a/src/KingSystem/Map/mapLazyTraverseList.h +++ b/src/KingSystem/Map/mapLazyTraverseList.h @@ -15,6 +15,8 @@ public: void updateFlags(); bool wereFlagsUpdated(); + bool empty() const { return !mHasEntries; } + private: struct Entry { sead::FixedSafeString<0x40> flag_name; diff --git a/src/KingSystem/Map/mapPlacementActors.h b/src/KingSystem/Map/mapPlacementActors.h index 0f34a16e..8479eaed 100644 --- a/src/KingSystem/Map/mapPlacementActors.h +++ b/src/KingSystem/Map/mapPlacementActors.h @@ -13,6 +13,7 @@ namespace ksys::map { class Object; class PlacementMap; +class PlacementAreaMgr; // TODO: rename enum class ActorFlag8 { @@ -103,13 +104,6 @@ public: }; KSYS_CHECK_SIZE_NX150(ActorData, 0x1A0); -// FIXME: incomplete -class PlacementStruct1 { -public: - sead::BitFlag16 mFlags; - bool mIsOneHitChallengeActive; -}; - class PlacementActors { public: u32 getNumStaticObjs() const; @@ -124,7 +118,7 @@ public: u8 _0[0x28 - 0x0]; sead::ReadWriteLock mLock; - PlacementStruct1* mStruct1; + PlacementAreaMgr* mStruct1; u8 _e8[0x538 - 0xe8]; sead::SafeArray mActorData; u8 _261b38[0x2a8058 - 0x261b38]; diff --git a/src/KingSystem/Map/mapPlacementAreaMgr.cpp b/src/KingSystem/Map/mapPlacementAreaMgr.cpp new file mode 100644 index 00000000..295e5bfd --- /dev/null +++ b/src/KingSystem/Map/mapPlacementAreaMgr.cpp @@ -0,0 +1,640 @@ +#include "KingSystem/Map/mapPlacementAreaMgr.h" + +#include "KingSystem/Map/mapLazyTraverseList.h" +#include "KingSystem/Map/mapMubinIter.h" +#include "KingSystem/Map/mapObject.h" +#include "KingSystem/Map/mapObjectLink.h" +#include "KingSystem/Utils/MathUtil.h" + +#include + +namespace ksys::map { + +static s32 s_npc_count = 0; + +enum class UnkFlag : u8 { + _1 = 1 << 0, + _2 = 1 << 1, + _4 = 1 << 2, + _20 = 1 << 5, + _40 = 1 << 6, + _80 = 1 << 7 +}; +static sead::TypedBitFlag s_unk_flag; + +static sead::SafeArray s_npc_scales = { + {0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.85f, 0.6f, 0.75f, 0.6f, 0.6f, 0.6f, 0.6f, + 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.75f, 0.6f, 0.6f}}; + +// NON_MATCHING: weird memsets, hard to replicate +PlacementAreaMgr::PlacementAreaMgr() { + mInnerHideCount = 0; + mPlayerPos = sead::Vector3f::zero; + mActiveNpc = 0; + + if (mObjects && !mObjects->empty()) { + s_unk_flag.set(UnkFlag::_80); + } + + mJudgeAreaCount = 0; + _2a468Count = 0; + mGeneralAreasCount = 0; + _2d5c4 = 0.0f; + mOptAreasCount = 0; + mFarModelsCount = 0; + mIntroArea = nullptr; + mFlags.set(Flag::_10); +} + +PlacementAreaMgr::~PlacementAreaMgr() { + s_npc_count = 0; +} + +// NON_MATCHING: many various parts don't match but should be a template +bool PlacementAreaMgr::parseAreas(const sead::SafeString& unit_config_name, + const MubinIter& obj_iter) { + if (unit_config_name == "AreaCulling_InnerHide") { + auto& area = mInnerHide[mInnerHideCount++]; + SRT srt; + obj_iter.getSRT(&srt); + + bool hide = true; + obj_iter.tryGetParamBoolByKey(&hide, "HideOutSide"); + area.params.hide_out_side = hide; + + s32 culling = 0; + obj_iter.tryGetIntByKey(&culling, "CullingOption"); + switch (static_cast(culling)) { + case Unknown1::CullingOption::_0: + area.params.culling = Unknown1::CullingType::None; + break; + case Unknown1::CullingOption::_1: + area.params.culling = Unknown1::CullingType::Culling_1; + break; + case Unknown1::CullingOption::_2: + area.params.culling = Unknown1::CullingType::Culling_3; + break; + case Unknown1::CullingOption::_3: + area.params.culling = Unknown1::CullingType::Culling_F; + break; + case Unknown1::CullingOption::_4: + area.params.culling = Unknown1::CullingType::Culling_1F; + break; + case Unknown1::CullingOption::_5: + area.params.culling = Unknown1::CullingType::Culling_33; + break; + case Unknown1::CullingOption::_6: + area.params.culling = Unknown1::CullingType::Culling_3B; + break; + case Unknown1::CullingOption::_7: + area.params.culling = Unknown1::CullingType::Culling_1B; + break; + case Unknown1::CullingOption::_8: + area.params.culling = Unknown1::CullingType::Culling_18; + break; + case Unknown1::CullingOption::_9: + area.params.culling = Unknown1::CullingType::Culling_10; + break; + case Unknown1::CullingOption::_A: + area.params.culling = Unknown1::CullingType::Culling_13; + break; + case Unknown1::CullingOption::_B: + area.params.culling = Unknown1::CullingType::Culling_43; + break; + case Unknown1::CullingOption::_C: + area.params.culling = Unknown1::CullingType::Culling_9B; + break; + + default: + break; + } + + bool connect; + obj_iter.tryGetBoolByKey(&connect, "IsConnectNeighborArea"); + area.params.is_connect_neighbor_area = connect; + + area.hide_room_num = 0xFF; + obj_iter.tryGetParamUInt8ByKey(&area.hide_room_num, "HideRoomNum"); + + bool weirdCheck = false; + // is this some sort of hack? + if (srt.translate.x > -440.0f && srt.translate.x < -436.0f && srt.translate.z > -1036.0f && + srt.translate.z < -1032.0f) { + srt.scale.x = 27.863f; + srt.translate.x = -439.24f; + weirdCheck = true; + srt.translate.z = -1033.4f; + srt.scale.z = 40.908f; + } + + area.translate = srt.translate; + + float scale = sead::Mathf::max3(srt.scale.x, srt.scale.y, srt.scale.z); // same logic + + float calcMaxSize = sead::Mathf::max(scale * 0.5f, 4.0f); + float calcXPlus = -1.0f; + obj_iter.tryGetParamFloatByKey(&calcXPlus, "CalcMarginXPlus"); + float calcXMinus = -1.0f; + obj_iter.tryGetParamFloatByKey(&calcXMinus, "CalcMarginXMinus"); + float calcYPlus = -1.0f; + obj_iter.tryGetParamFloatByKey(&calcYPlus, "CalcMarginYPlus"); + float calcYMinus = -1.0f; + obj_iter.tryGetParamFloatByKey(&calcYMinus, "CalcMarginYMinus"); + float calcZPlus = -1.0f; + obj_iter.tryGetParamFloatByKey(&calcZPlus, "CalcMarginZPlus"); + float calcZMinus = -1.0f; + obj_iter.tryGetParamFloatByKey(&calcZMinus, "CalcMarginZMinus"); + + if (weirdCheck) { + calcXMinus = 3.0f; + } + + if (calcXPlus < 0.0f) { + calcXPlus = calcMaxSize; + } + if (calcXMinus < 0.0f) { + calcXMinus = calcMaxSize; + } + if (calcYPlus < 0.0f) { + calcYPlus = calcMaxSize; + } + if (calcYMinus < 0.0f) { + calcYMinus = calcMaxSize; + } + if (calcZPlus < 0.0f) { + calcZPlus = calcMaxSize; + } + if (calcZMinus < 0.0f) { + calcZMinus = calcMaxSize; + } + + float loadMaxSize = calcMaxSize * 1.75f; + float loadXPlus = -1.0f; + obj_iter.tryGetParamFloatByKey(&loadXPlus, "LoadMarginXPlus"); + float loadXMinus = -1.0f; + obj_iter.tryGetParamFloatByKey(&loadXMinus, "LoadMarginXMinus"); + float loadYPlus = -1.0f; + obj_iter.tryGetParamFloatByKey(&loadYPlus, "LoadMarginYPlus"); + float loadYMinus = -1.0f; + obj_iter.tryGetParamFloatByKey(&loadYMinus, "LoadMarginYMinus"); + float loadZPlus = -1.0f; + obj_iter.tryGetParamFloatByKey(&loadZPlus, "LoadMarginZPlus"); + float loadZMinus = -1.0f; + obj_iter.tryGetParamFloatByKey(&loadZMinus, "LoadMarginZMinus"); + + if (loadXPlus < 0.0f) { + loadXPlus = loadMaxSize; + } + if (loadXMinus < 0.0f) { + loadXMinus = loadMaxSize; + } + if (loadYPlus < 0.0f) { + loadYPlus = loadMaxSize; + } + if (loadYMinus < 0.0f) { + loadYMinus = loadMaxSize; + } + if (loadZPlus < 0.0f) { + loadZPlus = loadMaxSize; + } + if (loadZMinus < 0.0f) { + loadZMinus = loadMaxSize; + } + + // same code in AreaCulling_TwinsHide + area.base = Axis{srt.scale}; + area.calc = Axis{srt.scale + sead::Vector3f{calcXPlus, calcYPlus, calcZPlus}, + srt.scale + sead::Vector3f{calcXMinus, calcYMinus, calcZMinus}}; + area.load = Axis{srt.scale + sead::Vector3f{loadXPlus, loadYPlus, loadZPlus}, + srt.scale + sead::Vector3f{loadXMinus, loadYMinus, loadZMinus}}; + area._d8 = Axis{-sead::Vector3f::ones}; + + sead::Matrix33f m; + m.makeR(srt.rotate); + + for (size_t i = 0; i < 6; ++i) { + area.base[i] = srt.translate + (m * area.base[i]); + area.calc[i] = srt.translate + (m * area.calc[i]); + area.load[i] = srt.translate + (m * area.load[i]); + area._d8[i] = m * area._d8[i]; + } + + return true; + } else if (unit_config_name == "AreaCulling_OuterNPCMementary") { + if (s_npc_count > 25) { // this if statement has ++ but this structure makes more sense + return true; + } + + auto& area = mNpc[s_npc_count++]; + SRT srt; + + obj_iter.getSRT(&srt); + area.translate = srt.translate; + area.scale = srt.scale.x; + return true; + } else if (unit_config_name.include("AreaCulling_TwinsHide")) { // no objects + Unknown1* area; + if (unit_config_name.include("Alpha")) { + area = &mAlpha; // 2980C + } else if (unit_config_name.include("Omega")) { + area = &mOmega; // 29958 + } else { + return true; + } + + SRT srt; + obj_iter.getSRT(&srt); + area->translate = srt.translate; + + area->base = Axis{srt.scale}; + area->calc = Axis{srt.scale}; + area->load = Axis{-sead::Vector3f::ones}; + area->_d8 = Axis{-sead::Vector3f::ones}; + + sead::Matrix33f m; + m.makeR(srt.rotate); + + for (size_t i = 0; i < 6; ++i) { + area->base[i] = srt.translate + (m * area->base[i]); + area->calc[i] = srt.translate + (m * area->calc[i]); + area->load[i] = srt.translate + (m * area->load[i]); + area->_d8[i] = m * area->_d8[i]; + } + + s_unk_flag.set(UnkFlag::_40); + return true; + } else if (unit_config_name == "AreaCulling_JudgeArea") { // no objects + SRT srt; + auto& area = mJudgeArea[mJudgeAreaCount++]; + + obj_iter.getSRT(&srt); + area.bb.set(srt.translate - srt.scale, srt.translate + srt.scale); + + MubinIter links; + if (!obj_iter.tryGetParamIterByKey(&links, "LinksToObj")) { + return true; + } + + for (int i = 0; i < links.getSize(); ++i) { + MubinIter iter; + if (links.tryGetIterByIndex(&iter, i)) { + u32 _id = 0; + if (iter.tryGetParamUIntByKey(&_id, "DestUnitHashId")) { + area.parent_ids[area.parent_count++] = _id; + } + } + } + + return true; + } else if (unit_config_name.include("AreaCulling_")) { // only InnerOn? + auto& area = mGeneralAreas[mGeneralAreasCount++]; + SRT srt; + + obj_iter.getSRT(&srt); + area.bb.set(srt.translate - srt.scale, srt.translate + srt.scale); + obj_iter.tryGetParamUIntByKey(&area.id, "HashId"); + + area.type = GeneralArea::Type::None; + + const char* param = nullptr; + if (obj_iter.tryGetParamStringByKey(¶m, "UniqueName")) { + if (sead::SafeString(param) == "LoadOpt") { + area.type = GeneralArea::Type::LoadOpt; + } + } + + if (area.type == GeneralArea::Type::LoadOpt) { + auto& subArea = mOptAreas[mOptAreasCount++]; + subArea.parent_area = &area; + subArea.id = "Demo102_0"; + } else { + auto& subArea = mJudgeArea[mJudgeAreaCount++]; + sead::Vector3f offset{20.0f, 0.0f, 20.0f}; + subArea.bb.set(srt.translate - srt.scale - offset, srt.translate + srt.scale + offset); + subArea.parent_ids[subArea.parent_count++] = 0; + subArea.parent_areas[0] = &area; + } + + return true; + } else if (unit_config_name == "FarModelCullingArea") { + auto& area = mFarModels[mFarModelsCount++]; + SRT srt; + + obj_iter.getSRT(&srt); + area.translate = srt.translate; + area.rotate = srt.rotate; + area.scale = srt.scale; + area.id.format("Area_%04d", mFarModels.size() - 1); + + return true; + } + + return false; +} + +PlacementAreaMgr::GeneralArea* PlacementAreaMgr::findGeneralArea(const u32& id) { + for (int i = 0; i < mGeneralAreasCount; i++) { + if (mGeneralAreas[i].id == id) { + return &mGeneralAreas[i]; + } + } + + return nullptr; +} + +// NON_MATCHING: stack doesn't match, not sure if that's due to the first loop +void PlacementAreaMgr::x() { + s_unk_flag.set(UnkFlag::_20); + s_unk_flag.set(UnkFlag::_4); + s_unk_flag.set(UnkFlag::_2); + s_unk_flag.set(UnkFlag::_1); + + for (int i = 0; i < mInnerHideCount; i++) { + auto& area = mInnerHide[i]; + + area._12c = isInsideNpcIdx(area.translate); + area._130_count = 0; + } + + for (int i = 0; i < mInnerHideCount; i++) { + auto& area = mInnerHide[i]; + + if (area.hide_room_num == 0xFF) { + continue; + } + + for (int j = 0; j < mInnerHideCount; j++) { + auto& subarea = mInnerHide[j]; + + if (i != j) { + if (subarea.hide_room_num != 0xFF && area._12c == subarea._12c && + area.hide_room_num == subarea.hide_room_num) { + area._130[area._130_count++] = j; + break; + } + } + } + } + + for (int i = 0; i < mInnerHideCount; i++) { + auto& area = mInnerHide[i]; + + if (area.params.is_connect_neighbor_area) { + for (int j = 0; j < mInnerHideCount; j++) { + auto& subarea = mInnerHide[j]; + + if (subarea.params.is_connect_neighbor_area && i != j) { + if (area._12c == subarea._12c && (x_0(i, j) || x_0(j, i))) { + addLinkPair(i, j); + } + } + } + } + } + + mAlpha._12c = -1; + mOmega._12c = -1; + for (int i = 0; i < mJudgeAreaCount; i++) { + auto& judgeArea = mJudgeArea[i]; + for (int j = 0; j < judgeArea.parent_count; j++) { + auto& id = judgeArea.parent_ids[j]; + + if (id == 0) { + continue; + } + + // doesn't necessarily exist, done to try and match + judgeArea.parent_areas[j] = findGeneralArea(id); + } + } +} + +void PlacementAreaMgr::Unknown1::addLink(const int& idx) { + if (_130_count == 0) { + _130[_130_count++] = idx; + } else { + // check to see if idx is in _130, add to end if not + for (u8 i = 0; i < _130_count && _130[i] != idx; i++) { + if (i == (_130_count - 1)) { + _130[_130_count++] = idx; + break; + } + } + + // weird logic because this would only trigger if _130_count was 6 in the loop + if ((_130_count - 1) >= _130.size()) { + --_130_count; + } + } +} + +void PlacementAreaMgr::addLinkPair(const int& idx, const int& sub_idx) { + mInnerHide[idx].addLink(sub_idx); + mInnerHide[sub_idx].addLink(idx); +} + +bool PlacementAreaMgr::insideInnerHideTrans(const int& idx) { + if (mActiveNpc != mInnerHide[idx]._12c) { + return util::sqXZDistance(mPlayerPos, mInnerHide[idx].translate) < + sead::Mathf::square(1000.0f); + } + + if (mActiveNpc == 5) + return true; + + if (mActiveNpc == 6) { + return util::sqXZDistance(mPlayerPos, mInnerHide[idx].translate) < + sead::Mathf::square(500.0f); + } + + if (mFlags.isOn(Flag::FinalTrial) || mFlags.isOn(Flag::_4000)) { + return true; + } + + if (mFlags.isOff(Flag::Remains)) { + if (mFlags.isOn(Flag::AocField)) { + return true; + } + + if (mActiveNpc != 21 && mActiveNpc != 16 && mActiveNpc != 14 && mActiveNpc != 7 && + mActiveNpc != 2 && mActiveNpc != 0) { + return util::sqXZDistance(mPlayerPos, mInnerHide[idx].translate) < + sead::Mathf::square(200.0f); + } + } + + return util::sqXZDistance(mPlayerPos, mInnerHide[idx].translate) < sead::Mathf::square(500.0f); +} + +bool PlacementAreaMgr::insideInnerHideBase(const int& idx) { + for (int i = 0; i < 6; i++) { + if ((mPlayerPos - mInnerHide[idx].base[i]).dot(mInnerHide[idx]._d8[i]) < 0.0f) { + return false; + } + } + + return true; +} + +bool PlacementAreaMgr::insideInnerHideCalc(const int& idx) { + for (int i = 0; i < 6; i++) { + if ((mPlayerPos - mInnerHide[idx].calc[i]).dot(mInnerHide[idx]._d8[i]) < 0.0f) { + return false; + } + } + + return true; +} + +bool PlacementAreaMgr::insideInnerHideLoad(const int& idx) { + for (int i = 0; i < 6; i++) { + if ((mPlayerPos - mInnerHide[idx].load[i]).dot(mInnerHide[idx]._d8[i]) < 0.0f) { + return false; + } + } + + return true; +} + +bool PlacementAreaMgr::insideInnerHideBase(const sead::Vector3f& pos, const float& dist_from_face, + const int& idx) { + for (int i = 0; i < 6; i++) { + if ((pos - mInnerHide[idx].base[i]).dot(mInnerHide[idx]._d8[i]) <= dist_from_face) { + return false; + } + } + + return true; +} + +bool PlacementAreaMgr::insideInnerHideCalc(const sead::Vector3f& pos, const float& dist_from_face, + const int& idx) { + for (int i = 0; i < 6; i++) { + if ((pos - mInnerHide[idx].calc[i]).dot(mInnerHide[idx]._d8[i]) <= dist_from_face) { + return false; + } + } + + return true; +} + +bool PlacementAreaMgr::insideAlphaBase() { + for (int i = 0; i < 6; i++) { + if ((mPlayerPos - mAlpha.base[i]).dot(mAlpha._d8[i]) < 0.0f) { + return false; + } + } + + return true; +} + +bool PlacementAreaMgr::insideAlphaBase(const sead::Vector3f& pos, const float& dist_from_face) { + for (int i = 0; i < 6; i++) { + if ((pos - mAlpha.base[i]).dot(mAlpha._d8[i]) < dist_from_face) { + return false; + } + } + + return true; +} + +bool PlacementAreaMgr::insideOmegaBase() { + for (int i = 0; i < 6; i++) { + if ((mPlayerPos - mOmega.base[i]).dot(mOmega._d8[i]) < 0.0f) { + return false; + } + } + + return true; +} + +bool PlacementAreaMgr::insideOmegaBase(const sead::Vector3f& pos, const float& dist_from_face) { + for (int i = 0; i < 6; i++) { + if ((pos - mOmega.base[i]).dot(mOmega._d8[i]) < dist_from_face) { + return false; + } + } + + return true; +} + +bool PlacementAreaMgr::isPlayerInsideNpc(const sead::Vector3f& pos) { + // matches with 2 && but this looks cleaner + if (mActiveNpc >= 0 && !isInsideNpc(pos)) { + if (util::sqXZDistance(mPlayerPos, mNpc[mActiveNpc].translate) < + (mNpc[mActiveNpc].scale * s_npc_scales[mActiveNpc])) { + return true; + } + } + + return false; +} + +// used to try and simplify PlacementAreaMgr::x and PlacementAreaMgr::isInsideNpc logic +// doesn't seem to match so maybe remove after matching x? +s8 PlacementAreaMgr::isInsideNpcIdx(const sead::Vector3f& pos) { + for (int i = 0; i < mNpc.size(); i++) { + if (!mNpc[i].isInside(pos)) { + continue; + } + + if (i == 5) { + if (mNpc[6].isInside(pos)) { + continue; + } + } + + return i; + } + + return -1; +} + +bool PlacementAreaMgr::isInsideNpc(const sead::Vector3f& pos) { + if (s_unk_flag.isOff(UnkFlag::_4)) { + return false; + } + + // XXX: what the heck Nintendo + int i = 0; + while (i < mNpc.size()) { + if (i == 5) { + if (mNpc[i].isInside(pos) && !mNpc[i + 1].isInside(pos)) { + return true; + } + } else { + if (mNpc[i].isInside(pos)) { + return true; + } + } + + ++i; + } + + return false; +} + +void PlacementAreaMgr::loadDemoCulling(const sead::SafeString& demo_name) { + mIntroArea = nullptr; + + for (int i = 0; i < mOptAreasCount; ++i) { + auto& area = mOptAreas[i]; + + bool loaded = false; + if (area.id == demo_name) { + loaded = true; + mIntroArea = &area; + } + area.parent_area->loaded = loaded; + } +} + +void PlacementAreaMgr::unloadDemoCulling() { + mIntroArea = nullptr; + + for (int i = 0; i < mOptAreasCount; ++i) { + mOptAreas[i].parent_area->loaded = false; + } +} + +} // namespace ksys::map \ No newline at end of file diff --git a/src/KingSystem/Map/mapPlacementAreaMgr.h b/src/KingSystem/Map/mapPlacementAreaMgr.h new file mode 100644 index 00000000..17ddd0ad --- /dev/null +++ b/src/KingSystem/Map/mapPlacementAreaMgr.h @@ -0,0 +1,244 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "KingSystem/Map/mapStagePreActorCache.h" +#include "KingSystem/Map/mapTypes.h" +#include "KingSystem/Utils/Types.h" + +namespace ksys::gdt { +class Manager; +} + +namespace ksys::map { + +class LazyTraverseList; +class MubinIter; +class Object; + +class PlacementAreaMgr { + // not "Axis" and probably not in this class but I dunno what it does + // contains + and - of each x, y, z as a vector + class Axis { + public: + Axis() = default; + explicit Axis(const sead::Vector3f& v) : Axis(v, v) {} + + // parameter names only based on default values + Axis(const sead::Vector3f& plus, const sead::Vector3f& minus) { + data[0] = {0.0f - minus.x, 0.0f, 0.0f}; + data[1] = {0.0f + plus.x, 0.0f, 0.0f}; + data[2] = {0.0f, 0.0f, 0.0f - minus.z}; + data[3] = {0.0f, 0.0f, 0.0f + plus.z}; + data[4] = {0.0f, 0.0f - minus.y, 0.0f}; + data[5] = {0.0f, 0.0f + plus.y, 0.0f}; + } + + // needs an assert + sead::Vector3f& operator[](size_t idx) { return data[idx]; } + + const sead::Vector3f& operator[](size_t idx) const { return data[idx]; } + + private: + sead::Vector3f data[6]{}; + }; + + struct Unknown1 { // only params appears in PlacementAreaMgr's ctor + // unsure about these culling enums and the params bitfield + enum class CullingOption : s32 { _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _A, _B, _C }; + + enum class CullingType : u8 { + None, + Culling_1 = 1 << 0, + Culling_2 = 1 << 1, + Culling_4 = 1 << 2, + Culling_8 = 1 << 3, + Culling_10 = 1 << 4, + Culling_20 = 1 << 5, + Culling_40 = 1 << 6, + Culling_80 = 1 << 7, + + Culling_3 = Culling_2 | Culling_1, + Culling_B = Culling_8 | Culling_3, + Culling_F = Culling_8 | Culling_4 | Culling_3, + Culling_13 = Culling_10 | Culling_3, + Culling_18 = Culling_10 | Culling_8, + Culling_1B = Culling_10 | Culling_B, + Culling_1F = Culling_10 | Culling_F, + Culling_30 = Culling_20 | Culling_10, + Culling_33 = Culling_30 | Culling_3, + Culling_3B = Culling_30 | Culling_B, + Culling_43 = Culling_40 | Culling_3, + Culling_9B = Culling_80 | Culling_1B, + }; + + Axis base{}, calc{}, load{}, _d8{}; + sead::Vector3f translate{}; + s8 _12c; // something to do with _29C54/mActiveNpc + u8 hide_room_num; + u8 _130_count; + sead::SafeArray _130{}; // used to index mInnerHide, parent idx? + struct Params { // _148 : u16 + bool hide_out_side : 1; + bool is_connect_neighbor_area : 1; + bool _4 : 1; + bool _8 : 1; + bool _10 : 1; + bool _20 : 1; + CullingType culling : 8; + } params{}; + KSYS_CHECK_SIZE_NX150(Params, 0x2); + + // helpers + Unknown1() { memset(¶ms, 0, sizeof(params)); } + + void addLink(const int& idx); // unsure about name + }; + KSYS_CHECK_SIZE_NX150(Unknown1, 0x14C); + + struct OuterNPCMementary { + sead::Vector3f translate; + f32 scale; + + // should probably remove + bool isInside(const sead::Vector3f& pos) const { + return (pos - translate).squaredLength() < sead::Mathf::square(scale); + } + }; + KSYS_CHECK_SIZE_NX150(OuterNPCMementary, 0x10); + + struct GeneralArea { // InnerOn? + enum class Type : u8 { None = 0, LoadOpt = 2 }; + + sead::BoundBox3f bb{}; + u32 id; + Type type; + bool loaded; + }; + KSYS_CHECK_SIZE_NX150(GeneralArea, 0x20); + + struct JudgeArea { + sead::BoundBox3f bb{}; + sead::SafeArray parent_areas{}; + sead::SafeArray parent_ids{}; + s32 parent_count = 0; + }; + KSYS_CHECK_SIZE_NX150(JudgeArea, 0x80); + + struct OptArea { + GeneralArea* parent_area; + sead::FixedSafeString<16> id{}; + }; + KSYS_CHECK_SIZE_NX150(OptArea, 0x30); + + struct FarModel { + sead::Vector3f translate; + sead::Vector3f rotate; + sead::Vector3f scale; + sead::FixedSafeString<16> id{}; + }; + KSYS_CHECK_SIZE_NX150(FarModel, 0x50); + +public: + enum class Flag : u16 { + Remains = 1 << 0, + AocField = 1 << 1, + FireOrWindRemains = 1 << 2, + _8 = 1 << 3, + _10 = 1 << 4, + WindRelicFlag = 1 << 5, + FirstInCastleBossRoom = 1 << 6, + LastBoss = 1 << 7, + _100 = 1 << 8, + _200 = 1 << 9, + _400 = 1 << 10, + _800 = 1 << 11, + _1000 = 1 << 12, + _2000 = 1 << 13, + _4000 = 1 << 14, + FinalTrial = 1 << 15 + }; + + // d1f4d8 + static bool getIsPlayedDemo145AndFirstInCastleBossRoom(ksys::gdt::Manager& gdm, + bool* first_in_ganon_boss_room, + bool* played_demo_145); + + // d1c8c0 + PlacementAreaMgr(); + ~PlacementAreaMgr(); + + // d1cecc + bool parseAreas(const sead::SafeString& unit_config_name, const MubinIter& obj_iter); + // d1e578 + void x(); + void addLinkPair(const int& idx, const int& sub_idx); + // d1eb24 + void postPlaceActorsUpdateFlagsAndLazyTraverse(); + bool insideInnerHideTrans(const int& idx); + bool insideInnerHideBase(const int& idx); + bool insideInnerHideCalc(const int& idx); + bool insideInnerHideLoad(const int& idx); + void loadDemoCulling(const sead::SafeString& demo_name); + void unloadDemoCulling(); + // d1fe78 + bool shouldSkipSpawnOneHitChallengeActor(const Object& obj); + // d20bcc + bool shouldSkipSpawn(const Object& obj, bool a3); + bool insideInnerHideBase(const sead::Vector3f& pos, const float& dist_from_face, + const int& idx); + bool isPlayerInsideNpc(const sead::Vector3f& pos); + // d21ddc + void boundsChecking(); + bool insideInnerHideCalc(const sead::Vector3f& pos, const float& dist_from_face, + const int& idx); + bool insideAlphaBase(const sead::Vector3f& pos, const float& dist_from_face); + bool insideOmegaBase(); + bool insideOmegaBase(const sead::Vector3f& pos, const float& dist_from_face); + bool insideAlphaBase(); + // d231cc + void boundsChecking_1(); // looks eerily similar to boundsChecking + // d2388c + void weirdSetup(); // perhaps used with teleport feature? + bool isInsideNpc(const sead::Vector3f& pos); + // d24400 + void pushFarModels(); + // d2449c + bool x_0(const int& idx, const int& sub_idx); + + s8 isInsideNpcIdx(const sead::Vector3f& pos); + GeneralArea* findGeneralArea(const u32& id); + + sead::TypedBitFlag mFlags; + bool mIsOneHitChallengeActive = false; + bool mNotAocField = false; + bool _4 = false; + sead::SafeArray mInnerHide{}; // parseObj + s32 mInnerHideCount; + Unknown1 mAlpha{}, mOmega{}; // parseObj + sead::SafeArray mNpc{}; + sead::Vector3f mPlayerPos; + f32 mCameraAngleMaybe = 50.0f; // fov? + s32 mActiveNpc; // 0 - 25 + LazyTraverseList* mObjects = StagePreActorCache::instance()->getObjects(); + sead::SafeArray mJudgeArea{}; + s32 mJudgeAreaCount; + sead::SafeArray _2a468{}; // probably not char + s32 _2a468Count; + sead::SafeArray mGeneralAreas{}; + s32 mGeneralAreasCount; + sead::SafeArray mOptAreas{}; + s32 mOptAreasCount; + OptArea* mIntroArea; + sead::SafeArray mFarModels{}; + s32 mFarModelsCount; + f32 _2d5c4; // distance? + u8 _2d5c8 = 0; // enum potentially +}; +KSYS_CHECK_SIZE_NX150(PlacementAreaMgr, 0x2D5D0); + +} // namespace ksys::map \ No newline at end of file diff --git a/src/KingSystem/Map/mapStagePreActorCache.h b/src/KingSystem/Map/mapStagePreActorCache.h index 7d87c958..666779e8 100644 --- a/src/KingSystem/Map/mapStagePreActorCache.h +++ b/src/KingSystem/Map/mapStagePreActorCache.h @@ -9,15 +9,20 @@ class ForestRenderer; namespace ksys::map { +class LazyTraverseList; + // TODO class StagePreActorCache { SEAD_SINGLETON_DISPOSER(StagePreActorCache) StagePreActorCache(); public: + LazyTraverseList* getObjects() const { return mObjects; } auto* getForestRenderer() { return mForestRenderer; } private: + char _0[0x20]; + LazyTraverseList* mObjects; gfx::ForestRenderer* mForestRenderer; }; diff --git a/src/KingSystem/Utils/MathUtil.h b/src/KingSystem/Utils/MathUtil.h new file mode 100644 index 00000000..6ceb07f5 --- /dev/null +++ b/src/KingSystem/Utils/MathUtil.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace ksys::util { + +// too specific for sead +inline float sqXZDistance(const sead::Vector3f& a, const sead::Vector3f& b) { + return sead::Mathf::square(a.x - b.x) + sead::Mathf::square(a.z - b.z); +} + +} // namespace ksys::util \ No newline at end of file