summaryrefslogtreecommitdiff
path: root/src/KingSystem/Resource/Actor
diff options
context:
space:
mode:
authorLéo Lam <leo@leolam.fr>2021-06-15 17:47:03 +0200
committerLéo Lam <leo@leolam.fr>2021-06-15 18:04:39 +0200
commitdd0cc52ce99b9d78f1c7022c1bf13349149f263a (patch)
tree2eb5acd7d704d612181399bd8ab2be7b84fe450f /src/KingSystem/Resource/Actor
parent38e5e47b07696a0954145bf68148cb0301cf133c (diff)
ksys: Move ActorParam classes into separate folder to declutter Resource/
Diffstat (limited to 'src/KingSystem/Resource/Actor')
-rw-r--r--src/KingSystem/Resource/Actor/resResourceAIProgram.cpp464
-rw-r--r--src/KingSystem/Resource/Actor/resResourceAIProgram.h144
-rw-r--r--src/KingSystem/Resource/Actor/resResourceAISchedule.cpp9
-rw-r--r--src/KingSystem/Resource/Actor/resResourceAISchedule.h22
-rw-r--r--src/KingSystem/Resource/Actor/resResourceAS.cpp170
-rw-r--r--src/KingSystem/Resource/Actor/resResourceAS.h56
-rw-r--r--src/KingSystem/Resource/Actor/resResourceASList.cpp251
-rw-r--r--src/KingSystem/Resource/Actor/resResourceASList.h109
-rw-r--r--src/KingSystem/Resource/Actor/resResourceASResource.cpp375
-rw-r--r--src/KingSystem/Resource/Actor/resResourceASResource.h166
-rw-r--r--src/KingSystem/Resource/Actor/resResourceASResourceExtension.cpp339
-rw-r--r--src/KingSystem/Resource/Actor/resResourceASResourceExtension.h259
-rw-r--r--src/KingSystem/Resource/Actor/resResourceActorCapture.cpp61
-rw-r--r--src/KingSystem/Resource/Actor/resResourceActorCapture.h72
-rw-r--r--src/KingSystem/Resource/Actor/resResourceActorLink.cpp103
-rw-r--r--src/KingSystem/Resource/Actor/resResourceActorLink.h137
-rw-r--r--src/KingSystem/Resource/Actor/resResourceAnimInfo.cpp132
-rw-r--r--src/KingSystem/Resource/Actor/resResourceAnimInfo.h59
-rw-r--r--src/KingSystem/Resource/Actor/resResourceAttCheck.cpp200
-rw-r--r--src/KingSystem/Resource/Actor/resResourceAttCheck.h332
-rw-r--r--src/KingSystem/Resource/Actor/resResourceAttClient.cpp185
-rw-r--r--src/KingSystem/Resource/Actor/resResourceAttClient.h95
-rw-r--r--src/KingSystem/Resource/Actor/resResourceAttPos.cpp15
-rw-r--r--src/KingSystem/Resource/Actor/resResourceAttPos.h25
-rw-r--r--src/KingSystem/Resource/Actor/resResourceAwareness.cpp87
-rw-r--r--src/KingSystem/Resource/Actor/resResourceAwareness.h86
-rw-r--r--src/KingSystem/Resource/Actor/resResourceBoneControl.cpp239
-rw-r--r--src/KingSystem/Resource/Actor/resResourceBoneControl.h166
-rw-r--r--src/KingSystem/Resource/Actor/resResourceChemical.cpp24
-rw-r--r--src/KingSystem/Resource/Actor/resResourceChemical.h25
-rw-r--r--src/KingSystem/Resource/Actor/resResourceDamageParam.cpp255
-rw-r--r--src/KingSystem/Resource/Actor/resResourceDamageParam.h155
-rw-r--r--src/KingSystem/Resource/Actor/resResourceDrop.cpp173
-rw-r--r--src/KingSystem/Resource/Actor/resResourceDrop.h71
-rw-r--r--src/KingSystem/Resource/Actor/resResourceGParamList.cpp304
-rw-r--r--src/KingSystem/Resource/Actor/resResourceGParamList.h136
-rw-r--r--src/KingSystem/Resource/Actor/resResourceLifeCondition.cpp82
-rw-r--r--src/KingSystem/Resource/Actor/resResourceLifeCondition.h45
-rw-r--r--src/KingSystem/Resource/Actor/resResourceLod.cpp22
-rw-r--r--src/KingSystem/Resource/Actor/resResourceLod.h37
-rw-r--r--src/KingSystem/Resource/Actor/resResourceModelList.cpp364
-rw-r--r--src/KingSystem/Resource/Actor/resResourceModelList.h169
-rw-r--r--src/KingSystem/Resource/Actor/resResourcePhysics.cpp15
-rw-r--r--src/KingSystem/Resource/Actor/resResourcePhysics.h24
-rw-r--r--src/KingSystem/Resource/Actor/resResourceRagdollBlendWeight.cpp78
-rw-r--r--src/KingSystem/Resource/Actor/resResourceRagdollBlendWeight.h48
-rw-r--r--src/KingSystem/Resource/Actor/resResourceRagdollConfig.cpp30
-rw-r--r--src/KingSystem/Resource/Actor/resResourceRagdollConfig.h32
-rw-r--r--src/KingSystem/Resource/Actor/resResourceRagdollConfigList.cpp80
-rw-r--r--src/KingSystem/Resource/Actor/resResourceRagdollConfigList.h67
-rw-r--r--src/KingSystem/Resource/Actor/resResourceRecipe.cpp67
-rw-r--r--src/KingSystem/Resource/Actor/resResourceRecipe.h48
-rw-r--r--src/KingSystem/Resource/Actor/resResourceShop.cpp115
-rw-r--r--src/KingSystem/Resource/Actor/resResourceShop.h55
-rw-r--r--src/KingSystem/Resource/Actor/resResourceUMii.cpp15
-rw-r--r--src/KingSystem/Resource/Actor/resResourceUMii.h28
56 files changed, 6922 insertions, 0 deletions
diff --git a/src/KingSystem/Resource/Actor/resResourceAIProgram.cpp b/src/KingSystem/Resource/Actor/resResourceAIProgram.cpp
new file mode 100644
index 00000000..4900aa20
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceAIProgram.cpp
@@ -0,0 +1,464 @@
+#include "KingSystem/Resource/Actor/resResourceAIProgram.h"
+#include <agl/Utils/aglParameter.h>
+#include <heap/seadHeapMgr.h>
+#include "KingSystem/ActorSystem/actAiActionBase.h"
+#include "KingSystem/ActorSystem/actAiClassDef.h"
+#include "KingSystem/Resource/resCurrentResNameMgr.h"
+#include "KingSystem/Utils/HeapUtil.h"
+
+namespace ksys::res {
+
+AIProgram::~AIProgram() = default;
+
+const sead::Buffer<AIProgram::AIActionDef>&
+AIProgram::getActionsOrAIs(act::ai::ActionType type) const {
+ return type == act::ai::ActionType::AI ? mAIs : mActions;
+}
+
+void AIProgram::doCreate_(u8*, u32, sead::Heap*) {
+ mStr = CurrentResNameMgr::instance()->getCurrentResName();
+}
+
+static bool parseAIActionIdx(agl::utl::ResParameterObj obj, sead::Buffer<u16>& buffer,
+ sead::Heap* heap, bool clear = false) {
+ if (obj.ptr() == nullptr)
+ return true;
+
+ const auto num = obj.getNum();
+ if (num == 0)
+ return true;
+
+ if (!buffer.tryAllocBuffer(num, heap))
+ return false;
+
+ if (clear) {
+ for (s32 i = 0; i < num; ++i)
+ buffer(i) = 0;
+ }
+
+ auto it = buffer.begin(), it_end = buffer.end();
+ auto it_res = obj.begin(), it_res_end = obj.end();
+ auto* res_ptr = it_res.getParam().ptr();
+ for (; it != it_end && it_res != it_res_end; ++it, ++it_res)
+ *it = *agl::utl::ResParameter{res_ptr + it.getIndex()}.getData<s32>();
+
+ return true;
+}
+
+static bool parseBehaviorIdx(agl::utl::ResParameterObj obj, sead::Buffer<u8>& buffer,
+ sead::Heap* heap) {
+ if (obj.ptr() == nullptr)
+ return true;
+
+ const auto num = obj.getNum();
+ if (num == 0)
+ return true;
+
+ if (!buffer.tryAllocBuffer(num, heap))
+ return false;
+
+ for (s32 i = 0; i < num; ++i)
+ buffer(i) = 0;
+
+ auto it = buffer.begin(), it_end = buffer.end();
+ auto it_res = obj.begin(), it_res_end = obj.end();
+ for (; it != it_end && it_res != it_res_end; ++it, ++it_res)
+ *it = *it_res.getParam().getData<s32>();
+
+ return true;
+}
+
+// NON_MATCHING: the parameter iteration loops in parseAIActionIdx and parseBehaviorIdx
+bool AIProgram::parse_(u8* data, size_t, sead::Heap* parent_heap) {
+ if (data) {
+ auto* heap = util::tryCreateDualHeap(parent_heap);
+ mHeap = heap;
+ if (!heap)
+ return false;
+
+ heap->enableWarning(false);
+ heap = mHeap;
+
+ agl::utl::ResParameterArchive archive{data};
+ const auto root = archive.getRootList();
+
+ if (!parseAIActions(mAIs, heap, mParamListAI, root, "AI") ||
+ !parseAIActions(mActions, heap, mParamListAction, root, "Action") ||
+ !parseBehaviors(heap, root) || !parseQueries(heap, root)) {
+ return false;
+ }
+
+ const auto ai_idx_obj = agl::utl::getResParameterObj(root, "DemoAIActionIdx");
+ if (!parseAIActionIdx(ai_idx_obj, mDemoAIActionIndices, heap)) {
+ mHeap->adjust();
+ return false;
+ }
+
+ const auto behavior_idx_obj = agl::utl::getResParameterObj(root, "DemoBehaviorIdx");
+ if (!parseBehaviorIdx(behavior_idx_obj, mDemoBehaviorIndices, heap)) {
+ mHeap->adjust();
+ return false;
+ }
+
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+ }
+
+ mHeap->adjust();
+ return true;
+}
+
+// NON_MATCHING: the parameter iteration loops in parseAIActionIdx and parseBehaviorIdx
+bool AIProgram::parseAIActions(sead::Buffer<AIActionDef>& defs, sead::Heap* heap,
+ agl::utl::ParameterList& target_list,
+ const agl::utl::ResParameterList& root, const char* type_name) {
+ const auto list = agl::utl::getResParameterList(root, type_name);
+ if (!list.ptr())
+ return false;
+
+ const auto num = list.getResParameterListNum();
+ if (num == 0)
+ return true;
+
+ if (!defs.tryAllocBuffer(num, heap))
+ return false;
+
+ for (auto& action : defs) {
+ action.mClassName = "";
+ action.mName = "";
+ action.mGroupName = "";
+ }
+
+ auto it_res = list.listBegin();
+ const auto it_res_end = list.listEnd();
+
+ sead::FixedSafeString<32> list_name{type_name};
+ list_name.append("_");
+ const s32 trim_length = list_name.calcLength();
+
+ auto it = defs.begin();
+ const auto it_end = defs.end();
+ for (; it != it_end && it_res != it_res_end; ++it, ++it_res) {
+ list_name.trim(trim_length);
+ list_name.appendWithFormat("%d", it.getIndex());
+ target_list.addList(&it->mList, list_name);
+
+ const auto res = *it_res;
+ const auto def_obj = agl::utl::getResParameterObj(res, "Def");
+ if (def_obj.ptr()) {
+ it->mName = agl::utl::getResParameter(def_obj, "Name").getData<char>();
+ it->mClassName = agl::utl::getResParameter(def_obj, "ClassName").getData<char>();
+
+ const auto group_name = agl::utl::getResParameter(def_obj, "GroupName");
+ if (group_name.ptr())
+ it->mGroupName = group_name.getData<char>();
+ else
+ it->mGroupName = "";
+ }
+
+ const auto child_idx_obj = agl::utl::getResParameterObj(res, "ChildIdx");
+ if (!parseAIActionIdx(child_idx_obj, it->mChildIndices, heap, true))
+ return false;
+
+ const auto behavior_idx_obj = agl::utl::getResParameterObj(res, "BehaviorIdx");
+ if (!parseBehaviorIdx(behavior_idx_obj, it->mBehaviorIndices, heap))
+ return false;
+
+ if (!parseDefParams(&*it, &defs, heap, res, &it->mTriggerAction, &it->mDynamicParamChild))
+ return false;
+ }
+
+ addList(&target_list, type_name);
+ return true;
+}
+
+bool AIProgram::parseBehaviors(sead::Heap* heap, const agl::utl::ResParameterList& root) {
+ const auto list = agl::utl::getResParameterList(root, "Behavior");
+ if (!list.ptr())
+ return true;
+
+ const auto num = list.getResParameterListNum();
+ if (num == 0)
+ return true;
+
+ if (!mBehaviors.tryAllocBuffer(num, heap))
+ return false;
+
+ for (auto& behavior : mBehaviors) {
+ behavior.mClassName = "";
+ behavior.mName = "";
+ }
+
+ auto it_res = list.listBegin();
+ const auto it_res_end = list.listEnd();
+
+ sead::FixedSafeString<32> list_name{"Behavior_"};
+ const s32 trim_length = list_name.calcLength();
+
+ auto it = mBehaviors.begin();
+ const auto it_end = mBehaviors.end();
+ for (; it != it_end && it_res != it_res_end; ++it, ++it_res) {
+ list_name.trim(trim_length);
+ list_name.appendWithFormat("%d", it.getIndex());
+ mParamListBehavior.addList(&it->mList, list_name);
+
+ const auto res = *it_res;
+ const auto obj = agl::utl::getResParameterObj(res, "Def");
+ if (obj.ptr()) {
+ const auto name_param = agl::utl::getResParameter(obj, "ClassName");
+ it->mClassName = name_param.getData<char>();
+ }
+
+ if (!parseDefParams(&*it, &mBehaviors, heap, res, &it->mCalcTiming, &it->mNoStop))
+ return false;
+ }
+
+ addList(&mParamListBehavior, "Behavior");
+ return true;
+}
+
+bool AIProgram::parseQueries(sead::Heap* heap, const agl::utl::ResParameterList& root) {
+ const auto list = agl::utl::getResParameterList(root, "Query");
+ if (!list.ptr())
+ return true;
+
+ const auto num = list.getResParameterListNum();
+ if (num == 0)
+ return true;
+
+ if (!mQueries.tryAllocBuffer(num, heap))
+ return false;
+
+ for (auto& query : mQueries) {
+ query.mClassName = "";
+ query.mName = "";
+ }
+
+ auto it_res = list.listBegin();
+ const auto it_res_end = list.listEnd();
+
+ sead::FixedSafeString<32> list_name{"Query_"};
+ const s32 trim_length = list_name.calcLength();
+
+ auto it = mQueries.begin();
+ const auto it_end = mQueries.end();
+ for (; it != it_end && it_res != it_res_end; ++it, ++it_res) {
+ list_name.trim(trim_length);
+ list_name.appendWithFormat("%d", it.getIndex());
+ mParamListQuery.addList(&it->mList, list_name);
+
+ const auto res = *it_res;
+ const auto obj = agl::utl::getResParameterObj(res, "Def");
+ if (obj.ptr()) {
+ const auto name_param = agl::utl::getResParameter(obj, "ClassName");
+ it->mClassName = name_param.getData<char>();
+ }
+
+ if (!parseDefParams(&*it, &mQueries, heap, res, nullptr, nullptr))
+ return false;
+ }
+
+ addList(&mParamListQuery, "Query");
+ return true;
+}
+
+void AIProgram::finalize_() {
+ {
+ sead::ScopedCurrentHeapSetter setter{mHeap};
+
+ finalizeAIActions(mAIs);
+ finalizeAIActions(mActions);
+ finalizeBehaviors();
+ finalizeQueries();
+ mDemoBehaviorIndices.freeBuffer();
+ mDemoAIActionIndices.freeBuffer();
+ }
+
+ if (mHeap) {
+ mHeap->destroy();
+ mHeap = nullptr;
+ }
+}
+
+void AIProgram::Definition::finalize_() {
+ for (auto*& param : mSInstParams) {
+ if (param) {
+ delete param;
+ param = nullptr;
+ }
+ }
+ mSInstParams.freeBuffer();
+}
+
+void AIProgram::AIActionDef::finalize_() {
+ Definition::finalize_();
+ mChildIndices.freeBuffer();
+ mBehaviorIndices.freeBuffer();
+}
+
+void AIProgram::finalizeAIActions(sead::Buffer<AIActionDef>& defs) {
+ for (auto& def : defs)
+ def.finalize_();
+ defs.freeBuffer();
+}
+
+void AIProgram::finalizeBehaviors() {
+ for (auto& def : mBehaviors)
+ def.finalize_();
+ mBehaviors.freeBuffer();
+}
+
+void AIProgram::finalizeQueries() {
+ for (auto& def : mQueries)
+ def.finalize_();
+ mQueries.freeBuffer();
+}
+
+const agl::utl::ParameterBase* AIProgram::Definition::findSInstParam(u32 name_hash) const {
+ for (const auto* param : mSInstParams) {
+ if (param && param->getNameHash() == name_hash)
+ return param;
+ }
+ return nullptr;
+}
+
+const agl::utl::ParameterBase*
+AIProgram::Definition::findSInstParam(const sead::SafeString& name) const {
+ return findSInstParam(agl::utl::ParameterBase::calcHash(name));
+}
+
+bool AIProgram::getSInstParam(const char** value, const AIProgram::Definition& def,
+ const sead::SafeString& param_name) const {
+ const auto* param = def.findSInstParam(param_name);
+ if (!param || param->getParameterType() != agl::utl::ParameterType::StringRef) {
+ *value = &sead::SafeString::cNullChar;
+ return false;
+ }
+ *value = param->ptrT<char>();
+ return true;
+}
+
+bool AIProgram::getSInstParam(sead::SafeString* value, const AIProgram::Definition& def,
+ const sead::SafeString& param_name) const {
+ const auto* param = def.findSInstParam(param_name);
+ if (!param || param->getParameterType() != agl::utl::ParameterType::StringRef) {
+ *value = sead::SafeString::cEmptyString;
+ return false;
+ }
+ *value = param->ptrT<char>();
+ return true;
+}
+
+bool AIProgram::getSInstParam(const s32** value, const AIProgram::Definition& def,
+ const sead::SafeString& param_name) const {
+ static const s32 sDefault{};
+ return getSInstParam_(value, def, param_name, agl::utl::ParameterType::Int, &sDefault);
+}
+
+bool AIProgram::getSInstParam(const f32** value, const AIProgram::Definition& def,
+ const sead::SafeString& param_name) const {
+ static const f32 sDefault{};
+ return getSInstParam_(value, def, param_name, agl::utl::ParameterType::F32, &sDefault);
+}
+
+bool AIProgram::getSInstParam(const sead::Vector3f** value, const AIProgram::Definition& def,
+ const sead::SafeString& param_name) const {
+ return getSInstParam_(value, def, param_name, agl::utl::ParameterType::Vec3,
+ &sead::Vector3f::zero);
+}
+
+bool AIProgram::getSInstParam(const bool** value, const AIProgram::Definition& def,
+ const sead::SafeString& param_name) const {
+ static const bool sDefault{};
+ return getSInstParam_(value, def, param_name, agl::utl::ParameterType::Bool, &sDefault);
+}
+
+bool AIProgram::parseDefParams(AIProgram::Definition* def, void* buffer, sead::Heap* heap,
+ const agl::utl::ResParameterList& res, u16* param1, u16* param2) {
+ const auto sinst_obj = agl::utl::getResParameterObj(res, "SInst");
+ const s32 sinst_num_params = sinst_obj.ptr() ? sinst_obj.getNum() : 0;
+
+ AIDef aidef;
+
+ if (&mAIs == buffer) {
+ AIClassDef::instance()->getDef(&aidef, def->mClassName, AIDefInstParamKind::Static,
+ AIDefType::AI);
+ *param1 = aidef.trigger_action;
+ *param2 = aidef.dynamic_param_child;
+ } else if (&mActions == buffer) {
+ AIClassDef::instance()->getDef(&aidef, def->mClassName, AIDefInstParamKind::Static,
+ AIDefType::Action);
+ *param1 = aidef.trigger_action;
+ *param2 = 0;
+ } else if (&mBehaviors == buffer) {
+ AIClassDef::instance()->getDef(&aidef, def->mClassName, AIDefInstParamKind::Static,
+ AIDefType::Behavior);
+ *param1 = u16(aidef.calc_timing);
+ *param2 = aidef.no_stop;
+ } else {
+ AIClassDef::instance()->getDef(&aidef, def->mClassName, AIDefInstParamKind::Static,
+ AIDefType::Query);
+ }
+
+ if (sinst_num_params != 0) {
+ const auto num_params =
+ aidef.num_params < sinst_num_params ? aidef.num_params : sinst_num_params;
+
+ if (!def->mSInstParams.tryAllocBuffer(sinst_num_params, heap))
+ return false;
+
+ for (s32 i = 0; i < sinst_num_params; ++i)
+ def->mSInstParams[i] = nullptr;
+
+ for (s32 i = 0; i < num_params; ++i) {
+ const char* name = aidef.param_names[i];
+ switch (aidef.param_types[i]) {
+ case AIDefParamType::String:
+ case AIDefParamType::Tree:
+ if (!def->addSInstParam_<sead::SafeString>(i, name, heap, ""))
+ return false;
+ break;
+ case AIDefParamType::UInt:
+ if (!def->addSInstParam_<u32>(i, name, heap, 0))
+ return false;
+ break;
+ case AIDefParamType::Int:
+ if (!def->addSInstParam_<s32>(i, name, heap, 0))
+ return false;
+ break;
+ case AIDefParamType::Float:
+ if (!def->addSInstParam_<f32>(i, name, heap, 0))
+ return false;
+ break;
+ case AIDefParamType::Vec3:
+ if (!def->addSInstParam_<sead::Vector3f>(i, name, heap, sead::Vector3f::zero))
+ return false;
+ break;
+ case AIDefParamType::Bool:
+ if (!def->addSInstParam_<bool>(i, name, heap, false))
+ return false;
+ break;
+ default:
+ def->mSInstParams[i] = nullptr;
+ break;
+ }
+ }
+ }
+
+ def->mList.addObj(&def->mSInstObj, "SInst");
+ return true;
+}
+
+template <typename T>
+bool AIProgram::Definition::addSInstParam_(s32 idx, const char* name, sead::Heap* heap,
+ const T& value) {
+ mSInstParams[idx] = new (heap) agl::utl::Parameter<T>;
+
+ auto* param = static_cast<agl::utl::Parameter<T>*>(mSInstParams[idx]);
+ if (!param)
+ return false;
+
+ param->initializeParameter(value, name, name, &mSInstObj);
+ return true;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceAIProgram.h b/src/KingSystem/Resource/Actor/resResourceAIProgram.h
new file mode 100644
index 00000000..c7a646bf
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceAIProgram.h
@@ -0,0 +1,144 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterList.h>
+#include <agl/Utils/aglParameterObj.h>
+#include <container/seadBuffer.h>
+#include <math/seadVector.h>
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+#include "KingSystem/Utils/Types.h"
+
+namespace ksys::act::ai {
+enum class ActionType : int;
+}
+
+namespace ksys::res {
+
+class AIProgram : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(AIProgram, Resource)
+public:
+ struct Definition {
+ const agl::utl::ParameterBase* findSInstParam(u32 name_hash) const;
+ const agl::utl::ParameterBase* findSInstParam(const sead::SafeString& name) const;
+
+ template <typename T>
+ bool addSInstParam_(s32 idx, const char* name, sead::Heap* heap, const T& value);
+
+ void finalize_();
+
+ agl::utl::ParameterList mList;
+ const char* mClassName;
+ const char* mName;
+ sead::Buffer<agl::utl::ParameterBase*> mSInstParams;
+ agl::utl::ParameterObj mSInstObj;
+ };
+ KSYS_CHECK_SIZE_NX150(Definition, 0x98);
+
+ struct AIActionDef : Definition {
+ void finalize_();
+
+ const char* mGroupName;
+ sead::Buffer<u16> mChildIndices;
+ sead::Buffer<u8> mBehaviorIndices;
+ u16 mTriggerAction;
+ u16 mDynamicParamChild;
+ u16 _c4;
+ };
+ KSYS_CHECK_SIZE_NX150(AIActionDef, 0xc8);
+
+ struct BehaviorDef : Definition {
+ u16 mCalcTiming;
+ u16 mNoStop;
+ };
+ KSYS_CHECK_SIZE_NX150(BehaviorDef, 0xa0);
+
+ struct QueryDef : Definition {};
+ KSYS_CHECK_SIZE_NX150(QueryDef, 0x98);
+
+ AIProgram() : ParamIO("aiprog", 0) {}
+ ~AIProgram() override;
+
+ const sead::Buffer<AIActionDef>& getActionsOrAIs(act::ai::ActionType type) const;
+ const sead::Buffer<BehaviorDef>& getBehaviors() const { return mBehaviors; }
+ const sead::Buffer<QueryDef>& getQueries() const { return mQueries; }
+
+ const AIActionDef& getAI(s32 index) const { return mAIs[index]; }
+ const sead::Buffer<AIActionDef>& getAIs() const { return mAIs; }
+ const sead::Buffer<AIActionDef>& getActions() const { return mActions; }
+
+ const AIActionDef& getAction(act::ai::ActionType type, s32 index) const {
+ return getActionsOrAIs(type)[index];
+ }
+
+ const sead::Buffer<u16>& getDemoAiActionIndices() const { return mDemoAIActionIndices; }
+ const sead::Buffer<u8>& getDemoBehaviorIndices() const { return mDemoBehaviorIndices; }
+
+ bool getSInstParam(const char** value, const Definition& def,
+ const sead::SafeString& param_name) const;
+ bool getSInstParam(sead::SafeString* value, const Definition& def,
+ const sead::SafeString& param_name) const;
+ bool getSInstParam(const s32** value, const Definition& def,
+ const sead::SafeString& param_name) const;
+ bool getSInstParam(const f32** value, const Definition& def,
+ const sead::SafeString& param_name) const;
+ bool getSInstParam(const sead::Vector3f** value, const Definition& def,
+ const sead::SafeString& param_name) const;
+ bool getSInstParam(const bool** value, const Definition& def,
+ const sead::SafeString& param_name) const;
+
+ void doCreate_(u8* buffer, u32 bufferSize, sead::Heap* heap) override;
+ bool needsParse() const override { return true; }
+
+private:
+ bool parse_(u8* data, size_t size, sead::Heap* parent_heap) override;
+ bool parseAIActions(sead::Buffer<AIActionDef>& defs, sead::Heap* heap,
+ agl::utl::ParameterList& target_list,
+ const agl::utl::ResParameterList& root, const char* type_name);
+ bool parseBehaviors(sead::Heap* heap, const agl::utl::ResParameterList& root);
+ bool parseQueries(sead::Heap* heap, const agl::utl::ResParameterList& root);
+ bool parseDefParams(Definition* def, void* buffer, sead::Heap* heap,
+ const agl::utl::ResParameterList& res, u16* param1, u16* param2);
+
+ void finalize_() override;
+ void finalizeAIActions(sead::Buffer<AIActionDef>& defs);
+ void finalizeBehaviors();
+ void finalizeQueries();
+
+ template <typename T>
+ bool getSInstParam_(const T** value, const Definition& def, const sead::SafeString& param_name,
+ agl::utl::ParameterType param_type, const T* default_value) const;
+
+ sead::Heap* mHeap = nullptr;
+ sead::SafeString mStr;
+
+ sead::Buffer<AIActionDef> mAIs;
+ sead::Buffer<AIActionDef> mActions;
+ sead::Buffer<BehaviorDef> mBehaviors;
+ sead::Buffer<QueryDef> mQueries;
+
+ agl::utl::ParameterList mParamListAI;
+ agl::utl::ParameterList mParamListAction;
+ agl::utl::ParameterList mParamListBehavior;
+ agl::utl::ParameterList mParamListQuery;
+
+ sead::Buffer<u16> mDemoAIActionIndices;
+ sead::Buffer<u8> mDemoBehaviorIndices;
+};
+KSYS_CHECK_SIZE_NX150(AIProgram, 0x448);
+
+template <typename T>
+inline bool AIProgram::getSInstParam_(const T** value, const AIProgram::Definition& def,
+ const sead::SafeString& param_name,
+ agl::utl::ParameterType param_type,
+ const T* default_value) const {
+ const auto* param = def.findSInstParam(param_name);
+ if (!param || param->getParameterType() != param_type) {
+ *value = default_value;
+ return false;
+ }
+ *value = param->ptrT<T>();
+ return true;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceAISchedule.cpp b/src/KingSystem/Resource/Actor/resResourceAISchedule.cpp
new file mode 100644
index 00000000..6f812de1
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceAISchedule.cpp
@@ -0,0 +1,9 @@
+#include "KingSystem/Resource/Actor/resResourceAISchedule.h"
+
+namespace ksys::res {
+
+void AISchedule::doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) {
+ mData = buffer;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceAISchedule.h b/src/KingSystem/Resource/Actor/resResourceAISchedule.h
new file mode 100644
index 00000000..d7de2803
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceAISchedule.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+
+namespace ksys::res {
+
+class AISchedule : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(AISchedule, Resource)
+public:
+ AISchedule() : ParamIO("baischedule", 0) {}
+
+ void doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) override;
+ bool needsParse() const override { return false; }
+
+ u8* getData() const { return mData; }
+
+private:
+ u8* mData{};
+};
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceAS.cpp b/src/KingSystem/Resource/Actor/resResourceAS.cpp
new file mode 100644
index 00000000..2453d491
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceAS.cpp
@@ -0,0 +1,170 @@
+#include "KingSystem/Resource/Actor/resResourceAS.h"
+#include <memory>
+#include <random/seadGlobalRandom.h>
+#include "KingSystem/Utils/HeapUtil.h"
+#include "KingSystem/Utils/SafeDelete.h"
+#include "resResourceASResource.h"
+
+namespace ksys::res {
+
+namespace {
+sead::SafeString str_Elements = "Elements";
+sead::SafeString str_CommonParams = "CommonParams";
+} // namespace
+
+AS::AS() : ParamIO("as", 0) {}
+
+AS::~AS() = default;
+
+void AS::doCreate_(u8*, u32, sead::Heap*) {}
+
+// NON_MATCHING: SafeString vtable load is reordered
+bool AS::parse_(u8* data, size_t size, sead::Heap* parent_heap) {
+ mHeap = util::tryCreateDualHeap(parent_heap);
+ if (!mHeap)
+ return false;
+
+ mHeap->enableWarning(false);
+ auto* heap = mHeap;
+
+ agl::utl::ResParameterArchive archive{data};
+ const auto root = archive.getRootList();
+
+ const auto Elements = agl::utl::getResParameterList(root, str_Elements);
+ if (!Elements) {
+ mHeap->adjust();
+ return true;
+ }
+
+ const int num_elements = Elements.getResParameterListNum();
+ if (num_elements == 0) {
+ mHeap->adjust();
+ return true;
+ }
+
+ if (!mElementResources.tryAllocBuffer(num_elements, heap)) {
+ mHeap->adjust();
+ return false;
+ }
+
+ for (int i = 0, n = mElementResources.size(); i < n; ++i)
+ mElementResources(i) = nullptr;
+
+ ASResource::ParseArgs args;
+ args.list = {};
+ args.heap = heap;
+ args.as = this;
+ args.index = 0;
+
+ auto res_it = Elements.listBegin();
+ const auto res_end = Elements.listEnd();
+
+ sead::FixedSafeString<16> name{"Element"};
+ const auto name_prefix_len = name.calcLength();
+
+ for (auto it = mElementResources.begin(), end = mElementResources.end();
+ it != end && res_it != res_end; ++it, ++res_it) {
+ args.list = res_it.getList();
+ *it = ASResource::make(args);
+ if (*it == nullptr) {
+ mHeap->adjust();
+ return false;
+ }
+
+ name.trim(name_prefix_len);
+ name.appendWithFormat("%d", args.index);
+ mElementsList.addList(&(*it)->getList(), name);
+ ++args.index;
+ }
+
+ addList(&mElementsList, str_Elements);
+
+ mRandomRateMin.init(1.0, "RandomRateMin", "ランダム再生率小", "Min=0.f,Max=10.f",
+ &mCommonParams);
+ mRandomRateMax.init(1.0, "RandomRateMax", "ランダム再生率大", "Min=0.f,Max=10.f",
+ &mCommonParams);
+ mForbidPartialDemoAS.init(false, "ForbidPartialDemoAS", "一括再生する", "", &mCommonParams);
+ mUseIK.init(true, "UseIK", "IKする", "", &mCommonParams);
+
+ addObj(&mCommonParams, str_CommonParams);
+
+ _3bb = 0;
+
+ res_it = Elements.listBegin();
+ args.index = 0;
+ for (auto it = mElementResources.begin(), end = mElementResources.end();
+ it != end && res_it != res_end; ++it, ++res_it) {
+ args.list = res_it.getList();
+ if (!(*it)->parse(args)) {
+ mHeap->adjust();
+ return false;
+ }
+
+ _3ba |= (*it)->m7() & 1;
+
+ switch ((*it)->getTypeIndex()) {
+ case 62:
+ _3bb |= 1 << 0;
+ break;
+ case 63:
+ _3bb |= 1 << 1;
+ break;
+ case 64:
+ _3bb |= 1 << 2;
+ break;
+ case 83:
+ _3bb |= 1 << 3;
+ break;
+ case 6:
+ _3bb |= 1 << 4;
+ break;
+ case 39:
+ _3bb |= 1 << 5;
+ break;
+ }
+ ++args.index;
+ }
+
+ applyResParameterArchive(archive);
+
+ if (auto* first = getFirstResource()) {
+ u32 x = first->m4();
+ x = x >= 0xff ? 0xff : x;
+ u32 y = first->m5();
+ y = y >= 0xff ? 0xff : y;
+
+ _3b8 = x;
+ _3b9 = y;
+ } else {
+ _3b8 = 0;
+ _3b9 = 0;
+ }
+
+ mHeap->adjust();
+ return true;
+}
+
+ASResource* AS::getFirstResource() const {
+ if (mElementResources.size() == 0)
+ return nullptr;
+ return mElementResources[0];
+}
+
+void AS::finalize_() {
+ for (auto* ptr : mElementResources) {
+ if (ptr)
+ std::destroy_at(ptr);
+ }
+
+ util::safeDeleteHeap(mHeap);
+}
+
+float AS::getRandomRate() const {
+ const auto max = getRandomRateMax();
+ const auto min = getRandomRateMin();
+ if (max - min > 0.0)
+ return sead::GlobalRandom::instance()->getF32Range(min, max);
+ return min;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceAS.h b/src/KingSystem/Resource/Actor/resResourceAS.h
new file mode 100644
index 00000000..14a6b1e4
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceAS.h
@@ -0,0 +1,56 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterList.h>
+#include <agl/Utils/aglParameterObj.h>
+#include <container/seadBuffer.h>
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+#include "KingSystem/Utils/Types.h"
+
+namespace ksys::res {
+
+class ASResource;
+
+class AS : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(AS, Resource)
+public:
+ AS();
+ ~AS() override;
+ AS(const AS&) = delete;
+ auto operator=(const AS&) = delete;
+
+ void doCreate_(u8*, u32, sead::Heap*) override;
+ bool needsParse() const override { return true; }
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+ void finalize_() override;
+
+ const sead::Buffer<ASResource*>& getElementResources() const { return mElementResources; }
+ const auto& getRandomRateMin() const { return *mRandomRateMin; }
+ const auto& getRandomRateMax() const { return *mRandomRateMax; }
+ const auto& getForbidPartialDemoAs() const { return *mForbidPartialDemoAS; }
+ const auto& getUseIk() const { return *mUseIK; }
+
+ ASResource* getFirstResource() const;
+ float getRandomRate() const;
+
+private:
+ friend class ASList;
+
+ sead::Buffer<ASResource*> mElementResources;
+ agl::utl::ParameterList mElementsList;
+
+ agl::utl::ParameterObj mCommonParams;
+ agl::utl::Parameter<f32> mRandomRateMin;
+ agl::utl::Parameter<f32> mRandomRateMax;
+ agl::utl::Parameter<bool> mForbidPartialDemoAS;
+ agl::utl::Parameter<bool> mUseIK;
+ u8 _3b8{};
+ u8 _3b9{};
+ u8 _3ba{};
+ u8 _3bb{};
+ sead::Heap* mHeap{};
+};
+KSYS_CHECK_SIZE_NX150(AS, 0x3c8);
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceASList.cpp b/src/KingSystem/Resource/Actor/resResourceASList.cpp
new file mode 100644
index 00000000..2a5a1f4c
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceASList.cpp
@@ -0,0 +1,251 @@
+#include "KingSystem/Resource/Actor/resResourceASList.h"
+#include "KingSystem/Resource/Actor/resResourceAS.h"
+
+namespace ksys::res {
+
+namespace {
+[[maybe_unused]] sead::SafeArray<sead::FixedSafeString<128>, 3> sStrings;
+}
+
+ASList::ASList() : ParamIO("aslist", 0) {}
+
+ASList::~ASList() {
+ mASDefines.freeBuffer();
+ for (auto& cf : mCFDefines) {
+ cf.posts.freeBuffer();
+ cf.excepts.freeBuffer();
+ }
+ mCFDefines.freeBuffer();
+ mAddReses.freeBuffer();
+}
+
+void ASList::doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) {}
+
+bool ASList::parse_(u8* data, size_t size, sead::Heap* heap) {
+ if (!data)
+ return false;
+
+ agl::utl::ResParameterArchive archive{data};
+ const auto root = archive.getRootList();
+
+ const auto ASDefines = agl::utl::getResParameterList(root, "ASDefines");
+ auto ASDefine_name = sead::FixedSafeString<32>{"ASDefine_"};
+ const auto ASDefine_prefix_len = ASDefine_name.calcLength();
+ if (!ASDefines.ptr())
+ return true;
+ const auto ASDefines_num = ASDefines.getResParameterObjNum();
+ if (ASDefines_num != 0) {
+ if (!mASDefines.tryAllocBuffer(ASDefines_num, heap))
+ return false;
+ for (auto it = mASDefines.begin(), end = mASDefines.end(); it != end; ++it) {
+ it->name.init("", "Name", "", &it->obj);
+ it->file_name.init("", "Filename", "", &it->obj);
+ it->as = nullptr;
+
+ ASDefine_name.trim(ASDefine_prefix_len);
+ ASDefine_name.appendWithFormat("%d", it.getIndex());
+ mASDefinesList.addObj(&it->obj, ASDefine_name);
+ }
+ addList(&mASDefinesList, "ASDefines");
+
+ const auto CFDefines = agl::utl::getResParameterList(root, "CFDefines");
+ if (int CFDefines_num;
+ CFDefines.ptr() && (CFDefines_num = CFDefines.getResParameterListNum()) != 0) {
+ if (!mCFDefines.tryAllocBuffer(CFDefines_num, heap))
+ return false;
+
+ auto CFDefine_name = sead::FixedSafeString<32>{"CFDefine_"};
+ const auto CFDefine_prefix_len = CFDefine_name.calcLength();
+
+ auto CFPost_name = sead::FixedSafeString<32>{"CFPost_"};
+ const auto CFPost_prefix_len = CFPost_name.calcLength();
+
+ auto Name_name = sead::FixedSafeString<32>{"Name_"};
+ const auto Name_prefix_len = Name_name.calcLength();
+
+ for (auto it = mCFDefines.begin(), end = mCFDefines.end(); it != end; ++it) {
+ CFDefine_name.trim(CFDefine_prefix_len);
+ CFDefine_name.appendWithFormat("%d", it.getIndex());
+ mCFDefinesList.addList(&it->list, CFDefine_name);
+ const auto CFDefine = agl::utl::getResParameterList(CFDefines, CFDefine_name);
+
+ const auto CFPosts = agl::utl::getResParameterList(CFDefine, "CFPosts");
+ if (int num; CFPosts.ptr() && (num = CFPosts.getResParameterObjNum()) != 0) {
+ if (!it->posts.tryAllocBuffer(num, heap))
+ return false;
+ for (auto post = it->posts.begin(), post_end = it->posts.end();
+ post != post_end; ++post) {
+ post->name.init("", "Name", "", &post->obj);
+ post->frame.init(0, "Frame", "", &post->obj);
+ post->start_frame_rate.init(0, "StartFrameRate", "", &post->obj);
+
+ CFPost_name.trim(CFPost_prefix_len);
+ CFPost_name.appendWithFormat("%d", post.getIndex());
+ it->posts_list.addObj(&post->obj, CFPost_name);
+ }
+ }
+
+ const auto CFExcepts = agl::utl::getResParameterObj(CFDefine, "CFExcepts");
+ if (int num; CFExcepts.ptr() && (num = CFExcepts.getNum()) != 0) {
+ if (!it->excepts.tryAllocBuffer(num, heap))
+ return false;
+ for (auto except = it->excepts.begin(), except_end = it->excepts.end();
+ except != except_end; ++except) {
+ Name_name.trim(Name_prefix_len);
+ Name_name.appendWithFormat("%d", except.getIndex());
+ except->name.init("", Name_name, "", &it->excepts_obj);
+ }
+ }
+
+ it->name.init("", "Name", "", &it->pre_obj);
+ it->list.addObj(&it->pre_obj, "CFPre");
+ it->list.addList(&it->posts_list, "CFPosts");
+ it->list.addObj(&it->excepts_obj, "CFExcepts");
+ }
+
+ addList(&mCFDefinesList, "CFDefines");
+ }
+ }
+
+ const auto AddReses = agl::utl::getResParameterList(root, "AddReses");
+ if (int num; AddReses.ptr() && (num = AddReses.getResParameterObjNum()) != 0) {
+ if (!mAddReses.tryAllocBuffer(num, heap))
+ return false;
+
+ auto AddRes_name = sead::FixedSafeString<32>{"AddRes_"};
+ const auto AddRes_prefix_len = AddRes_name.calcLength();
+ for (auto it = mAddReses.begin(), end = mAddReses.end(); it != end; ++it) {
+ it->anim.init("", "Anim", "", &it->obj);
+ it->retarget_model.init("", "RetargetModel", "", &it->obj);
+ it->retarget_no_correct.init(false, "RetargetNoCorrect", "", &it->obj);
+
+ AddRes_name.trim(AddRes_prefix_len);
+ AddRes_name.appendWithFormat("%d", it.getIndex());
+ mAddResesList.addObj(&it->obj, AddRes_name);
+ }
+ addList(&mAddResesList, "AddReses");
+ }
+
+ if (agl::utl::getResParameterObj(root, "Common").ptr()) {
+ const sead::SafeString name = "RateAll";
+ const sead::SafeString label = "";
+ mCommon->rate_all.init(1.0, name, label, &mCommon->obj);
+ addObj(&mCommon->obj, "Common");
+ } else {
+ mCommon->rate_all = 1.0;
+ }
+
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+ return true;
+}
+
+bool ASList::finishParsing_() {
+ return true;
+}
+
+bool ASList::m7_() {
+ for (auto& as : mASDefines)
+ as.as = nullptr;
+
+ _2b0 = 0;
+ _2b1 = 0;
+ _2b2 = 0;
+ _2b3 = 0;
+ return true;
+}
+
+void ASList::addAS_(s32 index, AS* as) {
+ mASDefines[index].as = as;
+
+ _2b0 = _2b0 > as->_3b8 ? _2b0 : as->_3b8;
+
+ const auto as_counter = as->_3b9;
+ if (_2b1 == 0) {
+ _2b1 = as_counter;
+ _2b3 = as->_3bb;
+ } else {
+ u8* flags1;
+ u8* flags2;
+ if (as_counter > _2b1) {
+ _2b1 = as_counter;
+ flags1 = &as->_3bb;
+ flags2 = &_2b3;
+ } else {
+ flags1 = &_2b3;
+ flags2 = &as->_3bb;
+ }
+
+ for (int i = 0; i < 6; ++i) {
+ const auto mask = 1u << i;
+ if ((*flags1 & mask) == 0 && (*flags2 & mask) != 0) {
+ _2b3 |= mask;
+ _2b1++;
+ }
+ }
+ }
+
+ _2b2 |= as->_3ba;
+}
+
+const char* ASList::getASFileName(const sead::SafeString& name) const {
+ const int idx = findASDefine(name);
+ if (idx == -1)
+ return nullptr;
+ return mASDefines[idx].getFileName();
+}
+
+int ASList::findASDefine(const sead::SafeString& name) const {
+ return mASDefines.binarySearch(
+ name, +[](const ASDefine& define, const sead::SafeString& key) {
+ return define.name->compare(key);
+ });
+}
+
+int ASList::findCFDefine(const sead::SafeString& name) const {
+ return mCFDefines.binarySearch(
+ name, +[](const CFDefine& define, const sead::SafeString& key) {
+ return define.name->compare(key);
+ });
+}
+
+int ASList::CFExcept::compare(const ASList::CFExcept& o, const sead::SafeString& n) {
+ return o.name->compare(n);
+}
+
+int ASList::CFPost::compare(const ASList::CFPost& o, const sead::SafeString& n) {
+ return o.name->compare(n);
+}
+
+bool ASList::getCFDefineInfo(float* frame, float* start_frame_value, const sead::SafeString& name,
+ const sead::SafeString& post_name, bool* is_default) const {
+ const int idx = findCFDefine(name);
+ *is_default = false;
+ if (idx == -1)
+ return false;
+
+ const auto& cfdefine = mCFDefines[idx];
+
+ const int except_idx = cfdefine.excepts.binarySearch(post_name, CFExcept::compare);
+ if (except_idx >= 0)
+ return false;
+
+ const CFPost* post = nullptr;
+ const int post_idx = cfdefine.posts.binarySearch(post_name, CFPost::compare);
+ if (post_idx != -1) {
+ post = std::addressof(cfdefine.posts[post_idx]);
+ } else {
+ post = std::addressof(cfdefine.posts[0]);
+ if (!post->name->isEmpty())
+ return false;
+ *is_default = true;
+ }
+
+ if (!post)
+ return false;
+
+ *frame = *post->frame;
+ *start_frame_value = *post->start_frame_rate;
+ return true;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceASList.h b/src/KingSystem/Resource/Actor/resResourceASList.h
new file mode 100644
index 00000000..d8836523
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceASList.h
@@ -0,0 +1,109 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterList.h>
+#include <agl/Utils/aglParameterObj.h>
+#include <container/seadBuffer.h>
+#include <prim/seadStorageFor.h>
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+
+namespace ksys::res {
+
+class AS;
+
+class ASList : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(ASList, Resource)
+public:
+ struct ASDefine {
+ const char* getFileName() const { return file_name.ref().cstr(); }
+
+ agl::utl::Parameter<sead::SafeString> name;
+ agl::utl::Parameter<sead::SafeString> file_name;
+ agl::utl::ParameterObj obj;
+ AS* as;
+ };
+ KSYS_CHECK_SIZE_NX150(ASDefine, 0x88);
+
+ struct CFPost {
+ static int compare(const CFPost& o, const sead::SafeString& n);
+
+ agl::utl::Parameter<sead::SafeString> name;
+ agl::utl::Parameter<float> frame;
+ agl::utl::Parameter<float> start_frame_rate;
+ agl::utl::ParameterObj obj;
+ };
+ KSYS_CHECK_SIZE_NX150(CFPost, 0x98);
+
+ struct CFExcept {
+ static int compare(const CFExcept& o, const sead::SafeString& n);
+
+ agl::utl::Parameter<sead::SafeString> name;
+ };
+ KSYS_CHECK_SIZE_NX150(CFExcept, 0x28);
+
+ struct CFDefine {
+ agl::utl::Parameter<sead::SafeString> name;
+ agl::utl::ParameterObj pre_obj;
+ sead::Buffer<CFPost> posts;
+ agl::utl::ParameterList posts_list;
+ sead::Buffer<CFExcept> excepts;
+ agl::utl::ParameterObj excepts_obj;
+ agl::utl::ParameterList list;
+ };
+ KSYS_CHECK_SIZE_NX150(CFDefine, 0x138);
+
+ struct AddRes {
+ agl::utl::Parameter<sead::SafeString> anim;
+ agl::utl::Parameter<sead::SafeString> retarget_model;
+ agl::utl::Parameter<bool> retarget_no_correct;
+ agl::utl::ParameterObj obj;
+ };
+ KSYS_CHECK_SIZE_NX150(AddRes, 0xa0);
+
+ struct Common {
+ agl::utl::Parameter<f32> rate_all;
+ agl::utl::ParameterObj obj;
+ };
+ KSYS_CHECK_SIZE_NX150(Common, 0x50);
+
+ ASList();
+ ~ASList() override;
+
+ void doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) override;
+ bool needsParse() const override { return true; }
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+
+ const sead::Buffer<ASDefine>& getASDefines() const { return mASDefines; }
+ const sead::Buffer<CFDefine>& getCFDefines() const { return mCFDefines; }
+ const sead::Buffer<AddRes>& getAddReses() const { return mAddReses; }
+ const Common& getCommon() const { return mCommon.ref(); }
+
+ void addAS_(s32 index, AS* as);
+
+ const char* getASFileName(const sead::SafeString& name) const;
+ int findASDefine(const sead::SafeString& name) const;
+ int findCFDefine(const sead::SafeString& name) const;
+ bool getCFDefineInfo(float* frame, float* key, const sead::SafeString& name,
+ const sead::SafeString& post_name, bool* is_default) const;
+
+protected:
+ bool finishParsing_() override;
+ bool m7_() override;
+
+private:
+ u8 _2b0 = 0;
+ u8 _2b1 = 0;
+ u8 _2b2 = 0;
+ u8 _2b3 = 0;
+ sead::Buffer<ASDefine> mASDefines;
+ sead::Buffer<CFDefine> mCFDefines;
+ sead::Buffer<AddRes> mAddReses;
+ agl::utl::ParameterList mCFDefinesList;
+ agl::utl::ParameterList mASDefinesList;
+ agl::utl::ParameterList mAddResesList;
+ sead::StorageFor<Common, true> mCommon{sead::ZeroInitializeTag{}};
+};
+KSYS_CHECK_SIZE_NX150(ASList, 0x410);
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceASResource.cpp b/src/KingSystem/Resource/Actor/resResourceASResource.cpp
new file mode 100644
index 00000000..e6b2be31
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceASResource.cpp
@@ -0,0 +1,375 @@
+#include "KingSystem/Resource/Actor/resResourceASResource.h"
+#include <limits>
+#include <prim/seadSafeString.h>
+#include "KingSystem/Resource/Actor/resResourceAS.h"
+
+namespace ksys::as {
+class Element;
+}
+
+namespace ksys::res {
+
+namespace {
+
+struct ASElementFactory {
+ const char* name;
+ ASResource* (*make_res)(int type_index, const ASResource::ParseArgs& args);
+ // FIXME: signature
+ as::Element* (*make)();
+ /// Arbitrary value that is passed to the Element class
+ int value;
+};
+
+sead::SafeString sStr_default = "default";
+
+template <typename T>
+ASResource* resFactoryImpl_(int type_index, const ASResource::ParseArgs& arg) {
+ return new (arg.heap) T(type_index, arg.index);
+}
+
+// FIXME: make functions
+sead::SafeArray<ASElementFactory, 107> sFactories{{
+ /* 000 */ {"AbsTemperatureBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 24},
+ /* 001 */ {"AbsTemperatureSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 24},
+ /* 002 */ {"ArmorSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 57},
+ /* 003 */ {"ArrowSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 60},
+ /* 004 */ {"AttentionSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 48},
+ /* 005 */ {"BoneBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 28},
+ /* 006 */ {"BoneVisibilityAsset", resFactoryImpl_<ASAssetExResource>, nullptr, -1},
+ /* 007 */ {"BoolSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 66},
+ /* 008 */ {"ButtonSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 46},
+ /* 009 */ {"ChargeSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 42},
+
+ /* 010 */ {"ClearMatAnmAsset", resFactoryImpl_<ASResource>, nullptr, -1},
+ /* 011 */ {"ComboSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 43},
+ /* 012 */ {"DiffAngleYBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 26},
+ /* 013 */ {"DiffAngleYSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 26},
+ /* 014 */ {"DirectionAngleBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 9},
+ /* 015 */ {"DirectionAngleSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 9},
+ /* 016 */ {"DistanceBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 16},
+ /* 017 */ {"DistanceSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 16},
+ /* 018 */ {"DungeonClearSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 64},
+ /* 019 */ {"DungeonNumberSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 41},
+
+ /* 020 */ {"EmotionSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 55},
+ /* 021 */ {"EventFlagSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 65},
+ /* 022 */ {"EyeSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 37},
+ /* 023 */ {"EyebrowSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 38},
+ /* 024 */ {"FaceEmotionSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 56},
+ /* 025 */ {"FootBLLifeSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 34},
+ /* 026 */ {"FootBRLifeSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 33},
+ /* 027 */ {"FootFLLifeSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 32},
+ /* 028 */ {"FootFRLifeSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 31},
+ /* 029 */ {"ForwardBentBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 18},
+
+ /* 030 */ {"ForwardBentSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 18},
+ /* 031 */ {"GearSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 64},
+ /* 032 */ {"GenerationSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 35},
+ /* 033 */ {"GrabTypeSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 49},
+ /* 034 */ {"GroundNormalBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 21},
+ /* 035 */ {"GroundNormalSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 21},
+ /* 036 */ {"GroundNormalSideBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 22},
+ /* 037 */ {"GroundNormalSideSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 22},
+ /* 038 */ {"MaskSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 58},
+ /* 039 */ {"MatVisibilityAsset", resFactoryImpl_<ASAssetExResource>, nullptr, -1},
+
+ /* 040 */ {"MouthSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 36},
+ /* 041 */ {"NoAnmAsset", resFactoryImpl_<ASResource>, nullptr, -1},
+ /* 042 */ {"NoLoopStickAngleBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 7},
+ /* 043 */ {"NoLoopStickAngleSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 7},
+ /* 044 */ {"NodePosSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 63},
+ /* 045 */ {"PersonalitySelector", resFactoryImpl_<ASSelectorResource>, nullptr, 50},
+ /* 046 */ {"PostureSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 59},
+ /* 047 */ {"PreASSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 51},
+ /* 048 */ {"PreExclusionRandomSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 30},
+ /* 049 */ {"RandomSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 30},
+
+ /* 050 */ {"RideSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 61},
+ /* 051 */ {"RightStickAngleBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 8},
+ /* 052 */ {"RightStickAngleSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 8},
+ /* 053 */ {"RightStickValueBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 3},
+ /* 054 */ {"RightStickValueSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 3},
+ /* 055 */ {"RightStickXBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 4},
+ /* 056 */ {"RightStickXSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 4},
+ /* 057 */ {"RightStickYBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 5},
+ /* 058 */ {"RightStickYSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 5},
+ /* 059 */ {"SelfHeightSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 39},
+
+ /* 060 */ {"SelfWeightSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 40},
+ /* 061 */
+ {"SequencePlayContainer", resFactoryImpl_<ASSequencePlayContainerResource>, nullptr, -1},
+ /* 062 */ {"ShaderParamAsset", resFactoryImpl_<ASAssetExResource>, nullptr, -1},
+ /* 063 */ {"ShaderParamColorAsset", resFactoryImpl_<ASAssetExResource>, nullptr, -1},
+ /* 064 */ {"ShaderParamTexSRTAsset", resFactoryImpl_<ASAssetExResource>, nullptr, -1},
+ /* 065 */ {"SizeBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 17},
+ /* 066 */ {"SizeSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 17},
+ /* 067 */ {"SkeltalAsset", resFactoryImpl_<ASSkeltalAssetResource>, nullptr, -1},
+ /* 068 */ {"SpeedBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 19},
+ /* 069 */ {"SpeedSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 19},
+
+ /* 070 */ {"StickAngleBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 6},
+ /* 071 */ {"StickAngleSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 6},
+ /* 072 */ {"StickValueBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 0},
+ /* 073 */ {"StickValueSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 0},
+ /* 074 */ {"StickXBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 1},
+ /* 075 */ {"StickXSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 1},
+ /* 076 */ {"StickYBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 2},
+ /* 077 */ {"StickYSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 2},
+ /* 078 */ {"StressBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 14},
+ /* 079 */ {"StressSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 14},
+
+ /* 080 */ {"SyncPlayContainer", resFactoryImpl_<ASResourceWithChildren>, nullptr, -1},
+ /* 081 */ {"TemperatureBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 23},
+ /* 082 */ {"TemperatureSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 23},
+ /* 083 */ {"TexturePatternAsset", resFactoryImpl_<ASAssetExResource>, nullptr, -1},
+ /* 084 */ {"TimeSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 52},
+ /* 085 */ {"TiredBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 13},
+ /* 086 */ {"TiredSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 13},
+ /* 087 */ {"UseItemSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 62},
+ /* 088 */ {"UserAngle2Blender", resFactoryImpl_<ASBlenderResource>, nullptr, 12},
+ /* 089 */ {"UserAngle2Selector", resFactoryImpl_<ASSelectorResource>, nullptr, 12},
+
+ /* 090 */ {"UserAngleBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 11},
+ /* 091 */ {"UserAngleSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 11},
+ /* 092 */ {"UserSpeedBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 10},
+ /* 093 */ {"UserSpeedSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 10},
+ /* 094 */ {"VariationSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 47},
+ /* 095 */ {"WallAngleBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 15},
+ /* 096 */ {"WallAngleSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 15},
+ /* 097 */ {"WeaponDetailSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 45},
+ /* 098 */ {"WeaponSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 44},
+ /* 099 */ {"WeatherSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 53},
+
+ /* 100 */ {"WeightBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 25},
+ /* 101 */ {"WeightSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 25},
+ /* 102 */ {"WindVelocityBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 27},
+ /* 103 */ {"YSpeedBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 20},
+ /* 104 */ {"YSpeedSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 20},
+ /* 105 */ {"ZEx00ExposureBlender", resFactoryImpl_<ASBlenderResource>, nullptr, 29},
+ /* 106 */ {"ZEx00ExposureSelector", resFactoryImpl_<ASSelectorResource>, nullptr, 29},
+}};
+
+} // namespace
+
+bool ASResource::parse(const ASResource::ParseArgs& args) {
+ if (!doParse(args))
+ return false;
+
+ ASExtensions::ParseArgs parse_args{};
+ parse_args.heap = args.heap;
+ parse_args.list = &mList;
+ parse_args.res_list = args.list;
+ return mExtensions.parse(parse_args);
+}
+
+int ASResource::findStringIndex(const sead::SafeString& value) const {
+ auto* parser = sead::DynamicCast<ASStringArrayParser>(
+ mExtensions.getParser(ASParamParser::Type::StringArray));
+
+ if (!parser)
+ return -1;
+
+ const u32 size = parser->getValues().size();
+ if (size == 0)
+ return -1;
+
+ for (u32 i = 0; i < size; ++i) {
+ if (value == *parser->getValues()[i].value)
+ return i;
+ }
+
+ const int default_idx = int(size - 1);
+ if (default_idx < 0)
+ return -1;
+ if (getDefaultStr() == *parser->getValues()[default_idx].value)
+ return default_idx;
+ return -1;
+}
+
+int ASResource::findIntIndex(int value) const {
+ auto* parser =
+ sead::DynamicCast<ASIntArrayParser>(mExtensions.getParser(ASParamParser::Type::IntArray));
+ if (parser) {
+ const u32 size = parser->getValues().size();
+ if (size == 0)
+ return -1;
+
+ for (u32 i = 0; i < size; ++i) {
+ if (*parser->getValues()[i].value == value)
+ return i;
+ }
+
+ const int default_idx = int(size - 1);
+ if (default_idx >= 0 &&
+ *parser->getValues()[default_idx].value == std::numeric_limits<int>::min()) {
+ return default_idx;
+ }
+ }
+ return -1;
+}
+
+const sead::SafeString& ASResource::getDefaultStr() {
+ return sStr_default;
+}
+
+ASResourceWithChildren::~ASResourceWithChildren() {
+ mChildren.freeBuffer();
+}
+
+// NON_MATCHING: getParameterData (redundant uxtw which leads to localised regalloc diffs)
+bool ASResourceWithChildren::doParse(const ASResource::ParseArgs& args) {
+ const auto Children = agl::utl::getResParameterObj(args.list, "Children");
+ if (!Children)
+ return true;
+
+ const auto size = Children.getNum();
+ if (size == 0)
+ return true;
+
+ if (!mChildren.tryAllocBuffer(size, args.heap))
+ return false;
+ for (int i = 0, n = mChildren.size(); i < n; ++i)
+ mChildren(i) = nullptr;
+
+ for (auto it = mChildren.begin(), end = mChildren.end(); it != end; ++it) {
+ const auto idx = *Children.getParameterData<int>(it.getIndex());
+ *it = args.as->getElementResources()[idx];
+ }
+
+ return true;
+}
+
+int ASResourceWithChildren::callOnChildren_(MemberFunction fn) {
+ int ret = 0;
+ for (int i = 0; i < mChildren.size(); ++i)
+ ret += (mChildren[i]->*fn)();
+ return ret;
+}
+
+bool ASSequencePlayContainerResource::doParse(const ASResource::ParseArgs& args) {
+ if (!ASResourceWithChildren::doParse(args))
+ return false;
+
+ mSequenceLoop.init(false, "SequenceLoop", "シーケンスループ", &mObj);
+
+ mList.addObj(&mObj, "Parameters");
+ return true;
+}
+
+int ASSequencePlayContainerResource::callOnChildren_(ASResourceWithChildren::MemberFunction fn) {
+ int ret = 0;
+ for (int i = 0; i < mChildren.size(); ++i) {
+ int value = (mChildren[i]->*fn)();
+ if (u32(ret) <= u32(value))
+ ret = value;
+ }
+ return ret;
+}
+
+int ASSequencePlayContainerResource::m7() {
+ auto* parser = sead::DynamicCast<ASFloatArrayParser>(
+ mExtensions.getParser(ASParamParser::Type::FloatArray));
+ if (!parser)
+ return 0;
+
+ for (int i = 0, n = parser->getValues().size(); i < n; ++i) {
+ if (*parser->getValues()[i].value < 1.0)
+ return 1;
+ }
+ return 0;
+}
+
+float ASSequencePlayContainerResource::getValue(int index) const {
+ auto* parser = sead::DynamicCast<ASFloatArrayParser>(
+ mExtensions.getParser(ASParamParser::Type::FloatArray));
+ if (!parser || parser->getValues().size() <= index)
+ return 1.0;
+ return *parser->getValues()[index].value;
+}
+
+bool ASSelectorResource::doParse(const ASResource::ParseArgs& args) {
+ if (!ASResourceWithChildren::doParse(args))
+ return false;
+
+ mNoSync.init(false, "NoSync", "非同期", &mObj);
+ mJudgeOnce.init(true, "JudgeOnce", "初期化時のみ判定", &mObj);
+
+ mList.addObj(&mObj, "Parameters");
+ return true;
+}
+
+int ASSelectorResource::callOnChildren_(ASResourceWithChildren::MemberFunction fn) {
+ int ret = 0;
+ for (int i = 0; i < mChildren.size(); ++i) {
+ int value = (mChildren[i]->*fn)();
+ if (u32(ret) <= u32(value))
+ ret = value;
+ }
+ return ret;
+}
+
+bool ASBlenderResource::doParse(const ASResource::ParseArgs& args) {
+ if (!ASResourceWithChildren::doParse(args))
+ return false;
+
+ mNoSync.init(false, "NoSync", "非同期", &mObj);
+ mJudgeOnce.init(false, "JudgeOnce", "初期化時のみ判定", &mObj);
+ mInputLimit.init(-1.0, "InputLimit", "入力変化制限", &mObj);
+
+ mList.addObj(&mObj, "Parameters");
+ return true;
+}
+
+int ASBlenderResource::callOnChildren_(ASResourceWithChildren::MemberFunction fn) {
+ if (mChildren.size() == 0)
+ return 0;
+
+ const u32 first = (mChildren[0]->*fn)();
+
+ u32 max = first;
+ u32 previous = first;
+ for (int i = 1; i < mChildren.size(); ++i) {
+ const u32 current = (mChildren[i]->*fn)();
+ if (max <= previous + current)
+ max = previous + current;
+ previous = current;
+ }
+
+ if (max <= first + previous)
+ max = first + previous;
+
+ return max;
+}
+
+bool ASAssetResource::doParse(const ASResource::ParseArgs& args) {
+ mFileName.init("", "FileName", "ファイル名", &mObj);
+
+ mList.addObj(&mObj, "Parameters");
+ return true;
+}
+
+bool ASSkeltalAssetResource::doParse(const ASResource::ParseArgs& args) {
+ ASAssetResource::doParse(args);
+
+ mMorph.init(5.0, "Morph", "補間", "Min=0.f,Max=20.f", &mObj);
+ mResetMorph.init(5.0, "ResetMorph", "リセット時補間", "Min=0.f,Max=20.f", &mObj);
+ mInitAnmDriven.init(false, "InitAnmDriven", "初期アニメドリブン", "", &mObj);
+
+ return true;
+}
+
+ASResource* ASResource::make(const ASResource::ParseArgs& args) {
+ const auto Parameters = agl::utl::getResParameterObj(args.list, "Parameters");
+
+ const auto TypeIndex = agl::utl::getResParameter(Parameters, "TypeIndex");
+ if (!TypeIndex.ptr())
+ return nullptr;
+
+ const auto type_index = *TypeIndex.getData<int>();
+ if (u32(type_index) >= u32(sFactories.size()))
+ return nullptr;
+
+ return sFactories[type_index].make_res(type_index, args);
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceASResource.h b/src/KingSystem/Resource/Actor/resResourceASResource.h
new file mode 100644
index 00000000..4160a2bf
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceASResource.h
@@ -0,0 +1,166 @@
+#pragma once
+
+#include <agl/Utils/aglParameterList.h>
+#include <agl/Utils/aglResParameter.h>
+#include <container/seadBuffer.h>
+#include <prim/seadRuntimeTypeInfo.h>
+#include "KingSystem/Resource/Actor/resResourceASResourceExtension.h"
+
+namespace ksys::res {
+
+class AS;
+
+class ASResource {
+ SEAD_RTTI_BASE(ASResource)
+public:
+ struct ParseArgs {
+ agl::utl::ResParameterList list;
+ sead::Heap* heap;
+ AS* as;
+ int index;
+ };
+
+ ASResource(int type_index, int index) : mTypeIndex(type_index), mIndex(index) {}
+ virtual ~ASResource() = default;
+
+ bool parse(const ParseArgs& args);
+
+ virtual int m4() { return 0; }
+ virtual int m5() { return 0; }
+ virtual int m6() { return 1; }
+ virtual int m7() { return 0; }
+
+ static const sead::SafeString& getDefaultStr();
+ static ASResource* make(const ParseArgs& args);
+
+ u16 getTypeIndex() const { return mTypeIndex; }
+ u16 getIndex() const { return mIndex; }
+ int findStringIndex(const sead::SafeString& value) const;
+ int findIntIndex(int value) const;
+
+ agl::utl::ParameterList& getList() { return mList; }
+
+protected:
+ virtual bool doParse(const ParseArgs& args) { return true; }
+
+ u16 mTypeIndex{};
+ u16 mIndex{};
+ agl::utl::ParameterList mList;
+ ASExtensions mExtensions;
+};
+
+class ASResourceWithChildren : public ASResource {
+ SEAD_RTTI_OVERRIDE(ASResourceWithChildren, ASResource)
+public:
+ using ASResource::ASResource;
+ ~ASResourceWithChildren() override;
+ ASResourceWithChildren(const ASResourceWithChildren&) = delete;
+ auto operator=(const ASResourceWithChildren&) = delete;
+
+ int m4() override { return callOnChildren_(&ASResource::m4); }
+ int m5() override { return callOnChildren_(&ASResource::m5); }
+ int m6() override { return callOnChildren_(&ASResource::m6) + 1; }
+
+protected:
+ using MemberFunction = int (ASResource::*)();
+
+ bool doParse(const ParseArgs& args) override;
+ virtual int callOnChildren_(MemberFunction fn);
+
+ sead::Buffer<ASResource*> mChildren;
+};
+
+class ASSequencePlayContainerResource : public ASResourceWithChildren {
+ SEAD_RTTI_OVERRIDE(ASSequencePlayContainerResource, ASResourceWithChildren)
+public:
+ using ASResourceWithChildren::ASResourceWithChildren;
+
+ const auto& getSequenceLoop() const { return *mSequenceLoop; }
+ float getValue(int index) const;
+
+protected:
+ bool doParse(const ParseArgs& args) override;
+ int callOnChildren_(MemberFunction fn) override;
+ int m7() override;
+
+ agl::utl::ParameterObj mObj;
+ agl::utl::Parameter<bool> mSequenceLoop;
+};
+
+class ASSelectorResource : public ASResourceWithChildren {
+ SEAD_RTTI_OVERRIDE(ASSelectorResource, ASResourceWithChildren)
+public:
+ using ASResourceWithChildren::ASResourceWithChildren;
+
+ const auto& getNoSync() const { return *mNoSync; }
+ const auto& getJudgeOnce() const { return *mJudgeOnce; }
+
+protected:
+ bool doParse(const ParseArgs& args) override;
+ int callOnChildren_(MemberFunction fn) override;
+
+ agl::utl::ParameterObj mObj;
+ agl::utl::Parameter<bool> mNoSync;
+ agl::utl::Parameter<bool> mJudgeOnce;
+};
+
+class ASBlenderResource : public ASResourceWithChildren {
+ SEAD_RTTI_OVERRIDE(ASBlenderResource, ASResourceWithChildren)
+public:
+ using ASResourceWithChildren::ASResourceWithChildren;
+
+ const auto& getNoSync() const { return *mNoSync; }
+ const auto& getJudgeOnce() const { return *mJudgeOnce; }
+ const auto& getInputLimit() const { return *mInputLimit; }
+
+protected:
+ bool doParse(const ParseArgs& args) override;
+ int callOnChildren_(MemberFunction fn) override;
+
+ agl::utl::ParameterObj mObj;
+ agl::utl::Parameter<bool> mNoSync;
+ agl::utl::Parameter<bool> mJudgeOnce;
+ agl::utl::Parameter<float> mInputLimit;
+};
+
+class ASAssetResource : public ASResource {
+ SEAD_RTTI_OVERRIDE(ASAssetResource, ASResource)
+public:
+ using ASResource::ASResource;
+
+ const sead::SafeString& getFileName() const { return *mFileName; }
+
+protected:
+ bool doParse(const ParseArgs& args) override;
+
+ agl::utl::ParameterObj mObj;
+ agl::utl::Parameter<sead::SafeString> mFileName;
+};
+
+class ASAssetExResource : public ASAssetResource {
+ SEAD_RTTI_OVERRIDE(ASAssetExResource, ASAssetResource)
+public:
+ using ASAssetResource::ASAssetResource;
+
+protected:
+ int m5() override { return 1; }
+};
+
+class ASSkeltalAssetResource : public ASAssetResource {
+ SEAD_RTTI_OVERRIDE(ASSkeltalAssetResource, ASAssetResource)
+public:
+ using ASAssetResource::ASAssetResource;
+
+ const auto& getInitAnmDriven() const { return *mInitAnmDriven; }
+ const auto& getMorph() const { return *mMorph; }
+ const auto& getResetMorph() const { return *mResetMorph; }
+
+protected:
+ bool doParse(const ParseArgs& args) override;
+
+ agl::utl::Parameter<int> mInitAnmDriven;
+ agl::utl::Parameter<float> mMorph;
+ agl::utl::Parameter<float> mResetMorph;
+};
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceASResourceExtension.cpp b/src/KingSystem/Resource/Actor/resResourceASResourceExtension.cpp
new file mode 100644
index 00000000..9a400cd1
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceASResourceExtension.cpp
@@ -0,0 +1,339 @@
+#include "KingSystem/Resource/Actor/resResourceASResourceExtension.h"
+#include <container/seadSafeArray.h>
+#include <optional>
+#include <prim/seadSafeString.h>
+#include "KingSystem/ActorSystem/actASSetting.h"
+#include "KingSystem/Resource/resResourceASSetting.h"
+
+namespace ksys::res {
+
+bool ASFrameCtrlParser::parse(const ASParamParser::ParseArgs& args) {
+ mRate.init(1.0, "Rate", "再生速度", "Min=0.f,Max=10.f", &mObj);
+ mStartFrame.init(0.0, "StartFrame", "開始フレーム", "Min=0.f,Max=100.f", &mObj);
+ mEndFrame.init(-1.0, "EndFrame", "終了フレーム", "Min=-1.f,Max=100.f", &mObj);
+ mLoopStopCount.init(-1.0, "LoopStopCount", "ループ停止回数", "Min=-1.f,Max=10.f", &mObj);
+ mLoopStopCountRandom.init(0.0, "LoopStopCountRandom", "ランダムループ追加回数",
+ "Min=0.f,Max=10.f", &mObj);
+ mReversePlay.init(false, "ReversePlay", "逆再生", "", &mObj);
+ mUseGlobalFrame.init(false, "UseGlobalFrame", "グローバルフレーム使う", "", &mObj);
+ mFootType.init(0, "FootType", "足解決", "", &mObj);
+ mConnect.init(false, "Connect", "接続", "", &mObj);
+ mAnmLoop.init(0, "AnmLoop", "ループ設定", "", &mObj);
+
+ mList.addObj(&mObj, "FrameCtrl0");
+ return true;
+}
+
+bool ASTriggerEventsParser::parse(const ASParamParser::ParseArgs& args) {
+ const int num_objs = args.res_list.getResParameterObjNum();
+ if (num_objs == 0)
+ return true;
+
+ if (!mEvents.tryAllocBuffer(num_objs, args.heap))
+ return false;
+
+ auto it = mEvents.begin();
+ const auto end = mEvents.end();
+
+ sead::FixedSafeString<32> obj_name{"Event"};
+ const auto obj_name_prefix_len = obj_name.calcLength();
+
+ auto res_it = args.res_list.objBegin();
+ const auto res_end = args.res_list.objEnd();
+
+ while (res_it != res_end && it != end) {
+ u32 type_index = -1;
+ const auto TypeIndex = agl::utl::getResParameter(res_it.getObj(), "TypeIndex");
+ if (TypeIndex.ptr())
+ type_index = *TypeIndex.getData<int>();
+
+ // TODO: add a TypeIndex enum
+ it->type_index = type_index > 33 ? 88 : int(type_index) + 54;
+
+ it->frame.init(0.0, "Frame", "フレーム", "Min=0.f,Max=100.f", &it->obj);
+ it->value.init("", "Value", "値", "", &it->obj);
+
+ obj_name.trim(obj_name_prefix_len);
+ obj_name.appendWithFormat("%d", it.getIndex());
+ mList.addObj(&it->obj, obj_name);
+
+ ++res_it;
+ ++it;
+ }
+
+ return true;
+}
+
+bool ASHoldEventsParser::parse(const ASParamParser::ParseArgs& args) {
+ const int num_objs = args.res_list.getResParameterObjNum();
+ if (num_objs == 0)
+ return true;
+
+ if (!mEvents.tryAllocBuffer(num_objs, args.heap))
+ return false;
+
+ auto it = mEvents.begin();
+ const auto end = mEvents.end();
+
+ sead::FixedSafeString<32> obj_name{"Event"};
+ const auto obj_name_prefix_len = obj_name.calcLength();
+
+ auto res_it = args.res_list.objBegin();
+ const auto res_end = args.res_list.objEnd();
+
+ while (res_it != res_end && it != end) {
+ u32 type_index = -1;
+ const auto TypeIndex = agl::utl::getResParameter(res_it.getObj(), "TypeIndex");
+ if (TypeIndex.ptr())
+ type_index = *TypeIndex.getData<int>();
+
+ // TODO: add a TypeIndex enum
+ it->type_index = type_index > 53 ? 88 : int(type_index);
+
+ it->start_frame.init(0.0, "StartFrame", "開始フレーム", "Min=0.f,Max=100.f", &it->obj);
+ it->end_frame.init(0.0, "EndFrame", "終了フレーム", "Min=0.f,Max=100.f", &it->obj);
+ it->value.init("", "Value", "値", "", &it->obj);
+
+ obj_name.trim(obj_name_prefix_len);
+ obj_name.appendWithFormat("%d", it.getIndex());
+ mList.addObj(&it->obj, obj_name);
+
+ ++res_it;
+ ++it;
+ }
+
+ return true;
+}
+
+bool ASStringArrayParser::parse(const ASParamParser::ParseArgs& args) {
+ const int size = args.res_list.getResParameterObj(0).getNum();
+ if (size != 0) {
+ if (!mValues.tryAllocBuffer(size, args.heap))
+ return false;
+
+ sead::FixedSafeString<32> param_name{"Value"};
+ const auto param_name_prefix_len = param_name.calcLength();
+
+ for (int i = 0; i < size; ++i) {
+ param_name.trim(param_name_prefix_len);
+ param_name.appendWithFormat("%d", i);
+ mValues[i].value.init("", param_name, "値", "", &mObj);
+ }
+ }
+
+ mList.addObj(&mObj, "StringArray0");
+ return true;
+}
+
+bool ASRangesParser::parse(const ASParamParser::ParseArgs& args) {
+ const int num_objs = args.res_list.getResParameterObjNum();
+ if (num_objs == 0)
+ return true;
+
+ if (!mRanges.tryAllocBuffer(num_objs, args.heap))
+ return false;
+
+ auto it = mRanges.begin();
+ const auto end = mRanges.end();
+
+ sead::FixedSafeString<32> obj_name{"Range"};
+ const auto obj_name_prefix_len = obj_name.calcLength();
+
+ auto res_it = args.res_list.objBegin();
+ const auto res_end = args.res_list.objEnd();
+
+ while (res_it != res_end && it != end) {
+ obj_name.trim(obj_name_prefix_len);
+ obj_name.appendWithFormat("%d", it.getIndex());
+
+ it->start.init(0.0, "Start", "開始", "Min=0.f,Max=1.f", &it->obj);
+ it->end.init(0.0, "End", "終了", "Min=0.f,Max=1.f", &it->obj);
+
+ mList.addObj(&it->obj, obj_name);
+
+ ++res_it;
+ ++it;
+ }
+
+ return true;
+}
+
+bool ASFloatArrayParser::parse(const ASParamParser::ParseArgs& args) {
+ const int size = args.res_list.getResParameterObj(0).getNum();
+ if (size != 0) {
+ if (!mValues.tryAllocBuffer(size, args.heap))
+ return false;
+
+ sead::FixedSafeString<32> param_name{"Value"};
+ const auto param_name_prefix_len = param_name.calcLength();
+
+ for (int i = 0; i < size; ++i) {
+ param_name.trim(param_name_prefix_len);
+ param_name.appendWithFormat("%d", i);
+ mValues[i].value.init(1.0, param_name, "値", "", &mObj);
+ }
+ }
+
+ mList.addObj(&mObj, "FloatArray0");
+ return true;
+}
+
+bool ASIntArrayParser::parse(const ASParamParser::ParseArgs& args) {
+ const int size = args.res_list.getResParameterObj(0).getNum();
+ if (size != 0) {
+ if (!mValues.tryAllocBuffer(size, args.heap))
+ return false;
+
+ sead::FixedSafeString<32> param_name{"Value"};
+ const auto param_name_prefix_len = param_name.calcLength();
+
+ for (int i = 0; i < size; ++i) {
+ param_name.trim(param_name_prefix_len);
+ param_name.appendWithFormat("%d", i);
+ mValues[i].value.init(1, param_name, "値", "", &mObj);
+ }
+ }
+
+ mList.addObj(&mObj, "IntArray0");
+ return true;
+}
+
+bool ASBitIndexParser::parse(const ASParamParser::ParseArgs& args) {
+ const auto obj = args.res_list.getResParameterObj(0);
+ if (obj.getNum() < 1)
+ return true;
+
+ const auto TypeIndex = agl::utl::getResParameter(obj, "TypeIndex");
+ if (!TypeIndex.ptr())
+ return true;
+
+ mTypeIndex = *TypeIndex.getData<int>();
+ return true;
+}
+
+template <typename T>
+static ASParamParser* factoryImpl_(sead::Heap* heap) {
+ return new (heap) T;
+}
+
+static ASParamParser* dummyASParserFactoryImpl_(sead::Heap*) {
+ return nullptr;
+}
+
+namespace {
+struct Factory {
+ const char* name;
+ ASParamParser* (*make)(sead::Heap* heap);
+};
+
+sead::SafeArray<Factory, ASParamParser::NumTypes> sASFactories{{
+ {"FrameCtrl", factoryImpl_<ASFrameCtrlParser>},
+ {"TriggerEvents", factoryImpl_<ASTriggerEventsParser>},
+ {"HoldEvents", factoryImpl_<ASHoldEventsParser>},
+ {"StringArray", factoryImpl_<ASStringArrayParser>},
+ {"Ranges", factoryImpl_<ASRangesParser>},
+ {"FloatArray", factoryImpl_<ASFloatArrayParser>},
+ {"IntArray", factoryImpl_<ASIntArrayParser>},
+ {"BitIndex", factoryImpl_<ASBitIndexParser>},
+ {"BlenderBone", dummyASParserFactoryImpl_},
+}};
+} // namespace
+
+ASExtensions::~ASExtensions() {
+ for (auto*& parser : mParsers) {
+ if (parser && parser->getType() != ASParamParser::Type::BlenderBone)
+ delete parser;
+ parser = nullptr;
+ }
+ mParsers.freeBuffer();
+}
+
+bool ASExtensions::parse(const ASExtensions::ParseArgs& args) {
+ const auto Extend = agl::utl::getResParameterList(args.res_list, "Extend");
+ if (!Extend)
+ return true;
+
+ const auto num_extensions = Extend.getResParameterListNum();
+ if (num_extensions == 0)
+ return true;
+
+ if (!mParsers.tryAllocBuffer(num_extensions, args.heap))
+ return false;
+ for (int i = 0, n = mParsers.size(); i < n; ++i)
+ mParsers(i) = nullptr;
+
+ auto it = mParsers.begin();
+ const auto end = mParsers.end();
+
+ ASParamParser::ParseArgs parse_args{};
+ parse_args.list = &mList;
+ parse_args.heap = args.heap;
+
+ auto res_it = Extend.listBegin();
+ const auto res_end = Extend.listEnd();
+
+ for (; it != end && res_it != res_end; ++it, ++res_it) {
+ parse_args.res_list = res_it.getList();
+ *it = makeParser(parse_args);
+
+ constexpr int bone = int(ASParamParser::Type::BlenderBone);
+ if (*it == nullptr && parse_args.res_list.getParameterListNameHash() !=
+ agl::utl::ParameterBase::calcHash(sASFactories[bone].name)) {
+ return false;
+ }
+ }
+
+ args.list->addList(&mList, "Extend");
+ return true;
+}
+
+ASParamParser* ASExtensions::makeParser(const ASParamParser::ParseArgs& args) const {
+ const auto is_factory = [&args](int i) {
+ return args.res_list.getParameterListNameHash() ==
+ agl::utl::ParameterBase::calcHash(sASFactories[i].name);
+ };
+
+ std::optional<int> type;
+ for (int i = 0; i < ASParamParser::NumTypes - 1; ++i) {
+ if (!is_factory(i))
+ continue;
+ type = i;
+ break;
+ }
+
+ if (!type.has_value() && is_factory(int(ASParamParser::Type::BlenderBone))) {
+ const auto obj = args.res_list.getResParameterObj(0);
+ if (obj.getNum() > 0) {
+ const sead::SafeString name = obj.getResParameter(0).getData<const char>();
+ return act::ASSetting::instance()->getBoneParams(name);
+ }
+ }
+
+ if (!type.has_value())
+ return nullptr;
+
+ const auto& factory = sASFactories[*type];
+
+ auto* parser = factory.make(args.heap);
+ if (!parser)
+ return nullptr;
+
+ if (!parser->parse(args)) {
+ delete parser;
+ return nullptr;
+ }
+
+ args.list->addList(&parser->getList(), factory.name);
+ return parser;
+}
+
+ASParamParser* ASExtensions::getParser(ASParamParser::Type type) const {
+ for (int i = 0, n = mParsers.size(); i < n; ++i) {
+ auto* parser = mParsers[i];
+ if (parser && parser->getType() == type)
+ return parser;
+ }
+ return nullptr;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceASResourceExtension.h b/src/KingSystem/Resource/Actor/resResourceASResourceExtension.h
new file mode 100644
index 00000000..fd244cc6
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceASResourceExtension.h
@@ -0,0 +1,259 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterList.h>
+#include <agl/Utils/aglParameterObj.h>
+#include <agl/Utils/aglResParameter.h>
+#include <array>
+#include <basis/seadTypes.h>
+#include <container/seadBuffer.h>
+#include <prim/seadRuntimeTypeInfo.h>
+#include "KingSystem/Utils/Types.h"
+
+namespace sead {
+class Heap;
+}
+
+namespace ksys::res {
+
+class ASParamParser {
+ SEAD_RTTI_BASE(ASParamParser)
+public:
+ enum class Type {
+ FrameCtrl = 0,
+ TriggerEvents = 1,
+ HoldEvents = 2,
+ StringArray = 3,
+ Ranges = 4,
+ FloatArray = 5,
+ IntArray = 6,
+ BitIndex = 7,
+ BlenderBone = 8,
+ };
+ static constexpr int NumTypes = 9;
+
+ struct ParseArgs {
+ agl::utl::ParameterList* list;
+ agl::utl::ResParameterList res_list;
+ sead::Heap* heap;
+ };
+
+ explicit ASParamParser(Type type) : mType(type) {}
+ virtual ~ASParamParser() = default;
+ virtual bool parse(const ParseArgs& args) { return true; }
+
+ Type getType() const { return mType; }
+ agl::utl::ParameterList& getList() { return mList; }
+ const agl::utl::ParameterList& getList() const { return mList; }
+
+protected:
+ Type mType;
+ agl::utl::ParameterList mList;
+};
+KSYS_CHECK_SIZE_NX150(ASParamParser, 0x58);
+
+class ASExtensions {
+public:
+ struct ParseArgs {
+ agl::utl::ResParameterList res_list;
+ agl::utl::ParameterList* list;
+ sead::Heap* heap;
+ };
+
+ ASExtensions() = default;
+ ~ASExtensions();
+ ASExtensions(const ASExtensions&) = delete;
+ auto operator=(const ASExtensions&) = delete;
+
+ const sead::Buffer<ASParamParser*>& getParsers() const { return mParsers; }
+ ASParamParser* getParser(ASParamParser::Type type) const;
+
+ bool parse(const ParseArgs& args);
+
+private:
+ ASParamParser* makeParser(const ASParamParser::ParseArgs& args) const;
+
+ agl::utl::ParameterList mList;
+ sead::Buffer<ASParamParser*> mParsers;
+};
+
+class ASFrameCtrlParser : public ASParamParser {
+ SEAD_RTTI_OVERRIDE(ASFrameCtrlParser, ASParamParser)
+public:
+ ASFrameCtrlParser() : ASParamParser(Type::FrameCtrl) {}
+
+ const auto& getRate() const { return *mRate; }
+ const auto& getStartFrame() const { return *mStartFrame; }
+ const auto& getEndFrame() const { return *mEndFrame; }
+ const auto& getLoopStopCount() const { return *mLoopStopCount; }
+ const auto& getLoopStopCountRandom() const { return *mLoopStopCountRandom; }
+ const auto& getReversePlay() const { return *mReversePlay; }
+ const auto& getUseGlobalFrame() const { return *mUseGlobalFrame; }
+ const auto& getConnect() const { return *mConnect; }
+ const auto& getFootType() const { return *mFootType; }
+ const auto& getAnmLoop() const { return *mAnmLoop; }
+
+ bool parse(const ParseArgs& args) override;
+
+private:
+ agl::utl::ParameterObj mObj;
+ agl::utl::Parameter<float> mRate;
+ agl::utl::Parameter<float> mStartFrame;
+ agl::utl::Parameter<float> mEndFrame;
+ agl::utl::Parameter<float> mLoopStopCount;
+ agl::utl::Parameter<float> mLoopStopCountRandom;
+ agl::utl::Parameter<bool> mReversePlay;
+ agl::utl::Parameter<bool> mUseGlobalFrame;
+ agl::utl::Parameter<int> mConnect;
+ agl::utl::Parameter<int> mFootType;
+ agl::utl::Parameter<int> mAnmLoop;
+};
+
+class ASTriggerEventsParser : public ASParamParser {
+ SEAD_RTTI_OVERRIDE(ASTriggerEventsParser, ASParamParser)
+public:
+ struct Event {
+ agl::utl::ParameterObj obj;
+ int type_index;
+ agl::utl::Parameter<float> frame;
+ agl::utl::Parameter<sead::SafeString> value;
+ };
+
+ ASTriggerEventsParser() : ASParamParser(Type::TriggerEvents) {}
+ ~ASTriggerEventsParser() override { mEvents.freeBuffer(); }
+ ASTriggerEventsParser(const ASTriggerEventsParser&) = delete;
+ auto operator=(const ASTriggerEventsParser&) = delete;
+
+ const sead::Buffer<Event>& getEvents() const { return mEvents; }
+
+ bool parse(const ParseArgs& args) override;
+
+private:
+ sead::Buffer<Event> mEvents;
+};
+
+class ASHoldEventsParser : public ASParamParser {
+ SEAD_RTTI_OVERRIDE(ASHoldEventsParser, ASParamParser)
+public:
+ struct Event {
+ agl::utl::ParameterObj obj;
+ int type_index;
+ agl::utl::Parameter<float> start_frame;
+ agl::utl::Parameter<float> end_frame;
+ agl::utl::Parameter<sead::SafeString> value;
+ };
+
+ ASHoldEventsParser() : ASParamParser(Type::HoldEvents) {}
+ ~ASHoldEventsParser() override { mEvents.freeBuffer(); }
+ ASHoldEventsParser(const ASHoldEventsParser&) = delete;
+ auto operator=(const ASHoldEventsParser&) = delete;
+
+ const sead::Buffer<Event>& getEvents() const { return mEvents; }
+
+ bool parse(const ParseArgs& args) override;
+
+private:
+ sead::Buffer<Event> mEvents;
+};
+
+class ASStringArrayParser : public ASParamParser {
+ SEAD_RTTI_OVERRIDE(ASStringArrayParser, ASParamParser)
+public:
+ struct Value {
+ agl::utl::Parameter<sead::SafeString> value;
+ };
+
+ ASStringArrayParser() : ASParamParser(Type::StringArray) {}
+ ~ASStringArrayParser() override { mValues.freeBuffer(); }
+ ASStringArrayParser(const ASStringArrayParser&) = delete;
+ auto operator=(const ASStringArrayParser&) = delete;
+
+ const sead::Buffer<Value>& getValues() const { return mValues; }
+
+ bool parse(const ParseArgs& args) override;
+
+private:
+ agl::utl::ParameterObj mObj;
+ sead::Buffer<Value> mValues;
+};
+
+class ASRangesParser : public ASParamParser {
+ SEAD_RTTI_OVERRIDE(ASRangesParser, ASParamParser)
+public:
+ struct Range {
+ agl::utl::ParameterObj obj;
+ agl::utl::Parameter<float> start;
+ agl::utl::Parameter<float> end;
+ };
+
+ ASRangesParser() : ASParamParser(Type::Ranges) {}
+ ~ASRangesParser() override { mRanges.freeBuffer(); }
+ ASRangesParser(const ASRangesParser&) = delete;
+ auto operator=(const ASRangesParser&) = delete;
+
+ const sead::Buffer<Range>& getRanges() const { return mRanges; }
+
+ bool parse(const ParseArgs& args) override;
+
+private:
+ sead::Buffer<Range> mRanges;
+};
+
+class ASFloatArrayParser : public ASParamParser {
+ SEAD_RTTI_OVERRIDE(ASFloatArrayParser, ASParamParser)
+public:
+ struct Value {
+ agl::utl::Parameter<float> value;
+ };
+
+ ASFloatArrayParser() : ASParamParser(Type::FloatArray) {}
+
+ ~ASFloatArrayParser() override { mValues.freeBuffer(); }
+ ASFloatArrayParser(const ASFloatArrayParser&) = delete;
+ auto operator=(const ASFloatArrayParser&) = delete;
+
+ const sead::Buffer<Value>& getValues() const { return mValues; }
+
+ bool parse(const ParseArgs& args) override;
+
+private:
+ agl::utl::ParameterObj mObj;
+ sead::Buffer<Value> mValues;
+};
+
+class ASIntArrayParser : public ASParamParser {
+ SEAD_RTTI_OVERRIDE(ASIntArrayParser, ASParamParser)
+public:
+ struct Value {
+ agl::utl::Parameter<int> value;
+ };
+
+ ASIntArrayParser() : ASParamParser(Type::IntArray) {}
+
+ ~ASIntArrayParser() override { mValues.freeBuffer(); }
+ ASIntArrayParser(const ASIntArrayParser&) = delete;
+ auto operator=(const ASIntArrayParser&) = delete;
+
+ const sead::Buffer<Value>& getValues() const { return mValues; }
+
+ bool parse(const ParseArgs& args) override;
+
+private:
+ agl::utl::ParameterObj mObj;
+ sead::Buffer<Value> mValues;
+};
+
+class ASBitIndexParser : public ASParamParser {
+ SEAD_RTTI_OVERRIDE(ASBitIndexParser, ASParamParser)
+public:
+ ASBitIndexParser() : ASParamParser(Type::BitIndex) {}
+
+ int getBitIndex() const { return mTypeIndex; }
+
+ bool parse(const ParseArgs& args) override;
+
+private:
+ int mTypeIndex = -1;
+};
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceActorCapture.cpp b/src/KingSystem/Resource/Actor/resResourceActorCapture.cpp
new file mode 100644
index 00000000..a2d0b3f4
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceActorCapture.cpp
@@ -0,0 +1,61 @@
+#include "KingSystem/Resource/Actor/resResourceActorCapture.h"
+
+namespace ksys::res {
+
+static ActorCapture::ActorCaptureConstants sConstants;
+
+const ActorCapture::ActorCaptureConstants& ActorCapture::getConstants() {
+ return sConstants;
+}
+
+ActorCapture::ActorCapture() : ParamIO("actcapt", 0) {
+ addObj(&mCameraInfoObj, "camera_info");
+ mCameraInfoObj.position.init(sConstants.camera_position, "pos", "カメラ位置", &mCameraInfoObj);
+ mCameraInfoObj.direction.init(sConstants.camera_direction, "at", "カメラ注視点",
+ &mCameraInfoObj);
+ mCameraInfoObj.fov.init(50.0, "fovy", "カメラ画角", &mCameraInfoObj);
+ mCameraInfoObj.tilt.init(0.0, "twist", "カメラひねり", &mCameraInfoObj);
+
+ addObj(&mActorInfoObj, "actor_info");
+ mActorInfoObj.position.init(sConstants.actor_position, "pos", "アクタ位置", &mActorInfoObj);
+ mActorInfoObj.rotation.init(sConstants.actor_rotation, "rotate", "アクタRotate",
+ &mActorInfoObj);
+ mActorInfoObj.as_name.init(sead::SafeString::cEmptyString, "as_name", "適用するASのKey名",
+ &mActorInfoObj);
+ mActorInfoObj.apply_skel_anim.init(false, "apply_skl_anim",
+ "スケルタルアニメを検索して直接適用する", &mActorInfoObj);
+ mActorInfoObj.frame.init(0.0, "as_frame", "ASのフレーム・経過時間指定", &mActorInfoObj);
+ mActorInfoObj.bounding_adjustment.init(false, "adjust_bounding",
+ "バウンディングを元に位置を調整する", &mActorInfoObj);
+ mActorInfoObj.force_idle.init(false, "force_idle", "強制待機", &mActorInfoObj);
+ mActorInfoObj.disable_cloth.init(false, "disable_cloth", "クロスを切る", &mActorInfoObj);
+
+ addObj(&mLightInfoObj, "light_info");
+ mLightInfoObj.direction.init(sConstants.light_direction, "dir", "ライト方向", &mLightInfoObj);
+}
+
+bool ActorCapture::parse_(u8* data, size_t, sead::Heap*) {
+ if (data)
+ applyResParameterArchive(agl::utl::ResParameterArchive(data));
+ return true;
+}
+
+void ActorCapture::reset() {
+ mCameraInfoObj.position.ref() = sConstants.camera_position;
+ mCameraInfoObj.direction.ref() = sConstants.camera_direction;
+ mCameraInfoObj.fov.ref() = 50;
+ mCameraInfoObj.tilt.ref() = 0;
+
+ mActorInfoObj.position.ref() = sConstants.actor_position;
+ mActorInfoObj.rotation.ref() = sConstants.actor_rotation;
+ mActorInfoObj.as_name.ref().copy(sead::FixedSafeString<32>(sead::SafeString::cEmptyString));
+ mActorInfoObj.apply_skel_anim.ref() = false;
+ mActorInfoObj.frame.ref() = 0;
+ mActorInfoObj.bounding_adjustment.ref() = false;
+ mActorInfoObj.force_idle.ref() = false;
+ mActorInfoObj.disable_cloth.ref() = false;
+
+ mLightInfoObj.direction.ref() = sConstants.light_direction;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceActorCapture.h b/src/KingSystem/Resource/Actor/resResourceActorCapture.h
new file mode 100644
index 00000000..51145a68
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceActorCapture.h
@@ -0,0 +1,72 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterObj.h>
+#include <math/seadVector.h>
+#include <prim/seadSafeString.h>
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+
+namespace ksys::res {
+
+class ActorCapture : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(ActorCapture, Resource)
+public:
+ struct ActorCaptureConstants {
+ // NON_MATCHING: equivalent but reordered
+ ActorCaptureConstants() {
+ camera_position = {0.0, 2.0, 5.0};
+ camera_direction = {0.0, 2.0, 0.0};
+ light_direction = {0.5720610022544861, -0.7071070075035095, -0.41562700271606445};
+ actor_position = sead::Vector3f::zero;
+ actor_rotation = sead::Vector3f::zero;
+ }
+ sead::Vector3f camera_position;
+ sead::Vector3f camera_direction;
+ sead::Vector3f light_direction;
+ sead::Vector3f actor_position;
+ sead::Vector3f actor_rotation;
+ };
+
+ struct CameraInfo : agl::utl::ParameterObj {
+ agl::utl::Parameter<sead::Vector3f> position;
+ agl::utl::Parameter<sead::Vector3f> direction;
+ agl::utl::Parameter<f32> fov;
+ agl::utl::Parameter<f32> tilt;
+ };
+
+ struct ActorInfo : agl::utl::ParameterObj {
+ agl::utl::Parameter<sead::Vector3f> position;
+ agl::utl::Parameter<sead::Vector3f> rotation;
+ agl::utl::Parameter<sead::FixedSafeString<32>> as_name;
+ agl::utl::Parameter<bool> apply_skel_anim;
+ agl::utl::Parameter<f32> frame;
+ agl::utl::Parameter<bool> bounding_adjustment;
+ agl::utl::Parameter<bool> force_idle;
+ agl::utl::Parameter<bool> disable_cloth;
+ };
+
+ struct LightInfo : agl::utl::ParameterObj {
+ agl::utl::Parameter<sead::Vector3f> direction;
+ };
+
+ ActorCapture();
+
+ void doCreate_(u8*, u32, sead::Heap*) override {}
+ bool needsParse() const override { return true; }
+ bool ParamIO_m0(char* data) override { return true; }
+
+ void reset();
+
+ static const ActorCaptureConstants& getConstants();
+
+ CameraInfo mCameraInfoObj;
+ ActorInfo mActorInfoObj;
+ LightInfo mLightInfoObj;
+
+private:
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+};
+KSYS_CHECK_SIZE_NX150(ActorCapture, 0x538);
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceActorLink.cpp b/src/KingSystem/Resource/Actor/resResourceActorLink.cpp
new file mode 100644
index 00000000..d941d417
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceActorLink.cpp
@@ -0,0 +1,103 @@
+#include "KingSystem/Resource/Actor/resResourceActorLink.h"
+#include <codec/seadHashCRC32.h>
+#include <heap/seadHeapMgr.h>
+
+namespace ksys::res {
+
+ActorLink::ActorLink() : ParamIO("xml", 0) {
+ const auto init_user = [this](auto& param, const char* key, const char* default_ = "Dummy") {
+ param.init(default_, key, "", &mUsers.obj);
+ };
+ init_user(mUsers.profile, "ProfileUser");
+ init_user(mUsers.actor_capture, "ActorCaptureUser");
+ init_user(mUsers.as, "ASUser", "");
+ init_user(mUsers.model, "ModelUser", "");
+ init_user(mUsers.anim, "AnimUser", "");
+ init_user(mUsers.ai_program, "AIProgramUser", "");
+ init_user(mUsers.gparam, "GParamUser", "");
+ init_user(mUsers.damage_param, "DamageParamUser");
+ init_user(mUsers.rg_config_list, "RgConfigListUser");
+ init_user(mUsers.rg_blend_weight, "RgBlendWeightUser");
+ init_user(mUsers.awareness, "AwarenessUser");
+ init_user(mUsers.elink, "ElinkUser");
+ init_user(mUsers.slink, "SlinkUser");
+ init_user(mUsers.xlink, "XlinkUser");
+ init_user(mUsers.attention, "AttentionUser");
+ init_user(mUsers.physics, "PhysicsUser");
+ init_user(mUsers.chemical, "ChemicalUser");
+ init_user(mUsers.drop_table, "DropTableUser");
+ init_user(mUsers.shop_data, "ShopDataUser");
+ init_user(mUsers.recipe, "RecipeUser");
+ init_user(mUsers.lod, "LODUser");
+ init_user(mUsers.ai_schedule, "AIScheduleUser");
+ init_user(mUsers.bone_control, "BoneControlUser");
+ init_user(mUsers.life_condition, "LifeConditionUser");
+ init_user(mUsers.umii, "UMiiUser");
+ init_user(mUsers.animation_info, "AnimationInfo");
+
+ mActorScale.init(1.0, "ActorScale", "", &mUsers.obj);
+ mActorNameJpn.init("不正な名前", "ActorNameJpn", "ActorNameJpn", &mUsers.obj);
+ mPriority.init("", "Priority", "Priority", &mUsers.obj);
+
+ addObj(&mUsers.obj, "LinkTarget");
+}
+
+bool ActorLink::parse_(u8* data, size_t, sead::Heap* heap) {
+ if (!data)
+ return true;
+
+ const agl::utl::ResParameterArchive archive{data};
+ const auto root = archive.getRootList();
+
+ const s32 tags_idx = root.searchObjIndex(agl::utl::ParameterBase::calcHash("Tags"));
+ // NON_MATCHING: getResParameter (redundant uxtw; i and res increment order)
+ const auto parse_tags = [&] {
+ if (tags_idx == -1)
+ return;
+
+ const auto obj = root.getResParameterObj(tags_idx);
+ if (!obj.ptr())
+ return;
+
+ const auto num_tags = obj.getNum();
+ if (num_tags == 0)
+ return;
+
+ mHeap = heap;
+ mTags.allocBufferAssert(num_tags, heap);
+
+ for (s32 i = 0; i != num_tags; ++i) {
+ const char* tag = obj.getResParameter(i).getData<char>();
+ mTags[i] = sead::HashCRC32::calcStringHash(tag);
+ }
+
+ if (num_tags > 1)
+ mTags.heapSort(0, num_tags - 1);
+ };
+ parse_tags();
+
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+ return true;
+}
+
+void ActorLink::finalize_() {
+ if (!mTags.isBufferReady())
+ return;
+
+ if (!mHeap)
+ return;
+
+ sead::ScopedCurrentHeapSetter setter{mHeap};
+ mTags.freeBuffer();
+}
+
+// NON_MATCHING: operands are swapped for an equality comparison in binarySearch
+bool ActorLink::hasTag(const char* tag_name) const {
+ return mTags.size() >= 1 && mTags.binarySearch(sead::HashCRC32::calcStringHash(tag_name)) != -1;
+}
+
+bool ActorLink::hasTag(u32 tag) const {
+ return mTags.size() >= 1 && mTags.binarySearch(tag) != -1;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceActorLink.h b/src/KingSystem/Resource/Actor/resResourceActorLink.h
new file mode 100644
index 00000000..0b2f017e
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceActorLink.h
@@ -0,0 +1,137 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterObj.h>
+#include <container/seadBuffer.h>
+#include "KingSystem/ActorSystem/actTag.h"
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+#include "KingSystem/Utils/Types.h"
+
+namespace ksys::res {
+
+class ActorLink : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(ActorLink, Resource)
+public:
+ enum class User {
+ Profile = 0,
+ ActorCapture = 1,
+ AS = 2,
+ Model = 3,
+ Anim = 4,
+ AIProgram = 5,
+ GParam = 6,
+ DamageParam = 7,
+ RgConfigList = 8,
+ RgBlendWeight = 9,
+ Awareness = 10,
+ Physics = 11,
+ Chemical = 12,
+ Attention = 13,
+ ELink = 14,
+ SLink = 15,
+ XLink = 16,
+ DropTable = 17,
+ ShopData = 18,
+ Recipe = 19,
+ LOD = 20,
+ BoneControl = 21,
+ AISchedule = 22,
+ LifeCondition = 23,
+ UMii = 24,
+ AnimationInfo = 25,
+ };
+
+ struct Users {
+ const agl::utl::Parameter<sead::SafeString>& getUser(User user) const {
+ return *(&profile + u32(user));
+ }
+
+ const char* getUserName(User user) const { return getUser(user).ref().cstr(); }
+
+ const char* getProfile() const { return profile.ref().cstr(); }
+ const char* getActorCapture() const { return actor_capture.ref().cstr(); }
+ const char* getAS() const { return as.ref().cstr(); }
+ const char* getModel() const { return model.ref().cstr(); }
+ const char* getAnim() const { return anim.ref().cstr(); }
+ const char* getAIProgram() const { return ai_program.ref().cstr(); }
+ const char* getGParam() const { return gparam.ref().cstr(); }
+ const char* getDamageParam() const { return damage_param.ref().cstr(); }
+ const char* getRgConfigList() const { return rg_config_list.ref().cstr(); }
+ const char* getRgBlendWeight() const { return rg_blend_weight.ref().cstr(); }
+ const char* getAwareness() const { return awareness.ref().cstr(); }
+ const char* getPhysics() const { return physics.ref().cstr(); }
+ const char* getChemical() const { return chemical.ref().cstr(); }
+ const char* getAttention() const { return attention.ref().cstr(); }
+ const char* getELink() const { return elink.ref().cstr(); }
+ const char* getSLink() const { return slink.ref().cstr(); }
+ const char* getXLink() const { return xlink.ref().cstr(); }
+ const char* getDropTable() const { return drop_table.ref().cstr(); }
+ const char* getShopData() const { return shop_data.ref().cstr(); }
+ const char* getRecipe() const { return recipe.ref().cstr(); }
+ const char* getLOD() const { return lod.ref().cstr(); }
+ const char* getBoneControl() const { return bone_control.ref().cstr(); }
+ const char* getAISchedule() const { return ai_schedule.ref().cstr(); }
+ const char* getLifeCondition() const { return life_condition.ref().cstr(); }
+ const char* getUMii() const { return umii.ref().cstr(); }
+ const char* getAnimationInfo() const { return animation_info.ref().cstr(); }
+
+ agl::utl::Parameter<sead::SafeString> profile;
+ agl::utl::Parameter<sead::SafeString> actor_capture;
+ agl::utl::Parameter<sead::SafeString> as;
+ agl::utl::Parameter<sead::SafeString> model;
+ agl::utl::Parameter<sead::SafeString> anim;
+ agl::utl::Parameter<sead::SafeString> ai_program;
+ agl::utl::Parameter<sead::SafeString> gparam;
+ agl::utl::Parameter<sead::SafeString> damage_param;
+ agl::utl::Parameter<sead::SafeString> rg_config_list;
+ agl::utl::Parameter<sead::SafeString> rg_blend_weight;
+ agl::utl::Parameter<sead::SafeString> awareness;
+ agl::utl::Parameter<sead::SafeString> physics;
+ agl::utl::Parameter<sead::SafeString> chemical;
+ agl::utl::Parameter<sead::SafeString> attention;
+ agl::utl::Parameter<sead::SafeString> elink;
+ agl::utl::Parameter<sead::SafeString> slink;
+ agl::utl::Parameter<sead::SafeString> xlink;
+ agl::utl::Parameter<sead::SafeString> drop_table;
+ agl::utl::Parameter<sead::SafeString> shop_data;
+ agl::utl::Parameter<sead::SafeString> recipe;
+ agl::utl::Parameter<sead::SafeString> lod;
+ agl::utl::Parameter<sead::SafeString> bone_control;
+ agl::utl::Parameter<sead::SafeString> ai_schedule;
+ agl::utl::Parameter<sead::SafeString> life_condition;
+ agl::utl::Parameter<sead::SafeString> umii;
+ agl::utl::Parameter<sead::SafeString> animation_info;
+ agl::utl::ParameterObj obj;
+ };
+
+ ActorLink();
+
+ void doCreate_(u8*, u32, sead::Heap*) override {}
+ bool needsParse() const override { return true; }
+
+ const Users& getUsers() const { return mUsers; }
+ const char* getUserName(User user) const { return getUsers().getUserName(user); }
+ const sead::SafeString& getActorNameJpn() const { return mActorNameJpn.ref(); }
+ const sead::SafeString& getPriority() const { return mPriority.ref(); }
+ f32 getActorScale() const { return mActorScale.ref(); }
+
+ bool hasTag(const char* tag_name) const;
+ bool hasTag(u32 tag) const;
+
+ const sead::Buffer<u32>& getTags() const { return mTags; }
+
+private:
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+ void finalize_() override;
+
+ Users mUsers;
+ agl::utl::Parameter<sead::SafeString> mActorNameJpn;
+ agl::utl::Parameter<sead::SafeString> mPriority;
+ agl::utl::Parameter<f32> mActorScale;
+ sead::Buffer<u32> mTags;
+ sead::Heap* mHeap = nullptr;
+};
+KSYS_CHECK_SIZE_NX150(ActorLink, 0x778);
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceAnimInfo.cpp b/src/KingSystem/Resource/Actor/resResourceAnimInfo.cpp
new file mode 100644
index 00000000..7c6da1b0
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceAnimInfo.cpp
@@ -0,0 +1,132 @@
+#include "KingSystem/Resource/Actor/resResourceAnimInfo.h"
+#include "KingSystem/Utils/Byaml/Byaml.h"
+#include "KingSystem/Utils/SafeDelete.h"
+
+namespace ksys::res {
+
+AnimInfo::AnimInfo() : ParamIO("animinfo", 0) {}
+
+AnimInfo::~AnimInfo() {
+ mAnims.freeBuffer();
+
+ if (mSwordBlurInfo) {
+ mSwordBlurInfo->finalize();
+ util::safeDelete(mSwordBlurInfo);
+ }
+}
+
+void AnimInfo::SwordBlurInfo::finalize() {
+ if (!entries)
+ return;
+
+ for (int i = 0; i < num_entries; ++i) {
+ SwordBlur& entry = entries[i];
+ auto* h = heap;
+ if (entry.frames) {
+ h->free(entry.frames);
+ entry.frames = nullptr;
+ }
+ if (entry._18) {
+ h->free(entry._18);
+ entry._18 = nullptr;
+ }
+ }
+
+ heap->free(entries);
+ entries = nullptr;
+}
+
+void AnimInfo::doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) {}
+
+bool AnimInfo::parse_(u8* data, size_t size, sead::Heap* heap) {
+ al::ByamlIter root_iter{data};
+
+ const int num_entries = root_iter.getSize();
+ int num_anims = num_entries - 1;
+ if (num_entries < 1)
+ return true;
+
+ al::ByamlIter iter;
+ int double_attack_anm_num = 0;
+ int num = 0;
+ bool allocate_anims = false;
+ if (root_iter.isExistKey("_sword_blur")) {
+ iter = root_iter.getIterByKey("_sword_blur");
+ const int num_sword_blurs = iter.getSize();
+ iter.tryGetIntByKey(&double_attack_anm_num, "double_attack_anm_num");
+ num = num_sword_blurs - 1;
+ if (num_entries > 1)
+ allocate_anims = true;
+ } else {
+ num = 0;
+ num_anims = num_entries;
+ allocate_anims = true;
+ }
+
+ if (allocate_anims) {
+ if (!mAnims.tryAllocBuffer(num_anims, heap))
+ return false;
+
+ for (int i = 0; i < num_anims; ++i) {
+ auto& anim = mAnims[i];
+ anim.scale.e.fill(0.0);
+
+ al::ByamlIter entry_iter;
+ if (root_iter.tryGetIterByIndex(&entry_iter, i)) {
+ const char* name_c;
+ if (root_iter.getKeyName(&name_c, i)) {
+ if (std::strcmp(name_c, "_sword_blur") == 0)
+ continue;
+ anim.name = name_c;
+ } else {
+ anim.name = "";
+ }
+ entry_iter.tryGetFloatByKey(&anim.scale.x, "scaleX");
+ entry_iter.tryGetFloatByKey(&anim.scale.y, "scaleY");
+ entry_iter.tryGetFloatByKey(&anim.scale.z, "scaleZ");
+ } else {
+ anim.name = "";
+ }
+ }
+ }
+
+ if (num >= 1) {
+ if (!mSwordBlurInfo) {
+ auto* info = new (heap) SwordBlurInfo;
+ info->entries = nullptr;
+ mSwordBlurInfo = info;
+ info->heap = heap;
+ info->num_entries = double_attack_anm_num;
+ /// @bug SwordBlur is not trivially constructible and not trivially copyable.
+ /// The only reason using SwordBlur::name doesn't crash is that Clang and GHS
+ /// are smart enough to devirtualize sead::SafeString virtual function calls,
+ /// which removes the need to go through the vtable.
+#ifdef AVOID_UB
+ info->entries = new (heap) SwordBlur[info->num_entries];
+#else
+ info->entries = static_cast<SwordBlur*>(
+ heap->tryAlloc(sizeof(SwordBlur) * info->num_entries, alignof(SwordBlur)));
+ std::memset(info->entries, 0, sizeof(SwordBlur) * info->num_entries);
+#endif
+ }
+
+ // TODO / FIXME: finish this
+ }
+
+ return true;
+}
+
+const AnimInfo::Anim* AnimInfo::getAnim(const sead::SafeString& name) const {
+ const auto idx = findAnimIndex(name);
+ if (idx < 0)
+ return nullptr;
+ return &mAnims[idx];
+}
+
+int AnimInfo::findAnimIndex(const sead::SafeString& name) const {
+ return mAnims.binarySearch(
+ name,
+ +[](const Anim& anim, const sead::SafeString& key) { return anim.name.compare(key); });
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceAnimInfo.h b/src/KingSystem/Resource/Actor/resResourceAnimInfo.h
new file mode 100644
index 00000000..b27e28a7
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceAnimInfo.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <container/seadBuffer.h>
+#include <math/seadMatrix.h>
+#include <math/seadVector.h>
+#include <prim/seadSafeString.h>
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+#include "KingSystem/Utils/Types.h"
+
+namespace ksys::res {
+
+// TODO
+class AnimInfo : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(AnimInfo, Resource)
+public:
+ struct Anim {
+ sead::SafeString name;
+ sead::Vector3f scale;
+ };
+ KSYS_CHECK_SIZE_NX150(Anim, 0x20);
+
+ struct SwordBlur {
+ int frame_num;
+ int start;
+ int end;
+ sead::Matrix34f* frames;
+ float* _18;
+ sead::SafeString name;
+ };
+ KSYS_CHECK_SIZE_NX150(SwordBlur, 0x30);
+
+ AnimInfo();
+ ~AnimInfo() override;
+
+ bool needsParse() const override { return true; }
+ void doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) override;
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+
+ const sead::Buffer<Anim>& getAnims() const { return mAnims; }
+
+ const Anim* getAnim(const sead::SafeString& name) const;
+ int findAnimIndex(const sead::SafeString& name) const;
+
+private:
+ struct SwordBlurInfo {
+ void finalize();
+
+ sead::Heap* heap;
+ int num_entries;
+ SwordBlur* entries;
+ };
+ KSYS_CHECK_SIZE_NX150(SwordBlurInfo, 0x18);
+
+ sead::Buffer<Anim> mAnims;
+ SwordBlurInfo* mSwordBlurInfo = nullptr;
+};
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceAttCheck.cpp b/src/KingSystem/Resource/Actor/resResourceAttCheck.cpp
new file mode 100644
index 00000000..e75d35d1
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceAttCheck.cpp
@@ -0,0 +1,200 @@
+#include "KingSystem/Resource/Actor/resResourceAttCheck.h"
+
+namespace ksys::res {
+
+void AttCheck::m4() {}
+
+bool AttCheck::check() {
+ return true;
+}
+
+float AttCheck::m6() {
+ return -1.0;
+}
+
+bool AttCheckLine::parse(const CreateArg& arg) {
+ mRadius.init(0.0, "Radius", "半径", "Min=0,Max=10", &mObj);
+ mAsLineOfSight.init(false, "AsLineOfSight", "視線を透かすコリジョンを無視する", "", &mObj);
+ return true;
+}
+
+bool AttCheckArea::parse(const CreateArg& arg) {
+ mAttPos.init(&mObj);
+ mFromPlayer.init(false, "FromPlayer", "目標側基準", "", &mObj);
+ return true;
+}
+
+bool AttCheckAreaSphere::parse(const CreateArg& arg) {
+ mForceEditModelArea.init(false, "ForceEditModelArea", "(モデル範囲)強制編集", "", &mObj);
+ mRadius.init(0.0, "Radius", "(モデル範囲)半径", "Min=0.f,Max=100.f", &mObj);
+ mFixedRadius.init(-1.0, "FixedRadius", "(アテンション範囲)半径", "Min=-1.f,Max=100.f", &mObj);
+ mForceEditMargin.init(false, "ForceEditMargin", "(あそびの範囲)強制編集", "", &mObj);
+ mMarginRadius.init(1.0, "MarginRadius", "(あそびの範囲)半径", "Min=0.f,Max=100.f", &mObj);
+ return AttCheckArea::parse(arg);
+}
+
+bool AttCheckAreaFan::parse(const CreateArg& arg) {
+ mAngleCheckIgnoreLockOn.init(false, "AngleCheckIgnoreLockOn", "ロックオン時も角度チェック有効",
+ "", &mObj);
+ mForceEditModelArea.init(false, "ForceEditModelArea", "(モデル範囲)強制編集", "", &mObj);
+ mRadius.init(0.0, "Radius", "(モデル範囲)半径", "Min=0.f,Max=100.f", &mObj);
+ mTop.init(0.0, "Top", "(モデル範囲)上辺", "Min=-100.f,Max=100.f", &mObj);
+ mBottom.init(0.0, "Bottom", "(モデル範囲)下辺", "Min=-100.f,Max=100.f", &mObj);
+ mAngle.init(0.0, "Angle", "(アテンション範囲)角度", "Min=0.f,Max=3.1415f", &mObj);
+ mFixedRadius.init(-1.0, "FixedRadius", "(アテンション範囲)半径", "Min=-1.f,Max=100.f", &mObj);
+ mFixedTop.init(-1.0, "FixedTop", "(アテンション範囲)上辺", "Min=-1.f,Max=100.f", &mObj);
+ mFixedBottom.init(-1.0, "FixedBottom", "(アテンション範囲)下辺", "Min=-1.f,Max=100.f", &mObj);
+ mForceEditMargin.init(false, "ForceEditMargin", "(あそびの範囲)強制編集", "", &mObj);
+ mMarginRadius.init(1.0, "MarginRadius", "(あそびの範囲)半径", "Min=0.f,Max=100.f", &mObj);
+ mMarginTop.init(1.0, "MarginTop", "(あそびの範囲)上辺", "Min=0.f,Max=100.f", &mObj);
+ mMarginBottom.init(1.0, "MarginBottom", "(あそびの範囲)下辺", "Min=0.f,Max=100.f", &mObj);
+ return AttCheckArea::parse(arg);
+}
+
+bool AttCheckAreaCylinderFan::parse(const CreateArg& arg) {
+ mAngleCheckIgnoreLockOn.init(false, "AngleCheckIgnoreLockOn", "ロックオン時も角度チェック有効",
+ "", &mObj);
+ mForceEditModelArea.init(false, "ForceEditModelArea", "(モデル範囲)強制編集", "", &mObj);
+ mRadius.init(0.0, "Radius", "(モデル範囲)半径", "Min=0.f,Max=100.f", &mObj);
+ mTop.init(0.0, "Top", "(モデル範囲)上辺", "Min=-100.f,Max=100.f", &mObj);
+ mBottom.init(0.0, "Bottom", "(モデル範囲)下辺", "Min=-100.f,Max=100.f", &mObj);
+ mAngle.init(0.0, "Angle", "(アテンション範囲)角度", "Min=0.f,Max=3.1415f", &mObj);
+ mFixedRadiusCylinder.init(-1.0, "FixedRadiusCylinder", "(アテンション範囲)円柱の半径",
+ "Min=-1.f,Max=100.f", &mObj);
+ mFixedRadiusFan.init(-1.0, "FixedRadiusFan", "(アテンション範囲)扇形の半径",
+ "Min=-1.f,Max=100.f", &mObj);
+ mFixedTop.init(-1.0, "FixedTop", "(アテンション範囲)上辺", "Min=-1.f,Max=100.f", &mObj);
+ mFixedBottom.init(-1.0, "FixedBottom", "(アテンション範囲)下辺", "Min=-1.f,Max=100.f", &mObj);
+ mForceEditMargin.init(false, "ForceEditMargin", "(あそびの範囲)強制編集", "", &mObj);
+ mMarginRadiusCylinder.init(1.0, "MarginRadiusCylinder", "(あそびの範囲)円柱の半径",
+ "Min=0.f,Max=100.f", &mObj);
+ mMarginRadiusFan.init(1.0, "MarginRadiusFan", "(あそびの範囲)扇形の半径", "Min=0.f,Max=100.f",
+ &mObj);
+ mMarginTop.init(1.0, "MarginTop", "(あそびの範囲)上辺", "Min=0.f,Max=100.f", &mObj);
+ mMarginBottom.init(1.0, "MarginBottom", "(あそびの範囲)下辺", "Min=0.f,Max=100.f", &mObj);
+ return AttCheckArea::parse(arg);
+}
+
+bool AttCheckAreaBox::parse(const CreateArg& arg) {
+ mForceEditModelArea.init(false, "ForceEditModelArea", "(モデル範囲)強制編集", "", &mObj);
+ mMin.init(sead::Vector3f::zero, "Min", "(モデル範囲)最小", "Min=-100.f,Max=100.f", &mObj);
+ mMax.init(sead::Vector3f::zero, "Max", "(モデル範囲)最大", "Min=-100.f,Max=100.f", &mObj);
+ mFixedMin.init(-1 * sead::Vector3f::ones, "FixedMin", "(アテンション範囲)最小",
+ "Min=-1.f,Max=100.f", &mObj);
+ mFixedMax.init(-1 * sead::Vector3f::ones, "FixedMax", "(アテンション範囲)最大",
+ "Min=-1.f,Max=100.f", &mObj);
+ mForceEditMargin.init(false, "ForceEditMargin", "(あそびの範囲)強制編集", "", &mObj);
+ mMarginMin.init(sead::Vector3f::ones, "MarginMin", "(あそびの範囲)最小", "Min=-100.f,Max=100.f",
+ &mObj);
+ mMarginMax.init(sead::Vector3f::ones, "MarginMax", "(あそびの範囲)最大", "Min=-100.f,Max=100.f",
+ &mObj);
+ return AttCheckArea::parse(arg);
+}
+
+AttCheckEachOtherArea::AttCheckEachOtherArea(AttCheckType type) : AttCheck(type) {}
+
+bool AttCheckEachOtherArea::parse(const CreateArg& arg) {
+ mForceEditModelArea.init(false, "ForceEditModelArea", "(モデル範囲)強制編集", "", &mObj);
+ mRadius.init(0.0, "Radius", "(モデル範囲)半径", "Min=0.f,Max=100.f", &mObj);
+ mTop.init(0.0, "Top", "(モデル範囲)上辺", "Min=-100.f,Max=100.f", &mObj);
+ mBottom.init(0.0, "Bottom", "(モデル範囲)下辺", "Min=-100.f,Max=100.f", &mObj);
+ mFixedRadius.init(-1.0, "FixedRadius", "(アテンション範囲)半径", "Min=-1.f,Max=100.f", &mObj);
+ mFixedTop.init(-1.0, "FixedTop", "(アテンション範囲)上辺", "Min=-1.f,Max=100.f", &mObj);
+ mFixedBottom.init(-1.0, "FixedBottom", "(アテンション範囲)下辺", "Min=-1.f,Max=100.f", &mObj);
+ mForceEditMargin.init(false, "ForceEditMargin", "(あそびの範囲)強制編集", "", &mObj);
+ mMarginRadius.init(1.0, "MarginRadius", "(あそびの範囲)半径", "Min=0.f,Max=100.f", &mObj);
+ mMarginTop.init(1.0, "MarginTop", "(あそびの範囲)上辺", "Min=0.f,Max=100.f", &mObj);
+ mMarginBottom.init(1.0, "MarginBottom", "(あそびの範囲)下辺", "Min=0.f,Max=100.f", &mObj);
+ mOffsetTop.init(0.0, "OffsetTop", "(アテンションを出される側の範囲オフセット)上辺",
+ "Min=-100,Max=100", &mObj);
+ mOffsetBottom.init(0.0, "OffsetBottom", "(アテンションを出される側の範囲オフセット)下辺",
+ "Min=-100,Max=100", &mObj);
+ return AttCheck::parse(arg);
+}
+
+bool AttCheckAngle::parse(const CreateArg& arg) {
+ mAttPos.init(&mObj);
+ mAngle.init(0.0, "Angle", "角度", "Min=0.f,Max=3.1415f", &mObj);
+ return AttCheck::parse(arg);
+}
+
+bool AttCheck::parse(const CreateArg& arg) {
+ return true;
+}
+
+namespace {
+struct AttCheckFactory {
+ const char* name;
+ AttCheck* (*make)(AttCheckType type, sead::Heap* heap);
+};
+
+template <typename T>
+constexpr AttCheckFactory makeFactory(const char* name) {
+ AttCheckFactory factory{};
+ factory.name = name;
+ factory.make = [](AttCheckType type, sead::Heap* heap) -> AttCheck* {
+ return new (heap) T(type);
+ };
+ return factory;
+}
+
+sead::SafeArray<AttCheckFactory, 19> sFactories{{
+ makeFactory<AttCheckLine>("Line"),
+ makeFactory<AttCheckScreen>("Screen"),
+ makeFactory<AttCheckAreaSphere>("AreaSphere"),
+ makeFactory<AttCheckAreaFan>("AreaFan"),
+ makeFactory<AttCheckAreaCylinderFan>("AreaCylinderFan"),
+ makeFactory<AttCheckAreaBox>("AreaBox"),
+ makeFactory<AttCheckEachOtherArea>("EachOtherArea"),
+ makeFactory<AttCheckAngle>("Angle"),
+ makeFactory<AttCheckWeight>("Weight"),
+ makeFactory<AttCheckRideHorse>("RideHorse"),
+ makeFactory<AttCheckRideSpace>("RideSpace"),
+ makeFactory<AttCheckSwim>("Swim"),
+ makeFactory<AttCheckCarry>("Carry"),
+ makeFactory<AttCheckNoCarry>("NoCarry"),
+ makeFactory<AttCheckGrab>("Grab"),
+ makeFactory<AttCheckBootFirstTower>("BootFirstTower"),
+ makeFactory<AttCheckFireContact>("FireContact"),
+ makeFactory<AttCheckCharacterOn>("CharacterOn"),
+ makeFactory<AttCheckUnderWater>("UnderWater"),
+}};
+} // namespace
+
+AttCheck* AttCheck::make(const CreateArg& arg) {
+ const auto Parameters = agl::utl::getResParameterObj(arg.res_list, "Parameters");
+ if (!Parameters)
+ return nullptr;
+
+ int type = sFactories.size();
+ const auto CheckType = agl::utl::getResParameter(Parameters, "CheckType");
+ const sead::SafeString check_type_str = CheckType.getData<char>();
+ for (int i = 0; i < sFactories.size(); ++i) {
+ if (check_type_str == sFactories[i].name) {
+ type = i;
+ break;
+ }
+ }
+
+ if (type == sFactories.size())
+ return nullptr;
+
+ auto* check = sFactories[type].make(AttCheckType(type), arg.heap);
+ if (!check)
+ return nullptr;
+
+ if (!check->init(arg)) {
+ delete check;
+ return nullptr;
+ }
+
+ return check;
+}
+
+bool AttCheck::init(const AttCheck::CreateArg& arg) {
+ mList.addObj(&mObj, "Parameters");
+ mClient = arg.client;
+ return parse(arg);
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceAttCheck.h b/src/KingSystem/Resource/Actor/resResourceAttCheck.h
new file mode 100644
index 00000000..5b788b33
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceAttCheck.h
@@ -0,0 +1,332 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterList.h>
+#include <agl/Utils/aglParameterObj.h>
+#include <prim/seadRuntimeTypeInfo.h>
+#include "resResourceAttClient.h"
+#include "resResourceAttPos.h"
+
+namespace ksys::res {
+
+enum class AttCheckType {
+ Line,
+ Screen,
+ AreaSphere,
+ AreaFan,
+ AreaCylinderFan,
+ AreaBox,
+ EachOtherArea,
+ Angle,
+ Weight,
+ RideHorse,
+ RideSpace,
+ Swim,
+ Carry,
+ NoCarry,
+ Grab,
+ BootFirstTower,
+ FireContact,
+ CharacterOn,
+ UnderWater,
+};
+
+class AttClient;
+
+class AttCheck {
+ SEAD_RTTI_BASE(AttCheck)
+public:
+ struct CreateArg {
+ agl::utl::ResParameterList res_list;
+ sead::Heap* heap;
+ AttClient* client;
+ };
+
+ static AttCheck* make(const CreateArg& arg);
+
+ explicit AttCheck(AttCheckType type) : mType{type} {}
+ virtual ~AttCheck() = default;
+
+ // FIXME: signatures and names
+ virtual void m4();
+ virtual bool check();
+ virtual float m6();
+ virtual void m7() {}
+
+ virtual bool parse(const CreateArg& arg);
+
+ // For internal use by AttClient.
+ agl::utl::ParameterList& getList_() { return mList; }
+
+protected:
+ bool init(const CreateArg& arg);
+
+ AttClient* mClient = nullptr;
+ AttCheckType mType{};
+ agl::utl::ParameterObj mObj;
+ agl::utl::ParameterList mList;
+};
+
+class AttCheckLine : public AttCheck {
+ SEAD_RTTI_OVERRIDE(AttCheckLine, AttCheck)
+public:
+ using AttCheck::AttCheck;
+
+ bool check() override;
+ bool parse(const CreateArg& arg) override;
+
+private:
+ agl::utl::Parameter<float> mRadius;
+ agl::utl::Parameter<bool> mAsLineOfSight;
+};
+
+class AttCheckScreen : public AttCheck {
+ SEAD_RTTI_OVERRIDE(AttCheckScreen, AttCheck)
+public:
+ using AttCheck::AttCheck;
+
+ bool check() override;
+};
+
+class AttCheckArea : public AttCheck {
+ SEAD_RTTI_OVERRIDE(AttCheckArea, AttCheck)
+public:
+ using AttCheck::AttCheck;
+
+ void m4() override;
+ bool check() override;
+ float m6() override;
+ void m7() override;
+ bool parse(const CreateArg& arg) override;
+
+ virtual bool m9() { return true; }
+ virtual float m10() { return 0; }
+ virtual void m11() {}
+
+protected:
+ AttPos mAttPos;
+ agl::utl::Parameter<bool> mFromPlayer;
+};
+
+class AttCheckAreaSphere : public AttCheckArea {
+ SEAD_RTTI_OVERRIDE(AttCheckAreaSphere, AttCheckArea)
+public:
+ using AttCheckArea::AttCheckArea;
+
+ bool parse(const CreateArg& arg) override;
+ bool m9() override;
+ float m10() override;
+ void m11() override;
+
+private:
+ agl::utl::Parameter<bool> mForceEditModelArea;
+ agl::utl::Parameter<float> mRadius;
+ agl::utl::Parameter<float> mFixedRadius;
+ agl::utl::Parameter<bool> mForceEditMargin;
+ agl::utl::Parameter<float> mMarginRadius;
+};
+
+class AttCheckAreaFan : public AttCheckArea {
+ SEAD_RTTI_OVERRIDE(AttCheckAreaFan, AttCheckArea)
+public:
+ using AttCheckArea::AttCheckArea;
+
+ bool parse(const CreateArg& arg) override;
+ bool m9() override;
+ float m10() override;
+ void m11() override;
+
+private:
+ agl::utl::Parameter<bool> mAngleCheckIgnoreLockOn;
+ agl::utl::Parameter<bool> mForceEditModelArea;
+ agl::utl::Parameter<float> mRadius;
+ agl::utl::Parameter<float> mAngle;
+ agl::utl::Parameter<float> mTop;
+ agl::utl::Parameter<float> mBottom;
+ agl::utl::Parameter<float> mMarginRadius;
+ agl::utl::Parameter<float> mMarginTop;
+ agl::utl::Parameter<float> mMarginBottom;
+ agl::utl::Parameter<bool> mForceEditMargin;
+ agl::utl::Parameter<float> mFixedRadius;
+ agl::utl::Parameter<float> mFixedTop;
+ agl::utl::Parameter<float> mFixedBottom;
+};
+
+class AttCheckAreaCylinderFan : public AttCheckArea {
+ SEAD_RTTI_OVERRIDE(AttCheckAreaCylinderFan, AttCheckArea)
+public:
+ using AttCheckArea::AttCheckArea;
+
+ bool parse(const CreateArg& arg) override;
+ bool m9() override;
+ float m10() override;
+
+private:
+ agl::utl::Parameter<bool> mAngleCheckIgnoreLockOn;
+ agl::utl::Parameter<bool> mForceEditModelArea;
+ agl::utl::Parameter<float> mRadius;
+ agl::utl::Parameter<float> mTop;
+ agl::utl::Parameter<float> mBottom;
+ agl::utl::Parameter<float> mAngle;
+ agl::utl::Parameter<float> mFixedRadiusCylinder;
+ agl::utl::Parameter<float> mFixedRadiusFan;
+ agl::utl::Parameter<float> mFixedTop;
+ agl::utl::Parameter<float> mFixedBottom;
+ agl::utl::Parameter<bool> mForceEditMargin;
+ agl::utl::Parameter<float> mMarginRadiusCylinder;
+ agl::utl::Parameter<float> mMarginRadiusFan;
+ agl::utl::Parameter<float> mMarginTop;
+ agl::utl::Parameter<float> mMarginBottom;
+};
+
+class AttCheckAreaBox : public AttCheckArea {
+ SEAD_RTTI_OVERRIDE(AttCheckAreaBox, AttCheckArea)
+public:
+ explicit AttCheckAreaBox(AttCheckType type) : AttCheckArea(type) {}
+
+ bool parse(const CreateArg& arg) override;
+ bool m9() override;
+ float m10() override;
+ void m11() override;
+
+private:
+ agl::utl::Parameter<bool> mForceEditModelArea;
+ agl::utl::Parameter<sead::Vector3f> mMin;
+ agl::utl::Parameter<sead::Vector3f> mMax;
+ agl::utl::Parameter<sead::Vector3f> mFixedMin;
+ agl::utl::Parameter<sead::Vector3f> mFixedMax;
+ agl::utl::Parameter<bool> mForceEditMargin;
+ agl::utl::Parameter<sead::Vector3f> mMarginMin;
+ agl::utl::Parameter<sead::Vector3f> mMarginMax;
+};
+
+class AttCheckEachOtherArea : public AttCheck {
+ SEAD_RTTI_OVERRIDE(AttCheckEachOtherArea, AttCheck)
+public:
+ explicit AttCheckEachOtherArea(AttCheckType type);
+
+ void m4() override;
+ bool check() override;
+ float m6() override;
+ void m7() override;
+ bool parse(const CreateArg& arg) override;
+
+private:
+ agl::utl::Parameter<bool> mForceEditModelArea;
+ agl::utl::Parameter<float> mRadius;
+ agl::utl::Parameter<float> mTop;
+ agl::utl::Parameter<float> mBottom;
+ agl::utl::Parameter<bool> mForceEditMargin;
+ agl::utl::Parameter<float> mMarginRadius;
+ agl::utl::Parameter<float> mMarginTop;
+ agl::utl::Parameter<float> mMarginBottom;
+ agl::utl::Parameter<float> mFixedRadius;
+ agl::utl::Parameter<float> mFixedTop;
+ agl::utl::Parameter<float> mFixedBottom;
+ agl::utl::Parameter<float> mOffsetTop;
+ agl::utl::Parameter<float> mOffsetBottom;
+};
+
+class AttCheckAngle : public AttCheck {
+ SEAD_RTTI_OVERRIDE(AttCheckAngle, AttCheck)
+public:
+ using AttCheck::AttCheck;
+
+ void m4() override;
+ bool check() override;
+ bool parse(const CreateArg& arg) override;
+
+private:
+ AttPos mAttPos;
+ agl::utl::Parameter<float> mAngle;
+};
+
+class AttCheckWeight : public AttCheck {
+ SEAD_RTTI_OVERRIDE(AttCheckWeight, AttCheck)
+public:
+ using AttCheck::AttCheck;
+
+ bool check() override;
+};
+
+class AttCheckRideHorse : public AttCheck {
+ SEAD_RTTI_OVERRIDE(AttCheckRideHorse, AttCheck)
+public:
+ using AttCheck::AttCheck;
+
+ bool check() override;
+};
+
+class AttCheckRideSpace : public AttCheck {
+ SEAD_RTTI_OVERRIDE(AttCheckRideSpace, AttCheck)
+public:
+ using AttCheck::AttCheck;
+
+ bool check() override;
+};
+
+class AttCheckSwim : public AttCheck {
+ SEAD_RTTI_OVERRIDE(AttCheckSwim, AttCheck)
+public:
+ using AttCheck::AttCheck;
+
+ bool check() override;
+};
+
+class AttCheckCarry : public AttCheck {
+ SEAD_RTTI_OVERRIDE(AttCheckCarry, AttCheck)
+public:
+ using AttCheck::AttCheck;
+
+ bool check() override;
+};
+
+class AttCheckNoCarry : public AttCheck {
+ SEAD_RTTI_OVERRIDE(AttCheckNoCarry, AttCheck)
+public:
+ using AttCheck::AttCheck;
+
+ bool check() override;
+};
+
+class AttCheckGrab : public AttCheck {
+ SEAD_RTTI_OVERRIDE(AttCheckGrab, AttCheck)
+public:
+ using AttCheck::AttCheck;
+
+ bool check() override;
+};
+
+class AttCheckBootFirstTower : public AttCheck {
+ SEAD_RTTI_OVERRIDE(AttCheckBootFirstTower, AttCheck)
+public:
+ using AttCheck::AttCheck;
+
+ bool check() override;
+};
+
+class AttCheckFireContact : public AttCheck {
+ SEAD_RTTI_OVERRIDE(AttCheckFireContact, AttCheck)
+public:
+ using AttCheck::AttCheck;
+
+ bool check() override;
+};
+
+class AttCheckCharacterOn : public AttCheck {
+ SEAD_RTTI_OVERRIDE(AttCheckCharacterOn, AttCheck)
+public:
+ using AttCheck::AttCheck;
+
+ bool check() override;
+};
+
+class AttCheckUnderWater : public AttCheck {
+ SEAD_RTTI_OVERRIDE(AttCheckUnderWater, AttCheck)
+public:
+ using AttCheck::AttCheck;
+
+ bool check() override;
+};
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceAttClient.cpp b/src/KingSystem/Resource/Actor/resResourceAttClient.cpp
new file mode 100644
index 00000000..070c5561
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceAttClient.cpp
@@ -0,0 +1,185 @@
+#include "resResourceAttClient.h"
+#include <container/seadSafeArray.h>
+#include "resResourceAttCheck.h"
+
+namespace ksys::res {
+
+AttClientList::~AttClientList() {
+ mClients.freeBuffer();
+}
+
+void AttClientList::doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) {}
+
+bool AttClientList::parse_(u8* data, size_t size, sead::Heap* heap) {
+ if (!data)
+ return false;
+
+ agl::utl::ResParameterArchive archive{data};
+ const auto root = archive.getRootList();
+
+ mAttPos.init(&mAttPosObj);
+ mForceEdit.init(false, "ForceEdit", "強制編集", "", &mAttPosObj);
+ addObj(&mAttPosObj, "AttPos");
+
+ const auto AttClients = agl::utl::getResParameterList(root, "AttClients");
+ if (int num; AttClients && (num = AttClients.getResParameterObjNum()) != 0) {
+ mClients.allocBufferAssert(num, heap);
+ for (auto it = mClients.begin(), end = mClients.end(); it != end; ++it) {
+ it->client = nullptr;
+ it->name.init("", "Name", "クライアントのキー名", "", &it->obj);
+ it->file_name.init("", "FileName", "クライアントのデータファイル名", "", &it->obj);
+ it->is_valid.init(true, "IsValid", "デフォルトの有効・無効状態", "", &it->obj);
+
+ mAttClientsList.addObj(
+ &it->obj, sead::FormatFixedSafeString<32>("%s%d", "AttClient_", it.getIndex()));
+ }
+ }
+
+ addList(&mAttClientsList, "AttClients");
+
+ applyResParameterArchive(archive);
+ return true;
+}
+
+bool AttClientList::finishParsing_() {
+ return true;
+}
+
+bool AttClientList::m7_() {
+ for (auto& client : mClients)
+ client.client = nullptr;
+ return true;
+}
+
+bool AttClientList::isForceEdit() const {
+ return mForceEdit.ref();
+}
+
+AttClient::~AttClient() {
+ for (int i = 0; i < mChecks.size(); ++i) {
+ if (mChecks[i]) {
+ delete mChecks[i];
+ mChecks[i] = nullptr;
+ }
+ }
+ mChecks.freeBuffer();
+}
+
+void AttClient::doCreate_(u8*, u32, sead::Heap*) {}
+
+namespace {
+
+// Keep this in sync with ksys::act::AttType!
+sead::SafeArray<const char*, 8> sAttTypes = {{
+ "Action",
+ "Lock",
+ "SwordSearch",
+ "Attack",
+ "Appeal",
+ "JumpRide",
+ "NameBalloon",
+ "LookOnly",
+}};
+
+void parseAttType(const agl::utl::ResParameterObj& AttClientParams, act::AttType* type) {
+ const sead::SafeString AttType =
+ agl::utl::getResParameter(AttClientParams, "AttType").getData<char>();
+
+ *type = act::AttType::Invalid;
+
+ for (int i = 0; i < sAttTypes.size(); ++i) {
+ if (AttType == sAttTypes[i]) {
+ *type = static_cast<act::AttType>(i);
+ break;
+ }
+ }
+
+ if (*type == act::AttType::Invalid)
+ *type = act::AttType::Action;
+}
+
+void parseActionType(const agl::utl::ResParameterObj& AttClientParams, act::AttActionCode* code) {
+ const sead::SafeString ActionType =
+ agl::utl::getResParameter(AttClientParams, "ActionType").getData<char>();
+
+ *code = act::AttActionCode::Dummy;
+
+ for (int i = int(act::AttActionCode::None); i < int(act::AttActionCode::Dummy); ++i) {
+ if (ActionType == act::AttActionType::text(i - int(act::AttActionCode::None))) {
+ *code = static_cast<act::AttActionCode>(i);
+ break;
+ }
+ }
+
+ if (*code == act::AttActionCode::Dummy)
+ *code = act::AttActionCode::None;
+}
+
+} // namespace
+
+bool AttClient::parse_(u8* data, size_t size, sead::Heap* heap) {
+ if (!data)
+ return false;
+
+ agl::utl::ResParameterArchive archive{data};
+ const auto root = archive.getRootList();
+ const auto AttClientParams = root.getResParameterObj(0);
+
+ parseAttType(AttClientParams, &mAttType);
+ parseActionType(AttClientParams, &mActionCode);
+ addObj(&mAttClientParamsObj, "AttClientParams");
+
+ mAttTypeParam.init({sAttTypes[0]}, "AttType", "", &mAttClientParamsObj);
+ mActionTypeParam.init({act::AttActionType(act::AttActionType::None).text()}, "ActionType", "",
+ &mAttClientParamsObj);
+ mPriorityTypeParam.init({act::AttPriorityType(act::AttPriorityType::Obj).text()},
+ "PriorityType", "", &mAttClientParamsObj);
+
+ const int num_checks = root.getResParameterListNum();
+ if (num_checks != 0) {
+ mChecks.allocBufferAssert(num_checks, heap);
+ for (int i = 0; i < num_checks; ++i)
+ mChecks[i] = nullptr;
+
+ AttCheck::CreateArg arg{};
+ arg.heap = heap;
+ arg.client = this;
+
+ auto it = mChecks.begin(), end = mChecks.end();
+ auto res_it = root.listBegin(), res_end = root.listEnd();
+ for (; it != end && res_it != res_end; ++res_it, ++it) {
+ arg.res_list = res_it.getList();
+
+ auto* check = *it = AttCheck::make(arg);
+ if (check == nullptr)
+ return false;
+
+ addList(&check->getList_(),
+ sead::FormatFixedSafeString<32>("%s%d", "Check_", it.getIndex()));
+ }
+ }
+
+ applyResParameterArchive(archive);
+
+ const sead::SafeString PriorityType = mPriorityTypeParam.ref();
+ for (auto priority : act::AttPriorityType{}) {
+ if (PriorityType == priority.text()) {
+ mPriorityType = priority;
+ mPriorityTypeStr = PriorityType;
+ return true;
+ }
+ }
+ mPriorityType = act::AttPriorityType::Obj;
+ mPriorityTypeStr = mPriorityType.text();
+ return true;
+}
+
+int AttClient::getNumChecks() const {
+ return mChecks.size();
+}
+
+void AttClient::appendPriority(sead::BufferedSafeString* str) {
+ str->append(mPriorityTypeStr);
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceAttClient.h b/src/KingSystem/Resource/Actor/resResourceAttClient.h
new file mode 100644
index 00000000..96df34d3
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceAttClient.h
@@ -0,0 +1,95 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterList.h>
+#include <agl/Utils/aglParameterObj.h>
+#include <container/seadBuffer.h>
+#include <prim/seadSafeString.h>
+#include "KingSystem/ActorSystem/Attention/actAttention.h"
+#include "KingSystem/Resource/Actor/resResourceAttPos.h"
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+#include "KingSystem/Utils/Types.h"
+
+namespace ksys::res {
+
+class AttCheck;
+
+class AttClient : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(AttClient, Resource)
+public:
+ AttClient() : ParamIO("atcl", 0) {}
+ ~AttClient() override;
+
+ act::AttType getAttType() const { return mAttType; }
+ act::AttActionCode getActionCode() const { return mActionCode; }
+ act::AttPriorityType getPriorityType() const { return mPriorityType; }
+ const sead::SafeString& getPriorityTypeStr() const { return mPriorityTypeStr; }
+ const sead::Buffer<AttCheck*>& getChecks() const { return mChecks; }
+
+ int getNumChecks() const;
+
+ // TODO: check functions
+
+ void appendPriority(sead::BufferedSafeString* str);
+
+ void doCreate_(u8*, u32, sead::Heap*) override;
+ bool needsParse() const override { return true; }
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+
+private:
+ act::AttType mAttType = act::AttType::Action;
+ act::AttActionCode mActionCode = act::AttActionCode::None;
+ act::AttPriorityType mPriorityType = act::AttPriorityType::Default;
+ sead::FixedSafeString<32> mPriorityTypeStr;
+ agl::utl::ParameterObj mAttClientParamsObj;
+ agl::utl::Parameter<sead::FixedSafeString<32>> mAttTypeParam;
+ agl::utl::Parameter<sead::FixedSafeString<32>> mActionTypeParam;
+ agl::utl::Parameter<sead::FixedSafeString<32>> mPriorityTypeParam;
+ sead::Buffer<AttCheck*> mChecks;
+};
+KSYS_CHECK_SIZE_NX150(AttClient, 0x428);
+
+class AttClientList : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(AttClientList, Resource)
+public:
+ struct Client {
+ const char* getFileName() const { return file_name.ref().cstr(); }
+
+ agl::utl::Parameter<sead::SafeString> name;
+ agl::utl::Parameter<sead::SafeString> file_name;
+ agl::utl::Parameter<bool> is_valid;
+ agl::utl::ParameterObj obj;
+ AttClient* client;
+ };
+ KSYS_CHECK_SIZE_NX150(Client, 0xa8);
+
+ AttClientList() : ParamIO("atcllist", 0) {}
+ ~AttClientList() override;
+ AttClientList(const AttClientList&) = delete;
+ auto operator=(const AttClientList&) = delete;
+
+ const AttPos& getAttPos() const { return mAttPos; }
+ bool isForceEdit() const;
+ const sead::Buffer<Client>& getClients() const { return mClients; }
+
+ // TODO: one more function
+
+ void addClient_(s32 index, AttClient* client) { mClients[index].client = client; }
+
+ void doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) override;
+ bool needsParse() const override { return true; }
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+ bool finishParsing_() override;
+ bool m7_() override;
+
+private:
+ agl::utl::ParameterList mAttClientsList;
+ agl::utl::ParameterObj mAttPosObj;
+ AttPos mAttPos;
+ agl::utl::Parameter<bool> mForceEdit;
+ sead::Buffer<Client> mClients;
+};
+KSYS_CHECK_SIZE_NX150(AttClientList, 0x3f0);
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceAttPos.cpp b/src/KingSystem/Resource/Actor/resResourceAttPos.cpp
new file mode 100644
index 00000000..1eb43c83
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceAttPos.cpp
@@ -0,0 +1,15 @@
+#include "KingSystem/Resource/Actor/resResourceAttPos.h"
+
+namespace ksys::res {
+
+AttPos::AttPos() = default;
+
+void AttPos::init(agl::utl::IParameterObj* obj, const char* node_key, const char* offset_key,
+ const char* rotate_key, const char* y_rot_only_key) {
+ node.init("", node_key, "ノード", "", obj);
+ offset.init(sead::Vector3f::zero, offset_key, "オフセット", "Min=-100.f,Max=100.f", obj);
+ rotate.init(sead::Vector3f::zero, rotate_key, "回転", "Min=-3.1415f,Max=3.1415f", obj);
+ y_rot_only.init(false, y_rot_only_key, "Y軸回転のみ有効", "", obj);
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceAttPos.h b/src/KingSystem/Resource/Actor/resResourceAttPos.h
new file mode 100644
index 00000000..0251b02c
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceAttPos.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterObj.h>
+#include "KingSystem/Utils/Types.h"
+
+namespace ksys::res {
+
+struct AttPos {
+ AttPos();
+
+ void init(agl::utl::IParameterObj* obj, const char* node_key = "Node",
+ const char* offset_key = "Offset", const char* rotate_key = "Rotate",
+ const char* y_rot_only_key = "YRotOnly");
+
+ // TODO: more functions
+
+ agl::utl::Parameter<sead::SafeString> node;
+ agl::utl::Parameter<sead::Vector3f> offset;
+ agl::utl::Parameter<sead::Vector3f> rotate;
+ agl::utl::Parameter<bool> y_rot_only;
+};
+KSYS_CHECK_SIZE_NX150(AttPos, 0x98);
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceAwareness.cpp b/src/KingSystem/Resource/Actor/resResourceAwareness.cpp
new file mode 100644
index 00000000..9011ac29
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceAwareness.cpp
@@ -0,0 +1,87 @@
+#include "KingSystem/Resource/Actor/resResourceAwareness.h"
+
+namespace ksys::res {
+
+bool Awareness::parse_(u8* data, size_t, sead::Heap*) {
+ addObj(&mBasisObj, "Basis");
+ addObj(&mSightObj, "Sight");
+ addObj(&mHearingObj, "Hearing");
+ addObj(&mSenseObj, "Sense");
+ addObj(&mWorryObj, "Worry");
+
+ contactpoint_num.init(64, "contactpoint_num", "センサーに引っ掛けるバッファの数", &mBasisObj);
+ base_node_name.init(sead::FixedSafeString<32>(""), "base_node_name", "前方基準ノード",
+ &mBasisObj);
+ is_bind_rot.init(false, "is_bind_rot", "姿勢を追従させる", &mBasisObj);
+ is_bind_pos.init(false, "is_bind_pos", "位置を追従させる", &mBasisObj);
+ base_offset.init(sead::Vector3f::zero, "base_offset", "位置オフセット", &mBasisObj);
+ base_dir.init(2, "base_dir", "基準方向", &mBasisObj);
+ up_dir.init(6, "up_dir", "UP方向", &mBasisObj);
+ awareness_target.init(sead::FixedSafeString<32>("一般敵"), "awareness_target", "種別設定",
+ &mBasisObj);
+ interest_lv1_radius.init(5.0, "interest_lv1_radius", "興味値用接近距離", &mBasisObj);
+ system_radius.init(0.0, "system_radius", "システム半径(プログラマ設定用)", &mBasisObj);
+ invalidate_wheather_ratio.init(false, "invalidate_wheather_ratio", "天候影響無視", &mBasisObj);
+
+ sight_buffer_num.init(32, "sight_buffer_num", "情報を格納するバッファの数", &mSightObj);
+ sight_radius.init(15.0, "sight_radius", "発見水平範囲", &mSightObj);
+ sight_angle.init(1.04719758034, "sight_angle", "発見有効角度", &mSightObj);
+ sight_height_max.init(4.0, "sight_height_max", "発見垂直範囲最大", &mSightObj);
+ sight_height_min.init(-2.0, "sight_height_min", "発見垂直範囲最小", &mSightObj);
+ sight_height_near_max.init(4.0, "sight_height_near_max", "至近発見垂直範囲最大", &mSightObj);
+ sight_height_near_min.init(-2.0, "sight_height_near_min", "至近発見垂直範囲最小", &mSightObj);
+ sight_alert_radius.init(0.0, "sight_alert_radius", "警戒水平範囲", &mSightObj);
+ sight_alert_angle.init(0.0, "sight_alert_angle", "警戒有効角度", &mSightObj);
+ sight_alert_height_max.init(0.0, "sight_alert_height_max", "警戒垂直範囲最大", &mSightObj);
+ sight_alert_height_min.init(0.0, "sight_alert_height_min", "警戒垂直範囲最小", &mSightObj);
+ sight_alert_height_near_max.init(0.0, "sight_alert_height_near_max", "至近警戒垂直範囲最大",
+ &mSightObj);
+ sight_alert_height_near_min.init(0.0, "sight_alert_height_near_min", "至近警戒垂直範囲最小",
+ &mSightObj);
+ sight_ignore_grass_radius.init(2.0, "sight_ignore_grass_radius", "草無視範囲", &mSightObj);
+ sight_delay_time_max.init(0, "sight_delay_time_max", "認識遅延時間最大", &mSightObj);
+ sight_ray_check_range_max.init(-1.0, "sight_ray_check_range_max", "レイキャストの長さ制限値",
+ &mSightObj);
+ sight_base_node_name.init(sead::FixedSafeString<32>(""), "sight_base_node_name",
+ "(上書き用)前方基準ノード", &mSightObj);
+ sight_far_use.init(false, "sight_far_use", "遠距離視界を使う", &mSightObj);
+ sight_radius_far.init(0.0, "sight_radius_far", "遠距離開始距離", &mSightObj);
+ sight_angle_far.init(1.04719758034, "sight_angle_far", "遠距離発見有効角度", &mSightObj);
+
+ hearing_buffer_num.init(32, "hearing_buffer_num", "情報を格納するバッファの数", &mHearingObj);
+ hearing_radius.init(15.0, "hearing_radius", "半径", &mHearingObj);
+ hearing_notice_level.init(1.0, "hearing_notice_level", "発見状態になるノイズレベル",
+ &mHearingObj);
+ hearing_alert_level.init(0.40000000596, "hearing_alert_level", "警戒状態になるノイズレベル",
+ &mHearingObj);
+ hearing_blind_angle.init(0.0, "hearing_blind_angle", "死角角度", &mHearingObj);
+ hearing_blind_margin_angle.init(0.0, "hearing_blind_margin_angle", "死角入り角度",
+ &mHearingObj);
+ hearing_reduce_ratio.init(0.0, "hearing_reduce_ratio", "死角に入られた際のノイズ反応倍率",
+ &mHearingObj);
+ hearing_delay_time_max.init(0, "hearing_delay_time_max", "認識遅延時間最大", &mHearingObj);
+ hearing_base_node_name.init(sead::FixedSafeString<32>(""), "hearing_base_node_name",
+ "(上書き用)前方基準ノード", &mHearingObj);
+
+ sense_buffer_num.init(32, "sense_buffer_num", "情報を格納するバッファの数", &mSenseObj);
+ sense_target.init(sead::FixedSafeString<32>("Enemy"), "sense_target", "テラー受付設定",
+ &mSenseObj);
+ sense_radius_offset.init(0.0, "sense_radius_offset", "水平範囲オフセット", &mSenseObj);
+ sense_angle.init(3.14159274101, "sense_angle", "有効角度", &mSenseObj);
+ sense_delay_time_max.init(0, "sense_delay_time_max", "認識遅延時間最大", &mSenseObj);
+ sense_base_node_name.init(sead::FixedSafeString<32>(""), "sense_base_node_name",
+ "(上書き用)前方基準ノード", &mSenseObj);
+
+ worry_buffer_num.init(0, "worry_buffer_num", "情報を格納するバッファの数", &mWorryObj);
+ worry_radius.init(0.0, "worry_radius", "範囲", &mWorryObj);
+ worry_delay_time_max.init(0, "worry_delay_time_max", "認識遅延時間最大", &mWorryObj);
+ worry_base_node_name.init(sead::FixedSafeString<32>(""), "worry_base_node_name",
+ "(上書き用)前方基準ノード", &mWorryObj);
+
+ if (data)
+ applyResParameterArchive(agl::utl::ResParameterArchive(data));
+
+ return true;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceAwareness.h b/src/KingSystem/Resource/Actor/resResourceAwareness.h
new file mode 100644
index 00000000..2e6888ef
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceAwareness.h
@@ -0,0 +1,86 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterObj.h>
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+
+namespace ksys::res {
+
+class Awareness : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(Awareness, Resource)
+public:
+ Awareness() : ParamIO("awareness", 0) {}
+ ~Awareness() override = default;
+
+ bool needsParse() const override { return true; }
+ bool ParamIO_m0(char* data) override { return true; }
+
+private:
+ void doCreate_(u8*, u32, sead::Heap*) override {}
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+
+ agl::utl::ParameterObj mBasisObj;
+ agl::utl::ParameterObj mSightObj;
+ agl::utl::ParameterObj mHearingObj;
+ agl::utl::ParameterObj mSenseObj;
+ agl::utl::ParameterObj mWorryObj;
+
+public:
+ agl::utl::Parameter<s32> contactpoint_num;
+ agl::utl::Parameter<sead::FixedSafeString<32>> base_node_name;
+ agl::utl::Parameter<bool> is_bind_rot;
+ agl::utl::Parameter<bool> is_bind_pos;
+ agl::utl::Parameter<sead::Vector3f> base_offset;
+ agl::utl::Parameter<s32> base_dir;
+ agl::utl::Parameter<s32> up_dir;
+ agl::utl::Parameter<sead::FixedSafeString<32>> awareness_target;
+ agl::utl::Parameter<f32> interest_lv1_radius;
+ agl::utl::Parameter<f32> system_radius;
+ agl::utl::Parameter<bool> invalidate_wheather_ratio;
+
+ agl::utl::Parameter<s32> sight_buffer_num;
+ agl::utl::Parameter<f32> sight_radius;
+ agl::utl::Parameter<f32> sight_angle;
+ agl::utl::Parameter<f32> sight_height_max;
+ agl::utl::Parameter<f32> sight_height_min;
+ agl::utl::Parameter<f32> sight_height_near_max;
+ agl::utl::Parameter<f32> sight_height_near_min;
+ agl::utl::Parameter<f32> sight_alert_radius;
+ agl::utl::Parameter<f32> sight_alert_angle;
+ agl::utl::Parameter<f32> sight_alert_height_max;
+ agl::utl::Parameter<f32> sight_alert_height_min;
+ agl::utl::Parameter<f32> sight_alert_height_near_max;
+ agl::utl::Parameter<f32> sight_alert_height_near_min;
+ agl::utl::Parameter<f32> sight_ignore_grass_radius;
+ agl::utl::Parameter<s32> sight_delay_time_max;
+ agl::utl::Parameter<f32> sight_ray_check_range_max;
+ agl::utl::Parameter<sead::FixedSafeString<32>> sight_base_node_name;
+ agl::utl::Parameter<bool> sight_far_use;
+ agl::utl::Parameter<f32> sight_radius_far;
+ agl::utl::Parameter<f32> sight_angle_far;
+
+ agl::utl::Parameter<s32> hearing_buffer_num;
+ agl::utl::Parameter<f32> hearing_radius;
+ agl::utl::Parameter<f32> hearing_notice_level;
+ agl::utl::Parameter<f32> hearing_alert_level;
+ agl::utl::Parameter<f32> hearing_blind_angle;
+ agl::utl::Parameter<f32> hearing_blind_margin_angle;
+ agl::utl::Parameter<f32> hearing_reduce_ratio;
+ agl::utl::Parameter<s32> hearing_delay_time_max;
+ agl::utl::Parameter<sead::FixedSafeString<32>> hearing_base_node_name;
+
+ agl::utl::Parameter<s32> sense_buffer_num;
+ agl::utl::Parameter<sead::FixedSafeString<32>> sense_target;
+ agl::utl::Parameter<f32> sense_radius_offset;
+ agl::utl::Parameter<f32> sense_angle;
+ agl::utl::Parameter<s32> sense_delay_time_max;
+ agl::utl::Parameter<sead::FixedSafeString<32>> sense_base_node_name;
+
+ agl::utl::Parameter<s32> worry_buffer_num;
+ agl::utl::Parameter<f32> worry_radius;
+ agl::utl::Parameter<s32> worry_delay_time_max;
+ agl::utl::Parameter<sead::FixedSafeString<32>> worry_base_node_name;
+};
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceBoneControl.cpp b/src/KingSystem/Resource/Actor/resResourceBoneControl.cpp
new file mode 100644
index 00000000..1c4fe869
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceBoneControl.cpp
@@ -0,0 +1,239 @@
+#include "KingSystem/Resource/Actor/resResourceBoneControl.h"
+
+namespace ksys::res {
+
+BoneControl::BoneControl() : ParamIO("bonectrl", 0) {}
+
+BoneControl::~BoneControl() {
+ mEyeSets.freeBuffer();
+ mSpine.spineNodes.freeBuffer();
+
+ for (auto& group : mBoneGroups)
+ group.bones.freeBuffer();
+
+ mBoneGroups.freeBuffer();
+}
+
+void BoneControl::doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) {}
+
+// NON_MATCHING: mFootIkController.isInvalidFt (???)
+bool BoneControl::parse_(u8* data, size_t size, sead::Heap* heap) {
+ if (!data)
+ return true;
+
+ agl::utl::ResParameterArchive archive{data};
+ const auto root = archive.getRootList();
+
+ mWhole.neckAndEyeRatio.init(0.0, "neckAndEyeRatio", "首向けと眼球制御の比率", &mWhole.obj);
+ mWhole.isFaceCtrlInvalid.init(true, "isFaceCtrlInvalid", "顔全体無効", &mWhole.obj);
+ addObj(&mWhole.obj, "Whole");
+
+ mEyeBall.isEyeBallCtrlInvalid.init(false, "isEyeBallCtrlInvalid", "無効にする", &mEyeBall.obj);
+ mEyeBall.isEyeBallRotWorldAxis.init(false, "isEyeBallRotWorldAxis", "ワールド軸で回転する",
+ &mEyeBall.obj);
+ mEyeBall.eyeBallSRTName.init("", "eyeBallSRTName", "眼球SRT名", &mEyeBall.obj);
+ mEyeBall.eyeRotRateLR.init(0.0, "eyeRotRateLR", "左右回転比率", &mEyeBall.obj);
+ mEyeBall.eyeRotRateUD.init(0.0, "eyeRotRateUD", "上下回転比率", &mEyeBall.obj);
+ mEyeBall.eyeMinRotPerFrame.init(0.5, "eyeMinRotPerFrame", "フレーム毎の最小回転量",
+ &mEyeBall.obj);
+ mEyeBall.eyeMaxRotPerFrame.init(6.0, "eyeMaxRotPerFrame", "フレーム毎の最大回転量",
+ &mEyeBall.obj);
+ mEyeBall.eyeSetNum.init(0, "eyeSetNum", "眼球セット数", &mEyeBall.obj);
+ addObj(&mEyeBall.obj, "EyeBall");
+
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+
+ const int eye_set_num = mEyeBall.eyeSetNum.ref();
+ if (eye_set_num > 0) {
+ if (!mEyeSets.tryAllocBuffer(eye_set_num, heap))
+ return false;
+
+ for (int i = 0; i < eye_set_num; ++i) {
+ sead::FormatFixedSafeString<64> name("");
+ name.format("EyeSet_%02d", i);
+
+ mEyeSets[i].isControlTexture.init(false, "isControlTexture", "テクスチャ制御する",
+ &mEyeSets[i].obj);
+ mEyeSets[i].materialName.init("", "materialName", "マテリアル名", &mEyeSets[i].obj);
+ mEyeSets[i].boneName.init("", "boneName", "ボーン名", &mEyeSets[i].obj);
+ mEyeSets[i].forwardBoneName.init("", "forwardBoneName", "前方方向ボーン名",
+ &mEyeSets[i].obj);
+ mEyeSets[i].forwardAxis.init(1, "forwardAxis", "前方方向とする軸", &mEyeSets[i].obj);
+ mEyeSets[i].axisLR.init(0, "axisLR", "左右回転軸", &mEyeSets[i].obj);
+ mEyeSets[i].axisUD.init(0, "axisUD", "上下回転軸", &mEyeSets[i].obj);
+ mEyeSets[i].lTransLimit.init(0.0, "lTransLimit", "左移動量上限", &mEyeSets[i].obj);
+ mEyeSets[i].rTransLimit.init(0.0, "rTransLimit", "右移動量上限", &mEyeSets[i].obj);
+ mEyeSets[i].dTransLimit.init(0.0, "dTransLimit", "下移動量上限", &mEyeSets[i].obj);
+ mEyeSets[i].uTransLimit.init(0.0, "uTransLimit", "上移動量上限", &mEyeSets[i].obj);
+ mEyeSets[i].isCorrectForward.init(false, "isCorrectForward", "前方方向を補正する",
+ &mEyeSets[i].obj);
+ mEyeSets[i].axisCorrect.init(0, "axisCorrect", "補正軸", &mEyeSets[i].obj);
+ mEyeSets[i].correctRot.init(0.0, "correctRot", "補正量", &mEyeSets[i].obj);
+ mEyeSets[i].lRotLimit.init(0.0, "lRotLimit", "左向き角上限", &mEyeSets[i].obj);
+ mEyeSets[i].rRotLimit.init(0.0, "rRotLimit", "右向き角上限", &mEyeSets[i].obj);
+ mEyeSets[i].dRotLimit.init(0.0, "dRotLimit", "下向き角上限", &mEyeSets[i].obj);
+ mEyeSets[i].uRotLimit.init(0.0, "uRotLimit", "上向き角上限", &mEyeSets[i].obj);
+ mEyeSets[i].offset.init({0.0, 0.0, 0.0}, "offset", "オフセット", &mEyeSets[i].obj);
+
+ addObj(&mEyeSets[i].obj, name);
+ }
+ }
+
+ mSpine.isInvalid.init(false, "isInvalid", "無効にする", &mSpine.obj);
+ mSpine.isBasisSelfPosNeckLR.init(false, "isBasisSelfPosNeckLR",
+ "左右計算を自分の位置基準にする", &mSpine.obj);
+ mSpine.isBasisSelfPosNeckUD.init(false, "isBasisSelfPosNeckUD",
+ "上下計算を自分の位置基準にする", &mSpine.obj);
+ mSpine.isBattleNeckRecalcUD.init(false, "isBattleNeckRecalcUD",
+ "戦闘時の首向け上下角を再計算する", &mSpine.obj);
+ mSpine.spineDisableBaseDirAlongXZ.init(false, "spineDisableBaseDirAlongXZ",
+ "基準方向をXZ平面に沿わせない", &mSpine.obj);
+ mSpine.spineRotRate.init(0.0, "spineRotRate", "回転比率", &mSpine.obj);
+ mSpine.spineRetRotRate.init(0.0, "spineRetRotRate", "戻り回転比率", &mSpine.obj);
+ mSpine.spineNeckBaseBone.init("", "spineNeckBaseBone", "首向け基準位置ボーン名", &mSpine.obj);
+ mSpine.neckPosOffset.init(sead::Vector3f::zero, "neckPosOffset", "オフセット", &mSpine.obj);
+ mSpine.spineNodeNum.init(0, "spineNodeNum", "背骨ノード数", &mSpine.obj);
+ mSpine.spineNeckNodeNum.init(0, "spineNeckNodeNum", "首とみなすノード数", &mSpine.obj);
+ addObj(&mSpine.obj, "Spine");
+
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+
+ const auto spine_node_num = mSpine.spineNodeNum.ref();
+ if (spine_node_num > 0) {
+ if (!mSpine.spineNodes.tryAllocBuffer(spine_node_num, heap))
+ return false;
+
+ for (int i = 0; i < spine_node_num; ++i) {
+ sead::FormatFixedSafeString<64> name("");
+ name.format("SpineNode_%02d", i);
+
+ mSpine.spineNodes[i].boneName.init("", "boneName", "ボーン名",
+ &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].isRotWorldAxis.init(
+ false, "isRotWorldAxis", "ワールド軸で回転する", &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].axisLR.init(0, "axisLR", "左右回転軸", &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].axisUD.init(0, "axisUD", "上下回転軸", &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].lLimit.init(0.0, "lLimit", "左向き角上限",
+ &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].rLimit.init(0.0, "rLimit", "右向き角上限",
+ &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].dLimit.init(0.0, "dLimit", "下向き角上限",
+ &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].uLimit.init(0.0, "uLimit", "上向き角上限",
+ &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].lBattleLimit.init(0.0, "lBattleLimit", "左向き角上限(戦闘時)",
+ &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].rBattleLimit.init(0.0, "rBattleLimit", "右向き角上限(戦闘時)",
+ &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].dBattleLimit.init(0.0, "dBattleLimit", "下向き角上限(戦闘時)",
+ &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].uBattleLimit.init(0.0, "uBattleLimit", "上向き角上限(戦闘時)",
+ &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].minRotPerFrame.init(
+ 0.5, "minRotPerFrame", "フレーム毎の最小回転量", &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].maxRotPerFrame.init(
+ 6.0, "maxRotPerFrame", "フレーム毎の最大回転量", &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].isEnableCorrect.init(
+ false, "isEnableCorrect", "左右回転時に補正する", &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].axisCorrect.init(0, "axisCorrect", "補正回転軸",
+ &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].lCorrect.init(0.0, "lCorrect", "左向き補正回転量",
+ &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].rCorrect.init(0.0, "rCorrect", "右向き補正回転量",
+ &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].lBattleCorrect.init(
+ 0.0, "lBattleCorrect", "左向き補正回転量(戦闘時)", &mSpine.spineNodes[i].obj);
+ mSpine.spineNodes[i].rBattleCorrect.init(
+ 0.0, "rBattleCorrect", "右向き補正回転量(戦闘時)", &mSpine.spineNodes[i].obj);
+
+ addObj(&mSpine.spineNodes[i].obj, name);
+ }
+ }
+
+ mFootIkController.isInvalidFt.init(true, "isInvalidFt", "無効", &mFootIkController.obj);
+ mFootIkController.calculateTypeFt.init(1, "calculateTypeFt", "計算タイプ",
+ &mFootIkController.obj);
+ mFootIkController.ankleOffsetYFt.init(0.125, "ankleOffsetYFt", "地面から足首(Ankle)までの高さ",
+ &mFootIkController.obj);
+ mFootIkController.ankleOffsetAngleDegFt.init(-27.0, "ankleOffsetAngleDegFt",
+ "足首オフセット角度(Deg)", &mFootIkController.obj);
+ mFootIkController.ankleAngleLimitUpDegFt.init(
+ 90.0, "ankleAngleLimitUpDegFt", "上方向への足首回転最大角度(Deg)", &mFootIkController.obj);
+ mFootIkController.ankleAngleLimitDownDegFt.init(-90.0, "ankleAngleLimitDownDegFt",
+ "下方向への足首回転最大角度(Deg)",
+ &mFootIkController.obj);
+ mFootIkController.ankleHeightLimitRateFt.init(
+ 0.8, "ankleHeightLimitRateFt", "地面に対して足位置の制限比率", &mFootIkController.obj);
+ mFootIkController.waistDownRateFt.init(0.7, "waistDownRateFt", "腰を落とす最長比率",
+ &mFootIkController.obj);
+ mFootIkController.kneeRotateAgnleMinDegFt.init(
+ 0.0, "kneeRotateAgnleMinDegFt", "ヒザの最小回転角度(Deg)", &mFootIkController.obj);
+ mFootIkController.kneeRotateAgnleMaxDegFt.init(
+ 180.0, "kneeRotateAgnleMaxDegFt", "ヒザの最大回転角度(Deg)", &mFootIkController.obj);
+ mFootIkController.enableLimitThighAngleFt.init(
+ false, "enableLimitThighAngleFt", "モモの角度制限を行なうか?", &mFootIkController.obj);
+ mFootIkController.thighRotateAngleMinDegFt.init(
+ -180.0, "thighRotateAngleMinDegFt", "モモの最小回転角度(Deg)", &mFootIkController.obj);
+ mFootIkController.thighRotateAngleMaxDegFt.init(
+ 180.0, "thighRotateAngleMaxDegFt", "モモの最大回転角度(Deg)", &mFootIkController.obj);
+ addObj(&mFootIkController.obj, "FootIkController");
+
+ const auto bone_groups = agl::utl::getResParameterList(root, "BoneGroups");
+ if (bone_groups.ptr() && bone_groups.getResParameterListNum() != 0) {
+ if (!mBoneGroups.tryAllocBuffer(bone_groups.getResParameterListNum(), heap))
+ return false;
+
+ sead::FixedSafeString<32> bone_group_name{"BoneGroup_"};
+ const auto bone_group_name_base_len = bone_group_name.calcLength();
+
+ sead::FixedSafeString<32> bone_name{"Bone_"};
+ const auto bone_name_base_len = bone_name.calcLength();
+
+ for (auto it = mBoneGroups.begin(), end = mBoneGroups.end(); it != end; ++it) {
+ const auto list = bone_groups.getResParameterList(it.getIndex());
+ if (!list.ptr())
+ continue;
+
+ const auto bones = agl::utl::getResParameterObj(list, "Bones");
+ if (!bones.ptr())
+ continue;
+
+ it->groupName.init("", "GroupName", "グループ名", &it->paramObj);
+
+ const auto num_bones = bones.getNum();
+ if (num_bones != 0 && !it->bones.tryAllocBuffer(num_bones, heap))
+ return false;
+
+ auto& bones_obj = it->bonesObj;
+ for (auto b = it->bones.begin(), bone_end = it->bones.end(); b != bone_end; ++b) {
+ bone_name.trim(bone_name_base_len);
+ bone_name.appendWithFormat("%d", b.getIndex());
+ b->name.init("", bone_name, "ボーン名", &bones_obj);
+ }
+
+ it->list.addObj(&bones_obj, "Bones");
+ it->list.addObj(&it->paramObj, "Param");
+
+ bone_group_name.trim(bone_group_name_base_len);
+ bone_group_name.appendWithFormat("%d", it.getIndex());
+ mBoneGroupsList.addList(&it->list, bone_group_name);
+ }
+
+ addList(&mBoneGroupsList, "BoneGroups");
+ }
+
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+ return true;
+}
+
+const BoneControl::BoneGroup* BoneControl::getBoneGroup(const sead::SafeString& name) const {
+ const auto idx = mBoneGroups.binarySearch(
+ name, +[](const BoneGroup& group, const sead::SafeString& key) {
+ return group.groupName.ref().compare(key);
+ });
+ if (idx == -1)
+ return nullptr;
+ return &mBoneGroups[idx];
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceBoneControl.h b/src/KingSystem/Resource/Actor/resResourceBoneControl.h
new file mode 100644
index 00000000..4cb74ea5
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceBoneControl.h
@@ -0,0 +1,166 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterList.h>
+#include <agl/Utils/aglParameterObj.h>
+#include <container/seadBuffer.h>
+#include <math/seadVector.h>
+#include <prim/seadSafeString.h>
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+#include "KingSystem/Utils/Types.h"
+
+namespace ksys::res {
+
+class BoneControl : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(BoneControl, Resource)
+public:
+ struct Whole {
+ agl::utl::ParameterObj obj;
+ agl::utl::Parameter<bool> isFaceCtrlInvalid;
+ agl::utl::Parameter<float> neckAndEyeRatio;
+ };
+ KSYS_CHECK_SIZE_NX150(Whole, 0x70);
+
+ struct EyeBall {
+ agl::utl::ParameterObj obj;
+ agl::utl::Parameter<bool> isEyeBallCtrlInvalid;
+ agl::utl::Parameter<bool> isEyeBallRotWorldAxis;
+ agl::utl::Parameter<sead::SafeString> eyeBallSRTName;
+ agl::utl::Parameter<float> eyeRotRateLR;
+ agl::utl::Parameter<float> eyeRotRateUD;
+ agl::utl::Parameter<float> eyeMinRotPerFrame;
+ agl::utl::Parameter<float> eyeMaxRotPerFrame;
+ agl::utl::Parameter<int> eyeSetNum;
+ };
+ KSYS_CHECK_SIZE_NX150(EyeBall, 0x138);
+
+ struct EyeSet {
+ agl::utl::ParameterObj obj;
+ agl::utl::Parameter<sead::SafeString> boneName;
+ agl::utl::Parameter<bool> isControlTexture;
+ agl::utl::Parameter<sead::SafeString> materialName;
+ agl::utl::Parameter<sead::SafeString> forwardBoneName;
+ agl::utl::Parameter<int> forwardAxis;
+ agl::utl::Parameter<int> axisLR;
+ agl::utl::Parameter<int> axisUD;
+ agl::utl::Parameter<float> lTransLimit;
+ agl::utl::Parameter<float> rTransLimit;
+ agl::utl::Parameter<float> dTransLimit;
+ agl::utl::Parameter<float> uTransLimit;
+ agl::utl::Parameter<bool> isCorrectForward;
+ agl::utl::Parameter<int> axisCorrect;
+ agl::utl::Parameter<float> correctRot;
+ agl::utl::Parameter<float> lRotLimit;
+ agl::utl::Parameter<float> rRotLimit;
+ agl::utl::Parameter<float> dRotLimit;
+ agl::utl::Parameter<float> uRotLimit;
+ agl::utl::Parameter<sead::Vector3f> offset;
+ };
+ KSYS_CHECK_SIZE_NX150(EyeSet, 0x2b0);
+
+ struct Bone {
+ agl::utl::Parameter<sead::SafeString> name;
+ };
+ KSYS_CHECK_SIZE_NX150(Bone, 0x28);
+
+ struct BoneGroup {
+ agl::utl::ParameterList list;
+ agl::utl::ParameterObj paramObj;
+ agl::utl::ParameterObj bonesObj;
+ agl::utl::Parameter<sead::SafeString> groupName;
+ sead::Buffer<Bone> bones;
+ };
+ KSYS_CHECK_SIZE_NX150(BoneGroup, 0xe0);
+
+ struct SpineNode {
+ agl::utl::ParameterObj obj;
+ agl::utl::Parameter<sead::SafeString> boneName;
+ agl::utl::Parameter<bool> isRotWorldAxis;
+ agl::utl::Parameter<int> axisLR;
+ agl::utl::Parameter<int> axisUD;
+ agl::utl::Parameter<float> lLimit;
+ agl::utl::Parameter<float> rLimit;
+ agl::utl::Parameter<float> dLimit;
+ agl::utl::Parameter<float> uLimit;
+ agl::utl::Parameter<float> lBattleLimit;
+ agl::utl::Parameter<float> rBattleLimit;
+ agl::utl::Parameter<float> dBattleLimit;
+ agl::utl::Parameter<float> uBattleLimit;
+ agl::utl::Parameter<bool> isEnableCorrect;
+ agl::utl::Parameter<int> axisCorrect;
+ agl::utl::Parameter<float> lCorrect;
+ agl::utl::Parameter<float> rCorrect;
+ agl::utl::Parameter<float> lBattleCorrect;
+ agl::utl::Parameter<float> rBattleCorrect;
+ agl::utl::Parameter<float> minRotPerFrame;
+ agl::utl::Parameter<float> maxRotPerFrame;
+ };
+ KSYS_CHECK_SIZE_NX150(SpineNode, 0x2b8);
+
+ struct Spine {
+ agl::utl::ParameterObj obj;
+ agl::utl::ParameterList list;
+ agl::utl::Parameter<bool> isInvalid;
+ agl::utl::Parameter<bool> isBasisSelfPosNeckLR;
+ agl::utl::Parameter<bool> isBasisSelfPosNeckUD;
+ agl::utl::Parameter<bool> isBattleNeckRecalcUD;
+ agl::utl::Parameter<bool> spineDisableBaseDirAlongXZ;
+ agl::utl::Parameter<float> spineRotRate;
+ agl::utl::Parameter<float> spineRetRotRate;
+ agl::utl::Parameter<sead::SafeString> spineNeckBaseBone;
+ agl::utl::Parameter<sead::Vector3f> neckPosOffset;
+ agl::utl::Parameter<int> spineNeckNodeNum;
+ agl::utl::Parameter<int> spineNodeNum;
+ sead::Buffer<SpineNode> spineNodes;
+ };
+ KSYS_CHECK_SIZE_NX150(Spine, 0x1f8);
+
+ struct FootIkController {
+ agl::utl::ParameterObj obj;
+ agl::utl::Parameter<bool> isInvalidFt;
+ agl::utl::Parameter<int> calculateTypeFt;
+ agl::utl::Parameter<float> ankleOffsetYFt;
+ agl::utl::Parameter<float> ankleOffsetAngleDegFt;
+ agl::utl::Parameter<float> ankleAngleLimitUpDegFt;
+ agl::utl::Parameter<float> ankleAngleLimitDownDegFt;
+ agl::utl::Parameter<float> ankleHeightLimitRateFt;
+ agl::utl::Parameter<float> waistDownRateFt;
+ agl::utl::Parameter<float> kneeRotateAgnleMinDegFt;
+ agl::utl::Parameter<float> kneeRotateAgnleMaxDegFt;
+ agl::utl::Parameter<bool> enableLimitThighAngleFt;
+ agl::utl::Parameter<float> thighRotateAngleMinDegFt;
+ agl::utl::Parameter<float> thighRotateAngleMaxDegFt;
+ };
+ KSYS_CHECK_SIZE_NX150(FootIkController, 0x1d0);
+
+ BoneControl();
+ ~BoneControl() override;
+
+ void doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) override;
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+ bool ParamIO_m0(char* data) override { return true; }
+ bool needsParse() const override { return true; }
+
+ const Whole& getWhole() const { return mWhole; }
+ const Spine& getSpine() const { return mSpine; }
+ const EyeBall& getEyeBall() const { return mEyeBall; }
+ const sead::Buffer<EyeSet>& getEyeSets() const { return mEyeSets; }
+ const FootIkController& getFootIkController() const { return mFootIkController; }
+ const sead::Buffer<BoneGroup>& getBoneGroups() const { return mBoneGroups; }
+
+ const BoneGroup* getBoneGroup(const sead::SafeString& name) const;
+
+private:
+ Whole mWhole;
+ Spine mSpine;
+ EyeBall mEyeBall;
+ agl::utl::ParameterList _650;
+ sead::Buffer<EyeSet> mEyeSets;
+ FootIkController mFootIkController;
+ agl::utl::ParameterList mBoneGroupsList;
+ sead::Buffer<BoneGroup> mBoneGroups;
+};
+KSYS_CHECK_SIZE_NX150(BoneControl, 0x8d0);
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceChemical.cpp b/src/KingSystem/Resource/Actor/resResourceChemical.cpp
new file mode 100644
index 00000000..45346d1f
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceChemical.cpp
@@ -0,0 +1,24 @@
+#include "KingSystem/Resource/Actor/resResourceChemical.h"
+
+namespace ksys::res {
+
+Chemical::Chemical() : ParamIO("chemical", 0) {
+ addList(&mRoot, "chemical_root");
+}
+
+void Chemical::doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) {}
+
+bool Chemical::parse_(u8* data, size_t size, sead::Heap* heap) {
+ if (!data)
+ return true;
+
+ agl::utl::ResParameterArchive archive{data};
+
+ const auto chemical_root_list =
+ agl::utl::getResParameterList(archive.getRootList(), "chemical_root");
+ mRoot.parse(chemical_root_list, heap);
+
+ return true;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceChemical.h b/src/KingSystem/Resource/Actor/resResourceChemical.h
new file mode 100644
index 00000000..4b27461c
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceChemical.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "KingSystem/Chemical/chmRoot.h"
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+
+namespace ksys::res {
+
+class Chemical : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(Chemical, Resource)
+
+public:
+ Chemical();
+
+ void doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) override;
+ bool needsParse() const override { return true; }
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+
+ const chm::Root& getRoot() const { return mRoot; }
+
+private:
+ chm::Root mRoot;
+};
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceDamageParam.cpp b/src/KingSystem/Resource/Actor/resResourceDamageParam.cpp
new file mode 100644
index 00000000..432f2fdc
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceDamageParam.cpp
@@ -0,0 +1,255 @@
+#include "KingSystem/Resource/Actor/resResourceDamageParam.h"
+
+namespace ksys::res {
+
+bool DamageParam::parse_(u8* data, size_t, sead::Heap* heap) {
+ mDamageRateBuffer.allocBufferAssert(DamageSource::size(), heap);
+ mDamageTypeBuffer.allocBufferAssert(DamageSource::size() * DamageSize::size(), heap);
+
+ sead::FormatFixedSafeString<64> str;
+ for (int i = 0; i < DamageSource::size(); ++i) {
+ str.format("%s", DamageSource::text(i));
+ mDamageRateBuffer[i].init(1.0, str, "", &mDamageRateObj);
+ }
+
+ for (int source = 0; source < DamageSource::size(); ++source) {
+ for (int size = 0; size < DamageSize::size(); ++size) {
+ str.format("%s-%s", DamageSource::text(source), DamageSize::text(size));
+ if (source == DamageSource::Arrow) {
+ mDamageTypeBuffer.get(source * 4 + size)
+ ->init("通常ダメージ", str, "", &mReactionTableObj);
+ } else if (size == DamageSize::Large || size == DamageSize::Huge ||
+ source == DamageSource::Bomb || source == DamageSource::LargeSword) {
+ mDamageTypeBuffer.get(source * 4 + size)
+ ->init("吹っ飛び", str, "", &mReactionTableObj);
+ } else if (size == DamageSize::Middle) {
+ mDamageTypeBuffer.get(source * 4 + size)
+ ->init("中ダメージ", str, "", &mReactionTableObj);
+ } else {
+ mDamageTypeBuffer.get(source * 4 + size)
+ ->init("通常ダメージ", str, "", &mReactionTableObj);
+ }
+ }
+ }
+
+ mBreakable.init(false, "Breakable", "壊れるかどうか(AI無しで破壊エフェクト・消滅挙動を行う)",
+ "", &mParametersObj);
+ mHammerAffect.init(
+ false, "HammerAffect",
+ "ハンマーが有効かどうか(ハンマー属性にチェックを入れた武器で攻撃されると即死する)", "",
+ &mParametersObj);
+ mWeakBreakerAffect.init(false, "WeakBreakerAffect", "Objを一撃破壊しない武器設定が有効か", "",
+ &mParametersObj);
+ mChemicalAttackAffect.init(true, "ChemicalAttackAffect",
+ "ケミカルAtによる追加ダメージを受けるか(Ex:ロッドの玉)", "",
+ &mParametersObj);
+ mSpAffectRatio.init(1.0, "SpAffectRatio", "特効ダメージ倍率(武器倍率と掛け算される)", "",
+ &mParametersObj);
+ mSpAffectDamage.init(0, "SpAffectDamage", "特効ダメージ加算値(ダメージ計算後に加算される)", "",
+ &mParametersObj);
+ mVanishAffect.init(false, "VanishAffect", "消滅攻撃有効(Ex:古代の矢)", "", &mParametersObj);
+ mIsCriticalBlowOff.init(true, "IsCriticalBlowOff", "クリティカルで吹き飛ぶ", "",
+ &mParametersObj);
+ mIsAcceptAtkImpulse.init(false, "IsAcceptAtkImpulse",
+ "攻撃的衝撃力を受けるかどうか(Ex:ボコブリンの石)", "",
+ &mParametersObj);
+ mIsCommonCalcImpuleDamage.init(
+ true, "IsCommonCalcImpuleDamage",
+ "衝撃力ダメージを自動計算する(Offの場合のみImpulseDamageTableが有効になります)", "",
+ &mParametersObj);
+ mIsHeavyBreak.init(false, "IsHeavyBreak", "巨体属性でへし折れる", "", &mParametersObj);
+ mImpulseThresholdLv0.init(-1.0, "ImpulseThresholdLv0", "(Small)衝撃力ダメージ閾値", "",
+ &mParametersObj);
+ mImpulseDamageLv0.init(0.0, "ImpulseDamageLv0", "(Small)衝撃力ダメージ値", "", &mParametersObj);
+ mImpulseThresholdLv1.init(-1.0, "ImpulseThresholdLv1", "(Middle)衝撃力ダメージ閾値", "",
+ &mParametersObj);
+ mImpulseDamageLv1.init(0.0, "ImpulseDamageLv1", "(Middle)衝撃力ダメージ値", "",
+ &mParametersObj);
+ mImpulseThresholdLv2.init(-1.0, "ImpulseThresholdLv2", "(Large)衝撃力ダメージ閾値", "",
+ &mParametersObj);
+ mImpulseDamageLv2.init(0.0, "ImpulseDamageLv2", "(Large)衝撃力ダメージ値", "", &mParametersObj);
+ mImpulseThresholdLv3.init(-1.0, "ImpulseThresholdLv3", "(Huge)衝撃力ダメージ閾値", "",
+ &mParametersObj);
+ mImpulseDamageLv3.init(0.0, "ImpulseDamageLv3", "(Huge)衝撃力ダメージ値", "", &mParametersObj);
+ mImpulseThresholdLv4.init(-1.0, "ImpulseThresholdLv4", "(即死)衝撃力ダメージ閾値", "",
+ &mParametersObj);
+ mFallDamageStartHeight.init(10000.0, "FallDamageStartHeight", "落下ダメージ開始高さ", "",
+ &mParametersObj);
+ mFallDamageMin.init(0, "FallDamageMin", "落下ダメージ最低値", "", &mParametersObj);
+ mFallDamagePerMeter.init(0.0, "FallDamagePerMeter", "1mあたりの加算落下ダメージ量", "",
+ &mParametersObj);
+ mFallDamageHighStart.init(-1.0, "FallDamageHighStart", "強落下ダメージ開始高さ(-1で無効)", "",
+ &mParametersObj);
+ mFallDamageHighPerMeter.init(0, "FallDamageHighPerMeter",
+ "強落下時1mあたりの加算落下ダメージ量", "", &mParametersObj);
+ mFallDamageNoWaterDepth.init(-1.0, "FallDamageNoWaterDepth", "落下ダメージ受けない水深", "",
+ &mParametersObj);
+ mWeakPointBone.init(sead::SafeString(""), "WeakPointBone", "弱手ボーン名(空白だとモデル原点)",
+ "", &mParametersObj);
+ mWeakPointCalcArea.init(0.0, "WeakPointCalcArea", "弱点判定エリア(弱点不要アクターはここを0に)",
+ "", &mParametersObj);
+ mWeakPointCalcOffset.init({0, 0, 0}, "WeakPointCalcOffset",
+ "弱手判定エリアの指定ボーンからのオフセット(ローカル座標系)", "",
+ &mParametersObj);
+ mWeakPointArea.init(0.0, "WeakPointArea", "弱点エリア", "", &mParametersObj);
+ mWeakPointOffset.init({0, 0, 0}, "WeakPointOffset",
+ "弱点エリアの指定ボーンからのオフセット(ローカル座標系)", "",
+ &mParametersObj);
+ mWeakPointRatio.init(4.0, "WeakPointRatio", "弱点ダメージ倍率", "", &mParametersObj);
+ mWeakPointNoUIFlag.init(false, "WeakPointNoUIFlag", "ヒット時説明UIを出さない", "",
+ &mParametersObj);
+ mWeakPointBone2.init(sead::SafeString(""), "WeakPointBone2", "弱手ボーン名(空白だとモデル原点)",
+ "", &mParametersObj);
+ mWeakPointCalcArea2.init(0.0, "WeakPointCalcArea2",
+ "弱点判定エリア(弱点不要アクターはここを0に)", "", &mParametersObj);
+ mWeakPointCalcOffset2.init({0, 0, 0}, "WeakPointCalcOffset2",
+ "弱手判定エリアの指定ボーンからのオフセット(ローカル座標系)", "",
+ &mParametersObj);
+ mWeakPointArea2.init(0.0, "WeakPointArea2", "弱点エリア", "", &mParametersObj);
+ mWeakPointOffset2.init({0, 0, 0}, "WeakPointOffset2",
+ "弱点エリアの指定ボーンからのオフセット(ローカル座標系)", "",
+ &mParametersObj);
+ mWeakPointRatio2.init(1.0, "WeakPointRatio2", "弱点ダメージ倍率", "", &mParametersObj);
+ mWeakPointNoUIFlag2.init(false, "WeakPointNoUIFlag2", "ヒット時説明UIを出さない", "",
+ &mParametersObj);
+ mSillentKillMultRatio.init(1.0, "SillentKillMultRatio", "奇襲ダメージ乗算倍率", "",
+ &mParametersObj);
+ mSillentKillAddDamage.init(0, "SillentKillAddDamage", "奇襲ダメージ加算値", "",
+ &mParametersObj);
+ mIsDeadBurnout.init(false, "IsDeadBurnout", "燃え尽きでライフ0", "", &mParametersObj);
+ mIsMetamorBurnout.init(false, "IsMetamorBurnout", "燃え尽きでアクタ変化", "", &mParametersObj);
+ mIsMatamorFromTg.init(true, "IsMatamorFromTg", "炎剣で切っても変化する", "", &mParametersObj);
+ mBurnable.init(false, "Burnable", "燃え状態有効", "", &mParametersObj);
+ mBurnDamage.init(10, "BurnDamage", "燃えダメージLv1(-1でダメージ受けない)", "",
+ &mParametersObj);
+ mBurnDamage2.init(-1, "BurnDamage2", "燃えダメージLv2(-1でLv1と同じ)", "", &mParametersObj);
+ mBurnDamage3.init(-1, "BurnDamage3", "燃えダメージLv3(-1でLv2と同じ)", "", &mParametersObj);
+ mBurnDamage4.init(-1, "BurnDamage4", "燃えダメージLv4(-1でLv3と同じ)", "", &mParametersObj);
+ mBurnDamage5.init(-1, "BurnDamage5", "燃えダメージLv5(-1でLv4と同じ)", "", &mParametersObj);
+ mBurnTime.init(10, "BurnTime", "燃え時間", "", &mParametersObj);
+ mBurnDamageInterval.init(-1, "BurnDamageInterval", "[火に接触時]燃えダメージ間隔(-1で無し)", "",
+ &mParametersObj);
+ mBurnContinuousDamage.init(0, "BurnContinuousDamage", "[火に接触時]燃え継続ダメージ", "",
+ &mParametersObj);
+ mBurnCritical.init(false, "BurnCritical", "火異常がクリティカル扱いか", "", &mParametersObj);
+ mProofBurnAtSmallDamage.init(false, "ProofBurnAtSmallDamage",
+ "火弾無効時小ダメージアクションをとるか", "", &mParametersObj);
+ mIsDeadIce.init(false, "IsDeadIce", "凍結でライフ0", "", &mParametersObj);
+ mIsMetamorIce.init(false, "IsMetamorIce", "凍結でアクタ変化", "", &mParametersObj);
+ mIceable.init(false, "Iceable", "凍結状態有効", "", &mParametersObj);
+ mIceDamage.init(10, "IceDamage", "凍結ダメージ", "", &mParametersObj);
+ mIceTime.init(10, "IceTime", "凍結時間(f)", "", &mParametersObj);
+ mIceBreakableByAtk.init(true, "IceBreakableByAtk", "凍結時攻撃で割れるか", "", &mParametersObj);
+ mIceBreakDamageRatio.init(4.0, "IceBreakDamageRatio", "凍結破壊ダメージ倍率", "",
+ &mParametersObj);
+ mIceCritical.init(false, "IceCritical",
+ "凍結異常がクリティカル扱いか(濡れたら死ぬようになります)", "",
+ &mParametersObj);
+ mProofIceAtSmallDamage.init(false, "ProofIceAtSmallDamage",
+ "冷凍弾無効時小ダメージアクションをとるか", "", &mParametersObj);
+ mIsDeadElectric.init(false, "IsDeadElectric", "帯電でライフ0", "", &mParametersObj);
+ mIsMetamorElectric.init(false, "IsMetamorElectric", "帯電でアクタ変化", "", &mParametersObj);
+ mElectricable.init(false, "Electricable", "痺れ状態有効", "", &mParametersObj);
+ mElectricDamage.init(0, "ElectricDamage", "痺れダメージ", "", &mParametersObj);
+ mElectricTime.init(10, "ElectricTime", "痺れ時間(f)", "", &mParametersObj);
+ mElecCancelableByAtk.init(false, "ElecCancelableByAtk", "痺れ時攻撃でキャンセルされるか", "",
+ &mParametersObj);
+ mProofElecAtSmallDamage.init(false, "ProofElecAtSmallDamage",
+ "電気弾無効時小ダメージアクションをとるか", "", &mParametersObj);
+ mWetAffect.init(false, "WetAffect", "濡れ有効", "", &mParametersObj);
+ mLightningAffect.init(true, "LightningAffect", "落雷有効", "", &mParametersObj);
+ mLightningDamage.init(999999, "LightningDamage", "落雷ダメージ", "", &mParametersObj);
+ mGerudoHeroAffect.init(true, "GerudoHeroAffect", "英傑加護(雷)有効", "", &mParametersObj);
+ mGerudoHeroDamage.init(100, "GerudoHeroDamage", "英傑加護(雷)ダメージ", "", &mParametersObj);
+ mGerudoHeroTime.init(300, "GerudoHeroTime", "英傑加護(雷)痺れ時間(0だと吹っ飛び)", "",
+ &mParametersObj);
+ mGustAffect.init(true, "GustAffect", "突風有効", "", &mParametersObj);
+ mDrownHeight.init(-1.0, "DrownHeight", "溺れる水深(-1で溺れない)", "", &mParametersObj);
+ mDrownDamage.init(0, "DrownDamage", "溺れた瞬間に食らうダメージ(特殊な場合を除き0でいい)", "",
+ &mParametersObj);
+ mColdWaterFrozenTime.init(-1, "ColdWaterFrozenTime", "冷たい水地形で凍結する時間", "",
+ &mParametersObj);
+ mColdWaterAffect.init(true, "ColdWaterAffect", "冷たい水地形でダメージ有", "", &mParametersObj);
+ mColdWaterDamage.init(0, "ColdWaterDamage", "冷たい水ダメージ値", "", &mParametersObj);
+ mColdWaterDamageInterval.init(-1, "ColdWaterDamageInterval", "冷たい水ダメージ最浅間隔", "",
+ &mParametersObj);
+ mColdWaterDamageStartDepth.init(0.0, "ColdWaterDamageStartDepth", "冷たい水ダメージ最浅閾値",
+ "", &mParametersObj);
+ mColdWaterDamageIntervalDeep.init(-1, "ColdWaterDamageIntervalDeep",
+ "冷たい水ダメージ最深間隔(-1なら使わない)", "",
+ &mParametersObj);
+ mColdWaterDamageDeepDepth.init(-1.0, "ColdWaterDamageDeepDepth",
+ "冷たい水ダメージ最深閾値(-1で最深間隔は使わない)", "",
+ &mParametersObj);
+ mHotWaterBoiledTime.init(
+ -1, "HotWaterBoiledTime",
+ "熱湯地形で茹であがる時間(-1で無効,有効にする場合は以下は無効設定にして下さい)", "",
+ &mParametersObj);
+ mHotWaterHealAffect.init(false, "HotWaterHealAffect", "温泉地形で影響有", "", &mParametersObj);
+ mHotWaterHeal.init(0, "HotWaterHeal", "温泉回復値", "", &mParametersObj);
+ mHotWaterHealInterval.init(-1, "HotWaterHealInterval", "温泉影響最浅間隔", "", &mParametersObj);
+ mHotWaterHealStartDepth.init(0.0, "HotWaterHealStartDepth", "温泉影響最浅閾値", "",
+ &mParametersObj);
+ mHotWaterHealIntervalDeep.init(-1, "HotWaterHealIntervalDeep",
+ "温泉影響最深間隔(-1なら使わない)", "", &mParametersObj);
+ mHotWaterHealDeepDepth.init(-1.0, "HotWaterHealDeepDepth",
+ "温泉影響最深閾値(-1で最深間隔は使わない)", "", &mParametersObj);
+ mHotWaterChemCrit.init(false, "HotWaterChemCrit", "温泉入るとケミカルクリティカル", "",
+ &mParametersObj);
+ mPoisonBogAffect.init(true, "PoisonBogAffect", "毒沼地形でダメージ有", "", &mParametersObj);
+ mPoisonBogDamage.init(0, "PoisonBogDamage", "毒沼ダメージ値", "", &mParametersObj);
+ mPoisonBogDamageInterval.init(-1, "PoisonBogDamageInterval", "毒沼ダメージ間隔", "",
+ &mParametersObj);
+ mLavaAffect.init(true, "LavaAffect", "溶岩地形でダメージ有", "", &mParametersObj);
+ mLavaDamage.init(999999, "LavaDamage", "溶岩ダメージ値", "", &mParametersObj);
+ mLavaDamageInterval.init(-1, "LavaDamageInterval", "溶岩ダメージ間隔", "", &mParametersObj);
+ mLavaDeepDepth.init(-1.0, "LavaDeepDepth", "深い溶岩閾値", "", &mParametersObj);
+ mLavaDeepDamage.init(999999, "LavaDeepDamage", "深い溶岩ダメージ値", "", &mParametersObj);
+ mCurseAffect.init(false, "CurseAffect", "怨念ダメージ状態有効", "", &mParametersObj);
+ mCurseDamage.init(30, "CurseDamage", "怨念ダメージ", "", &mParametersObj);
+ mCurseInterval.init(60, "CurseInterval", "怨念ダメージ間隔(-1で無し)", "", &mParametersObj);
+ mCurseContinuousDamage.init(5, "CurseContinuousDamage", "怨念継続ダメージ", "",
+ &mParametersObj);
+ mHeavySnowColdTime.init(-1, "HeavySnowColdTime", "大雪時凍るまでの時間(-1で凍らない)", "",
+ &mParametersObj);
+
+ mKeyString.init(sead::SafeString(""), "key", "", &mDamageTypeObj);
+
+ mParamList.addObj(&mDamageRateObj, "DamageRate");
+ mParamList.addObj(&mReactionTableObj, "ReactionTable");
+ mParamList.addObj(&mParametersObj, "Parameters");
+ mParamList.addObj(&mDamageTypeObj, "DamageType");
+ addList(&mParamList, "damage_param");
+
+ if (data)
+ applyResParameterArchive(agl::utl::ResParameterArchive(data));
+
+ return true;
+}
+
+f32 DamageParam::getDamageRate(const sead::SafeString& damage_source) {
+ u32 hash = agl::utl::ParameterBase::calcHash(damage_source);
+
+#pragma clang loop unroll(full)
+ for (int idx = 0; idx != 10; ++idx) {
+ if (hash == mDamageRateBuffer[idx].getNameHash())
+ return mDamageRateBuffer[idx].ref();
+ }
+ return 0.0;
+}
+
+const sead::SafeString& DamageParam::getDamageReaction(const sead::SafeString& damage_source,
+ const sead::SafeString& size) {
+ sead::FormatFixedSafeString<128> key("%s-%s", damage_source.cstr(), size.cstr());
+
+ u32 hash = agl::utl::ParameterBase::calcHash(key);
+
+#pragma clang loop unroll(full)
+ for (int idx = 0; idx != 40; ++idx) {
+ if (hash == mDamageTypeBuffer.get(idx)->getNameHash())
+ return mDamageTypeBuffer.get(idx)->ref();
+ }
+ return sead::SafeString::cEmptyString;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceDamageParam.h b/src/KingSystem/Resource/Actor/resResourceDamageParam.h
new file mode 100644
index 00000000..30d75c44
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceDamageParam.h
@@ -0,0 +1,155 @@
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterObj.h>
+#include <container/seadBuffer.h>
+#include <prim/seadEnum.h>
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+
+namespace ksys::res {
+
+class DamageParam : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(DamageParam, Resource)
+
+public:
+ DamageParam() : ParamIO("dmgparam", 0) {}
+ ~DamageParam() override = default;
+
+ bool needsParse() const override { return true; }
+ bool ParamIO_m0(char* data) override { return false; }
+
+ f32 getDamageRate(const sead::SafeString& damage_source);
+ const sead::SafeString& getDamageReaction(const sead::SafeString& damage_source,
+ const sead::SafeString& damage_size);
+
+ // clang-format off
+ SEAD_ENUM(DamageSize,Small,Middle,Large,Huge)
+ SEAD_ENUM(DamageSource,Sword,LargeSword,Spear,Arrow,Bomb,Body,Ancient,ShockWave,Impulse,GanonBeam)
+ // clang-format on
+
+private:
+ void doCreate_(u8*, u32, sead::Heap*) override {}
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+
+ agl::utl::ParameterList mParamList;
+ agl::utl::ParameterObj mReactionTableObj;
+ agl::utl::ParameterObj mDamageRateObj;
+ agl::utl::ParameterObj mParametersObj;
+ agl::utl::ParameterObj mDamageTypeObj;
+
+ sead::Buffer<agl::utl::Parameter<f32>> mDamageRateBuffer;
+ sead::Buffer<agl::utl::Parameter<sead::SafeString>> mDamageTypeBuffer;
+
+public:
+ agl::utl::Parameter<sead::SafeString> mKeyString;
+ agl::utl::Parameter<bool> mBreakable;
+ agl::utl::Parameter<bool> mHammerAffect;
+ agl::utl::Parameter<bool> mWeakBreakerAffect;
+ agl::utl::Parameter<bool> mChemicalAttackAffect;
+ agl::utl::Parameter<f32> mSpAffectRatio;
+ agl::utl::Parameter<s32> mSpAffectDamage;
+ agl::utl::Parameter<bool> mVanishAffect;
+ agl::utl::Parameter<bool> mIsCriticalBlowOff;
+ agl::utl::Parameter<bool> mIsAcceptAtkImpulse;
+ agl::utl::Parameter<bool> mIsCommonCalcImpuleDamage;
+ agl::utl::Parameter<bool> mIsHeavyBreak;
+ agl::utl::Parameter<f32> mImpulseThresholdLv0;
+ agl::utl::Parameter<f32> mImpulseDamageLv0;
+ agl::utl::Parameter<f32> mImpulseThresholdLv1;
+ agl::utl::Parameter<f32> mImpulseDamageLv1;
+ agl::utl::Parameter<f32> mImpulseThresholdLv2;
+ agl::utl::Parameter<f32> mImpulseDamageLv2;
+ agl::utl::Parameter<f32> mImpulseThresholdLv3;
+ agl::utl::Parameter<f32> mImpulseDamageLv3;
+ agl::utl::Parameter<f32> mImpulseThresholdLv4;
+ agl::utl::Parameter<f32> mFallDamageStartHeight;
+ agl::utl::Parameter<s32> mFallDamageMin;
+ agl::utl::Parameter<f32> mFallDamagePerMeter;
+ agl::utl::Parameter<f32> mFallDamageHighStart;
+ agl::utl::Parameter<s32> mFallDamageHighPerMeter;
+ agl::utl::Parameter<f32> mFallDamageNoWaterDepth;
+ agl::utl::Parameter<sead::SafeString> mWeakPointBone;
+ agl::utl::Parameter<f32> mWeakPointCalcArea;
+ agl::utl::Parameter<sead::Vector3f> mWeakPointCalcOffset;
+ agl::utl::Parameter<f32> mWeakPointArea;
+ agl::utl::Parameter<sead::Vector3f> mWeakPointOffset;
+ agl::utl::Parameter<f32> mWeakPointRatio;
+ agl::utl::Parameter<bool> mWeakPointNoUIFlag;
+ agl::utl::Parameter<sead::SafeString> mWeakPointBone2;
+ agl::utl::Parameter<f32> mWeakPointCalcArea2;
+ agl::utl::Parameter<sead::Vector3f> mWeakPointCalcOffset2;
+ agl::utl::Parameter<f32> mWeakPointArea2;
+ agl::utl::Parameter<sead::Vector3f> mWeakPointOffset2;
+ agl::utl::Parameter<f32> mWeakPointRatio2;
+ agl::utl::Parameter<bool> mWeakPointNoUIFlag2;
+ agl::utl::Parameter<f32> mSillentKillMultRatio;
+ agl::utl::Parameter<s32> mSillentKillAddDamage;
+ agl::utl::Parameter<bool> mIsDeadBurnout;
+ agl::utl::Parameter<bool> mIsMetamorBurnout;
+ agl::utl::Parameter<bool> mIsMatamorFromTg;
+ agl::utl::Parameter<bool> mBurnable;
+ agl::utl::Parameter<s32> mBurnDamage;
+ agl::utl::Parameter<s32> mBurnDamage2;
+ agl::utl::Parameter<s32> mBurnDamage3;
+ agl::utl::Parameter<s32> mBurnDamage4;
+ agl::utl::Parameter<s32> mBurnDamage5;
+ agl::utl::Parameter<s32> mBurnTime;
+ agl::utl::Parameter<s32> mBurnDamageInterval;
+ agl::utl::Parameter<s32> mBurnContinuousDamage;
+ agl::utl::Parameter<bool> mBurnCritical;
+ agl::utl::Parameter<bool> mProofBurnAtSmallDamage;
+ agl::utl::Parameter<bool> mIsDeadIce;
+ agl::utl::Parameter<bool> mIsMetamorIce;
+ agl::utl::Parameter<bool> mIceable;
+ agl::utl::Parameter<s32> mIceDamage;
+ agl::utl::Parameter<s32> mIceTime;
+ agl::utl::Parameter<bool> mIceBreakableByAtk;
+ agl::utl::Parameter<f32> mIceBreakDamageRatio;
+ agl::utl::Parameter<bool> mIceCritical;
+ agl::utl::Parameter<bool> mProofIceAtSmallDamage;
+ agl::utl::Parameter<bool> mIsDeadElectric;
+ agl::utl::Parameter<bool> mIsMetamorElectric;
+ agl::utl::Parameter<bool> mElectricable;
+ agl::utl::Parameter<s32> mElectricDamage;
+ agl::utl::Parameter<s32> mElectricTime;
+ agl::utl::Parameter<bool> mElecCancelableByAtk;
+ agl::utl::Parameter<bool> mProofElecAtSmallDamage;
+ agl::utl::Parameter<bool> mWetAffect;
+ agl::utl::Parameter<bool> mLightningAffect;
+ agl::utl::Parameter<s32> mLightningDamage;
+ agl::utl::Parameter<bool> mGerudoHeroAffect;
+ agl::utl::Parameter<s32> mGerudoHeroDamage;
+ agl::utl::Parameter<s32> mGerudoHeroTime;
+ agl::utl::Parameter<bool> mGustAffect;
+ agl::utl::Parameter<f32> mDrownHeight;
+ agl::utl::Parameter<s32> mDrownDamage;
+ agl::utl::Parameter<s32> mColdWaterFrozenTime;
+ agl::utl::Parameter<bool> mColdWaterAffect;
+ agl::utl::Parameter<s32> mColdWaterDamage;
+ agl::utl::Parameter<s32> mColdWaterDamageInterval;
+ agl::utl::Parameter<f32> mColdWaterDamageStartDepth;
+ agl::utl::Parameter<s32> mColdWaterDamageIntervalDeep;
+ agl::utl::Parameter<f32> mColdWaterDamageDeepDepth;
+ agl::utl::Parameter<s32> mHotWaterBoiledTime;
+ agl::utl::Parameter<bool> mHotWaterHealAffect;
+ agl::utl::Parameter<s32> mHotWaterHeal;
+ agl::utl::Parameter<s32> mHotWaterHealInterval;
+ agl::utl::Parameter<f32> mHotWaterHealStartDepth;
+ agl::utl::Parameter<s32> mHotWaterHealIntervalDeep;
+ agl::utl::Parameter<f32> mHotWaterHealDeepDepth;
+ agl::utl::Parameter<bool> mHotWaterChemCrit;
+ agl::utl::Parameter<bool> mPoisonBogAffect;
+ agl::utl::Parameter<s32> mPoisonBogDamage;
+ agl::utl::Parameter<s32> mPoisonBogDamageInterval;
+ agl::utl::Parameter<bool> mLavaAffect;
+ agl::utl::Parameter<s32> mLavaDamage;
+ agl::utl::Parameter<s32> mLavaDamageInterval;
+ agl::utl::Parameter<f32> mLavaDeepDepth;
+ agl::utl::Parameter<s32> mLavaDeepDamage;
+ agl::utl::Parameter<bool> mCurseAffect;
+ agl::utl::Parameter<s32> mCurseDamage;
+ agl::utl::Parameter<s32> mCurseInterval;
+ agl::utl::Parameter<s32> mCurseContinuousDamage;
+ agl::utl::Parameter<s32> mHeavySnowColdTime;
+};
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceDrop.cpp b/src/KingSystem/Resource/Actor/resResourceDrop.cpp
new file mode 100644
index 00000000..d3696ba3
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceDrop.cpp
@@ -0,0 +1,173 @@
+#include "KingSystem/Resource/Actor/resResourceDrop.h"
+#include <random/seadGlobalRandom.h>
+
+namespace ksys::res {
+
+bool Drop::parse_(u8* data, size_t, sead::Heap* heap) {
+ mTableNum.init(0, "TableNum", "テーブルの数", &mObj);
+ addObj(&mObj, "Header");
+
+ if (!data)
+ return true;
+
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+
+ const s32 num_tables = mTableNum.ref();
+ if (num_tables < 1)
+ return true;
+
+ mTables.allocBufferAssert(num_tables, heap);
+
+ const agl::utl::ResParameterArchive archive{data};
+ const auto root = archive.getRootList();
+ const auto header_obj = root.getResParameterObj(0);
+
+ for (s32 i = 0; i < num_tables; ++i) {
+ sead::FormatFixedSafeString<64> name;
+ name.format("Table%02d", i + 1);
+ mTables[i].name.init("", name, "テーブル名", &mObj);
+ }
+
+ mObj.applyResParameterObj(header_obj);
+
+ for (s32 i = 0; i < num_tables; ++i) {
+ const auto obj = root.getResParameterObj(i + 1);
+
+ mTables[i].repeat_num_min.init(0, "RepeatNumMin", "抽選回数最小", &mTables[i].obj);
+ mTables[i].repeat_num_max.init(0, "RepeatNumMax", "抽選回数最大", &mTables[i].obj);
+ mTables[i].approach_type.init(0, "ApproachType", "姿勢", &mTables[i].obj);
+ mTables[i].occurrence_speed_type.init(0, "OccurrenceSpeedType", "発生速度",
+ &mTables[i].obj);
+ mTables[i].column_num.init(0, "ColumnNum", "行数", &mTables[i].obj);
+
+ addObj(&mTables[i].obj, mTables[i].name.ref());
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+ mTables[i].obj.applyResParameterObj(obj);
+
+ if (mTables[i].column_num.ref() > 0) {
+ mTables[i].items.allocBufferAssert(mTables[i].column_num.ref(), heap);
+ }
+ }
+
+ for (s32 i = 0; i < num_tables; ++i) {
+ parseItems_(i);
+ }
+
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+ return true;
+}
+
+void Drop::parseItems_(s32& table_idx) {
+ const s32 num = mTables[table_idx].column_num.ref();
+ for (s32 i = 0; i < num; ++i) {
+ sead::FormatFixedSafeString<64> name;
+ name.format("ItemName%02d", i + 1);
+ mTables[table_idx].items[i].name.init("", name, "アイテム名", &mTables[table_idx].obj);
+
+ sead::FormatFixedSafeString<64> name2;
+ name2.format("ItemProbability%02d", i + 1);
+ mTables[table_idx].items[i].probability.init(0.0, name2, "確率", &mTables[table_idx].obj);
+ }
+}
+
+s32 Drop::findTableIndex(const sead::SafeString& table_name) const {
+ if (!mTables.isBufferReady())
+ return -1;
+
+ const s32 num = mTableNum.ref();
+ for (s32 i = 0; i < num; ++i) {
+ if (mTables[i].name.ref() == table_name)
+ return i;
+ }
+
+ return -1;
+}
+
+s32 Drop::findTableIndexOrNormal(const sead::SafeString& table_name) const {
+ if (!mTables.isBufferReady())
+ return -1;
+
+ s32 normal_idx = -1;
+ const s32 num = mTableNum.ref();
+ for (s32 i = 0; i < num; ++i) {
+ if (mTables[i].name.ref() == table_name)
+ return i;
+
+ if (normal_idx < 0 && mTables[i].name.ref() == "Normal")
+ normal_idx = i;
+ }
+
+ return normal_idx;
+}
+
+const sead::SafeString& Drop::getRandomDropFromTable(const sead::SafeString& table_name) const {
+ return getRandomDropFromTable(findTableIndexOrNormal(table_name));
+}
+
+const sead::SafeString& Drop::getRandomDropFromTable(s32 table_idx) const {
+ if (!mTables.isBufferReady())
+ return sead::SafeString::cEmptyString;
+
+ /// @bug The index check should be done first...
+ if (!mTables[table_idx].items.isBufferReady() || table_idx < 0)
+ return sead::SafeString::cEmptyString;
+
+ f32 x = sead::GlobalRandom::instance()->getF32() * 100.0;
+
+ const Table& table = mTables[table_idx];
+ const s32 num_items = table.column_num.ref();
+ for (s32 i = 0; i < num_items; ++i) {
+ const Item& item = table.items[i];
+
+ const f32 probability = item.probability.ref();
+ if (x < probability)
+ return item.name.ref();
+
+ x -= probability;
+ }
+
+ return sead::SafeString::cEmptyString;
+}
+
+s32 Drop::getApproachType(s32 table_idx) const {
+ if (!mTables.isBufferReady())
+ return 0;
+
+ /// @bug This bounds checking is bugged: the order is absurd and the second check is off-by-one.
+ if (!mTables[table_idx].items.isBufferReady() || table_idx < 0 || table_idx > mTables.size())
+ return 0;
+
+ return mTables[table_idx].approach_type.ref();
+}
+
+s32 Drop::getOccurrenceSpeedType(s32 table_idx) const {
+ if (!mTables.isBufferReady())
+ return 0;
+
+ /// @bug This bounds checking is bugged: the order is absurd and the second check is off-by-one.
+ if (!mTables[table_idx].items.isBufferReady() || table_idx < 0 || table_idx > mTables.size())
+ return 0;
+
+ return mTables[table_idx].occurrence_speed_type.ref();
+}
+
+s32 Drop::getRepeatNum(s32 table_idx) const {
+ if (!mTables.isBufferReady())
+ return 0;
+
+ const Table& table = mTables[table_idx];
+ if (!mTables[table_idx].items.isBufferReady() || table_idx < 0 || table_idx > mTables.size())
+ return 0;
+
+ const s32 num_min = table.repeat_num_min.ref();
+ const s32 num_max = table.repeat_num_max.ref();
+ if (num_min == num_max)
+ return num_min;
+ return num_min + sead::GlobalRandom::instance()->getU32(1 - num_min + num_max);
+}
+
+s32 Drop::getRepeatNum(const sead::SafeString& table_name) const {
+ return getRepeatNum(findTableIndexOrNormal(table_name));
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceDrop.h b/src/KingSystem/Resource/Actor/resResourceDrop.h
new file mode 100644
index 00000000..198f26e5
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceDrop.h
@@ -0,0 +1,71 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterObj.h>
+#include <container/seadBuffer.h>
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+#include "KingSystem/Utils/Types.h"
+
+namespace ksys::res {
+
+class Drop : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(Drop, Resource)
+public:
+ Drop() : ParamIO("drop", 0) {}
+
+ /// Returns the name of a randomly chosen drop from the specified table or from the Normal table
+ /// if the table cannot be found.
+ /// The empty string is returned if no drop was chosen.
+ const sead::SafeString& getRandomDropFromTable(const sead::SafeString& table_name) const;
+
+ /// Returns the name of a randomly chosen drop from the specified table.
+ /// The empty string is returned if no drop was chosen.
+ const sead::SafeString& getRandomDropFromTable(s32 table_idx) const;
+
+ /// Returns the index of the specified table. If the table cannot be found, the index of the
+ /// first Normal table is returned. Note that it may be equal to -1 if there is no Normal table.
+ s32 findTableIndexOrNormal(const sead::SafeString& table_name) const;
+
+ /// Returns the index of the specified table or -1 if it cannot be found.
+ s32 findTableIndex(const sead::SafeString& table_name) const;
+
+ s32 getApproachType(s32 table_idx) const;
+ s32 getOccurrenceSpeedType(s32 table_idx) const;
+ s32 getRepeatNum(s32 table_idx) const;
+ s32 getRepeatNum(const sead::SafeString& table_name) const;
+
+ bool ParamIO_m0(char* data) override { return true; }
+ void doCreate_(u8*, u32, sead::Heap*) override {}
+ bool needsParse() const override { return true; }
+
+private:
+ struct Item {
+ agl::utl::Parameter<sead::SafeString> name;
+ agl::utl::Parameter<f32> probability;
+ };
+ KSYS_CHECK_SIZE_NX150(Item, 0x48);
+
+ struct Table {
+ agl::utl::ParameterObj obj;
+ agl::utl::Parameter<sead::SafeString> name;
+ agl::utl::Parameter<s32> repeat_num_min;
+ agl::utl::Parameter<s32> repeat_num_max;
+ agl::utl::Parameter<s32> approach_type;
+ agl::utl::Parameter<s32> occurrence_speed_type;
+ agl::utl::Parameter<s32> column_num;
+ sead::Buffer<Item> items;
+ };
+ KSYS_CHECK_SIZE_NX150(Table, 0x108);
+
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+ void parseItems_(s32& table_idx);
+
+ agl::utl::ParameterObj mObj;
+ agl::utl::Parameter<s32> mTableNum;
+ sead::Buffer<void*> _300;
+ sead::Buffer<Table> mTables;
+};
+KSYS_CHECK_SIZE_NX150(Drop, 0x320);
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceGParamList.cpp b/src/KingSystem/Resource/Actor/resResourceGParamList.cpp
new file mode 100644
index 00000000..22512e64
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceGParamList.cpp
@@ -0,0 +1,304 @@
+#include "KingSystem/Resource/Actor/resResourceGParamList.h"
+#include <agl/Utils/aglParameter.h>
+#include <prim/seadRuntimeTypeInfo.h>
+#include "KingSystem/ActorSystem/actActorParamMgr.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObject.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectAirWall.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectAnimalFollowOffset.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectAnimalUnit.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectArmor.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectArmorEffect.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectArmorHead.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectArmorUpper.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectArrow.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectAttack.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectAttackInterval.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectAutoGen.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectBeam.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectBindActor.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectBindBone.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectBow.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectBullet.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectCamera.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectChemicalType.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectClothReaction.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectCookSpice.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectCureItem.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectEatTarget.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectEnemy.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectEnemyLevel.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectEnemyRace.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectEnemyShown.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectEvent.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectExtendedEntity.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectFish.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectGelEnemy.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectGeneral.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectGiantArmor.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectGiantArmorSlot.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectGlobal.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectGolem.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectGolemIK.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectGrab.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectGuardian.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectGuardianMini.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectGuardianMiniWeapon.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectHorse.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectHorseCreator.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectHorseObject.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectHorseRider.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectHorseTargetedInfo.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectHorseUnit.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectInsect.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectItem.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectLargeSword.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectLiftable.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectLumberjackTree.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectMasterSword.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectMonsterShop.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectMotorcycle.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectNest.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectNpc.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectNpcEquipment.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectPictureBook.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectPlayer.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectPrey.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectRod.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectRope.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectRupee.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectSandworm.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectSeriesArmor.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectShiekerStone.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectShield.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectSmallSword.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectSpear.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectStalEnemy.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectSwarm.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectSystem.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectTraveler.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectWeaponCommon.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectWeaponOption.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectWeaponThrow.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectWizzrobe.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectWolfLink.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListObjectZora.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListTraits.h"
+
+namespace ksys::res {
+
+void GParamList::doCreate_(u8*, u32, sead::Heap*) {}
+
+bool GParamList::parse_(u8* data, size_t, sead::Heap* heap) {
+ GParamList* dummy_list = nullptr;
+ if (!sead::IsDerivedFrom<DummyGParamList>(this) && act::ActorParamMgr::instance())
+ dummy_list = act::ActorParamMgr::instance()->getDummyGParamList();
+
+ mObjects.allocBufferAssert(NumGParamListObjTypes, heap);
+
+ const agl::utl::ResParameterArchive archive{data};
+
+#define KSYS_GPARAM_ADD_(NAME) \
+ do { \
+ using Traits = GParamListObjTypeTraits<GParamListObjType::NAME>; \
+ const auto pobj = agl::utl::getResParameterObj(archive.getRootList(), #NAME); \
+ if (pobj.ptr()) { \
+ auto* obj = new (heap) Traits::type; \
+ if (obj) \
+ addObj(&obj->getObj(), obj->getName()); \
+ \
+ mObjects[Traits::index] = obj; \
+ \
+ } else { \
+ mObjects[Traits::index] = dummy_list ? dummy_list->mObjects[Traits::index] : nullptr; \
+ } \
+ } while (0)
+
+ KSYS_GPARAM_ADD_(System);
+ KSYS_GPARAM_ADD_(General);
+ KSYS_GPARAM_ADD_(Enemy);
+ KSYS_GPARAM_ADD_(EnemyLevel);
+ KSYS_GPARAM_ADD_(EnemyRace);
+ KSYS_GPARAM_ADD_(AttackInterval);
+ KSYS_GPARAM_ADD_(EnemyShown);
+ KSYS_GPARAM_ADD_(BindBone);
+ KSYS_GPARAM_ADD_(Attack);
+ KSYS_GPARAM_ADD_(WeaponCommon);
+ KSYS_GPARAM_ADD_(WeaponThrow);
+ KSYS_GPARAM_ADD_(Sandworm);
+ KSYS_GPARAM_ADD_(SmallSword);
+ KSYS_GPARAM_ADD_(Rod);
+ KSYS_GPARAM_ADD_(LargeSword);
+ KSYS_GPARAM_ADD_(Spear);
+ KSYS_GPARAM_ADD_(Shield);
+ KSYS_GPARAM_ADD_(Bow);
+ KSYS_GPARAM_ADD_(WeaponOption);
+ KSYS_GPARAM_ADD_(MasterSword);
+ KSYS_GPARAM_ADD_(GuardianMiniWeapon);
+ KSYS_GPARAM_ADD_(Player);
+ KSYS_GPARAM_ADD_(Camera);
+ KSYS_GPARAM_ADD_(Grab);
+ KSYS_GPARAM_ADD_(Armor);
+ KSYS_GPARAM_ADD_(ArmorEffect);
+ KSYS_GPARAM_ADD_(ArmorHead);
+ KSYS_GPARAM_ADD_(ArmorUpper);
+ KSYS_GPARAM_ADD_(ShiekerStone);
+ KSYS_GPARAM_ADD_(SeriesArmor);
+ KSYS_GPARAM_ADD_(Liftable);
+ KSYS_GPARAM_ADD_(Item);
+ KSYS_GPARAM_ADD_(Rupee);
+ KSYS_GPARAM_ADD_(Arrow);
+ KSYS_GPARAM_ADD_(Bullet);
+ KSYS_GPARAM_ADD_(CureItem);
+ KSYS_GPARAM_ADD_(CookSpice);
+ KSYS_GPARAM_ADD_(LumberjackTree);
+ KSYS_GPARAM_ADD_(Npc);
+ KSYS_GPARAM_ADD_(NpcEquipment);
+ KSYS_GPARAM_ADD_(Zora);
+ KSYS_GPARAM_ADD_(Traveler);
+ KSYS_GPARAM_ADD_(Prey);
+ KSYS_GPARAM_ADD_(AnimalFollowOffset);
+ KSYS_GPARAM_ADD_(ExtendedEntity);
+ KSYS_GPARAM_ADD_(BindActor);
+ KSYS_GPARAM_ADD_(EatTarget);
+ KSYS_GPARAM_ADD_(AnimalUnit);
+ KSYS_GPARAM_ADD_(Insect);
+ KSYS_GPARAM_ADD_(Fish);
+ KSYS_GPARAM_ADD_(Rope);
+ KSYS_GPARAM_ADD_(Horse);
+ KSYS_GPARAM_ADD_(HorseUnit);
+ KSYS_GPARAM_ADD_(HorseObject);
+ KSYS_GPARAM_ADD_(HorseRider);
+ KSYS_GPARAM_ADD_(HorseCreator);
+ KSYS_GPARAM_ADD_(GiantArmorSlot);
+ KSYS_GPARAM_ADD_(GiantArmor);
+ KSYS_GPARAM_ADD_(Guardian);
+ KSYS_GPARAM_ADD_(MonsterShop);
+ KSYS_GPARAM_ADD_(Swarm);
+ KSYS_GPARAM_ADD_(GelEnemy);
+ KSYS_GPARAM_ADD_(Nest);
+ KSYS_GPARAM_ADD_(Wizzrobe);
+ KSYS_GPARAM_ADD_(StalEnemy);
+ KSYS_GPARAM_ADD_(GuardianMini);
+ KSYS_GPARAM_ADD_(ClothReaction);
+ KSYS_GPARAM_ADD_(Global);
+ KSYS_GPARAM_ADD_(Beam);
+ KSYS_GPARAM_ADD_(AutoGen);
+ KSYS_GPARAM_ADD_(ChemicalType);
+ KSYS_GPARAM_ADD_(Golem);
+ KSYS_GPARAM_ADD_(HorseTargetedInfo);
+ KSYS_GPARAM_ADD_(WolfLink);
+ KSYS_GPARAM_ADD_(Event);
+ KSYS_GPARAM_ADD_(GolemIK);
+ KSYS_GPARAM_ADD_(PictureBook);
+ KSYS_GPARAM_ADD_(AirWall);
+ KSYS_GPARAM_ADD_(Motorcycle);
+
+#undef KSYS_GPARAM_ADD_
+
+ if (data)
+ applyResParameterArchive(archive);
+
+ return true;
+}
+
+void GParamList::finalize_() {}
+
+static constexpr size_t getResourceFactoryFallbackSizeConst() {
+ size_t size = 0;
+ size += sizeof(GParamListObjectSystem);
+ size += sizeof(GParamListObjectGeneral);
+ size += sizeof(GParamListObjectEnemy);
+ size += sizeof(GParamListObjectEnemyLevel);
+ size += sizeof(GParamListObjectEnemyRace);
+ size += sizeof(GParamListObjectAttackInterval);
+ size += sizeof(GParamListObjectEnemyShown);
+ size += sizeof(GParamListObjectBindBone);
+ size += sizeof(GParamListObjectAttack);
+ size += sizeof(GParamListObjectWeaponCommon);
+ size += sizeof(GParamListObjectWeaponThrow);
+ size += sizeof(GParamListObjectSandworm);
+ size += sizeof(GParamListObjectSmallSword);
+ size += sizeof(GParamListObjectRod);
+ size += sizeof(GParamListObjectLargeSword);
+ size += sizeof(GParamListObjectSpear);
+ size += sizeof(GParamListObjectShield);
+ size += sizeof(GParamListObjectBow);
+ size += sizeof(GParamListObjectWeaponOption);
+ size += sizeof(GParamListObjectMasterSword);
+ size += sizeof(GParamListObjectGuardianMiniWeapon);
+ size += sizeof(GParamListObjectPlayer);
+ size += sizeof(GParamListObjectCamera);
+ size += sizeof(GParamListObjectGrab);
+ size += sizeof(GParamListObjectArmor);
+ size += sizeof(GParamListObjectArmorEffect);
+ size += sizeof(GParamListObjectArmorHead);
+ size += sizeof(GParamListObjectArmorUpper);
+ size += sizeof(GParamListObjectShiekerStone);
+ size += sizeof(GParamListObjectSeriesArmor);
+ size += sizeof(GParamListObjectLiftable);
+ size += sizeof(GParamListObjectItem);
+ size += sizeof(GParamListObjectRupee);
+ size += sizeof(GParamListObjectArrow);
+ size += sizeof(GParamListObjectBullet);
+ size += sizeof(GParamListObjectCureItem);
+ size += sizeof(GParamListObjectCookSpice);
+ size += sizeof(GParamListObjectLumberjackTree);
+ size += sizeof(GParamListObjectNpc);
+ size += sizeof(GParamListObjectNpcEquipment);
+ size += sizeof(GParamListObjectZora);
+ size += sizeof(GParamListObjectTraveler);
+ size += sizeof(GParamListObjectPrey);
+ size += sizeof(GParamListObjectAnimalFollowOffset);
+ size += sizeof(GParamListObjectExtendedEntity);
+ size += sizeof(GParamListObjectBindActor);
+ size += sizeof(GParamListObjectEatTarget);
+ size += sizeof(GParamListObjectAnimalUnit);
+ size += sizeof(GParamListObjectInsect);
+ size += sizeof(GParamListObjectFish);
+ size += sizeof(GParamListObjectRope);
+ size += sizeof(GParamListObjectHorse);
+ size += sizeof(GParamListObjectHorseUnit);
+ size += sizeof(GParamListObjectHorseObject);
+ size += sizeof(GParamListObjectHorseRider);
+ size += sizeof(GParamListObjectHorseCreator);
+ size += sizeof(GParamListObjectGiantArmorSlot);
+ size += sizeof(GParamListObjectGiantArmor);
+ size += sizeof(GParamListObjectGuardian);
+ size += sizeof(GParamListObjectMonsterShop);
+ size += sizeof(GParamListObjectSwarm);
+ size += sizeof(GParamListObjectGelEnemy);
+ size += sizeof(GParamListObjectNest);
+ size += sizeof(GParamListObjectWizzrobe);
+ size += sizeof(GParamListObjectStalEnemy);
+ size += sizeof(GParamListObjectGuardianMini);
+ size += sizeof(GParamListObjectClothReaction);
+ size += sizeof(GParamListObjectGlobal);
+ size += sizeof(GParamListObjectBeam);
+ size += sizeof(GParamListObjectAutoGen);
+ size += sizeof(GParamListObjectChemicalType);
+ size += sizeof(GParamListObjectGolem);
+ size += sizeof(GParamListObjectHorseTargetedInfo);
+ size += sizeof(GParamListObjectWolfLink);
+ size += sizeof(GParamListObjectEvent);
+ size += sizeof(GParamListObjectGolemIK);
+ size += sizeof(GParamListObjectPictureBook);
+ size += sizeof(GParamListObjectAirWall);
+ size += sizeof(GParamListObjectMotorcycle);
+ size += sizeof(GParamList);
+ return size;
+}
+
+u32 GParamList::getResourceFactoryFallbackSize() {
+ constexpr size_t size = getResourceFactoryFallbackSizeConst();
+ static_assert(size == static_cast<u32>(size));
+ return static_cast<u32>(size);
+}
+
+void DummyGParamList::doCreate_(u8*, u32, sead::Heap*) {}
+
+bool DummyGParamList::parse_(u8* data, size_t size, sead::Heap* heap) {
+ GParamList::parse_(data, size, heap);
+ return true;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceGParamList.h b/src/KingSystem/Resource/Actor/resResourceGParamList.h
new file mode 100644
index 00000000..98ac36f0
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceGParamList.h
@@ -0,0 +1,136 @@
+#pragma once
+
+#include <container/seadBuffer.h>
+#include "KingSystem/Resource/GeneralParamList/resGParamListObject.h"
+#include "KingSystem/Resource/GeneralParamList/resGParamListTraits.h"
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+#include "KingSystem/Utils/Types.h"
+
+namespace ksys::res {
+
+class GParamList : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(GParamList, Resource)
+public:
+ GParamList() : ParamIO("bgparamlist", 0) {}
+ ~GParamList() override = default;
+
+ bool ParamIO_m0(char* data) override { return false; }
+ void doCreate_(u8* buffer, u32 bufferSize, sead::Heap* heap) override;
+ bool needsParse() const override { return true; }
+
+ static u32 getResourceFactoryFallbackSize();
+
+ template <GParamListObjType Type>
+ const auto* get() const {
+ using Traits = GParamListObjTypeTraits<Type>;
+ return reinterpret_cast<const typename Traits::type*>(mObjects[Traits::index]);
+ }
+
+#define KSYS_GPARAM_DEFINE_GETTER_(NAME) \
+ const auto* get##NAME() const { return get<GParamListObjType::NAME>(); }
+
+ KSYS_GPARAM_DEFINE_GETTER_(System)
+ KSYS_GPARAM_DEFINE_GETTER_(General)
+ KSYS_GPARAM_DEFINE_GETTER_(Enemy)
+ KSYS_GPARAM_DEFINE_GETTER_(EnemyLevel)
+ KSYS_GPARAM_DEFINE_GETTER_(EnemyRace)
+ KSYS_GPARAM_DEFINE_GETTER_(AttackInterval)
+ KSYS_GPARAM_DEFINE_GETTER_(EnemyShown)
+ KSYS_GPARAM_DEFINE_GETTER_(BindBone)
+ KSYS_GPARAM_DEFINE_GETTER_(Attack)
+ KSYS_GPARAM_DEFINE_GETTER_(WeaponCommon)
+ KSYS_GPARAM_DEFINE_GETTER_(WeaponThrow)
+ KSYS_GPARAM_DEFINE_GETTER_(Sandworm)
+ KSYS_GPARAM_DEFINE_GETTER_(SmallSword)
+ KSYS_GPARAM_DEFINE_GETTER_(Rod)
+ KSYS_GPARAM_DEFINE_GETTER_(LargeSword)
+ KSYS_GPARAM_DEFINE_GETTER_(Spear)
+ KSYS_GPARAM_DEFINE_GETTER_(Shield)
+ KSYS_GPARAM_DEFINE_GETTER_(Bow)
+ KSYS_GPARAM_DEFINE_GETTER_(WeaponOption)
+ KSYS_GPARAM_DEFINE_GETTER_(MasterSword)
+ KSYS_GPARAM_DEFINE_GETTER_(GuardianMiniWeapon)
+ KSYS_GPARAM_DEFINE_GETTER_(Player)
+ KSYS_GPARAM_DEFINE_GETTER_(Camera)
+ KSYS_GPARAM_DEFINE_GETTER_(Grab)
+ KSYS_GPARAM_DEFINE_GETTER_(Armor)
+ KSYS_GPARAM_DEFINE_GETTER_(ArmorEffect)
+ KSYS_GPARAM_DEFINE_GETTER_(ArmorHead)
+ KSYS_GPARAM_DEFINE_GETTER_(ArmorUpper)
+ KSYS_GPARAM_DEFINE_GETTER_(ShiekerStone)
+ KSYS_GPARAM_DEFINE_GETTER_(SeriesArmor)
+ KSYS_GPARAM_DEFINE_GETTER_(Liftable)
+ KSYS_GPARAM_DEFINE_GETTER_(Item)
+ KSYS_GPARAM_DEFINE_GETTER_(Rupee)
+ KSYS_GPARAM_DEFINE_GETTER_(Arrow)
+ KSYS_GPARAM_DEFINE_GETTER_(Bullet)
+ KSYS_GPARAM_DEFINE_GETTER_(CureItem)
+ KSYS_GPARAM_DEFINE_GETTER_(CookSpice)
+ KSYS_GPARAM_DEFINE_GETTER_(LumberjackTree)
+ KSYS_GPARAM_DEFINE_GETTER_(Npc)
+ KSYS_GPARAM_DEFINE_GETTER_(NpcEquipment)
+ KSYS_GPARAM_DEFINE_GETTER_(Zora)
+ KSYS_GPARAM_DEFINE_GETTER_(Traveler)
+ KSYS_GPARAM_DEFINE_GETTER_(Prey)
+ KSYS_GPARAM_DEFINE_GETTER_(AnimalFollowOffset)
+ KSYS_GPARAM_DEFINE_GETTER_(ExtendedEntity)
+ KSYS_GPARAM_DEFINE_GETTER_(BindActor)
+ KSYS_GPARAM_DEFINE_GETTER_(EatTarget)
+ KSYS_GPARAM_DEFINE_GETTER_(AnimalUnit)
+ KSYS_GPARAM_DEFINE_GETTER_(Insect)
+ KSYS_GPARAM_DEFINE_GETTER_(Fish)
+ KSYS_GPARAM_DEFINE_GETTER_(Rope)
+ KSYS_GPARAM_DEFINE_GETTER_(Horse)
+ KSYS_GPARAM_DEFINE_GETTER_(HorseUnit)
+ KSYS_GPARAM_DEFINE_GETTER_(HorseObject)
+ KSYS_GPARAM_DEFINE_GETTER_(HorseRider)
+ KSYS_GPARAM_DEFINE_GETTER_(HorseCreator)
+ KSYS_GPARAM_DEFINE_GETTER_(GiantArmorSlot)
+ KSYS_GPARAM_DEFINE_GETTER_(GiantArmor)
+ KSYS_GPARAM_DEFINE_GETTER_(Guardian)
+ KSYS_GPARAM_DEFINE_GETTER_(MonsterShop)
+ KSYS_GPARAM_DEFINE_GETTER_(Swarm)
+ KSYS_GPARAM_DEFINE_GETTER_(GelEnemy)
+ KSYS_GPARAM_DEFINE_GETTER_(Nest)
+ KSYS_GPARAM_DEFINE_GETTER_(Wizzrobe)
+ KSYS_GPARAM_DEFINE_GETTER_(StalEnemy)
+ KSYS_GPARAM_DEFINE_GETTER_(GuardianMini)
+ KSYS_GPARAM_DEFINE_GETTER_(ClothReaction)
+ KSYS_GPARAM_DEFINE_GETTER_(Global)
+ KSYS_GPARAM_DEFINE_GETTER_(Beam)
+ KSYS_GPARAM_DEFINE_GETTER_(AutoGen)
+ KSYS_GPARAM_DEFINE_GETTER_(ChemicalType)
+ KSYS_GPARAM_DEFINE_GETTER_(Golem)
+ KSYS_GPARAM_DEFINE_GETTER_(HorseTargetedInfo)
+ KSYS_GPARAM_DEFINE_GETTER_(WolfLink)
+ KSYS_GPARAM_DEFINE_GETTER_(Event)
+ KSYS_GPARAM_DEFINE_GETTER_(GolemIK)
+ KSYS_GPARAM_DEFINE_GETTER_(PictureBook)
+ KSYS_GPARAM_DEFINE_GETTER_(AirWall)
+ KSYS_GPARAM_DEFINE_GETTER_(Motorcycle)
+
+#undef KSYS_GPARAM_DEFINE_GETTER_
+
+protected:
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+ void finalize_() override;
+
+private:
+ sead::Buffer<GParamListObject*> mObjects;
+};
+KSYS_CHECK_SIZE_NX150(GParamList, 0x2c0);
+
+class DummyGParamList : public GParamList {
+ SEAD_RTTI_OVERRIDE(DummyGParamList, GParamList)
+public:
+ using GParamList::GParamList;
+ ~DummyGParamList() override = default;
+
+ void doCreate_(u8* buffer, u32 bufferSize, sead::Heap* heap) override;
+
+private:
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+};
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceLifeCondition.cpp b/src/KingSystem/Resource/Actor/resResourceLifeCondition.cpp
new file mode 100644
index 00000000..84edd261
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceLifeCondition.cpp
@@ -0,0 +1,82 @@
+#include "KingSystem/Resource/Actor/resResourceLifeCondition.h"
+
+namespace ksys::res {
+
+// NON_MATCHING: two instructions swapped
+bool LifeCondition::parse_(u8* data, size_t, sead::Heap* heap) {
+ if (!data)
+ return true;
+
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+ const agl::utl::ResParameterArchive archive{data};
+ const auto root = archive.getRootList();
+
+ const auto invalid_weathers_obj = agl::utl::getResParameterObj(root, "InvalidWeathers");
+ if (invalid_weathers_obj.ptr())
+ LifeCondition::parseArray(&invalid_weathers_obj, &mInvalidWeathersObj,
+ &mInvalidWeathersBuffer, "InvalidWeathers", "天候", heap);
+
+ const auto invalid_times_obj = agl::utl::getResParameterObj(root, "InvalidTimes");
+ if (invalid_times_obj.ptr())
+ LifeCondition::parseArray(&invalid_times_obj, &mInvalidTimesObj, &mInvalidTimesBuffer,
+ "InvalidTimes", "時間", heap);
+
+ const auto display_distance_obj = agl::utl::getResParameterObj(root, "DisplayDistance");
+ if (display_distance_obj.ptr()) {
+ mDisplayDistance.init(0.0, "Item", "表示距離", &mDisplayDistanceObj);
+ addObj(&mDisplayDistanceObj, "DisplayDistance");
+ mDisplayDistanceObj.applyResParameterObj(display_distance_obj);
+ }
+
+ const auto auto_display_distance_algorithm_obj =
+ agl::utl::getResParameterObj(root, "AutoDisplayDistanceAlgorithm");
+ if (auto_display_distance_algorithm_obj.ptr()) {
+ mBoundingY.init("Bouding.Y", "Item", "自動距離算出アルゴリズム", &mBoundingYObj);
+ addObj(&mBoundingYObj, "AutoDisplayDistanceAlgorithm");
+ mBoundingYObj.applyResParameterObj(auto_display_distance_algorithm_obj);
+ }
+
+ const auto y_limit_algorithm_obj = agl::utl::getResParameterObj(root, "YLimitAlgorithm");
+ if (y_limit_algorithm_obj.ptr()) {
+ mYLimitAlgorithm.init("NoLimit", "Item", "Y制限アルゴリズム", &mYLimitAlgorithmObj);
+ addObj(&mYLimitAlgorithmObj, "YLimitAlgorithm");
+ mYLimitAlgorithmObj.applyResParameterObj(y_limit_algorithm_obj);
+ }
+
+ const auto delete_weathers_obj = agl::utl::getResParameterObj(root, "DeleteWeathers");
+ if (delete_weathers_obj.ptr())
+ LifeCondition::parseArray(&delete_weathers_obj, &mDeleteWeathersObj, &mDeleteWeathersBuffer,
+ "DeleteWeathers", "天候", heap);
+
+ const auto delete_times_obj = agl::utl::getResParameterObj(root, "DeleteTimes");
+ if (delete_times_obj.ptr())
+ LifeCondition::parseArray(&delete_times_obj, &mDeleteTimesObj, &mDeleteTimesBuffer,
+ "DeleteTimes", "時間", heap);
+
+ return true;
+}
+
+void LifeCondition::parseArray(const agl::utl::ResParameterObj* data, agl::utl::IParameterObj* obj,
+ sead::Buffer<agl::utl::Parameter<sead::SafeString>>* buffer,
+ const sead::SafeString& key, const sead::SafeString& desc,
+ sead::Heap* heap) {
+ addObj(obj, key);
+ obj->applyResParameterObj(*data);
+
+ auto count = data->mPtr->getNumParameters();
+ if (count == 0)
+ return;
+
+ buffer->allocBufferAssert(count, heap);
+
+ for (int i = 0; i != count; ++i) {
+ sead::FormatFixedSafeString<64> s;
+ s.format("Item%03d", (i + 1));
+
+ (*buffer)[i].init("", s, desc, obj);
+ }
+
+ obj->applyResParameterObj(*data);
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceLifeCondition.h b/src/KingSystem/Resource/Actor/resResourceLifeCondition.h
new file mode 100644
index 00000000..8f6d6d9d
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceLifeCondition.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterObj.h>
+#include <container/seadBuffer.h>
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+
+namespace ksys::res {
+
+class LifeCondition : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(LifeCondition, Resource)
+public:
+ LifeCondition() : ParamIO("lifecondition", 0) {}
+ ~LifeCondition() override = default;
+
+ bool needsParse() const override { return true; }
+ bool ParamIO_m0(char* data) override { return true; }
+
+private:
+ void doCreate_(u8*, u32, sead::Heap*) override {}
+ void parseArray(const agl::utl::ResParameterObj* data, agl::utl::IParameterObj* obj,
+ sead::Buffer<agl::utl::Parameter<sead::SafeString>>* buffer,
+ const sead::SafeString& key, const sead::SafeString& desc, sead::Heap* heap);
+
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+
+ agl::utl::ParameterObj mInvalidWeathersObj;
+ agl::utl::ParameterObj mInvalidTimesObj;
+ agl::utl::ParameterObj mDisplayDistanceObj;
+ agl::utl::ParameterObj mDeleteWeathersObj;
+ agl::utl::ParameterObj mDeleteTimesObj;
+ agl::utl::ParameterObj mBoundingYObj;
+ agl::utl::ParameterObj mYLimitAlgorithmObj;
+
+ sead::Buffer<agl::utl::Parameter<sead::SafeString>> mInvalidWeathersBuffer;
+ sead::Buffer<agl::utl::Parameter<sead::SafeString>> mInvalidTimesBuffer;
+ agl::utl::Parameter<f32> mDisplayDistance;
+ agl::utl::Parameter<sead::SafeString> mBoundingY;
+ agl::utl::Parameter<sead::SafeString> mYLimitAlgorithm;
+ sead::Buffer<agl::utl::Parameter<sead::SafeString>> mDeleteWeathersBuffer;
+ sead::Buffer<agl::utl::Parameter<sead::SafeString>> mDeleteTimesBuffer;
+};
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceLod.cpp b/src/KingSystem/Resource/Actor/resResourceLod.cpp
new file mode 100644
index 00000000..04c02b3e
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceLod.cpp
@@ -0,0 +1,22 @@
+#include "KingSystem/Resource/Actor/resResourceLod.h"
+
+namespace ksys::res {
+
+bool Lod::parse_(u8* data, size_t, sead::Heap*) {
+ mDisableOutScreenCalcStop.init(false, "DisableOutScreenCalcStop", "", &mHeader);
+ mDisableXLinkSkip.init(false, "DisableXLinkSkip", "", &mHeader);
+ mDisableCalcSkipFrame.init(4, "DisableCalcSkipFrame", "", &mHeader);
+ mDisableConstActor.init(false, "DisableConstActor", "", &mHeader);
+ mDistanceScale.init(1.0, "DistanceScale", "", &mHeader);
+ mDisableBehaviorSkip.init(false, "DisableBehaviorSkip", "", &mHeader);
+ mDisableCalcRescueDistLimit.init(false, "DisableCalcRescueDistLimit", "", &mHeader);
+
+ addObj(&mHeader, "Header");
+
+ if (data)
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+
+ return true;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceLod.h b/src/KingSystem/Resource/Actor/resResourceLod.h
new file mode 100644
index 00000000..098faee2
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceLod.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterIO.h>
+#include <agl/Utils/aglParameterObj.h>
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+
+namespace ksys::res {
+
+class Lod : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(Lod, Resource)
+public:
+ Lod() : ParamIO("lod", 0) {}
+ ~Lod() override = default;
+
+ bool ParamIO_m0(char* data) override { return true; }
+ void doCreate_(u8*, u32, sead::Heap*) override {}
+ bool needsParse() const override { return true; }
+
+protected:
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+
+ agl::utl::ParameterObj mHeader;
+
+public:
+ agl::utl::Parameter<bool> mDisableOutScreenCalcStop;
+ agl::utl::Parameter<bool> mDisableXLinkSkip;
+ agl::utl::Parameter<s32> mDisableCalcSkipFrame;
+ agl::utl::Parameter<bool> mDisableConstActor;
+ agl::utl::Parameter<f32> mDistanceScale;
+ agl::utl::Parameter<bool> mDisableBehaviorSkip;
+ agl::utl::Parameter<bool> mDisableCalcRescueDistLimit;
+};
+KSYS_CHECK_SIZE_NX150(Lod, 0x3c0);
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceModelList.cpp b/src/KingSystem/Resource/Actor/resResourceModelList.cpp
new file mode 100644
index 00000000..18b9c0e8
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceModelList.cpp
@@ -0,0 +1,364 @@
+#include "KingSystem/Resource/Actor/resResourceModelList.h"
+#include <container/seadSafeArray.h>
+#include "KingSystem/Resource/resModelResourceDivide.h"
+
+namespace ksys::res {
+
+namespace {
+sead::SafeString str_ModelData{"ModelData"};
+sead::SafeString str_Unit{"Unit"};
+sead::SafeString str_AnmTarget{"AnmTarget"};
+sead::SafeString str_Partial{"Partial"};
+
+sead::SafeArray<const char*, 6> sLocatorTypes{{
+ "Trunk",
+ "Branch",
+ "GlowStone",
+ "OnTree",
+ "MagnePos",
+ "StopTimerPos",
+}};
+
+constexpr u32 NumUnitMax = 8;
+} // namespace
+
+ModelList::ModelList() : ParamIO("modellist", 0) {}
+
+ModelList::~ModelList() {
+ for (auto& entry : mModelData)
+ entry.units.freeBuffer();
+ mModelData.freeBuffer();
+
+ for (auto& entry : mAnmTargets)
+ entry.partials.freeBuffer();
+ mAnmTargets.freeBuffer();
+}
+
+void ModelList::doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) {}
+
+// NON_MATCHING: reorderings
+bool ModelList::parse_(u8* data, size_t size, sead::Heap* heap) {
+ agl::utl::ResParameterArchive archive{data};
+ const auto root = archive.getRootList();
+
+ const auto model_data_list = agl::utl::getResParameterList(root, str_ModelData);
+ if (model_data_list.ptr() && model_data_list.getResParameterListNum() > 0) {
+ if (!parseModelData(model_data_list, heap))
+ return false;
+ }
+ addList(&mModelDataList, str_ModelData);
+
+ const auto anm_target_list = agl::utl::getResParameterList(root, str_AnmTarget);
+ if (anm_target_list.ptr() && anm_target_list.getResParameterListNum() > 0) {
+ if (!parseAnmTarget(anm_target_list, heap))
+ return false;
+ }
+ addList(&mAnmTargetList, str_AnmTarget);
+
+ mControllerInfo->mAddColor.init({0.0, 0.0, 0.0, 0.0}, "AddColor", "", &mControllerInfo->mObj);
+ mControllerInfo->mMulColor.init({1.0, 1.0, 1.0, 1.0}, "MulColor", "", &mControllerInfo->mObj);
+ mControllerInfo->mBaseScale.init(sead::Vector3f::ones, "BaseScale", "", &mControllerInfo->mObj);
+ mControllerInfo->mVariationMatAnim.init("", "VariationMatAnim", "", &mControllerInfo->mObj);
+ mControllerInfo->mVariationMatAnimFrame.init(0, "VariationMatAnimFrame", "",
+ &mControllerInfo->mObj);
+ mControllerInfo->mVariationShaderAnim.init("", "VariationShaderAnim", "",
+ &mControllerInfo->mObj);
+ mControllerInfo->mVariationShaderAnimFrame.init(0, "VariationShaderAnimFrame", "",
+ &mControllerInfo->mObj);
+ mControllerInfo->mCalcAABBASKey.init("Wait", "CalcAABBASKey", "", &mControllerInfo->mObj);
+ addObj(&mControllerInfo->mObj, sead::FormatFixedSafeString<128>("ControllerInfo"));
+
+ mAttention->mIsEnableAttention.init(false, "IsEnableAttention", "", &mAttention->mObj);
+ mAttention->mLookAtBone.init("", "LookAtBone", "", &mAttention->mObj);
+ mAttention->mLookAtOffset.init(sead::Vector3f::zero, "LookAtOffset", "", &mAttention->mObj);
+ mAttention->mCursorOffsetY.init(0.0, "CursorOffsetY", "", &mAttention->mObj);
+ mAttention->mAIInfoOffsetY.init(0.0, "AIInfoOffsetY", "", &mAttention->mObj);
+ mAttention->mCutTargetBone.init("", "CutTargetBone", "", &mAttention->mObj);
+ mAttention->mCutTargetOffset.init(sead::Vector3f::zero, "CutTargetOffset", "",
+ &mAttention->mObj);
+ mAttention->mGameCameraBone.init("", "GameCameraBone", "", &mAttention->mObj);
+ mAttention->mGameCameraOffset.init(sead::Vector3f::zero, "GameCameraOffset", "",
+ &mAttention->mObj);
+ mAttention->mBowCameraBone.init("", "BowCameraBone", "", &mAttention->mObj);
+ mAttention->mBowCameraOffset.init(sead::Vector3f::zero, "BowCameraOffset", "",
+ &mAttention->mObj);
+ mAttention->mAttackTargetBone.init("", "AttackTargetBone", "", &mAttention->mObj);
+ mAttention->mAttackTargetOffset.init(sead::Vector3f::zero, "AttackTargetOffset", "",
+ &mAttention->mObj);
+ mAttention->mAttackTargetOffsetBack.init(0.0, "AttackTargetOffsetBack", "", &mAttention->mObj);
+ mAttention->mAtObstacleChkUseLookAtPos.init(true, "AtObstacleChkUseLookAtPos", "",
+ &mAttention->mObj);
+ mAttention->mAtObstacleChkOffsetBone.init("", "AtObstacleChkOffsetBone", "", &mAttention->mObj);
+ mAttention->mAtObstacleChkOffset.init(sead::Vector3f::zero, "AtObstacleChkOffset", "",
+ &mAttention->mObj);
+ mAttention->mCursorAIInfoBaseBone.init("", "CursorAIInfoBaseBone", "", &mAttention->mObj);
+ mAttention->mCursorAIInfoBaseOffset.init(sead::Vector3f::zero, "CursorAIInfoBaseOffset", "",
+ &mAttention->mObj);
+ addObj(&mAttention->mObj, sead::FormatFixedSafeString<128>("Attention"));
+
+ if (data) {
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+ mRawData = data;
+ }
+
+ return true;
+}
+
+bool ModelList::parseModelData(const agl::utl::ResParameterList& res, sead::Heap* heap) {
+ if (!mModelData.tryAllocBuffer(res.getResParameterListNum() != 0, heap))
+ return false;
+
+ sead::FixedSafeString<32> list_name{str_ModelData};
+ list_name.append("_");
+ const auto list_name_base_len = list_name.calcLength();
+
+ for (auto it = mModelData.begin(), it_end = mModelData.end(); it != it_end; ++it) {
+ list_name.trim(list_name_base_len);
+ list_name.appendWithFormat("%d", it.getIndex());
+
+ it->folder.init("", "Folder", "", &it->base_obj);
+ it->list.addObj(&it->base_obj, "Base");
+
+ const auto unit_list =
+ agl::utl::getResParameterList(res.getResParameterList(it.getIndex()), str_Unit);
+ if (unit_list.ptr()) {
+ const u32 num_units = unit_list.getResParameterObjNum();
+ if (num_units != 0) {
+ if (!it->units.tryAllocBuffer(std::min(num_units, NumUnitMax), heap))
+ return false;
+
+ sead::FixedSafeString<32> unit_name{str_Unit};
+ unit_name.append("_");
+ const auto unit_name_base_len = unit_name.calcLength();
+
+ for (auto unit = it->units.begin(), end = it->units.end(); unit != end; ++unit) {
+ unit_name.trim(unit_name_base_len);
+ unit_name.appendWithFormat("%d", unit.getIndex());
+
+ unit->unit_name.init("", "UnitName", "", &unit->obj);
+ unit->bind_bone.init("", "BindBone", "", &unit->obj);
+ it->unit_list.addObj(&unit->obj, unit_name);
+ }
+ }
+ }
+
+ it->list.addList(&it->unit_list, str_Unit);
+ mModelDataList.addList(&it->list, list_name);
+ }
+
+ return true;
+}
+
+bool ModelList::parseAnmTarget(const agl::utl::ResParameterList& res, sead::Heap* heap) {
+ if (!mAnmTargets.tryAllocBuffer(std::min<u32>(res.getResParameterListNum(), NumUnitMax), heap))
+ return false;
+
+ sead::FixedSafeString<32> list_name{str_AnmTarget};
+ list_name.append("_");
+ const auto list_name_base_len = list_name.calcLength();
+
+ for (auto it = mAnmTargets.begin(), it_end = mAnmTargets.end(); it != it_end; ++it) {
+ list_name.trim(list_name_base_len);
+ list_name.appendWithFormat("%d", it.getIndex());
+
+ it->num_as_slot.init(0, "NumASSlot", "", &it->base_obj);
+ // "is particle enabled" (particle is misspelled as "partical" [sic])
+ it->is_partical_enable.init(false, "IsParticalEnable", "", &it->base_obj);
+ it->target_type.init(0, "TargetType", "", &it->base_obj);
+ it->list.addObj(&it->base_obj, "Base");
+
+ const auto partials =
+ agl::utl::getResParameterList(res.getResParameterList(it.getIndex()), str_Partial);
+ if (partials.ptr() && partials.getResParameterObjNum() != 0) {
+ if (!it->partials.tryAllocBuffer(partials.getResParameterObjNum(), heap))
+ return false;
+
+ sead::FixedSafeString<32> partial_name{str_Partial};
+ partial_name.append("_");
+ const auto partial_name_base_len = partial_name.calcLength();
+
+ for (auto partial = it->partials.begin(), partial_end = it->partials.end();
+ partial != partial_end; ++partial) {
+ partial_name.trim(partial_name_base_len);
+ partial_name.appendWithFormat("%d", partial.getIndex());
+
+ partial->bone.init("", "Bone", "", &partial->obj);
+ partial->bind_flag.init(0, "BindFlg", "", &partial->obj);
+ partial->recursible.init(true, "Recursible", "", &partial->obj);
+ it->partial_list.addObj(&partial->obj, partial_name);
+ }
+ }
+
+ it->list.addList(&it->partial_list, str_Partial);
+ mAnmTargetList.addList(&it->list, list_name);
+ }
+
+ return true;
+}
+
+int ModelList::getNumAnmTargets() const {
+ return mAnmTargets.size();
+}
+
+void ModelList::getModelDataInfo(ModelList::ModelDataInfo* info) const {
+ *info = {};
+
+ if (mModelData.size() > 0) {
+ info->num_model_data = mModelData.size();
+ for (auto it = mModelData.begin(), end = mModelData.end(); it != end; ++it) {
+ const s32 idx = it.getIndex();
+
+ info->folder_name[idx] = it->folder.ref().cstr();
+ info->num_units[idx] = it->units.size();
+
+ for (auto unit = it->units.begin(), uend = it->units.end(); unit != uend; ++unit) {
+ const s32 unit_idx = unit.getIndex();
+ info->unit_names[idx][unit_idx] = unit->unit_name.ref().cstr();
+ info->unit_bind_bones[idx][unit_idx] = unit->bind_bone.ref().cstr();
+ }
+
+ if (info->unit_names[idx][0]) {
+ const char* name = ModelResourceDivide::instance()->getModelResource(
+ info->folder_name[idx], info->unit_names[idx][0]);
+ if (name)
+ info->folder_name[idx] = name;
+ }
+ }
+ }
+
+ info->base_scale = mControllerInfo->mBaseScale.ref();
+}
+
+bool ModelList::getAttentionInfo(AttentionInfo* info) const {
+ if (!mAttention->mIsEnableAttention.ref()) {
+ info->look_at_bone = {};
+ info->look_at_offset = sead::Vector3f::zero;
+
+ info->cursor_offset_y = {};
+ info->ai_info_offset_y = {};
+
+ info->cut_target_bone = {};
+ info->cut_target_offset = sead::Vector3f::zero;
+
+ info->game_camera_bone = {};
+ info->game_camera_offset = sead::Vector3f::zero;
+
+ info->bow_camera_bone = {};
+ info->bow_camera_offset = sead::Vector3f::zero;
+
+ info->attack_target_bone = {};
+ info->attack_target_offset = sead::Vector3f::zero;
+ info->attack_target_offset_back = {};
+
+ info->cursor_ai_info_base_bone = {};
+ info->cursor_ai_info_base_offset = sead::Vector3f::zero;
+
+ return false;
+ }
+
+ info->look_at_bone = mAttention->mLookAtBone.ref().cstr();
+ info->look_at_offset = mAttention->mLookAtOffset.ref();
+
+ info->cursor_offset_y = mAttention->mCursorOffsetY.ref();
+ info->ai_info_offset_y = mAttention->mAIInfoOffsetY.ref();
+
+ info->cut_target_bone = mAttention->mCutTargetBone.ref().cstr();
+ info->cut_target_offset = mAttention->mCutTargetOffset.ref();
+
+ info->game_camera_bone = mAttention->mGameCameraBone.ref().cstr();
+ info->game_camera_offset = mAttention->mGameCameraOffset.ref();
+
+ info->bow_camera_bone = mAttention->mBowCameraBone.ref().cstr();
+ info->bow_camera_offset = mAttention->mBowCameraOffset.ref();
+
+ info->attack_target_bone = mAttention->mAttackTargetBone.ref().cstr();
+ info->attack_target_offset = mAttention->mAttackTargetOffset.ref();
+ info->attack_target_offset_back = mAttention->mAttackTargetOffsetBack.ref();
+
+ if (mAttention->mAtObstacleChkUseLookAtPos.ref()) {
+ info->at_obstacle_chk_bone = mAttention->mLookAtBone.ref().cstr();
+ info->at_obstacle_chk_offset = mAttention->mLookAtOffset.ref();
+ } else {
+ info->at_obstacle_chk_bone = mAttention->mAtObstacleChkOffsetBone.ref().cstr();
+ info->at_obstacle_chk_offset = mAttention->mAtObstacleChkOffset.ref();
+ }
+
+ info->cursor_ai_info_base_bone = mAttention->mCursorAIInfoBaseBone.ref().cstr();
+ info->cursor_ai_info_base_offset = mAttention->mCursorAIInfoBaseOffset.ref();
+
+ const auto clear_if_empty = [](const char** s) {
+ if (!(*s)[0])
+ *s = nullptr;
+ };
+
+ clear_if_empty(&info->look_at_bone);
+ clear_if_empty(&info->cut_target_bone);
+ clear_if_empty(&info->game_camera_bone);
+ clear_if_empty(&info->bow_camera_bone);
+ clear_if_empty(&info->attack_target_bone);
+ clear_if_empty(&info->at_obstacle_chk_bone);
+ clear_if_empty(&info->cursor_ai_info_base_bone);
+
+ return true;
+}
+
+act::InfoData::Locator::Type ModelList::getLocatorTypeFromStr(const sead::SafeString& type) {
+ for (s32 i = 0; i < sLocatorTypes.size(); ++i) {
+ if (type == sLocatorTypes[i])
+ return act::InfoData::Locator::Type(i);
+ }
+ return act::InfoData::Locator::Type::Invalid;
+}
+
+// NON_MATCHING: weird unrolling and Vector3f store (str should be a stp)
+bool ModelList::getLocatorInfo(act::InfoData::Locator* info,
+ act::InfoData::Locator::Type type) const {
+ agl::utl::ResParameterArchive archive{mRawData};
+ const auto root = archive.getRootList();
+
+ for (int i = 0;; ++i) {
+ const auto obj =
+ agl::utl::getResParameterObj(root, sead::FormatFixedSafeString<32>("Locator_%d", i));
+ if (!obj.ptr())
+ return false;
+
+ const char* expected_type_str = sLocatorTypes[u32(type)];
+
+ if (sead::SafeString(getString(obj, "Type", "")) != expected_type_str)
+ continue;
+
+ info->pos = getVec3(obj, "Pos", sead::Vector3f::zero);
+ info->rot = getVec3(obj, "Rot", sead::Vector3f::zero);
+ info->type = type;
+ return true;
+ }
+
+ return false;
+}
+
+bool ModelList::isParticalEnable(int anm_target_idx) const {
+ return mAnmTargets[anm_target_idx].is_partical_enable.ref();
+}
+
+int ModelList::getNumASSlot(int anm_target_idx) const {
+ return mAnmTargets[anm_target_idx].num_as_slot.ref();
+}
+
+int ModelList::getNumPartials(int anm_target_idx) const {
+ return mAnmTargets[anm_target_idx].partials.size();
+}
+
+void ModelList::getPartialInfo(PartialInfo* info, int anm_target_idx, int partial_idx) const {
+ if (mAnmTargets.size() > 0) {
+ const auto& partial = mAnmTargets[anm_target_idx].partials[partial_idx];
+ info->bone = partial.bone.ref();
+ info->bind_flag = partial.bind_flag.ref();
+ info->recursible = partial.recursible.ref();
+ } else {
+ info->bone = "";
+ info->bind_flag = 0;
+ info->recursible = true;
+ }
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceModelList.h b/src/KingSystem/Resource/Actor/resResourceModelList.h
new file mode 100644
index 00000000..1d399f46
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceModelList.h
@@ -0,0 +1,169 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterObj.h>
+#include <container/seadBuffer.h>
+#include <prim/seadSafeString.h>
+#include <prim/seadStorageFor.h>
+#include "KingSystem/ActorSystem/actInfoData.h"
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+
+namespace ksys::res {
+
+class ModelList : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(ModelList, Resource)
+public:
+ struct ControllerInfo {
+ agl::utl::Parameter<sead::Color4f> mAddColor;
+ agl::utl::Parameter<sead::Color4f> mMulColor;
+ agl::utl::ParameterObj mObj;
+ agl::utl::Parameter<sead::Vector3f> mBaseScale;
+ agl::utl::Parameter<sead::SafeString> mVariationMatAnim;
+ agl::utl::Parameter<s32> mVariationMatAnimFrame;
+ agl::utl::Parameter<sead::SafeString> mVariationShaderAnim;
+ agl::utl::Parameter<s32> mVariationShaderAnimFrame;
+ agl::utl::Parameter<sead::SafeString> mCalcAABBASKey;
+ };
+ KSYS_CHECK_SIZE_NX150(ControllerInfo, 0x160);
+
+ struct Attention {
+ agl::utl::Parameter<bool> mIsEnableAttention;
+ agl::utl::Parameter<sead::SafeString> mLookAtBone;
+ agl::utl::Parameter<sead::Vector3f> mLookAtOffset;
+ agl::utl::Parameter<f32> mCursorOffsetY;
+ agl::utl::Parameter<f32> mAIInfoOffsetY;
+ agl::utl::Parameter<sead::SafeString> mCutTargetBone;
+ agl::utl::Parameter<sead::Vector3f> mCutTargetOffset;
+ agl::utl::Parameter<sead::SafeString> mGameCameraBone;
+ agl::utl::Parameter<sead::Vector3f> mGameCameraOffset;
+ agl::utl::Parameter<sead::SafeString> mBowCameraBone;
+ agl::utl::Parameter<sead::Vector3f> mBowCameraOffset;
+ agl::utl::Parameter<sead::SafeString> mAttackTargetBone;
+ agl::utl::Parameter<sead::Vector3f> mAttackTargetOffset;
+ agl::utl::Parameter<f32> mAttackTargetOffsetBack;
+ agl::utl::Parameter<sead::SafeString> mAtObstacleChkOffsetBone;
+ agl::utl::Parameter<sead::Vector3f> mAtObstacleChkOffset;
+ agl::utl::Parameter<bool> mAtObstacleChkUseLookAtPos;
+ agl::utl::Parameter<sead::SafeString> mCursorAIInfoBaseBone;
+ agl::utl::Parameter<sead::Vector3f> mCursorAIInfoBaseOffset;
+ agl::utl::ParameterObj mObj;
+ };
+ KSYS_CHECK_SIZE_NX150(Attention, 0x300);
+
+ struct Unit {
+ agl::utl::Parameter<sead::SafeString> unit_name;
+ agl::utl::Parameter<sead::SafeString> bind_bone;
+ agl::utl::ParameterObj obj;
+ };
+ KSYS_CHECK_SIZE_NX150(Unit, 0x80);
+
+ struct ModelData {
+ agl::utl::Parameter<sead::SafeString> folder;
+ agl::utl::ParameterObj base_obj;
+ sead::Buffer<Unit> units;
+ agl::utl::ParameterList unit_list;
+ agl::utl::ParameterList list;
+ };
+ KSYS_CHECK_SIZE_NX150(ModelData, 0xf8);
+
+ // Misspelling of "partical", which is a misspelling of "particle"?
+ struct Partial {
+ agl::utl::Parameter<sead::SafeString> bone;
+ agl::utl::Parameter<s32> bind_flag;
+ agl::utl::Parameter<bool> recursible;
+ agl::utl::ParameterObj obj;
+ };
+ KSYS_CHECK_SIZE_NX150(Partial, 0x98);
+
+ struct AnmTarget {
+ agl::utl::Parameter<s32> num_as_slot;
+ agl::utl::Parameter<bool> is_partical_enable;
+ agl::utl::Parameter<s32> target_type;
+ agl::utl::ParameterObj base_obj;
+ sead::Buffer<Partial> partials;
+ agl::utl::ParameterList partial_list;
+ agl::utl::ParameterList list;
+ };
+ KSYS_CHECK_SIZE_NX150(AnmTarget, 0x130);
+
+ struct ModelDataInfo {
+ std::array<std::array<const char*, 8>, 1> unit_names;
+ std::array<std::array<const char*, 8>, 1> unit_bind_bones;
+ std::array<int, 1> num_units;
+ std::array<const char*, 1> folder_name;
+ int num_model_data;
+ sead::Vector3f base_scale;
+ };
+ KSYS_CHECK_SIZE_NX150(ModelDataInfo, 0xa0);
+
+ struct AttentionInfo {
+ const char* look_at_bone;
+ sead::Vector3f look_at_offset;
+ float cursor_offset_y;
+ float ai_info_offset_y;
+ const char* cut_target_bone;
+ sead::Vector3f cut_target_offset;
+ const char* game_camera_bone;
+ sead::Vector3f game_camera_offset;
+ const char* bow_camera_bone;
+ sead::Vector3f bow_camera_offset;
+ const char* attack_target_bone;
+ sead::Vector3f attack_target_offset;
+ float attack_target_offset_back;
+ const char* at_obstacle_chk_bone;
+ sead::Vector3f at_obstacle_chk_offset;
+ const char* cursor_ai_info_base_bone;
+ sead::Vector3f cursor_ai_info_base_offset;
+ };
+ KSYS_CHECK_SIZE_NX150(AttentionInfo, 0xb0);
+
+ struct PartialInfo {
+ sead::SafeString bone;
+ int bind_flag;
+ bool recursible;
+ };
+ KSYS_CHECK_SIZE_NX150(PartialInfo, 0x18);
+
+ ModelList();
+ ~ModelList() override;
+
+ void doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) override;
+ bool needsParse() const override { return true; }
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+
+ const ControllerInfo& getControllerInfo() const { return mControllerInfo.ref(); }
+ const Attention& getAttention() const { return mAttention.ref(); }
+ const sead::Buffer<ModelData>& getModelData() const { return mModelData; }
+ const sead::Buffer<AnmTarget>& getAnmTargets() const { return mAnmTargets; }
+
+ bool isDummy() const { return mIsDummy; }
+ void markAsDummy() { mIsDummy = true; }
+
+ int getNumAnmTargets() const;
+ void getModelDataInfo(ModelDataInfo* info) const;
+ bool getAttentionInfo(AttentionInfo* info) const;
+ bool getLocatorInfo(act::InfoData::Locator* info, act::InfoData::Locator::Type type) const;
+ bool isParticalEnable(int anm_target_idx) const;
+ int getNumASSlot(int anm_target_idx) const;
+ int getNumPartials(int anm_target_idx) const;
+ void getPartialInfo(PartialInfo* info, int anm_target_idx, int partial_idx) const;
+
+ static act::InfoData::Locator::Type getLocatorTypeFromStr(const sead::SafeString& type);
+
+private:
+ bool parseModelData(const agl::utl::ResParameterList& res, sead::Heap* heap);
+ bool parseAnmTarget(const agl::utl::ResParameterList& res, sead::Heap* heap);
+
+ sead::StorageFor<ControllerInfo, true> mControllerInfo{sead::ZeroInitializeTag{}};
+ sead::StorageFor<Attention, true> mAttention{sead::ZeroInitializeTag{}};
+ u8* mRawData{};
+ sead::Buffer<ModelData> mModelData;
+ agl::utl::ParameterList mModelDataList;
+ sead::Buffer<AnmTarget> mAnmTargets;
+ agl::utl::ParameterList mAnmTargetList;
+ bool mIsDummy = false;
+};
+KSYS_CHECK_SIZE_NX150(ModelList, 0x7d0);
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourcePhysics.cpp b/src/KingSystem/Resource/Actor/resResourcePhysics.cpp
new file mode 100644
index 00000000..f9a90eda
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourcePhysics.cpp
@@ -0,0 +1,15 @@
+#include "KingSystem/Resource/Actor/resResourcePhysics.h"
+
+namespace ksys::res {
+
+void Physics::doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) {}
+
+bool Physics::parse_(u8* data, size_t size, sead::Heap* heap) {
+ if (!data)
+ return true;
+
+ mParamSet.parse(this, agl::utl::ResParameterArchive{data}, heap);
+ return true;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourcePhysics.h b/src/KingSystem/Resource/Actor/resResourcePhysics.h
new file mode 100644
index 00000000..5eef2cfd
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourcePhysics.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "KingSystem/Physics/System/physParamSet.h"
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+
+namespace ksys::res {
+
+class Physics : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(Physics, Resource)
+public:
+ Physics() : ParamIO("physics", 0) {}
+
+ phys::ParamSet& getParamSet() { return mParamSet; }
+
+ void doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) override;
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+ bool needsParse() const override { return true; }
+
+private:
+ phys::ParamSet mParamSet;
+};
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceRagdollBlendWeight.cpp b/src/KingSystem/Resource/Actor/resResourceRagdollBlendWeight.cpp
new file mode 100644
index 00000000..07aec2d7
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceRagdollBlendWeight.cpp
@@ -0,0 +1,78 @@
+#include "KingSystem/Resource/Actor/resResourceRagdollBlendWeight.h"
+
+namespace ksys::res {
+
+void RagdollBlendWeight::doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) {}
+
+bool RagdollBlendWeight::parse_(u8* data, size_t size, sead::Heap* heap) {
+ if (!data)
+ return false;
+
+ using FormatString = sead::FormatFixedSafeString<32>;
+
+ agl::utl::ResParameterArchive archive{data};
+ const auto root = archive.getRootList();
+ const auto num_states = root.getResParameterListNum();
+
+ if (num_states != 0) {
+ mStates.allocBufferAssert(num_states, heap);
+
+ int state_idx = 1;
+ for (auto it = mStates.begin(), end = mStates.end(); it != end; ++it) {
+ const auto State =
+ agl::utl::getResParameterList(root, FormatString("%s%d", "State_", state_idx));
+
+ if (State) {
+ it->state_key.init("", "StateKey", "ステートキー", "", &it->setting_obj);
+ it->system_key.init("", "SystemKey", "システムキー", "", &it->setting_obj);
+
+ const auto InputWeightList =
+ agl::utl::getResParameterList(State, "InputWeightList");
+
+ if (int num_weights; InputWeightList &&
+ (num_weights = InputWeightList.getResParameterObjNum()) != 0) {
+ it->input_weights.allocBufferAssert(num_weights, heap);
+
+ int weight_idx = 1;
+ for (auto wit = it->input_weights.begin(), wend = it->input_weights.end();
+ wit != wend; ++wit) {
+ wit->rigid_name.init("", "RigidName", "ボーン名", "", &wit->obj);
+ wit->blend_rate.init(1.0, "BlendRate", "ブレンド率", "", &wit->obj);
+
+ it->input_weight_list.addObj(
+ &wit->obj, FormatString("%s%d", "InputWeight_", weight_idx));
+ ++weight_idx;
+ }
+ }
+ }
+
+ it->list.addObj(&it->setting_obj, FormatString("Setting"));
+ it->list.addList(&it->input_weight_list, FormatString("InputWeightList"));
+
+ addList(&it->list, FormatString("%s%d", "State_", state_idx));
+ ++state_idx;
+ }
+ }
+
+ applyResParameterArchive(archive);
+ return true;
+}
+
+const sead::SafeString& RagdollBlendWeight::getWeightRigidName(int state_idx,
+ int weight_idx) const {
+ return mStates[state_idx].input_weights[weight_idx].rigid_name.ref();
+}
+
+float RagdollBlendWeight::getWeightBlendRate(int state_idx, int weight_idx) const {
+ return mStates[state_idx].input_weights[weight_idx].blend_rate.ref();
+}
+
+int RagdollBlendWeight::findStateIdx(const sead::SafeString& key) const {
+ for (int i = 0; i < mStates.size(); ++i) {
+ if (mStates[i].state_key.ref() == key)
+ return i;
+ }
+ return -1;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceRagdollBlendWeight.h b/src/KingSystem/Resource/Actor/resResourceRagdollBlendWeight.h
new file mode 100644
index 00000000..257c3184
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceRagdollBlendWeight.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterList.h>
+#include <agl/Utils/aglParameterObj.h>
+#include <container/seadBuffer.h>
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+
+namespace ksys::res {
+
+class RagdollBlendWeight : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(RagdollBlendWeight, Resource)
+public:
+ struct InputWeight {
+ agl::utl::Parameter<sead::SafeString> rigid_name;
+ agl::utl::Parameter<float> blend_rate;
+ agl::utl::ParameterObj obj;
+ };
+
+ struct State {
+ agl::utl::ParameterList list;
+ agl::utl::Parameter<sead::SafeString> state_key;
+ agl::utl::Parameter<sead::SafeString> system_key;
+ agl::utl::ParameterObj setting_obj;
+ agl::utl::ParameterList input_weight_list;
+ sead::Buffer<InputWeight> input_weights;
+ };
+
+ RagdollBlendWeight() : ParamIO("rgbw", 0) {}
+ ~RagdollBlendWeight() override { mStates.freeBuffer(); }
+
+ const sead::Buffer<State>& getStates() const { return mStates; }
+
+ const sead::SafeString& getWeightRigidName(int state_idx, int weight_idx) const;
+ float getWeightBlendRate(int state_idx, int weight_idx) const;
+ int findStateIdx(const sead::SafeString& key) const;
+
+ bool ParamIO_m0(char* data) override { return true; }
+ void doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) override;
+ bool needsParse() const override { return true; }
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+
+private:
+ sead::Buffer<State> mStates;
+};
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceRagdollConfig.cpp b/src/KingSystem/Resource/Actor/resResourceRagdollConfig.cpp
new file mode 100644
index 00000000..dfadf9fd
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceRagdollConfig.cpp
@@ -0,0 +1,30 @@
+#include "KingSystem/Resource/Actor/resResourceRagdollConfig.h"
+
+namespace ksys::res {
+
+const int RagdollConfig::cNumReceiveObjs = 3;
+const int RagdollConfig::cNumImpulseObjs = 5;
+const int RagdollConfig::cNumImpulseParams = 10;
+const std::array<char, 64> RagdollConfig::cImpulseParamNames[10] = {
+ {"Default"}, {"Sword"}, {"LargeSword"}, {"Spear"}, {"Arrow"},
+ {"Bomb"}, {"HeadShot"}, {"ShockWave"}, {"SilentKill"}, {"Gust"},
+};
+
+RagdollConfig::RagdollConfig() : ParamIO("rgconfig", 0) {}
+
+RagdollConfig::~RagdollConfig() = default;
+
+void RagdollConfig::doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) {}
+
+bool RagdollConfig::parse_(u8* data, size_t size, sead::Heap* heap) {
+ if (!data)
+ return false;
+
+ addList(&mConfig, "ConfigRoot");
+
+ agl::utl::ResParameterArchive archive{data};
+ applyResParameterArchive(archive);
+ return true;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceRagdollConfig.h b/src/KingSystem/Resource/Actor/resResourceRagdollConfig.h
new file mode 100644
index 00000000..46e71cf4
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceRagdollConfig.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <array>
+
+#include "KingSystem/Physics/Ragdoll/physRagdollConfig.h"
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+
+namespace ksys::res {
+
+class RagdollConfig : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(RagdollConfig, Resource)
+public:
+ RagdollConfig();
+ ~RagdollConfig() override;
+
+ void doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) override;
+ bool needsParse() const override { return true; }
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+
+ const phys::RagdollConfig& getConfig() const { return mConfig; }
+
+ static const int cNumReceiveObjs;
+ static const int cNumImpulseObjs;
+ static const int cNumImpulseParams;
+ static const std::array<char, 64> cImpulseParamNames[10];
+
+private:
+ phys::RagdollConfig mConfig;
+};
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceRagdollConfigList.cpp b/src/KingSystem/Resource/Actor/resResourceRagdollConfigList.cpp
new file mode 100644
index 00000000..cf230021
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceRagdollConfigList.cpp
@@ -0,0 +1,80 @@
+#include "KingSystem/Resource/Actor/resResourceRagdollConfigList.h"
+#include "KingSystem/Resource/Actor/resResourceRagdollConfig.h"
+
+namespace ksys::res {
+
+RagdollConfigList::RagdollConfigList()
+ : ParamIO("rgconfiglist", 0),
+ mUpperLimitHeight(0.0, "UpperLimitHight" /*sic*/, "ヒット位置判定の境界(上)", &mCommonData),
+ mLowerLimitHeight(0.0, "LowerLimitHight" /*sic*/, "ヒット位置判定の境界(下)",
+ &mCommonData) {}
+
+RagdollConfigList::~RagdollConfigList() {
+ mImpulseParams.freeBuffer();
+ mBodyParams.freeBuffer();
+}
+
+void RagdollConfigList::doCreate_(u8*, u32, sead::Heap*) {}
+
+bool RagdollConfigList::parse_(u8* data, size_t size, sead::Heap* heap) {
+ if (!data)
+ return false;
+
+ if (RagdollConfig::cNumImpulseParams > 0) {
+ mImpulseParams.allocBufferAssert(RagdollConfig::cNumImpulseParams, heap);
+ if (!mImpulseParams.isBufferReady())
+ return false;
+
+#ifdef MATCHING_HACK_NX_CLANG
+ __builtin_assume(RagdollConfig::cNumImpulseParams != 0);
+#endif
+
+ for (int i = 0; i != RagdollConfig::cNumImpulseParams; ++i) {
+ mImpulseParams[i].config = nullptr;
+ mImpulseParams[i].file_name.init("", "FileName", "データファイル名", "",
+ &mImpulseParams[i].obj);
+ mImpulseParamList.addObj(
+ &mImpulseParams[i].obj,
+ sead::FormatFixedSafeString<32>("%s%s", "ImpulseParam_",
+ RagdollConfig::cImpulseParamNames[i].data()));
+ }
+ }
+ addList(&mImpulseParamList, "ImpulseParamList");
+
+ addObj(&mCommonData, "CommonData");
+
+ agl::utl::ResParameterArchive archive{data};
+ const auto root = archive.getRootList();
+ const auto BodyParamList = agl::utl::getResParameterList(root, "BodyParamList");
+ if (int num_params;
+ BodyParamList && (num_params = BodyParamList.getResParameterObjNum()) != 0) {
+ mBodyParams.allocBufferAssert(num_params, heap);
+ for (int i = 0; i < num_params; ++i) {
+ mBodyParams[i].rigid_name.init(sead::SafeString::cEmptyString, "RigidName", "RigidName",
+ "RigidName", &mBodyParams[i]);
+ mBodyParams[i].friction_scale.init(1.0, "FrictionScale", "FrictionScale",
+ "FrictionScale", &mBodyParams[i]);
+ mBodyParams[i].buoyancy_scale.init(1.0, "BuoyancyScale", "BuoyancyScale",
+ "BuoyancyScale", &mBodyParams[i]);
+ mBodyParamList.addObj(&mBodyParams[i],
+ sead::FormatFixedSafeString<32>("BodyParam_%d", i));
+ }
+ }
+ addList(&mBodyParamList, "BodyParamList");
+
+ applyResParameterArchive(archive);
+ return true;
+}
+
+bool RagdollConfigList::finishParsing_() {
+ return true;
+}
+
+bool RagdollConfigList::m7_() {
+ for (auto& param : mImpulseParams)
+ param.config = nullptr;
+
+ return true;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceRagdollConfigList.h b/src/KingSystem/Resource/Actor/resResourceRagdollConfigList.h
new file mode 100644
index 00000000..9b207189
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceRagdollConfigList.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterList.h>
+#include <agl/Utils/aglParameterObj.h>
+#include <container/seadBuffer.h>
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+#include "KingSystem/Utils/Types.h"
+
+namespace ksys::res {
+
+class RagdollConfig;
+
+class RagdollConfigList : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(RagdollConfigList, Resource)
+public:
+ struct ImpulseParam {
+ const char* getFileName() const { return file_name.ref().cstr(); }
+
+ agl::utl::Parameter<sead::SafeString> file_name;
+ agl::utl::ParameterObj obj;
+ RagdollConfig* config;
+ };
+ KSYS_CHECK_SIZE_NX150(ImpulseParam, 0x60);
+
+ struct BodyParam : agl::utl::ParameterObj {
+ agl::utl::Parameter<sead::SafeString> rigid_name;
+ agl::utl::Parameter<f32> friction_scale;
+ agl::utl::Parameter<f32> buoyancy_scale;
+ };
+ KSYS_CHECK_SIZE_NX150(BodyParam, 0x98);
+
+ RagdollConfigList();
+ ~RagdollConfigList() override;
+ RagdollConfigList(const RagdollConfigList&) = delete;
+ auto operator=(const RagdollConfigList&) = delete;
+
+ void doCreate_(u8*, u32, sead::Heap*) override;
+ bool needsParse() const override { return true; }
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+ bool finishParsing_() override;
+ bool m7_() override;
+
+ const sead::Buffer<ImpulseParam>& getImpulseParams() const { return mImpulseParams; }
+ f32 getUpperLimitHeight() const { return mUpperLimitHeight.ref(); }
+ f32 getLowerLimitHeight() const { return mLowerLimitHeight.ref(); }
+ const sead::Buffer<BodyParam>& getBodyParams() const { return mBodyParams; }
+
+ void addImpulseParamConfig_(s32 index, RagdollConfig* config) {
+ mImpulseParams[index].config = config;
+ }
+
+private:
+ agl::utl::ParameterList mImpulseParamList;
+ sead::Buffer<ImpulseParam> mImpulseParams;
+
+ agl::utl::ParameterObj mCommonData;
+ agl::utl::Parameter<f32> mUpperLimitHeight;
+ agl::utl::Parameter<f32> mLowerLimitHeight;
+
+ agl::utl::ParameterList mBodyParamList;
+ sead::Buffer<BodyParam> mBodyParams;
+};
+KSYS_CHECK_SIZE_NX150(RagdollConfigList, 0x3d0);
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceRecipe.cpp b/src/KingSystem/Resource/Actor/resResourceRecipe.cpp
new file mode 100644
index 00000000..c2921e62
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceRecipe.cpp
@@ -0,0 +1,67 @@
+#include "KingSystem/Resource/Actor/resResourceRecipe.h"
+
+namespace ksys::res {
+
+// NON_MATCHING: first line (see also Drop::parse_)
+bool Recipe::parse_(u8* data, size_t, sead::Heap* heap) {
+ mTableNum.init(0, "TableNum", "テーブルの数", &mObj);
+ addObj(&mObj, "Header");
+
+ if (!data)
+ return true;
+
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+
+ const s32 num_tables = mTableNum.ref();
+ if (num_tables < 1)
+ return true;
+
+ mTables.allocBufferAssert(num_tables, heap);
+
+ const agl::utl::ResParameterArchive archive{data};
+ const auto root = archive.getRootList();
+ const auto header_obj = root.getResParameterObj(0);
+
+ for (s32 i = 0; i < num_tables; ++i) {
+ sead::FormatFixedSafeString<64> name;
+ name.format("Table%02d", i + 1);
+ mTables[i].name.init("", name, "テーブル名", &mObj);
+ }
+
+ mObj.applyResParameterObj(header_obj);
+
+ for (s32 i = 0; i < num_tables; ++i) {
+ const auto obj = root.getResParameterObj(i + 1);
+ mTables[i].column_num.init(0, "ColumnNum", "行数", &mTables[i].obj);
+
+ addObj(&mTables[i].obj, mTables[i].name.ref());
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+ mTables[i].obj.applyResParameterObj(obj);
+
+ if (mTables[i].column_num.ref() > 0) {
+ mTables[i].items.allocBufferAssert(mTables[i].column_num.ref(), heap);
+ }
+ }
+
+ for (s32 i = 0; i < num_tables; ++i) {
+ parseTable_(i);
+ }
+
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+ return true;
+}
+
+void Recipe::parseTable_(const s32& table_idx) {
+ const s32 num = mTables[table_idx].column_num.ref();
+ for (s32 i = 0; i < num; ++i) {
+ sead::FormatFixedSafeString<64> name;
+ name.format("ItemName%02d", i + 1);
+ mTables[table_idx].items[i].name.init("", name, "アイテム名", &mTables[table_idx].obj);
+
+ sead::FormatFixedSafeString<64> name2;
+ name2.format("ItemNum%02d", i + 1);
+ mTables[table_idx].items[i].num.init(0, name2, "個数", &mTables[table_idx].obj);
+ }
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceRecipe.h b/src/KingSystem/Resource/Actor/resResourceRecipe.h
new file mode 100644
index 00000000..0429451b
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceRecipe.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterObj.h>
+#include <container/seadBuffer.h>
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+#include "KingSystem/Utils/Types.h"
+
+namespace ksys::res {
+
+class Recipe : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(Recipe, Resource)
+public:
+ struct Item {
+ agl::utl::Parameter<sead::SafeString> name;
+ agl::utl::Parameter<s32> num;
+ };
+ KSYS_CHECK_SIZE_NX150(Item, 0x48);
+
+ struct Table {
+ agl::utl::ParameterObj obj;
+ agl::utl::Parameter<sead::SafeString> name;
+ agl::utl::Parameter<s32> column_num;
+ sead::Buffer<Item> items;
+ };
+ KSYS_CHECK_SIZE_NX150(Table, 0x88);
+
+ Recipe() : ParamIO("recipe", 0) {}
+
+ bool ParamIO_m0(char* data) override { return true; }
+ void doCreate_(u8*, u32, sead::Heap*) override {}
+ bool needsParse() const override { return true; }
+
+ const sead::Buffer<Table>& getTables() const { return mTables; }
+
+private:
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+ void parseTable_(const s32& table_idx);
+
+ agl::utl::ParameterObj mObj;
+ agl::utl::Parameter<s32> mTableNum;
+ sead::Buffer<void*> _300;
+ sead::Buffer<Table> mTables;
+};
+KSYS_CHECK_SIZE_NX150(Recipe, 0x320);
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceShop.cpp b/src/KingSystem/Resource/Actor/resResourceShop.cpp
new file mode 100644
index 00000000..1d6e9974
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceShop.cpp
@@ -0,0 +1,115 @@
+#include "KingSystem/Resource/Actor/resResourceShop.h"
+
+namespace ksys::res {
+
+bool Shop::parse_(u8* data, size_t, sead::Heap* heap) {
+ mTableNum.init(0, "TableNum", "テーブルの数", &mObj);
+ addObj(&mObj, "Header");
+
+ if (!data)
+ return true;
+
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+
+ const s32 num_tables = mTableNum.ref();
+ if (num_tables < 1)
+ return true;
+
+ mTables.allocBufferAssert(num_tables, heap);
+
+ const agl::utl::ResParameterArchive archive{data};
+ const auto root = archive.getRootList();
+ const auto header_obj = root.getResParameterObj(0);
+
+ for (s32 i = 0; i < num_tables; ++i) {
+ sead::FormatFixedSafeString<64> name;
+ name.format("Table%02d", i + 1);
+ mTables[i].name.init("", name, "テーブル名", &mObj);
+ }
+
+ mObj.applyResParameterObj(header_obj);
+
+ for (s32 i = 0; i < num_tables; ++i) {
+ const auto obj = root.getResParameterObj(i + 1);
+ mTables[i].column_num.init(0, "ColumnNum", "行数", &mTables[i].obj);
+
+ addObj(&mTables[i].obj, mTables[i].name.ref());
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+ mTables[i].obj.applyResParameterObj(obj);
+
+ if (mTables[i].column_num.ref() > 0) {
+ mTables[i].items.allocBufferAssert(mTables[i].column_num.ref(), heap);
+ }
+ }
+
+ for (s32 i = 0; i < num_tables; ++i) {
+ parseTable_(i);
+ }
+
+ applyResParameterArchive(agl::utl::ResParameterArchive{data});
+
+ return true;
+}
+
+void Shop::parseTable_(const s32& table_idx) {
+ const s32 num = mTables[table_idx].column_num.ref();
+ for (s32 i = 0; i < num; ++i) {
+ sead::FormatFixedSafeString<64> name;
+ name.format("ItemSort%03d", i + 1);
+ mTables[table_idx].items[i].sort_value.init(0, name, "ソート値", &mTables[table_idx].obj);
+
+ sead::FormatFixedSafeString<64> name2;
+ name2.format("ItemName%03d", i + 1);
+ mTables[table_idx].items[i].name.init("", name2, "アイテム名", &mTables[table_idx].obj);
+
+ sead::FormatFixedSafeString<64> name3;
+ name3.format("ItemNum%03d", i + 1);
+ mTables[table_idx].items[i].num_stock.init(0, name3, "販売個数", &mTables[table_idx].obj);
+
+ sead::FormatFixedSafeString<64> name4;
+ name4.format("ItemAdjustPrice%03d", i + 1);
+ mTables[table_idx].items[i].price_adjustment.init(0, name4, "値段調整値",
+ &mTables[table_idx].obj);
+
+ sead::FormatFixedSafeString<64> name5;
+ name5.format("ItemLookGetFlg%03d", i + 1);
+ mTables[table_idx].items[i].demo_flag.init(false, name5, "ゲットフラグを見るか",
+ &mTables[table_idx].obj);
+
+ sead::FormatFixedSafeString<64> name6;
+ name6.format("ItemAmount%03d", i + 1);
+ mTables[table_idx].items[i].price.init(0, name6, "価値", &mTables[table_idx].obj);
+ }
+}
+
+s32 Shop::findTableIndex(const sead::SafeString& table_name) const {
+ if (!mTables.isBufferReady())
+ return -1;
+
+ const s32 num = mTableNum.ref();
+ for (s32 i = 0; i < num; ++i) {
+ if (mTables[i].name.ref() == table_name)
+ return i;
+ }
+
+ return -1;
+}
+
+s32 Shop::findTableIndexOrNormal(const sead::SafeString& table_name) const {
+ if (!mTables.isBufferReady())
+ return -1;
+
+ s32 normal_idx = -1;
+ const s32 num = mTableNum.ref();
+ for (s32 i = 0; i < num; ++i) {
+ if (mTables[i].name.ref() == table_name)
+ return i;
+
+ if (normal_idx < 0 && mTables[i].name.ref() == "Normal")
+ normal_idx = i;
+ }
+
+ return normal_idx;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceShop.h b/src/KingSystem/Resource/Actor/resResourceShop.h
new file mode 100644
index 00000000..14c383e1
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceShop.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <agl/Utils/aglParameter.h>
+#include <agl/Utils/aglParameterObj.h>
+#include <container/seadBuffer.h>
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+
+namespace ksys::res {
+
+class Shop : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(Shop, Resource)
+
+public:
+ struct Item {
+ agl::utl::Parameter<s32> sort_value;
+ agl::utl::Parameter<sead::SafeString> name;
+ agl::utl::Parameter<s32> num_stock;
+ agl::utl::Parameter<s32> price_adjustment;
+ agl::utl::Parameter<bool> demo_flag;
+ agl::utl::Parameter<s32> price;
+ };
+ KSYS_CHECK_SIZE_NX150(Item, 0xC8);
+
+ struct Table {
+ agl::utl::ParameterObj obj;
+ agl::utl::Parameter<sead::SafeString> name;
+ agl::utl::Parameter<s32> column_num;
+ sead::Buffer<Item> items;
+ };
+ KSYS_CHECK_SIZE_NX150(Table, 0x88);
+
+ Shop() : ParamIO("shop", 0) {}
+
+ s32 findTableIndexOrNormal(const sead::SafeString& table_name) const;
+ s32 findTableIndex(const sead::SafeString& table_name) const;
+
+ bool ParamIO_m0(char* data) override { return true; }
+ void doCreate_(u8*, u32, sead::Heap*) override {}
+ bool needsParse() const override { return true; }
+
+ const sead::Buffer<Table>& getTables() const { return mTables; }
+
+private:
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+ void parseTable_(const s32& table_idx);
+
+ agl::utl::ParameterObj mObj;
+ agl::utl::Parameter<s32> mTableNum;
+ sead::Buffer<void*> _300;
+ sead::Buffer<Table> mTables;
+};
+KSYS_CHECK_SIZE_NX150(Shop, 0x320);
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceUMii.cpp b/src/KingSystem/Resource/Actor/resResourceUMii.cpp
new file mode 100644
index 00000000..2f432137
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceUMii.cpp
@@ -0,0 +1,15 @@
+#include "KingSystem/Resource/Actor/resResourceUMii.h"
+
+namespace ksys::res {
+
+UMii::UMii() : ParamIO("umii", 0) {}
+
+UMii::~UMii() = default;
+
+bool UMii::parse_(u8* data, size_t, sead::Heap*) {
+ agl::utl::ResParameterArchive archive{data + mAllocSize};
+ mArchive = archive;
+ return true;
+}
+
+} // namespace ksys::res
diff --git a/src/KingSystem/Resource/Actor/resResourceUMii.h b/src/KingSystem/Resource/Actor/resResourceUMii.h
new file mode 100644
index 00000000..ed4f8d0d
--- /dev/null
+++ b/src/KingSystem/Resource/Actor/resResourceUMii.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <agl/Utils/aglResParameter.h>
+#include "KingSystem/Resource/resResource.h"
+#include "KingSystem/Utils/ParamIO.h"
+#include "KingSystem/Utils/Types.h"
+
+namespace ksys::res {
+
+class UMii : public ParamIO, public Resource {
+ SEAD_RTTI_OVERRIDE(UMii, Resource)
+public:
+ UMii();
+ ~UMii() override;
+
+ agl::utl::ResParameterArchive getArchive() const { return mArchive; }
+
+ bool needsParse() const override { return true; }
+ bool m2_() override { return mArchive.isValid(); }
+ void doCreate_(u8*, u32, sead::Heap*) override {}
+ bool parse_(u8* data, size_t size, sead::Heap* heap) override;
+
+private:
+ agl::utl::ResParameterArchive mArchive{};
+};
+KSYS_CHECK_SIZE_NX150(UMii, 0x2b8);
+
+} // namespace ksys::res