1#include "Enemy/Gamane.h"
2
3#include <math/seadVector.h>
4
5#include "Library/Collision/KCollisionServer.h"
6#include "Library/Effect/EffectSystemInfo.h"
7#include "Library/Item/ItemUtil.h"
8#include "Library/LiveActor/ActorActionFunction.h"
9#include "Library/LiveActor/ActorAnimFunction.h"
10#include "Library/LiveActor/ActorAreaFunction.h"
11#include "Library/LiveActor/ActorClippingFunction.h"
12#include "Library/LiveActor/ActorCollisionFunction.h"
13#include "Library/LiveActor/ActorInitUtil.h"
14#include "Library/LiveActor/ActorModelFunction.h"
15#include "Library/LiveActor/ActorMovementFunction.h"
16#include "Library/LiveActor/ActorPoseUtil.h"
17#include "Library/LiveActor/ActorSensorUtil.h"
18#include "Library/Math/MathUtil.h"
19#include "Library/Movement/EnemyStateBlowDown.h"
20#include "Library/Nerve/NerveSetupUtil.h"
21#include "Library/Nerve/NerveUtil.h"
22#include "Library/Player/PlayerUtil.h"
23#include "Library/Shadow/ActorShadowUtil.h"
24
25#include "Enemy/EnemyStateHackStart.h"
26#include "Enemy/EnemyStateSwoon.h"
27#include "Enemy/GamaneBullet.h"
28#include "Enemy/GamaneHackState.h"
29#include "Enemy/HackerDepthShadowMapCtrl.h"
30#include "Player/HackerJudge.h"
31#include "Player/PlayerHackStartShaderCtrl.h"
32#include "Util/Hack.h"
33#include "Util/ItemUtil.h"
34#include "Util/JudgeUtil.h"
35#include "Util/SensorMsgFunction.h"
36#include "Util/ShadowUtil.h"
37
38namespace {
39NERVE_IMPL(Gamane, Wait)
40NERVE_IMPL(Gamane, Find)
41NERVE_IMPL(Gamane, Runaway)
42NERVE_IMPL(Gamane, Fall)
43NERVE_IMPL(Gamane, Land)
44NERVE_IMPL(Gamane, Swoon)
45NERVE_IMPL(Gamane, HackStart)
46NERVE_IMPL(Gamane, Hack)
47NERVE_IMPL(Gamane, Trampled)
48NERVE_IMPL(Gamane, PressDown)
49NERVE_IMPL(Gamane, BlowDown)
50
51NERVES_MAKE_NOSTRUCT(Gamane, Land)
52NERVES_MAKE_STRUCT(Gamane, Wait, Swoon, Hack, BlowDown, HackStart, Trampled, PressDown, Fall, Find,
53 Runaway)
54} // namespace
55
56static al::EnemyStateBlowDownParam gEnemyStateBlowDownParam =
57 al::EnemyStateBlowDownParam("BlowDown", 18.0f, 35.0f, 1.0f, 0.98f, 120, true);
58
59static PlayerHackStartShaderParam gPlayerHackStartShaderParam =
60 PlayerHackStartShaderParam(false, 300.0f, 10, 20);
61
62static EnemyStateSwoonInitParam gEnemyStateSwoonInitParam = EnemyStateSwoonInitParam(
63 "SwoonStart", "Swoon", "SwoonEnd", nullptr, "SwoonStartFall", "SwoonStartLand");
64
65Gamane::Gamane(const char* name) : al::LiveActor(name) {}
66
67void Gamane::init(const al::ActorInitInfo& initInfo) {
68 al::initActorWithArchiveName(actor: this, initInfo, archiveName: "Gamane", suffix: nullptr);
69 al::initNerve(actor: this, nerve: &NrvGamane.Wait, maxStates: 5);
70 mCapTargetInfo = rs::createCapTargetInfo(this, nullptr);
71
72 mStateSwoon = new EnemyStateSwoon(this, "SwoonStart", "Swoon", "SwoonEnd", false, true);
73
74 gEnemyStateSwoonInitParam.swoonDuration = 180;
75 gEnemyStateSwoonInitParam.hitReactionAnimName = "着地";
76 mStateSwoon->initParams(initParam: gEnemyStateSwoonInitParam);
77 mStateSwoon->enableLockOnDelay(hasLockOnDelay: true);
78
79 al::initNerveState(user: this, state: mStateSwoon, nerve: &NrvGamane.Swoon, hostName: "気絶");
80
81 mHackState = new GamaneHackState(this);
82 al::initNerveState(user: this, state: mHackState, nerve: &NrvGamane.Hack, hostName: "憑依");
83 mHackState->initialize(info: initInfo);
84
85 mStateBlowDown = new al::EnemyStateBlowDown(this, &gEnemyStateBlowDownParam, "吹き飛び状態");
86 al::initNerveState(user: this, state: mStateBlowDown, nerve: &NrvGamane.BlowDown, hostName: "吹き飛び");
87
88 mJudgeNormalFall = new HackerJudgeNormalFall(this, 5);
89
90 mCollisionPartsFilter = new al::CollisionPartsFilterSpecialPurpose("MoveLimit");
91 al::setColliderFilterCollisionParts(this, mCollisionPartsFilter);
92
93 mStateHackStart = new EnemyStateHackStart(this, nullptr, &gPlayerHackStartShaderParam);
94 al::initNerveState(user: this, state: mStateHackStart, nerve: &NrvGamane.HackStart, hostName: "ひょうい開始");
95
96 al::setMaterialProgrammable(this);
97 mMaterialIndex = al::getMaterialIndex(actor: this, "TransMT");
98 mShadowMaskIntensity = al::getShadowMaskIntensity(actor: this, maskName: "シャドウマスク");
99 al::hideShadowMask(actor: this);
100
101 al::startVisAnim(this, "Off");
102 al::startVisAnim(this, "CapOff");
103 al::startMtpAnim(this, "CapOff");
104 al::setModelMaterialParameterF32(actor: this, mMaterialIndex, "const_single0", 1.0f);
105 makeActorAlive();
106
107 rs::initHackShadow(this);
108 mDepthShadowMapCtrl = new HackerDepthShadowMapCtrl(this, "Ground", 50.0f, 0.3f, 0.5f);
109}
110
111void Gamane::attackSensor(al::HitSensor* self, al::HitSensor* other) {
112 if (al::isNerve(user: this, nerve: &NrvGamane.PressDown) || al::isNerve(user: this, nerve: &NrvGamane.BlowDown))
113 return;
114
115 if (mPlayerHack != nullptr) {
116 mHackState->attackSensor(self, other);
117 return;
118 }
119
120 if (al::isSensorEnemyBody(self) && al::isSensorEnemyBody(other))
121 al::sendMsgPushAndKillVelocityToTarget(this, self, other);
122
123 if (al::isSensorEnemyBody(self)) {
124 al::sendMsgPush(receiver: other, sender: self);
125 rs::sendMsgPushToPlayer(source: other, target: self);
126 }
127}
128
129bool Gamane::receiveMsg(const al::SensorMsg* message, al::HitSensor* other, al::HitSensor* self) {
130 if ((!al::isSensorEnemyBody(self) || al::isNerve(user: this, nerve: &NrvGamane.PressDown) ||
131 al::isNerve(user: this, nerve: &NrvGamane.BlowDown)) &&
132 (al::isMsgPlayerDisregard(msg: message) || rs::isMsgPlayerDisregardHomingAttack(message) ||
133 rs::isMsgPlayerDisregardTargetMarker(message)))
134 return true;
135
136 if (al::isNerve(user: this, nerve: &NrvGamane.PressDown))
137 return false;
138
139 if (al::isNerve(user: this, nerve: &NrvGamane.BlowDown))
140 return false;
141
142 if (rs::tryReceiveMsgInitCapTargetAndSetCapTargetInfo(message, mCapTargetInfo))
143 return true;
144
145 if (!al::isNerve(user: this, nerve: &NrvGamane.Hack) && !al::isNerve(user: this, nerve: &NrvGamane.HackStart) &&
146 !al::isNerve(user: this, nerve: &NrvGamane.Swoon)) {
147 if (rs::isMsgCapEnableLockOn(message) || rs::isMsgCapCancelLockOn(message) ||
148 mStateSwoon->tryReceiveMsgStartLockOn(message))
149 return true;
150 if (mStateSwoon->tryReceiveMsgStartHack(message)) {
151 startHack(message, other, self);
152 al::setNerve(user: this, nerve: &NrvGamane.HackStart);
153 return true;
154 }
155 }
156
157 if (al::isNerve(user: this, nerve: &NrvGamane.Swoon)) {
158 if (mStateSwoon->tryReceiveMsgEnableLockOn(message))
159 return true;
160 if (mStateSwoon->tryReceiveMsgStartHack(message)) {
161 startHack(message, other, self);
162 al::setNerve(user: this, nerve: &NrvGamane.HackStart);
163 return true;
164 }
165 if (mStateSwoon->tryReceiveMsgEndSwoon(message))
166 return true;
167 }
168
169 if (mPlayerHack != nullptr) {
170 if (rs::isMsgHackMarioCheckpointFlagWarp(message)) {
171 rs::endHack(&mPlayerHack);
172 rs::endHackShadow(this);
173 al::startVisAnim(this, "CapOff");
174 al::startMtpAnim(this, "CapOff");
175 al::setColliderFilterCollisionParts(this, mCollisionPartsFilter);
176 return true;
177 }
178
179 bool result = mHackState->receiveMsg(message, other, self);
180 if (mHackState->isHackEnd()) {
181 mPlayerHack = nullptr;
182 rs::endHackShadow(this);
183 al::startVisAnim(this, "CapOff");
184 al::startMtpAnim(this, "CapOff");
185 al::setColliderFilterCollisionParts(this, mCollisionPartsFilter);
186 al::setNerve(user: this, nerve: &NrvGamane.Swoon);
187 }
188
189 return result;
190 }
191
192 if ((al::isSensorEnemyBody(other) || al::isSensorMapObj(other)) &&
193 al::tryReceiveMsgPushAndAddVelocity(this, message, other, self, 3.0f))
194 return true;
195
196 if (al::isMsgPlayerTrampleReflect(msg: message) || rs::isMsgSenobiTrample(message)) {
197 rs::requestHitReactionToAttacker(message, self, other);
198 if (mCoinsLeft > 0) {
199 rs::tryAppearMultiCoinFromObj(actor: this, sensor: al::getHitSensor(this, "Body"), step: 0, offsetAbove: 150.0f);
200 mCoinsLeft--;
201 }
202 endRefract(transitionTime: 25);
203 if (al::isNerve(user: this, nerve: &NrvGamane.Swoon)) {
204 mStateSwoon->requestTrampled();
205 return true;
206 }
207 al::setNerve(user: this, nerve: &NrvGamane.Trampled);
208 return true;
209 }
210
211 if (rs::isMsgPressDown(message) && !al::isMsgPlayerTrample(msg: message)) {
212 rs::requestHitReactionToAttacker(message, self, other);
213 al::setNerve(user: this, nerve: &NrvGamane.PressDown);
214 return true;
215 }
216
217 if (rs::isMsgBlowDown(message)) {
218 if (rs::isMsgGamaneBullet(message)) {
219 GamaneBullet* bullet = (GamaneBullet*)al::getSensorHost(other);
220 if (bullet != nullptr && bullet->getParent() == this)
221 return false;
222 }
223
224 rs::requestHitReactionToAttacker(message, self, other);
225 mStateBlowDown->start(other);
226 al::setNerve(user: this, nerve: &NrvGamane.BlowDown);
227 return true;
228 }
229
230 if (rs::isMsgKillByShineGet(message) || rs::isMsgKillByHomeDemo(message)) {
231 al::tryKillEmitterAndParticleAll(this);
232 makeActorDead();
233 return true;
234 }
235
236 if (rs::tryReceiveMsgNpcScareByEnemyIgnoreTargetHack(message, mCapTargetInfo))
237 return true;
238
239 if (al::isMsgEnemyAttackFire(msg: message)) {
240 rs::requestHitReactionToAttacker(message, self, other);
241 return true;
242 }
243
244 return false;
245}
246
247void Gamane::control() {
248 updateRefract();
249 if (al::isCollidedGround(this)) {
250 al::setMaterialCode(actor: this, materialCode: al::getCollidedFloorMaterialCodeName(this));
251 al::updateMaterialCodePuddle(actor: this);
252 }
253
254 if (al::isNerve(user: this, nerve: &NrvGamane.Hack) && mPlayerHack != nullptr)
255 mDepthShadowMapCtrl->update(playerCollider: nullptr);
256
257 if (!al::isNerve(user: this, nerve: &NrvGamane.Hack) || al::isHideModel(actor: this))
258 al::hideSilhouetteModelIfShow(actor: this);
259 else
260 al::showSilhouetteModelIfHide(actor: this);
261
262 if (al::isInDeathArea(actor: this) || al::isInWaterArea(actor: this) ||
263 al::isCollidedFloorCode(this, "DamageFire") || al::isCollidedFloorCode(this, "Poison")) {
264 if (!al::isInDeathArea(actor: this) && mPlayerHack != nullptr)
265 rs::endHack(&mPlayerHack);
266
267 al::startHitReaction(actor: this, name: "消滅");
268 kill();
269 }
270}
271
272void Gamane::endClipped() {
273 al::LiveActor::endClipped();
274 if (mIsStartRefract)
275 al::hideShadowMask(actor: this);
276}
277
278void Gamane::updateCollider() {
279 if (al::isNerve(user: this, nerve: &NrvGamane.HackStart))
280 return;
281
282 if (al::isNerve(user: this, nerve: &NrvGamane.Hack)) {
283 sead::Vector3f hackVelocity = mHackState->getVelocity();
284 sead::Vector3f velocity = al::getVelocity(actor: this);
285 velocity += hackVelocity;
286 al::setVelocity(actor: this, vel: velocity);
287 mHackState->setVelocity(sead::Vector3f(0.0f, 0.0f, 0.0f));
288 }
289
290 al::LiveActor::updateCollider();
291}
292
293void Gamane::startHack(const al::SensorMsg* message, al::HitSensor* other, al::HitSensor* self) {
294 al::invalidateClipping(actor: this);
295 mPlayerHack = mStateHackStart->tryStart(message, other, self);
296 mHackState->setPlayerHackAction(mPlayerHack);
297 rs::hideShadowHackCap(mPlayerHack);
298 rs::setupHackShadow(this);
299 al::setColliderFilterCollisionParts(this, nullptr);
300 endRefract(transitionTime: 50);
301}
302
303void Gamane::updateRefract() {
304 if (mRefractTransitionTime == 0) {
305 al::setShadowMaskIntensity(actor: this, maskName: "シャドウマスク", intensity: mShadowMaskIntensity);
306 return;
307 }
308
309 f32 refractPercentage = mRefractTransitionTime / 50.0f;
310 if (mIsStartRefract != false)
311 refractPercentage = 1.0f - refractPercentage;
312 al::setModelMaterialParameterF32(actor: this, mMaterialIndex, "const_single0", refractPercentage);
313 al::setModelMaterialParameterF32(actor: this, mMaterialIndex, "const_single2",
314 refractPercentage * 0.5);
315 mRefractTransitionTime--;
316
317 if (mPlayerHack != nullptr)
318 return;
319
320 f32 intensity = al::lerpValue(a: mShadowMaskIntensity, b: 1.0, t: refractPercentage);
321 al::setShadowMaskIntensity(actor: this, maskName: "シャドウマスク", intensity);
322 if (mRefractTransitionTime == 0) {
323 if (mIsStartRefract) {
324 al::hideShadowMask(actor: this);
325 al::validateClipping(actor: this);
326 } else {
327 al::startVisAnim(this, "On");
328 al::invalidateClipping(actor: this);
329 }
330 }
331}
332
333void Gamane::updateMovement() {
334 sead::Vector3f gravityDir = sead::Vector3f(0.0f, 0.0f, 0.0f);
335 al::calcGravityDir(gravity: &gravityDir, actor: this);
336
337 if (al::isOnGround(this, 0))
338 al::scaleVelocityDirection(actor: this, direction: gravityDir, factor: 0.0f);
339
340 al::scaleVelocityParallelVertical(actor: this, direction: gravityDir, parallel: 0.99f, vertical: 0.1f);
341 al::addVelocityToGravityNaturalOrFittedGround(actor: this, force: 1.5f);
342}
343
344void Gamane::startRefract(s32 transitionTime) {
345 if (mRefractTransitionTime < 1 && !mIsStartRefract) {
346 mRefractTransitionTime = transitionTime;
347 mIsStartRefract = true;
348 al::startVisAnim(this, "Off");
349 }
350}
351
352void Gamane::endRefract(s32 transitionTime) {
353 if (mRefractTransitionTime < 1 && mIsStartRefract) {
354 mRefractTransitionTime = transitionTime;
355 mIsStartRefract = false;
356 al::showShadowMask(actor: this);
357 }
358}
359
360void Gamane::exeWait() {
361 if (al::isFirstStep(user: this)) {
362 al::setVelocityZero(this);
363 al::startAction(actor: this, actionName: "Wait");
364 rs::resetJudge(judge: mJudgeNormalFall);
365 }
366
367 updateMovement();
368
369 if (rs::updateJudgeAndResult(judge: mJudgeNormalFall)) {
370 al::setNerve(user: this, nerve: &NrvGamane.Fall);
371 return;
372 }
373
374 if (al::isStep(user: this, step: 180))
375 startRefract(transitionTime: 50);
376
377 if (al::calcDistanceH(actor: this, target: al::getPlayerActor(this, 0)) < 1000.0f)
378 al::setNerve(user: this, nerve: &NrvGamane.Find);
379}
380
381void Gamane::exeFind() {
382 if (al::isFirstStep(user: this)) {
383 al::startAction(actor: this, actionName: "Find");
384 al::setVelocityZero(this);
385 al::faceToTarget(actor: this, target: al::getPlayerActor(this, 0));
386 endRefract(transitionTime: 50);
387 }
388
389 updateMovement();
390
391 if (al::isActionEnd(actor: this)) {
392 al::startVisAnim(this, "On");
393 al::setNerve(user: this, nerve: &NrvGamane.Runaway);
394 }
395}
396
397void Gamane::exeRunaway() {
398 if (al::isFirstStep(user: this)) {
399 al::startAction(actor: this, actionName: "Run");
400 rs::resetJudge(judge: mJudgeNormalFall);
401 mRunAwayRefractDelay = 0;
402 }
403
404 sead::Vector3f dirToActor;
405 al::calcDirToActorH(out: &dirToActor, actor: this, target: al::getPlayerActor(this, 0));
406 dirToActor.negate();
407 al::turnToDirection(actor: this, dir: dirToActor, deg: 4.0f);
408
409 sead::Vector3f quatFront;
410 al::calcQuatFront(front: &quatFront, actor: this);
411 al::addVelocityToDirection(actor: this, dir: quatFront, force: 0.4f);
412
413 sead::Vector3f gravityDir = sead::Vector3f(0.0f, 0.0f, 0.0f);
414 al::calcGravityDir(gravity: &gravityDir, actor: this);
415
416 if (al::isOnGround(this, 0))
417 al::scaleVelocityDirection(actor: this, direction: gravityDir, factor: 0.0f);
418
419 al::scaleVelocityParallelVertical(actor: this, direction: gravityDir, parallel: 0.99f, vertical: 0.95f);
420 al::addVelocityToGravityNaturalOrFittedGround(actor: this, force: 1.8f);
421
422 if (!mIsStartRefract)
423 mRunAwayRefractDelay++;
424
425 if (mRunAwayRefractDelay == 240) {
426 startRefract(transitionTime: 50);
427 mRunAwayRefractDelay = 0;
428 }
429
430 if (rs::updateJudgeAndResult(judge: mJudgeNormalFall)) {
431 al::setNerve(user: this, nerve: &NrvGamane.Fall);
432 return;
433 }
434
435 if (al::calcDistanceH(actor: this, target: al::getPlayerActor(this, 0)) > 1500.0f)
436 al::setNerve(user: this, nerve: &NrvGamane.Wait);
437}
438
439void Gamane::exeFall() {
440 if (al::isFirstStep(user: this)) {
441 al::startAction(actor: this, actionName: "Fall");
442 al::invalidateClipping(actor: this);
443 }
444
445 al::addVelocityToGravity(actor: this, force: 0.7f);
446 al::scaleVelocity(actor: this, factor: 0.99f);
447
448 if (al::isOnGround(this, 0)) {
449 al::reboundVelocityFromCollision(actor: this, reboundStrength: 0.0f, reboundMin: 0.0f, friction: 1.0f);
450 al::setNerve(user: this, nerve: &Land);
451 al::startHitReaction(actor: this, name: "着地");
452 }
453}
454
455void Gamane::exeLand() {
456 if (al::isFirstStep(user: this)) {
457 al::startAction(actor: this, actionName: "Land");
458 al::setVelocityZero(this);
459 }
460
461 updateMovement();
462
463 if (al::isActionEnd(actor: this))
464 al::setNerve(user: this, nerve: &NrvGamane.Wait);
465}
466
467void Gamane::exeSwoon() {
468 if (al::isFirstStep(user: this)) {
469 sead::Vector3f velocity = sead::Vector3f(0.0f, 25.0f, 0.0f);
470 sead::Vector3f frontDir = sead::Vector3f::ez;
471 al::calcFrontDir(front: &frontDir, actor: this);
472 velocity += frontDir * 0.0f;
473 al::setVelocity(actor: this, vel: velocity);
474 mIsKeepSwoon = false;
475 }
476
477 updateMovement();
478
479 if (al::updateNerveState(user: this) && !mIsKeepSwoon) {
480 if (al::calcDistanceH(actor: this, target: al::getPlayerActor(this, 0)) < 1000.0f)
481 al::setNerve(user: this, nerve: &NrvGamane.Runaway);
482 else
483 al::setNerve(user: this, nerve: &NrvGamane.Wait);
484 }
485}
486
487void Gamane::exeHackStart() {
488 if (mStateHackStart->isHackStart() && mStateHackStart->calcHackStartNerveRate() == 0.0f) {
489 al::startVisAnim(this, "CapOn");
490 al::startMtpAnim(this, "CapOn");
491 }
492
493 updateMovement();
494
495 if (al::updateNerveState(user: this)) {
496 al::startVisAnim(this, "On");
497 al::setNerve(user: this, nerve: &NrvGamane.Hack);
498 }
499}
500
501void Gamane::exeHack() {
502 al::updateNerveState(user: this);
503 if (mCoinsLeft > 0 && mHackCoinAppearCounter == 0) {
504 rs::tryAppearMultiCoinFromObj(actor: this, sensor: al::getHitSensor(this, "Body"), step: 0, offsetAbove: 150.0f);
505 mCoinsLeft--;
506 }
507
508 mHackCoinAppearCounter = al::modi(a: (mHackCoinAppearCounter++ + 1) + 6, b: 6);
509}
510
511void Gamane::exeTrampled() {
512 if (al::isFirstStep(user: this)) {
513 al::setVelocityZero(this);
514 al::startAction(actor: this, actionName: "Trampled");
515 }
516
517 updateMovement();
518
519 if (al::isActionEnd(actor: this))
520 al::setNerve(user: this, nerve: &NrvGamane.Runaway);
521}
522
523void Gamane::exePressDown() {
524 if (al::isFirstStep(user: this)) {
525 al::startAction(actor: this, actionName: "PressDown");
526 al::invalidateClipping(actor: this);
527 endRefract(transitionTime: 50);
528 }
529
530 updateMovement();
531
532 if (al::isActionEnd(actor: this)) {
533 al::startHitReaction(actor: this, name: "死亡");
534 al::appearItemTiming(actor: this, "倒す");
535 kill();
536 }
537}
538
539void Gamane::exeBlowDown() {
540 if (al::isFirstStep(user: this))
541 endRefract(transitionTime: 50);
542
543 if (al::updateNerveState(user: this)) {
544 al::startHitReaction(actor: this, name: "死亡");
545 al::appearItemTiming(actor: this, "倒す");
546 kill();
547 }
548}
549