From aad80e1f5eb4e184574d0742338f4e2bb7d747bd Mon Sep 17 00:00:00 2001 From: GRAnimated Date: Mon, 23 Feb 2026 16:50:52 -0500 Subject: [PATCH 1/2] Boss: Implement BossRaidNpc Co-Authored-By: Claude Sonnet 4.6 --- data/file_list.yml | 18 +++--- lib/al/Library/Joint/JointAimInfo.h | 49 ++++++++++++++++ src/Boss/BossRaid/BossRaidNpc.cpp | 91 +++++++++++++++++++++++++++++ src/Boss/BossRaid/BossRaidNpc.h | 25 ++++++++ src/Scene/ProjectActorFactory.cpp | 3 +- src/Util/NpcEventFlowUtil.h | 16 +++-- 6 files changed, 188 insertions(+), 14 deletions(-) create mode 100644 lib/al/Library/Joint/JointAimInfo.h create mode 100644 src/Boss/BossRaid/BossRaidNpc.cpp create mode 100644 src/Boss/BossRaid/BossRaidNpc.h diff --git a/data/file_list.yml b/data/file_list.yml index 211267e3..93d1aae6 100644 --- a/data/file_list.yml +++ b/data/file_list.yml @@ -8237,40 +8237,40 @@ Boss/BossRaid/BossRaidNpc.o: - offset: 0x0413d8 size: 128 label: _ZN11BossRaidNpcC2EPKc - status: NotDecompiled + status: Matching - offset: 0x041458 size: 140 label: _ZN11BossRaidNpcC1EPKc - status: NotDecompiled + status: Matching - offset: 0x0414e4 size: 672 label: _ZN11BossRaidNpc4initERKN2al13ActorInitInfoE - status: NotDecompiled + status: Matching - offset: 0x041784 size: 164 label: _ZN11BossRaidNpc10receiveMsgEPKN2al9SensorMsgEPNS0_9HitSensorES5_ - status: NotDecompiled + status: Matching - offset: 0x041828 size: 68 label: _ZNK11BossRaidNpc16isEnableReactionEv - status: NotDecompiled + status: Matching - offset: 0x04186c size: 80 label: _ZN11BossRaidNpc8exeEventEv - status: NotDecompiled + status: Matching - offset: 0x0418bc size: 152 label: _ZN11BossRaidNpc11exeReactionEv - status: NotDecompiled + status: Matching - offset: 0x041954 size: 80 label: _ZNK12_GLOBAL__N_119BossRaidNpcNrvEvent7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x0419a4 size: 8 label: _ZNK12_GLOBAL__N_122BossRaidNpcNrvReaction7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true Boss/BossRaid/BossRaidRivet.o: '.text': diff --git a/lib/al/Library/Joint/JointAimInfo.h b/lib/al/Library/Joint/JointAimInfo.h new file mode 100644 index 00000000..999a9278 --- /dev/null +++ b/lib/al/Library/Joint/JointAimInfo.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +namespace al { + +class JointAimInfo { +public: + JointAimInfo(); + void makeTurnQuat(sead::Quatf* quat, const sead::Vector3f& targetDir) const; + void makeTurnQuatCircle(sead::Quatf* quat, const sead::Vector3f& targetDir) const; + void makeTurnQuatOval(sead::Quatf* quat, const sead::Vector3f& targetDir) const; + void makeTurnQuatRect(sead::Quatf* quat, const sead::Vector3f& targetDir) const; + void setBaseAimLocalDir(const sead::Vector3f& dir); + void setBaseUpLocalDir(const sead::Vector3f& dir); + void setBaseSideLocalDir(const sead::Vector3f& dir); + void setBaseOffsetLocal(const sead::Vector3f& offset); + void setBaseMtxPtr(const sead::Matrix34f* mtxPtr); + void setTargetPos(const sead::Vector3f& pos); + void setPowerRate(f32 rate); + void setLimitDegreeCircle(f32 degree); + void setLimitDegreeOval(f32 a, f32 b, f32 c, f32 d); + void setLimitDegreeRect(f32 a, f32 b, f32 c, f32 d); + void setEnableBackAim(bool enable); + void addPowerRate(f32 rate); + void subPowerRate(f32 rate); + void setInterpoleRate(f32 rate); + +private: + sead::Vector3f mTargetPos = sead::Vector3f::zero; + sead::Vector3f mBaseAimLocalDir = sead::Vector3f::ez; + sead::Vector3f mBaseSideLocalDir = sead::Vector3f::ex; + sead::Vector3f mBaseUpLocalDir = sead::Vector3f::ey; + sead::Vector3f mBaseOffsetLocal = sead::Vector3f::zero; + const sead::Matrix34f* mBaseMtxPtr = nullptr; + f32 mPowerRate = 1.0f; + f32 mInterpoleRate = 0.1f; + f32 _50 = 30.0f; + f32 _54 = 30.0f; + f32 _58 = 30.0f; + f32 _5c = 30.0f; + s32 _60 = 0; + bool mIsEnableBackAim = false; +}; + +static_assert(sizeof(JointAimInfo) == 0x68); + +} // namespace al diff --git a/src/Boss/BossRaid/BossRaidNpc.cpp b/src/Boss/BossRaid/BossRaidNpc.cpp new file mode 100644 index 00000000..1cdb20d5 --- /dev/null +++ b/src/Boss/BossRaid/BossRaidNpc.cpp @@ -0,0 +1,91 @@ +#include "Boss/BossRaid/BossRaidNpc.h" + +#include + +#include "Library/Demo/DemoFunction.h" +#include "Library/Joint/JointAimInfo.h" +#include "Library/Joint/JointControllerKeeper.h" +#include "Library/LiveActor/ActorActionFunction.h" +#include "Library/LiveActor/ActorInitUtil.h" +#include "Library/LiveActor/ActorSensorUtil.h" +#include "Library/Nerve/NerveSetupUtil.h" +#include "Library/Nerve/NerveUtil.h" + +#include "Util/NpcEventFlowUtil.h" +#include "Util/PlayerUtil.h" +#include "Util/SensorMsgFunction.h" + +namespace { +NERVE_IMPL(BossRaidNpc, Event); +NERVE_IMPL(BossRaidNpc, Reaction); +NERVES_MAKE_NOSTRUCT(BossRaidNpc, Event, Reaction); +} // namespace + +BossRaidNpc::BossRaidNpc(const char* name) : al::LiveActor(name) {} + +void BossRaidNpc::init(const al::ActorInitInfo& info) { + al::initActorWithArchiveName(this, info, "BossRaid", "Npc"); + al::initNerve(this, &Event, 0); + mEventFlowExecutor = rs::initEventFlow(this, info, "BossRaid", "Talk"); + if (rs::isDefinedEventCamera(mEventFlowExecutor, "Default")) + rs::initEventCameraObject(mEventFlowExecutor, info, "Default"); + rs::startEventFlow(mEventFlowExecutor, "Init"); + al::initJointControllerKeeper(this, 2); + + mJointAimInfoL = new al::JointAimInfo(); + mJointAimInfoL->setPowerRate(1.0f); + mJointAimInfoL->setInterpoleRate(0.1f); + mJointAimInfoL->setBaseAimLocalDir(sead::Vector3f::ex); + mJointAimInfoL->setBaseUpLocalDir(sead::Vector3f::ey); + mJointAimInfoL->setBaseSideLocalDir(sead::Vector3f::ez); + mJointAimInfoL->setLimitDegreeRect(43.0f, 43.0f, 25.0f, 20.0f); + mJointAimInfoL->setEnableBackAim(true); + al::initJointAimController(this, mJointAimInfoL, "EyeL"); + + mJointAimInfoR = new al::JointAimInfo(); + mJointAimInfoR->setPowerRate(1.0f); + mJointAimInfoR->setInterpoleRate(0.1f); + mJointAimInfoR->setBaseAimLocalDir(-sead::Vector3f::ex); + mJointAimInfoR->setBaseUpLocalDir(-sead::Vector3f::ey); + mJointAimInfoR->setBaseSideLocalDir(-sead::Vector3f::ez); + mJointAimInfoR->setLimitDegreeRect(43.0f, 43.0f, 25.0f, 20.0f); + mJointAimInfoR->setEnableBackAim(true); + al::initJointAimController(this, mJointAimInfoR, "EyeR"); + + al::registActorToDemoInfo(this, info); + makeActorAlive(); +} + +bool BossRaidNpc::receiveMsg(const al::SensorMsg* msg, al::HitSensor* other, al::HitSensor* self) { + if (rs::isMsgPlayerDisregardTargetMarker(msg)) + return true; + if (rs::isMsgNpcCapReactionAll(msg) && al::isSensorEnemyBody(self) && isEnableReaction()) { + rs::requestHitReactionToAttacker(msg, self, other); + al::setNerve(this, &Reaction); + return true; + } + return false; +} + +bool BossRaidNpc::isEnableReaction() const { + if (al::isNerve(this, &Reaction)) + return al::isGreaterEqualStep(this, 20); + return true; +} + +void BossRaidNpc::exeEvent() { + rs::updateEventFlow(mEventFlowExecutor); + mJointAimInfoL->setTargetPos(rs::getPlayerBodyPos(this)); + mJointAimInfoR->setTargetPos(rs::getPlayerBodyPos(this)); +} + +void BossRaidNpc::exeReaction() { + if (al::isFirstStep(this)) + al::startAction(this, "Reaction"); + mJointAimInfoL->setTargetPos(rs::getPlayerBodyPos(this)); + mJointAimInfoR->setTargetPos(rs::getPlayerBodyPos(this)); + if (al::isActionEnd(this)) { + al::startAction(this, "Wait"); + al::setNerve(this, &Event); + } +} diff --git a/src/Boss/BossRaid/BossRaidNpc.h b/src/Boss/BossRaid/BossRaidNpc.h new file mode 100644 index 00000000..0c2d3c48 --- /dev/null +++ b/src/Boss/BossRaid/BossRaidNpc.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Library/LiveActor/LiveActor.h" + +namespace al { +class EventFlowExecutor; +class JointAimInfo; +} // namespace al + +class BossRaidNpc : public al::LiveActor { +public: + explicit BossRaidNpc(const char* name); + void init(const al::ActorInitInfo& info) override; + bool receiveMsg(const al::SensorMsg* msg, al::HitSensor* other, al::HitSensor* self) override; + bool isEnableReaction() const; + void exeEvent(); + void exeReaction(); + +private: + al::EventFlowExecutor* mEventFlowExecutor = nullptr; + al::JointAimInfo* mJointAimInfoL = nullptr; + al::JointAimInfo* mJointAimInfoR = nullptr; +}; + +static_assert(sizeof(BossRaidNpc) == 0x120); diff --git a/src/Scene/ProjectActorFactory.cpp b/src/Scene/ProjectActorFactory.cpp index ef795849..91e355e3 100644 --- a/src/Scene/ProjectActorFactory.cpp +++ b/src/Scene/ProjectActorFactory.cpp @@ -36,6 +36,7 @@ #include "Boss/BarrierField.h" #include "Boss/BossForest/BossForestWander.h" +#include "Boss/BossRaid/BossRaidNpc.h" #include "Boss/Mofumofu/MofumofuScrap.h" #include "Camera/ScenarioStartCamera.h" #include "Enemy/Bubble.h" @@ -158,7 +159,7 @@ const al::NameToCreator sProjectActorFactoryEntries[] {"BossKnuckleFix", nullptr}, {"BossMagma", nullptr}, {"BossRaid", nullptr}, - {"BossRaidNpc", nullptr}, + {"BossRaidNpc", al::createActorFunction}, {"BossRaidRivet", nullptr}, {"BreakablePole", nullptr}, {"Breeda", nullptr}, diff --git a/src/Util/NpcEventFlowUtil.h b/src/Util/NpcEventFlowUtil.h index 4a102a28..2eacc5ca 100644 --- a/src/Util/NpcEventFlowUtil.h +++ b/src/Util/NpcEventFlowUtil.h @@ -16,12 +16,20 @@ al::EventFlowExecutor* initEventFlowSuffix(al::LiveActor*, const al::ActorInitIn const char*, const char*); void startEventFlow(al::EventFlowExecutor*, const char*); bool updateEventFlow(al::EventFlowExecutor*); +bool isDefinedEventCamera(const al::EventFlowExecutor*, const char*); +void initEventCameraObject(al::EventFlowExecutor*, const al::ActorInitInfo&, const char*); +void initEventCameraObjectAfterKeepPose(al::EventFlowExecutor*, const al::ActorInitInfo&, + const char*); void initEventMessageTagDataHolder(al::EventFlowExecutor*, const al::MessageTagDataHolder*); -void initEventCameraObject(al::EventFlowExecutor* flowExecutor, const al::ActorInitInfo& initInfo, - const char* name); -void initEventCameraObjectAfterKeepPose(al::EventFlowExecutor* flowExecutor, - const al::ActorInitInfo& initInfo, const char* name); void setEventBalloonFilterOnlyMiniGame(const al::LiveActor*); void resetEventBalloonFilter(const al::LiveActor*); +bool tryStartEventCutSceneDemo(al::LiveActor*); +void endEventCutSceneDemoOrTryEndEventCutSceneDemoBySkip(al::LiveActor*); +void tryHideDemoPlayerIfRequested(al::LiveActor*, al::EventFlowExecutor*); +void tryShowDemoPlayerIfRequested(al::LiveActor*, al::EventFlowExecutor*); +void tryStartDemoPlayerActionIfRequested(al::LiveActor*, al::EventFlowExecutor*); +bool isPlayingTextPaneAnimEventTalkMessage(const al::LiveActor*); +bool isCloseNpcDemoEventTalkMessage(const al::LiveActor*); void requestSwitchTalkNpcEventVolleyBall(al::LiveActor*, s32); +void skipEventDemo(al::EventFlowExecutor*); } // namespace rs From 9b07d2d427d7342879f380048d5b49731e2b732c Mon Sep 17 00:00:00 2001 From: GRAnimated Date: Mon, 23 Feb 2026 17:08:18 -0500 Subject: [PATCH 2/2] fix matching state --- data/file_list.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/file_list.yml b/data/file_list.yml index 93d1aae6..03e558f8 100644 --- a/data/file_list.yml +++ b/data/file_list.yml @@ -133693,7 +133693,7 @@ Scene/ProjectActorFactory.o: - offset: 0x4b6778 size: 52 label: _ZN2al19createActorFunctionI11BossRaidNpcEEPNS_9LiveActorEPKc - status: NotDecompiled + status: Matching lazy: true - offset: 0x4b67ac size: 52