1#include "Enemy/EnemyStateSwoon.h"
2
3#include <math/seadVector.h>
4
5#include "Library/Collision/CollisionPartsKeeperUtil.h"
6#include "Library/Item/ItemUtil.h"
7#include "Library/LiveActor/ActorActionFunction.h"
8#include "Library/LiveActor/ActorAreaFunction.h"
9#include "Library/LiveActor/ActorCollisionFunction.h"
10#include "Library/LiveActor/ActorMovementFunction.h"
11#include "Library/LiveActor/ActorPoseUtil.h"
12#include "Library/LiveActor/ActorSensorUtil.h"
13#include "Library/LiveActor/LiveActorFunction.h"
14#include "Library/Math/MathUtil.h"
15#include "Library/Nature/WaterSurfaceFinder.h"
16#include "Library/Nerve/NerveSetupUtil.h"
17#include "Library/Nerve/NerveUtil.h"
18
19#include "Util/SensorMsgFunction.h"
20
21namespace {
22NERVE_IMPL(EnemyStateSwoon, SwoonStart);
23NERVE_IMPL(EnemyStateSwoon, SwoonStartFall);
24NERVE_IMPL(EnemyStateSwoon, SwoonStartLand);
25NERVE_IMPL(EnemyStateSwoon, SwoonLoop);
26NERVE_IMPL(EnemyStateSwoon, SwoonEndSign);
27NERVE_IMPL(EnemyStateSwoon, SwoonEnd);
28NERVE_IMPL(EnemyStateSwoon, SwoonTrampled);
29
30NERVES_MAKE_NOSTRUCT(EnemyStateSwoon, SwoonStart, SwoonTrampled);
31NERVES_MAKE_STRUCT(EnemyStateSwoon, SwoonEnd, SwoonLoop, SwoonStartFall, SwoonStartLand,
32 SwoonEndSign);
33} // namespace
34
35bool isNearWater(al::WaterSurfaceFinder* waterSurfaceFinder, al::LiveActor* actor) {
36 if (!waterSurfaceFinder || !waterSurfaceFinder->isFoundSurface())
37 return false;
38
39 sead::Vector3f gravity = al::getGravity(actor);
40 sead::Vector3f nextPosition = al::getTrans(actor) - gravity * 100.0f;
41 sead::Vector3f direction = gravity * 800.0f;
42 sead::Vector3f hitPos;
43 if (!alCollisionUtil::getHitPosOnArrow(actor, &hitPos, nextPosition, direction, nullptr,
44 nullptr) ||
45 !(gravity.dot(t: hitPos - waterSurfaceFinder->getSurfacePosition()) < 0.0f)) {
46 bool isNearWater = waterSurfaceFinder->isNearSurface(distance: 60.0f);
47 if (gravity.dot(t: al::getVelocity(actor)) > 0.0)
48 isNearWater = waterSurfaceFinder->getDistance() > -60.0f;
49 return isNearWater;
50 }
51 return false;
52}
53
54EnemyStateSwoon::EnemyStateSwoon(al::LiveActor* actor, const char* startAnimName,
55 const char* loopAnimName, const char* endAnimName,
56 bool hasSubActors, bool hasStartLandAnimation)
57 : al::ActorStateBase("気絶ステート", actor), mStartAnimName(startAnimName),
58 mLoopAnimName(loopAnimName), mEndAnimName(endAnimName), mHasSubActors(hasSubActors),
59 mHasStartLandAnimation(hasStartLandAnimation) {
60 initNerve(nerve: &SwoonStart, stateCount: 0);
61}
62
63void EnemyStateSwoon::appear() {
64 al::NerveStateBase::appear();
65 mIsLockOn = false;
66 al::setNerve(user: this, nerve: &SwoonStart);
67}
68
69void EnemyStateSwoon::control() {
70 if (_24 > 0)
71 _24--;
72}
73
74bool EnemyStateSwoon::tryReceiveMsgAttack(const al::SensorMsg* message) {
75 if (isDead())
76 return rs::isMsgCapAttack(message);
77 return false;
78}
79
80bool EnemyStateSwoon::tryReceiveMsgStartHack(const al::SensorMsg* message) {
81 if (rs::isMsgStartHack(message)) {
82 if (!mHasLockOnDelay && al::isNerve(user: this, nerve: &SwoonStart))
83 return al::isGreaterEqualStep(user: this, step: 40);
84 return true;
85 }
86 return false;
87}
88
89bool EnemyStateSwoon::tryReceiveMsgEndSwoon(const al::SensorMsg* message) {
90 if (rs::isMsgCapCancelLockOn(message)) {
91 if (!al::isNerve(user: this, nerve: &NrvEnemyStateSwoon.SwoonEnd))
92 al::setNerve(user: this, nerve: &NrvEnemyStateSwoon.SwoonEnd);
93 return true;
94 }
95 return false;
96}
97
98bool EnemyStateSwoon::tryReceiveMsgPressDown(const al::SensorMsg* message) {
99 if (rs::isMsgPressDown(message))
100 return !mIsLockOn;
101 return false;
102}
103
104bool EnemyStateSwoon::tryReceiveMsgObjHipDropAll(const al::SensorMsg* message) {
105 if (rs::isMsgPlayerAndCapObjHipDropAll(message))
106 return !mIsLockOn;
107 return false;
108}
109
110bool EnemyStateSwoon::tryReceiveMsgTrample(const al::SensorMsg* message) {
111 if (al::isMsgPlayerTrample(msg: message))
112 return requestTrampled();
113 return false;
114}
115
116bool EnemyStateSwoon::tryReceiveMsgTrample(const al::SensorMsg* message, const al::HitSensor* other,
117 const al::HitSensor* self) {
118 if (al::isMsgPlayerTrampleForCrossoverSensor(msg: message, other, self))
119 return tryReceiveMsgTrample(message);
120 return false;
121}
122
123bool EnemyStateSwoon::tryReceiveMsgTrampleReflect(const al::SensorMsg* message) {
124 if (al::isMsgPlayerTrampleReflect(msg: message))
125 return requestTrampled();
126 return false;
127}
128
129bool EnemyStateSwoon::tryReceiveMsgTrampleReflect(const al::SensorMsg* message,
130 const al::HitSensor* other,
131 const al::HitSensor* self) {
132 if (al::isMsgPlayerTrampleForCrossoverSensor(msg: message, other, self))
133 return tryReceiveMsgTrampleReflect(message);
134 return false;
135}
136
137bool EnemyStateSwoon::tryReceiveMsgObjHipDropReflect(const al::SensorMsg* message) {
138 if (rs::isMsgPlayerAndCapObjHipDropReflectAll(message)) {
139 al::setNerve(user: this, nerve: &SwoonTrampled);
140 return true;
141 }
142 return false;
143}
144
145bool EnemyStateSwoon::tryReceiveMsgObjLeapFrog(const al::SensorMsg* message,
146 const al::HitSensor* other,
147 const al::HitSensor* self) {
148 if (rs::isMsgPlayerObjLeapFrog(message) &&
149 !al::isNearZero(vec: al::getActorVelocity(other), tolerance: 0.001f)) {
150 if (al::getActorVelocity(other).dot(t: al::getActorGravity(self)) < 0.0f)
151 return false;
152 return requestTrampled();
153 }
154 return false;
155}
156
157bool EnemyStateSwoon::tryReceiveMsgEnableLockOn(const al::SensorMsg* message) {
158 if (rs::isMsgCapEnableLockOn(message)) {
159 mIsLockOn = true;
160 return true;
161 }
162 return false;
163}
164
165bool EnemyStateSwoon::tryReceiveMsgStartLockOn(const al::SensorMsg* message) {
166 if (rs::isMsgCapEnableLockOn(message))
167 return mHasLockOnDelay;
168 return false;
169}
170
171bool EnemyStateSwoon::requestTrampled() {
172 if (!al::isNerve(user: this, nerve: &SwoonTrampled) || !al::isLessStep(user: this, step: 10)) {
173 al::setNerve(user: this, nerve: &SwoonTrampled);
174 return true;
175 }
176 return false;
177}
178
179void EnemyStateSwoon::initParams(s32 swoonDuration, const char* trampledAnimName) {
180 mSwoonDuration = swoonDuration;
181 if (trampledAnimName != nullptr)
182 mTrampledAnimName = trampledAnimName;
183}
184
185void EnemyStateSwoon::initParams(const EnemyStateSwoonInitParam& initParam) {
186 mStartAnimName = initParam.startAnimName;
187 mLoopAnimName = initParam.loopAnimName;
188 mEndAnimName = initParam.endAnimName;
189 if (initParam.trampledAnimName)
190 mTrampledAnimName = initParam.trampledAnimName;
191 mStartFallAnimName = initParam.startFallAnimName;
192 mStartLandAnimName = initParam.startLandAnimName;
193 mEndSignAnimName = initParam.endSignAnimName;
194 mNearWaterStartAnimName = initParam.nearWaterStartAnimName;
195 mNearWaterLoopAnimName = initParam.nearWaterLoopAnimName;
196 mNearWaterEndAnimName = initParam.nearWaterEndAnimName;
197 mNearWaterStartLandAnimName = initParam.nearWaterStartLandAnimName;
198 mNearWaterTrampledAnimName = initParam.nearWaterTrampledAnimName;
199 mHitReactionAnimName = initParam.hitReactionAnimName;
200 mHitReactionLandAnimName = initParam.hitReactionLandAnimName;
201 mHasSubActors = initParam.hasSubActors;
202 mHasStartLandAnimation = initParam.hasStartLandAnimation;
203 mHasLockOnDelay = initParam.hasLockOnDelay;
204 mSwoonDuration = initParam.swoonDuration;
205 mEndSignDelay = initParam.endSignDelay;
206 mIsCancelLoopOnProhibitedArea = initParam.isCancelLoopOnProhibitedArea;
207}
208
209const char* EnemyStateSwoon::getSwoonStartAnimName() const {
210 if (isNearWater(waterSurfaceFinder: mWaterSurfaceFinder, actor: mActor) && mNearWaterStartAnimName)
211 return mNearWaterStartAnimName;
212 return mStartAnimName;
213}
214
215const char* EnemyStateSwoon::getSwoonStartLandAnimName() const {
216 if (isNearWater(waterSurfaceFinder: mWaterSurfaceFinder, actor: mActor) && mNearWaterStartLandAnimName)
217 return mNearWaterStartLandAnimName;
218 return mStartLandAnimName;
219}
220
221bool EnemyStateSwoon::isOnGroundOrWaterSurface() const {
222 if (isNearWater(waterSurfaceFinder: mWaterSurfaceFinder, actor: mActor))
223 return true;
224 return al::isOnGround(mActor, 0);
225}
226
227bool EnemyStateSwoon::tryStartHitReactionLand() {
228 if (isNearWater(waterSurfaceFinder: mWaterSurfaceFinder, actor: mActor)) {
229 if (mHitReactionLandAnimName) {
230 al::startHitReaction(actor: mActor, name: mHitReactionLandAnimName);
231 return true;
232 }
233 return false;
234 }
235
236 if (mHitReactionAnimName) {
237 al::startHitReaction(actor: mActor, name: mHitReactionAnimName);
238 return true;
239 }
240
241 return false;
242}
243
244const char* EnemyStateSwoon::getSwoonLoopAnimName() const {
245 if (isNearWater(waterSurfaceFinder: mWaterSurfaceFinder, actor: mActor) && mNearWaterLoopAnimName)
246 return mNearWaterLoopAnimName;
247 return mLoopAnimName;
248}
249
250bool EnemyStateSwoon::isPlayingActionIncorrect() const {
251 al::LiveActor* actor = mActor;
252 if (mNearWaterLoopAnimName && al::isActionPlaying(actor, actionName: mNearWaterLoopAnimName) &&
253 al::isOnGround(actor, 0))
254 return true;
255
256 if (mLoopAnimName && al::isActionPlaying(actor, actionName: mLoopAnimName) &&
257 isNearWater(waterSurfaceFinder: mWaterSurfaceFinder, actor: mActor)) {
258 return true;
259 }
260 return false;
261}
262
263const char* EnemyStateSwoon::getSwoonEndAnimName() const {
264 if (isNearWater(waterSurfaceFinder: mWaterSurfaceFinder, actor: mActor) && mNearWaterEndAnimName)
265 return mNearWaterEndAnimName;
266 return mEndAnimName;
267}
268
269const char* EnemyStateSwoon::getSwoonTrampledAnimName() const {
270 if (isNearWater(waterSurfaceFinder: mWaterSurfaceFinder, actor: mActor) && mNearWaterTrampledAnimName)
271 return mNearWaterTrampledAnimName;
272 return mTrampledAnimName;
273}
274
275void inline startAction(al::LiveActor* actor, const char* animName, bool hasSubActors) {
276 al::startAction(actor, actionName: animName);
277 if (hasSubActors) {
278 s32 size = al::getSubActorNum(actor);
279 for (s32 i = 0; i < size; i++)
280 al::startAction(actor: al::getSubActor(actor, index: i), actionName: animName);
281 }
282}
283
284void inline tryStartAction(al::LiveActor* actor, const char* animName, bool hasSubActors) {
285 al::tryStartAction(actor, actionName: animName);
286 if (hasSubActors) {
287 s32 size = al::getSubActorNum(actor);
288 for (s32 i = 0; i < size; i++)
289 al::tryStartAction(actor: al::getSubActor(actor, index: i), actionName: animName);
290 }
291}
292
293void EnemyStateSwoon::exeSwoonStart() {
294 if (al::isFirstStep(user: this))
295 startAction(actor: mActor, animName: getSwoonStartAnimName(), hasSubActors: mHasSubActors);
296
297 if (al::isActionEnd(actor: mActor)) {
298 if (mHasStartLandAnimation && al::isExistAction(actor: mActor, actionName: getSwoonStartLandAnimName())) {
299 if (!isNearWater(waterSurfaceFinder: mWaterSurfaceFinder, actor: mActor) && !al::isOnGround(mActor, 0)) {
300 al::setNerve(user: this, nerve: &NrvEnemyStateSwoon.SwoonStartFall);
301 return;
302 }
303 if (!isNearWater(waterSurfaceFinder: mWaterSurfaceFinder, actor: mActor)) {
304 al::setNerve(user: this, nerve: &NrvEnemyStateSwoon.SwoonStartLand);
305 return;
306 }
307 }
308 al::setNerve(user: this, nerve: &NrvEnemyStateSwoon.SwoonLoop);
309 }
310}
311
312void EnemyStateSwoon::exeSwoonStartFall() {
313 if (al::isFirstStep(user: this))
314 tryStartAction(actor: mActor, animName: mStartFallAnimName, hasSubActors: mHasSubActors);
315
316 if (isNearWater(waterSurfaceFinder: mWaterSurfaceFinder, actor: mActor) || al::isOnGround(mActor, 0)) {
317 tryStartHitReactionLand();
318 al::setNerve(user: this, nerve: &NrvEnemyStateSwoon.SwoonStartLand);
319 }
320}
321
322void EnemyStateSwoon::exeSwoonStartLand() {
323 if (al::isFirstStep(user: this))
324 startAction(actor: mActor, animName: getSwoonStartLandAnimName(), hasSubActors: mHasSubActors);
325
326 if (al::isActionEnd(actor: mActor))
327 al::setNerve(user: this, nerve: &NrvEnemyStateSwoon.SwoonLoop);
328}
329
330void EnemyStateSwoon::exeSwoonLoop() {
331 if (mIsCancelLoopOnProhibitedArea && al::isInAreaObj(actor: mActor, name: "HackCancelSwoonProhibitedArea")) {
332 kill();
333 return;
334 }
335
336 if (al::isFirstStep(user: this))
337 startAction(actor: mActor, animName: getSwoonLoopAnimName(), hasSubActors: mHasSubActors);
338
339 if (isPlayingActionIncorrect()) {
340 kill();
341 return;
342 }
343
344 if (al::isGreaterEqualStep(user: this, step: mSwoonDuration)) {
345 if (mEndSignAnimName) {
346 al::setNerve(user: this, nerve: &NrvEnemyStateSwoon.SwoonEndSign);
347 return;
348 }
349 al::setNerve(user: this, nerve: &NrvEnemyStateSwoon.SwoonEnd);
350 }
351}
352
353void EnemyStateSwoon::exeSwoonEndSign() {
354 if (al::isFirstStep(user: this))
355 startAction(actor: mActor, animName: mEndSignAnimName, hasSubActors: mHasSubActors);
356
357 if (al::isGreaterEqualStep(user: this, step: mEndSignDelay))
358 al::setNerve(user: this, nerve: &NrvEnemyStateSwoon.SwoonEnd);
359}
360
361void EnemyStateSwoon::exeSwoonEnd() {
362 if (al::isFirstStep(user: this))
363 startAction(actor: mActor, animName: getSwoonEndAnimName(), hasSubActors: mHasSubActors);
364
365 if (al::isActionEnd(actor: mActor))
366 kill();
367}
368
369void EnemyStateSwoon::exeSwoonTrampled() {
370 if (al::isFirstStep(user: this))
371 startAction(actor: mActor, animName: getSwoonTrampledAnimName(), hasSubActors: mHasSubActors);
372
373 if (al::isActionEnd(actor: mActor)) {
374 if (mIsAppearItem) {
375 al::appearItem(actor: mActor);
376 mIsAppearItem = false;
377 }
378 al::setNerve(user: this, nerve: &NrvEnemyStateSwoon.SwoonLoop);
379 }
380}
381