1#include "MapObj/CapSwitch.h"
2
3#include "Library/Collision/PartsConnector.h"
4#include "Library/LiveActor/ActorActionFunction.h"
5#include "Library/LiveActor/ActorClippingFunction.h"
6#include "Library/LiveActor/ActorInitFunction.h"
7#include "Library/LiveActor/ActorInitUtil.h"
8#include "Library/LiveActor/ActorModelFunction.h"
9#include "Library/LiveActor/ActorPoseUtil.h"
10#include "Library/LiveActor/ActorSensorUtil.h"
11#include "Library/Math/MathUtil.h"
12#include "Library/Nerve/NerveSetupUtil.h"
13#include "Library/Nerve/NerveUtil.h"
14#include "Library/Placement/PlacementFunction.h"
15#include "Library/Stage/StageSwitchUtil.h"
16#include "Library/Thread/FunctorV0M.h"
17
18#include "MapObj/AppearSwitchSave.h"
19#include "Player/CapTargetInfo.h"
20#include "Util/Hack.h"
21#include "Util/PlayerUtil.h"
22#include "Util/SensorMsgFunction.h"
23
24namespace {
25NERVE_IMPL(CapSwitch, OffWaitInvalid);
26NERVE_IMPL(CapSwitch, OffWait);
27NERVE_IMPL(CapSwitch, OnWait);
28NERVE_IMPL(CapSwitch, ReturnOff);
29NERVE_IMPL(CapSwitch, OffWaitCapHold);
30NERVE_IMPL(CapSwitch, HitReaction);
31
32NERVES_MAKE_NOSTRUCT(CapSwitch, HitReaction);
33NERVES_MAKE_STRUCT(CapSwitch, OffWaitInvalid, OffWait, OnWait, ReturnOff, OffWaitCapHold);
34} // namespace
35
36CapSwitch::CapSwitch(const char* name) : al::LiveActor(name) {}
37
38void CapSwitch::init(const al::ActorInitInfo& info) {
39 using CapSwitchFunctor = al::FunctorV0M<CapSwitch*, void (CapSwitch::*)()>;
40
41 al::initActorWithArchiveName(actor: this, initInfo: info, archiveName: "CapSwitch", suffix: nullptr);
42
43 if (al::tryGetBoolArgOrFalse(initInfo: info, key: "IsNoReaction"))
44 al::initNerve(actor: this, nerve: &NrvCapSwitch.OffWaitInvalid, maxStates: 0);
45 else {
46 al::initNerve(actor: this, nerve: &NrvCapSwitch.OffWait, maxStates: 0);
47 mMtxConnector = al::tryCreateMtxConnector(actor: this, info);
48
49 if (al::isObjectName(initInfo: info, name: "CapSwitchSave")) {
50 mAppearSwitchSave = new AppearSwitchSave(this, info);
51 if (mAppearSwitchSave->isOn()) {
52 al::tryOnStageSwitch(user: this, linkName: "CapAttackOn");
53 al::setNerve(user: this, nerve: &NrvCapSwitch.OnWait);
54 makeActorAlive();
55 return;
56 }
57 }
58
59 mCapTargetInfo = rs::createCapTargetInfo(this, nullptr);
60
61 if (al::listenStageSwitchOnStart(user: this, action: CapSwitchFunctor(this, &CapSwitch::listenStart)))
62 al::setNerve(user: this, nerve: &NrvCapSwitch.OffWaitInvalid);
63 al::listenStageSwitchOn(user: this, eventName: "ResetSwitch",
64 action: CapSwitchFunctor(this, &CapSwitch::listenReset));
65 }
66
67 makeActorAlive();
68}
69
70void CapSwitch::listenStart() {
71 al::setNerve(user: this, nerve: &NrvCapSwitch.OffWait);
72}
73
74void CapSwitch::listenReset() {
75 if (isOn())
76 al::setNerve(user: this, nerve: &NrvCapSwitch.ReturnOff);
77}
78
79bool CapSwitch::receiveMsg(const al::SensorMsg* msg, al::HitSensor* other, al::HitSensor* self) {
80 if (al::isNerve(user: this, nerve: &NrvCapSwitch.OffWaitInvalid))
81 return rs::isMsgPlayerDisregardHomingAttack(msg);
82
83 if (rs::isMsgCapStartLockOn(msg) && al::isNerve(user: this, nerve: &NrvCapSwitch.OffWait))
84 return true;
85
86 if (rs::tryReceiveMsgInitCapTargetAndSetCapTargetInfo(msg, mCapTargetInfo)) {
87 rs::tryGetFlyingCapPos(&mFlyingCapPos, this);
88 al::invalidateClipping(actor: this);
89 al::setNerve(user: this, nerve: &NrvCapSwitch.OffWaitCapHold);
90 return true;
91 }
92
93 if ((rs::isMsgCapKeepLockOn(msg) || rs::isMsgCapIgnoreCancelLockOn(msg)) &&
94 al::isNerve(user: this, nerve: &NrvCapSwitch.OffWaitCapHold))
95 return true;
96
97 if (rs::isMsgCapCancelLockOn(msg))
98 return true;
99
100 return false;
101}
102
103void CapSwitch::initAfterPlacement() {
104 if (mMtxConnector)
105 al::attachMtxConnectorToCollision(mtxConnector: mMtxConnector, actor: this, false);
106}
107
108void CapSwitch::control() {
109 if (!al::isNerve(user: this, nerve: &NrvCapSwitch.OffWaitInvalid) && mMtxConnector)
110 al::connectPoseQT(actor: this, mtxConnector: mMtxConnector);
111}
112
113void CapSwitch::exeOffWait() {
114 if (al::isFirstStep(user: this)) {
115 al::validateClipping(actor: this);
116 al::validateHitSensors(this);
117 al::startAction(actor: this, actionName: "OffWait");
118 }
119}
120
121void CapSwitch::exeOffWaitCapHold() {
122 if (al::isFirstStep(user: this)) {
123 al::startAction(actor: this, actionName: "HitReaction");
124 mPlayerPos = rs::getPlayerPos(this);
125 }
126
127 sead::Vector3f hitTargetPos = sead::Vector3f::zero;
128 al::calcJointPos(&hitTargetPos, actor: this, "HatTarget");
129
130 sead::Vector3f frontDir = sead::Vector3f::ez;
131 al::calcFrontDir(front: &frontDir, actor: this);
132
133 f32 angle = al::isNearZero(value: (hitTargetPos - mPlayerPos).normalize()) ?
134 0.0f :
135 al::calcAngleDegree(a: frontDir, b: hitTargetPos - mPlayerPos);
136
137 sead::Vector3f dir =
138 sead::Vector3f(0, al::isLeftTarget(actor: this, pos: mPlayerPos) ? -angle : angle, 270.0f);
139
140 mCapTargetInfo->setFollowLockOnMtx(jointName: "HatTarget", localTrans: sead::Vector3f::zero, localRotate: dir);
141
142 if (al::isActionPlaying(actor: this, actionName: "HitReaction") && al::isActionEnd(actor: this))
143 al::startAction(actor: this, actionName: mIsFacingFront ? "HitReactionKeepFront" : "HitReactionKeepBack");
144
145 if (al::isGreaterEqualStep(user: this, step: 30)) {
146 rs::requestLockOnCapHitReaction(this, mCapTargetInfo, "帽子スイッチをオンにした");
147 al::setNerve(user: this, nerve: &HitReaction);
148 }
149}
150
151void CapSwitch::exeHitReaction() {
152 if (al::isFirstStep(user: this)) {
153 al::startHitReaction(actor: this, name: "起動");
154 al::startAction(actor: this, actionName: mIsFacingFront ? "HitReactionFront" : "HitReactionBack");
155 }
156
157 if (al::isActionEnd(actor: this)) {
158 al::tryOnStageSwitch(user: this, linkName: "CapAttackOn");
159 if (mAppearSwitchSave) {
160 mAppearSwitchSave->onSwitch();
161 al::tryOnStageSwitch(user: this, linkName: "CapAttackFirstOn");
162 }
163 al::setNerve(user: this, nerve: &NrvCapSwitch.OnWait);
164 }
165}
166
167void CapSwitch::exeOnWait() {
168 if (al::isFirstStep(user: this)) {
169 al::invalidateHitSensors(this);
170 al::validateClipping(actor: this);
171 al::startAction(actor: this, actionName: mIsFacingFront ? "OnWaitFront" : "OnWaitBack");
172 }
173}
174
175void CapSwitch::exeReturnOff() {
176 if (al::isFirstStep(user: this))
177 al::startAction(actor: this, actionName: mIsFacingFront ? "OffFront" : "OffBack");
178
179 if (al::isActionEnd(actor: this)) {
180 al::tryOffStageSwitch(user: this, linkName: "CapAttackOn");
181 al::setNerve(user: this, nerve: &NrvCapSwitch.OffWait);
182 }
183}
184
185void CapSwitch::exeOffWaitInvalid() {}
186
187bool CapSwitch::isOn() const {
188 return al::isNerve(user: this, nerve: &NrvCapSwitch.OnWait);
189}
190