1#include "Enemy/Nokonoko.h"
2
3#include "Library/LiveActor/ActorActionFunction.h"
4#include "Library/LiveActor/ActorClippingFunction.h"
5#include "Library/LiveActor/ActorCollisionFunction.h"
6#include "Library/LiveActor/ActorInitInfo.h"
7#include "Library/LiveActor/ActorInitUtil.h"
8#include "Library/LiveActor/ActorMovementFunction.h"
9#include "Library/LiveActor/ActorPoseUtil.h"
10#include "Library/LiveActor/ActorSensorUtil.h"
11#include "Library/LiveActor/LiveActor.h"
12#include "Library/Math/MathUtil.h"
13#include "Library/Nature/NatureUtil.h"
14#include "Library/Nature/WaterSurfaceFinder.h"
15#include "Library/Nerve/NerveSetupUtil.h"
16#include "Library/Nerve/NerveUtil.h"
17#include "Library/Placement/PlacementFunction.h"
18#include "Library/Rail/RailUtil.h"
19
20#include "Enemy/EnemyStateSwoon.h"
21#include "Player/CapTargetInfo.h"
22#include "Util/Hack.h"
23#include "Util/SensorMsgFunction.h"
24
25namespace {
26NERVE_IMPL(Nokonoko, Wait);
27NERVE_IMPL(Nokonoko, Swoon);
28NERVE_IMPL(Nokonoko, CaptureSpin);
29NERVE_IMPL_(Nokonoko, CaptureSpinCapRethrow, CaptureSpin);
30NERVE_IMPL(Nokonoko, Die);
31NERVE_IMPL(Nokonoko, CaptureStart);
32NERVE_IMPL(Nokonoko, CaptureEnd);
33NERVE_IMPL(Nokonoko, CaptureStartEnd);
34NERVE_IMPL(Nokonoko, CaptureWait);
35NERVE_IMPL_(Nokonoko, CaptureSwimWait, CaptureWait);
36NERVE_IMPL(Nokonoko, CaptureSpinStandbyStart);
37NERVE_IMPL(Nokonoko, CaptureJumpStart);
38NERVE_IMPL_(Nokonoko, CaptureSwim, CaptureJump);
39NERVE_IMPL(Nokonoko, CaptureWalk);
40NERVE_IMPL_(Nokonoko, CaptureSwimWalk, CaptureWalk);
41NERVE_IMPL(Nokonoko, CaptureJump);
42NERVE_IMPL(Nokonoko, CaptureJumpEnd);
43NERVE_IMPL_(Nokonoko, CaptureSpinDrift, CaptureSpin);
44NERVE_IMPL(Nokonoko, CaptureSpinEnd);
45
46NERVES_MAKE_NOSTRUCT(Nokonoko, CaptureStartEnd)
47NERVES_MAKE_STRUCT(Nokonoko, Wait, Swoon, CaptureSpin, CaptureSpinCapRethrow, Die, CaptureStart,
48 CaptureEnd, CaptureWait, CaptureSwimWait, CaptureSpinStandbyStart,
49 CaptureJumpStart, CaptureSwim, CaptureWalk, CaptureSwimWalk, CaptureJump,
50 CaptureJumpEnd, CaptureSpinDrift, CaptureSpinEnd);
51} // namespace
52
53const sead::Vector3f sShellLockOnOffset = {0.0f, 72.0f, 37.0f};
54const sead::Vector3f sShellLockOnRotation = {-18.0f, 0.0f, 0.0f};
55
56const sead::Vector3f sShellSpinLockOnOffset = {0.0f, 100.0f, 0.0f};
57const sead::Vector3f sShellSpinLockOnRotation = {0.0f, 0.0f, 0.0f};
58
59Nokonoko::Nokonoko(const char* name) : al::LiveActor(name) {}
60
61void Nokonoko::init(const al::ActorInitInfo& info) {
62 al::initActor(actor: this, initInfo: info);
63 al::initNerve(actor: this, nerve: &NrvNokonoko.Wait, maxStates: 2);
64
65 al::tryGetArg(arg: (s32*)&mMoveType, initInfo: info, key: "MoveType");
66
67 if (al::isExistRail(initInfo: info, linkName: "Rail"))
68 al::setSyncRailToNearestPos(this);
69
70 mWaterSurfaceFinder = new al::WaterSurfaceFinder(this);
71 mCapTargetInfo = rs::createCapTargetInfo(this, nullptr);
72 mStateSwoon = new EnemyStateSwoon(this, "SwoonStart", "Swoon", "SwoonEnd", false, false);
73
74 al::initNerveState(user: this, state: mStateSwoon, nerve: &NrvNokonoko.Swoon, hostName: "気絶");
75 makeActorAlive();
76}
77
78void Nokonoko::control() {}
79
80void Nokonoko::attackSensor(al::HitSensor* self, al::HitSensor* other) {
81 if (al::isNerve(user: this, nerve: &NrvNokonoko.Wait)) {
82 if (al::isSensorName(self, "Attack")) {
83 al::sendMsgEnemyAttack(receiver: other, sender: self);
84 return;
85 }
86 } else if (al::isNerve(user: this, nerve: &NrvNokonoko.Swoon) && al::isSensorEnemyBody(self)) {
87 al::sendMsgPushAndKillVelocityToTarget(this, self, other);
88 }
89
90 if (!mHackActor)
91 return;
92
93 if (al::isSensorEnemyBody(self)) {
94 if (rs::sendMsgHackerNoReaction(mHackActor, other, self))
95 return;
96 if (rs::sendMsgHackAttackMapObj(source: other, target: self))
97 return;
98 }
99
100 if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSpin) ||
101 al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSpinCapRethrow)) {
102 rs::sendMsgCapItemGet(source: other, target: self);
103
104 if (al::isSensorName(self, "AttackSpin")) {
105 if (al::isSensorEnemyBody(other)) {
106 if (rs::sendMsgHackAttack(source: other, target: self))
107 return;
108 if (al::getVelocity(actor: this).y < -1.0f &&
109 al::sendMsgPlayerAttackTrample(receiver: other, sender: self, comboCounter: nullptr))
110 return;
111 }
112 if (al::isSensorMapObj(other))
113 rs::sendMsgHackUpperPunch(source: other, target: self);
114 }
115 }
116}
117
118bool Nokonoko::receiveMsg(const al::SensorMsg* message, al::HitSensor* other, al::HitSensor* self) {
119 if (rs::tryReceiveMsgInitCapTargetAndSetCapTargetInfo(message, mCapTargetInfo))
120 return true;
121
122 if (rs::isMsgHackMarioDead(message)) {
123 endCapture();
124 al::setNerve(user: this, nerve: &NrvNokonoko.Die);
125 return true;
126 }
127
128 if (rs::isMsgHackMarioInWater(message))
129 return true;
130
131 if (al::isNerve(user: this, nerve: &NrvNokonoko.Wait)) {
132 if (rs::isMsgHackAttack(message) || al::isMsgPlayerTrample(msg: message) ||
133 rs::isMsgPlayerAndCapObjHipDropAll(message)) {
134 rs::requestHitReactionToAttacker(message, self, other);
135 al::setNerve(user: this, nerve: &NrvNokonoko.Swoon);
136 return true;
137 }
138 if (rs::isMsgCapEnableLockOn(message))
139 return true;
140
141 if (rs::isMsgStartHack(message)) {
142 al::invalidateClipping(actor: this);
143 mHackActor = rs::startHack(self, other, nullptr);
144 rs::startHackStartDemo(mHackActor, this);
145 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureStart);
146 return true;
147 }
148
149 return false;
150 }
151
152 if (al::isNerve(user: this, nerve: &NrvNokonoko.Swoon)) {
153 if (mStateSwoon->tryReceiveMsgEnableLockOn(message))
154 return true;
155 if (mStateSwoon->tryReceiveMsgEndSwoon(message))
156 return true;
157 if (mStateSwoon->tryReceiveMsgStartHack(message)) {
158 al::invalidateClipping(actor: this);
159 mHackActor = rs::startHack(self, other, nullptr);
160 rs::startHackStartDemo(mHackActor, this);
161 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureStart);
162 return true;
163 }
164 return false;
165 }
166
167 if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureStart))
168 return false;
169
170 if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureWait) ||
171 al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSwimWait) ||
172 al::isNerve(user: this, nerve: &NrvNokonoko.CaptureWalk) ||
173 al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSwimWalk) ||
174 al::isNerve(user: this, nerve: &NrvNokonoko.CaptureJumpStart) ||
175 al::isNerve(user: this, nerve: &NrvNokonoko.CaptureJump) ||
176 al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSwim) ||
177 al::isNerve(user: this, nerve: &NrvNokonoko.CaptureJumpEnd) ||
178 al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSpinStandbyStart) ||
179 al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSpin) ||
180 al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSpinDrift) ||
181 al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSpinCapRethrow) ||
182 al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSpinEnd)) {
183 if ((al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSpin) ||
184 al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSpinCapRethrow)) &&
185 rs::isMsgCapRethrow(message)) {
186 rs::tryGetCapRethrowPos(al::getTransPtr(actor: this), message);
187 sead::Vector3f rethrowFront = sead::Vector3f(0.0f, 0.0f, 0.0f);
188 if (rs::tryGetCapRethrowFront(&rethrowFront, message)) {
189 al::makeQuatFrontUp(outQuat: al::getQuatPtr(actor: this), front: rethrowFront, up: sead::Vector3f::ey);
190 al::setVelocity(actor: this, vel: rethrowFront * 80.0f);
191 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureSpinCapRethrow);
192 return true;
193 }
194 }
195
196 if (rs::isMsgCancelHack(message) ||
197 ((al::isMsgEnemyAttack(msg: message) || al::isMsgExplosion(msg: message)) &&
198 al::isSensorEnemyBody(self))) {
199 endCapture();
200 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureEnd);
201 return true;
202 }
203 }
204 return false;
205}
206
207void Nokonoko::endCapture() {
208 rs::endHack(&mHackActor);
209 mCapTargetInfo->setFollowLockOnMtx(jointName: "Head", localTrans: sShellLockOnOffset, localRotate: sShellLockOnRotation);
210 al::validateClipping(actor: this);
211}
212
213void updateNokonokoVelocity(al::LiveActor* self, al::WaterSurfaceFinder* waterSurfaceFinder) {
214 if (!waterSurfaceFinder || !waterSurfaceFinder->isFoundSurface()) {
215 if (al::isInWater(self)) {
216 al::addVelocityToGravity(actor: self, force: 0.1f);
217 if (al::isNerve(user: self, nerve: &NrvNokonoko.CaptureSpin) ||
218 al::isNerve(user: self, nerve: &NrvNokonoko.CaptureSpinDrift))
219 al::addVelocityToGravity(actor: self, force: -0.4f);
220 } else {
221 al::addVelocityToGravity(
222 actor: self, force: al::isNerve(user: self, nerve: &NrvNokonoko.CaptureSpinCapRethrow) ? 0.0f : 0.9f);
223 }
224 }
225 if (waterSurfaceFinder && waterSurfaceFinder->isFoundSurface()) {
226 al::scaleVelocity(actor: self, factor: 0.98f);
227 return;
228 }
229
230 if (al::isInWater(self)) {
231 al::scaleVelocity(actor: self, factor: al::isOnGround(self, 0) ? 0.9f : 0.98f);
232 return;
233 }
234
235 if (al::isOnGround(self, 0)) {
236 sead::Vector3f collidedGroundNormal = al::getCollidedGroundNormal(self);
237 if (collidedGroundNormal.dot(t: al::getVelocity(actor: self)) < 0.0f) {
238 sead::Vector3f upVel;
239 al::parallelizeVec(&upVel, collidedGroundNormal, al::getVelocity(actor: self));
240 al::addVelocity(actor: self, vel: collidedGroundNormal * (upVel.length() * 0.9f));
241 }
242
243 f32 scale;
244 if (al::isNerve(user: self, nerve: &NrvNokonoko.CaptureSpin))
245 scale = 0.98f;
246 else if (al::isNerve(user: self, nerve: &NrvNokonoko.CaptureSpinDrift) ||
247 al::isNerve(user: self, nerve: &NrvNokonoko.CaptureSpinEnd))
248 scale = 0.99f;
249 else if (al::isNerve(user: self, nerve: &NrvNokonoko.CaptureSpinStandbyStart))
250 scale = 0.95f;
251 else
252 scale = 0.9f;
253 al::scaleVelocity(actor: self, factor: scale);
254 } else
255 al::scaleVelocity(actor: self, factor: 0.998f);
256}
257
258inline bool Nokonoko::updateAccelStick() {
259 sead::Vector3f stickAccel;
260 if (!rs::addHackActorAccelStick(this, mHackActor, &stickAccel,
261 al::isOnGround(this, 0) ? 0.2f : 1.2f, sead::Vector3f::ey))
262 return false;
263
264 sead::Quatf targetQuat;
265 al::makeQuatFrontUp(outQuat: &targetQuat, front: stickAccel, up: sead::Vector3f::ey);
266 al::slerpQuat(al::getQuatPtr(actor: this), al::getQuat(actor: this), targetQuat, 0.3f);
267 return true;
268}
269
270void Nokonoko::exeWait() {
271 if (al::isFirstStep(user: this)) {
272 if (al::isExistRail(railHolder: this))
273 al::startAction(actor: this, actionName: "Walk");
274 else
275 al::startAction(actor: this, actionName: "Wait");
276 }
277 if (al::isExistRail(railHolder: this)) {
278 sead::Vector3f moveDirection;
279 al::calcRailMoveDir(&moveDirection, railHolder: this);
280
281 switch (mMoveType) {
282 case al::MoveType::Loop:
283 al::moveSyncRailLoop(actor: this, speed: 5.0f);
284 break;
285 case al::MoveType::Turn: {
286 sead::Vector3f frontDir;
287 al::calcFrontDir(front: &frontDir, actor: this);
288 if (al::isParallelDirection(a: moveDirection, b: sead::Vector3f::ey, tolerance: 0.01f) ||
289 al::calcAngleDegree(a: frontDir, b: moveDirection) < 20.0f)
290 al::moveSyncRailTurn(actor: this, speed: 5.0f);
291 break;
292 }
293 default:
294 al::moveSyncRail(actor: this, 5.0f);
295 break;
296 }
297
298 if (!al::isParallelDirection(a: moveDirection, b: sead::Vector3f::ey, tolerance: 0.01f))
299 al::turnToRailDir(actor: this, al::calcNerveRate(user: this, max: 60) * 5.0f);
300 } else
301 updateNokonokoVelocity(self: this, waterSurfaceFinder: nullptr);
302}
303
304void Nokonoko::exeSwoon() {
305 if (al::updateNerveState(user: this))
306 al::setNerve(user: this, nerve: &NrvNokonoko.Wait);
307 else
308 updateNokonokoVelocity(self: this, waterSurfaceFinder: nullptr);
309}
310
311void Nokonoko::exeCaptureStart() {
312 if (rs::isHackStartDemoEnterMario(mHackActor))
313 al::setNerve(user: this, nerve: &CaptureStartEnd);
314}
315
316void Nokonoko::exeCaptureStartEnd() {
317 if (al::isFirstStep(user: this))
318 al::startAction(actor: this, actionName: "HackStart");
319 if (al::isActionEnd(actor: this)) {
320 rs::endHackStartDemo(mHackActor, this);
321 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureWait);
322 }
323}
324
325void Nokonoko::exeCaptureEnd() {
326 if (al::isFirstStep(user: this))
327 al::setVelocityZero(this);
328 if (al::isGreaterStep(user: this, step: 10))
329 al::setNerve(user: this, nerve: &NrvNokonoko.Swoon);
330}
331
332void Nokonoko::exeCaptureWait() {
333 if (al::isFirstStep(user: this))
334 al::startAction(actor: this, actionName: al::isNerve(user: this, nerve: &NrvNokonoko.CaptureWait) ? "Wait" : "SwimWait");
335
336 if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureWait)) {
337 if (al::isInWater(this)) {
338 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureSwimWait);
339 return;
340 }
341 } else if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSwimWait) && !al::isInWater(this)) {
342 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureWait);
343 return;
344 }
345
346 updateNokonokoVelocity(self: this, waterSurfaceFinder: nullptr);
347
348 if (rs::isHoldHackAction(mHackActor)) {
349 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureSpinStandbyStart);
350 return;
351 }
352
353 if (rs::isTriggerHackJump(mHackActor)) {
354 if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureWait)) {
355 if (al::isOnGround(this, 0)) {
356 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureJumpStart);
357 return;
358 }
359 } else if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSwimWait) && al::isInWater(this)) {
360 al::addVelocityJump(actor: this, force: 10.0f);
361 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureSwim);
362 return;
363 }
364 }
365
366 if (updateAccelStick() && al::isOnGround(this, 0)) {
367 al::Nerve* nerve;
368
369 if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureWait))
370 nerve = &NrvNokonoko.CaptureWalk;
371 else
372 nerve = &NrvNokonoko.CaptureSwimWalk;
373
374 al::setNerve(user: this, nerve);
375 }
376}
377
378void Nokonoko::exeCaptureWalk() {
379 if (al::isFirstStep(user: this))
380 al::startAction(actor: this, actionName: al::isNerve(user: this, nerve: &NrvNokonoko.CaptureWalk) ? "Walk" : "SwimWalk");
381
382 updateNokonokoVelocity(self: this, waterSurfaceFinder: nullptr);
383
384 if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureWalk)) {
385 if (al::isInWater(this)) {
386 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureSwimWalk);
387 return;
388 }
389 } else if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSwimWalk) && !al::isInWater(this)) {
390 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureWalk);
391 return;
392 }
393
394 updateNokonokoVelocity(self: this, waterSurfaceFinder: nullptr);
395
396 if (rs::isHoldHackAction(mHackActor)) {
397 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureSpinStandbyStart);
398 return;
399 }
400
401 if (rs::isTriggerHackJump(mHackActor)) {
402 if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureWalk)) {
403 if (al::isOnGround(this, 0)) {
404 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureJumpStart);
405 return;
406 }
407 } else if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSwimWalk)) {
408 al::addVelocityJump(actor: this, force: 10.0f);
409 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureSwim);
410 return;
411 }
412 }
413
414 if (!updateAccelStick()) {
415 al::Nerve* nerve;
416
417 if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureWalk))
418 nerve = &NrvNokonoko.CaptureWait;
419 else
420 nerve = &NrvNokonoko.CaptureSwimWait;
421
422 al::setNerve(user: this, nerve);
423 }
424}
425
426void Nokonoko::exeCaptureJumpStart() {
427 if (al::isFirstStep(user: this)) {
428 al::startAction(actor: this, actionName: "JumpStart");
429 al::addVelocityJump(actor: this, force: 10.0f);
430 }
431
432 updateNokonokoVelocity(self: this, waterSurfaceFinder: nullptr);
433 updateAccelStick();
434
435 if (al::isActionEnd(actor: this))
436 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureJump);
437}
438
439void Nokonoko::exeCaptureJump() {
440 if (al::isFirstStep(user: this))
441 al::startAction(actor: this, actionName: al::isNerve(user: this, nerve: &NrvNokonoko.CaptureJump) ? "Jump" : "Swim");
442
443 updateNokonokoVelocity(self: this, waterSurfaceFinder: nullptr);
444
445 if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureJump)) {
446 if (al::isInWater(this)) {
447 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureSwim);
448 return;
449 }
450 } else if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSwim) && !al::isInWater(this)) {
451 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureJump);
452 return;
453 }
454
455 updateAccelStick();
456
457 if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureJump)) {
458 if (al::isOnGround(this, 0))
459 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureJumpEnd);
460 }
461
462 else if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSwim)) {
463 if (rs::isTriggerHackJump(mHackActor)) {
464 al::addVelocityJump(actor: this, force: 10.0f);
465 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureSwim);
466 return;
467 }
468
469 if (al::isOnGround(this, 0) || al::isActionEnd(actor: this))
470 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureSwimWait);
471 }
472}
473
474void Nokonoko::exeCaptureJumpEnd() {
475 if (al::isFirstStep(user: this))
476 al::startAction(actor: this, actionName: "JumpEnd");
477
478 updateNokonokoVelocity(self: this, waterSurfaceFinder: nullptr);
479 updateAccelStick();
480
481 if (al::isActionEnd(actor: this))
482 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureWait);
483}
484
485void Nokonoko::exeCaptureSpinStandbyStart() {
486 if (al::isFirstStep(user: this))
487 al::startAction(actor: this, actionName: "SpinStandbyStart");
488
489 updateNokonokoVelocity(self: this, waterSurfaceFinder: nullptr);
490
491 if (al::isActionEnd(actor: this)) {
492 sead::Vector3f hackerMoveDir;
493 if (rs::calcHackerMoveDir(&hackerMoveDir, mHackActor, sead::Vector3f::ey))
494 al::makeQuatFrontUp(outQuat: al::getQuatPtr(actor: this), front: hackerMoveDir, up: sead::Vector3f::ey);
495
496 mCapTargetInfo->setFollowLockOnMtx(jointName: "Shell", localTrans: sShellSpinLockOnOffset,
497 localRotate: sShellSpinLockOnRotation);
498 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureSpin);
499 }
500}
501
502void Nokonoko::exeCaptureSpin() {
503 if (al::isFirstStep(user: this)) {
504 al::tryStartActionIfNotPlaying(actor: this, actionName: "Spin");
505 mHackSwingCooldown = 0;
506 }
507
508 mWaterSurfaceFinder->update(position: al::getTrans(actor: this), gravity: -al::getGravity(actor: this), distance: 50.0f);
509 updateNokonokoVelocity(self: this, waterSurfaceFinder: mWaterSurfaceFinder);
510
511 if (mWaterSurfaceSpringCooldown)
512 mWaterSurfaceSpringCooldown--;
513 else
514 al::approachWaterSurfaceSpringDumper(this, mWaterSurfaceFinder, -5.0f, 12.0f, 1.0f, 0.05f,
515 0.9f);
516
517 sead::Vector3f frontDir;
518 al::calcFrontDir(front: &frontDir, actor: this);
519
520 if (mHackSwingCooldown)
521 mHackSwingCooldown--;
522 else if (rs::isTriggerHackSwing(mHackActor)) {
523 mHackSwingCooldown = 30;
524 al::startHitReaction(actor: this, name: "振りで加速");
525 al::addVelocity(actor: this, vel: frontDir * 15.0f);
526 }
527
528 if ((al::isOnGround(this, 0) || mWaterSurfaceFinder->isFoundSurface()) &&
529 rs::isTriggerHackJump(mHackActor)) {
530 al::addVelocityJump(actor: this, force: mWaterSurfaceFinder->isFoundSurface() ? 20.0f : 17.0f);
531 if (mWaterSurfaceFinder->isFoundSurface())
532 mWaterSurfaceSpringCooldown = 15;
533 }
534
535 if (al::isCollidedWallVelocity(this)) {
536 sead::Vector3f collisionNormal = al::isCollidedWallVelocity(this) ?
537 al::getCollidedWallNormal(this) :
538 al::getCollidedCeilingNormal(this);
539 if (collisionNormal.dot(t: al::getVelocity(actor: this)) < 0.0f) {
540 sead::Vector3f collidedPos = al::isCollidedWall(this) ? al::getCollidedWallPos(this) :
541 al::getCollidedCeilingPos(this);
542 al::startHitReactionHitEffect(actor: this, name: "衝突", pos: collidedPos);
543 sead::Vector3f upVel;
544 al::parallelizeVec(&upVel, collisionNormal, al::getVelocity(actor: this));
545 al::addVelocity(actor: this, vel: upVel.length() * 1.9f * collisionNormal);
546
547 sead::Vector3f frontDir;
548 al::calcFrontDir(front: &frontDir, actor: this);
549
550 f32 dot = collisionNormal.dot(t: frontDir);
551 sead::Vector3f reflection = frontDir - (dot * (2 * collisionNormal));
552
553 if (al::tryNormalizeOrZero(out: &reflection))
554 al::makeQuatFrontUp(outQuat: al::getQuatPtr(actor: this), front: reflection, up: sead::Vector3f::ey);
555 if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSpinDrift)) {
556 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureSpin);
557 return;
558 }
559 }
560 }
561
562 bool isOnGround = al::isOnGround(this, 0);
563 bool isFoundWater = mWaterSurfaceFinder->isFoundSurface();
564 f32 frontVel = (isOnGround || isFoundWater) ? 0.9f : 0.2f;
565
566 al::addVelocity(actor: this, vel: frontDir * frontVel);
567
568 if (al::isOnGround(this, 0) || al::isInWater(this) || mWaterSurfaceFinder->isFoundSurface()) {
569 f32 hackMoveStick = rs::getHackMoveStickRaw(mHackActor);
570 f32 rotateMaxDegree = al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSpinDrift) ? 5.0f : 1.7f;
571 if (al::isInWater(this))
572 rotateMaxDegree = 1.6f;
573
574 al::rotateQuatYDirDegree(al::getQuatPtr(actor: this), al::getQuat(actor: this),
575 -(hackMoveStick * rotateMaxDegree));
576
577 if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSpinDrift)) {
578 sead::Vector3f frontDir;
579 al::calcFrontDir(front: &frontDir, actor: this);
580 if (al::calcAngleDegree(a: frontDir, b: mStartSpinDriftFrontDir) > 135.0f) {
581 sead::Quatf quat;
582 al::makeQuatFrontUp(outQuat: &quat, front: mStartSpinDriftFrontDir, up: sead::Vector3f::ey);
583 al::rotateQuatYDirDegree(al::getQuatPtr(actor: this), quat,
584 al::sign(x: mStartSpinDriftSideDir.dot(t: frontDir)) * 135.0f);
585 }
586 }
587 }
588
589 if (!al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSpin)) {
590 if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSpinDrift)) {
591 if (al::isGreaterStep(user: this, step: 90) || !rs::isHoldHackJump(mHackActor))
592 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureSpin);
593 } else if (al::isNerve(user: this, nerve: &NrvNokonoko.CaptureSpinCapRethrow) &&
594 al::isGreaterStep(user: this, step: 15))
595 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureSpin);
596 return;
597 }
598
599 if (!rs::isHoldHackAction(mHackActor)) {
600 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureSpinEnd);
601 return;
602 }
603
604 if (al::isOnGround(this, 0) && rs::isHoldHackJump(mHackActor) &&
605 !rs::isTriggerHackJump(mHackActor)) {
606 sead::Vector3f moveVec;
607 rs::calcHackerMoveVec(&moveVec, mHackActor, sead::Vector3f::ey);
608 if (moveVec.length() > 0.5f) {
609 al::startHitReaction(actor: this, name: "ドリフト開始");
610 al::calcSideDir(side: &mStartSpinDriftSideDir, actor: this);
611 al::calcFrontDir(front: &mStartSpinDriftFrontDir, actor: this);
612
613 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureSpinDrift);
614 }
615 }
616}
617
618void Nokonoko::exeCaptureSpinEnd() {
619 if (al::isFirstStep(user: this)) {
620 al::startAction(actor: this, actionName: "SpinEnd");
621 mCapTargetInfo->setFollowLockOnMtx(jointName: "Head", localTrans: sShellLockOnOffset, localRotate: sShellLockOnRotation);
622 }
623
624 updateNokonokoVelocity(self: this, waterSurfaceFinder: nullptr);
625
626 if (al::isActionEnd(actor: this))
627 al::setNerve(user: this, nerve: &NrvNokonoko.CaptureWait);
628}
629
630void Nokonoko::exeDie() {
631 al::setVelocityZero(this);
632 al::startHitReaction(actor: this, name: "死亡");
633 kill();
634}
635