1#include "Enemy/KaronWing.h"
2
3#include "Library/LiveActor/ActorActionFunction.h"
4#include "Library/LiveActor/ActorAnimFunction.h"
5#include "Library/LiveActor/ActorAreaFunction.h"
6#include "Library/LiveActor/ActorClippingFunction.h"
7#include "Library/LiveActor/ActorCollisionFunction.h"
8#include "Library/LiveActor/ActorInitInfo.h"
9#include "Library/LiveActor/ActorInitUtil.h"
10#include "Library/LiveActor/ActorMovementFunction.h"
11#include "Library/LiveActor/ActorParamMove.h"
12#include "Library/LiveActor/ActorPoseKeeper.h"
13#include "Library/LiveActor/ActorPoseUtil.h"
14#include "Library/LiveActor/ActorSensorUtil.h"
15#include "Library/Nerve/NerveSetupUtil.h"
16#include "Library/Nerve/NerveUtil.h"
17#include "Library/Placement/PlacementFunction.h"
18#include "Library/Player/PlayerUtil.h"
19
20#include "Enemy/EnemyCap.h"
21#include "Enemy/EnemyStateHackStart.h"
22#include "Enemy/EnemyStateReviveInsideScreen.h"
23#include "Enemy/EnemyStateSwoon.h"
24#include "Enemy/FlyerStateWander.h"
25#include "Enemy/KaronWingStateHack.h"
26#include "Player/IUsePlayerCollision.h"
27#include "Util/Hack.h"
28#include "Util/PlayerUtil.h"
29#include "Util/SensorMsgFunction.h"
30
31namespace {
32NERVE_IMPL(KaronWing, Attack);
33NERVE_IMPL(KaronWing, Break);
34NERVE_IMPL(KaronWing, Chase);
35NERVE_IMPL(KaronWing, DamageCap);
36NERVE_IMPL(KaronWing, Find);
37NERVE_IMPL(KaronWing, Hack);
38NERVE_IMPL(KaronWing, HackStart);
39NERVE_IMPL(KaronWing, Revive);
40NERVE_IMPL(KaronWing, ReviveAppear);
41NERVE_IMPL(KaronWing, Swoon);
42NERVE_IMPL(KaronWing, Turn);
43NERVE_IMPL(KaronWing, Wait);
44NERVE_IMPL(KaronWing, Wander);
45
46NERVES_MAKE_STRUCT(KaronWing, Wait, Hack, Swoon, Break, Revive, HackStart, Wander, Attack,
47 DamageCap, Turn, Find, Chase, ReviveAppear);
48} // namespace
49
50const al::ActorParamMove cMoveParam{.forceFront: 0.1f, .forceGravity: 0.0f, .decay: 0.95f, .turnDegrees: 0.7f};
51static FlyerStateWanderParam cWanderParam{30, 540, 180, "EnemyFly", &cMoveParam};
52
53// NON_MATCHING: creating the `EnemyStateSwoonInitParam` (https://decomp.me/scratch/CDB2W)
54void KaronWing::init(const al::ActorInitInfo& info) {
55 al::initActor(actor: this, initInfo: info);
56 bool wearingCap = true;
57 al::tryGetArg(arg: &wearingCap, initInfo: info, key: "IsWearingCap");
58 if (wearingCap)
59 mEnemyCap = rs::tryCreateEnemyCap(this, info, "EnemyCapKoopa");
60
61 al::initNerve(actor: this, nerve: &NrvKaronWing.Wait, maxStates: 8);
62
63 mStateHack = new KaronWingStateHack(this, info, &mPlayerHack);
64 al::initNerveState(user: this, state: mStateHack, nerve: &NrvKaronWing.Hack, hostName: "キャプチャー");
65 mStateSwoon = new EnemyStateSwoon(this, "SwoonStart", "Swoon", "SwoonEnd", false, true);
66
67 EnemyStateSwoonInitParam swoonParam{"Trampled", "BreakWait", "Recover",
68 "BreakReaction", "Break", "BreakGroundHit"};
69 swoonParam.hasStartLandAnimation = true;
70 swoonParam.hasLockOnDelay = true;
71 swoonParam.isCancelLoopOnProhibitedArea = false;
72 swoonParam.swoonDuration = 180;
73 swoonParam.endSignDelay = 60;
74 swoonParam.endSignAnimName = "RecoverSign";
75
76 mStateSwoon->initParams(initParam: swoonParam);
77 al::initNerveState(user: this, state: mStateSwoon, nerve: &NrvKaronWing.Swoon, hostName: "気絶");
78
79 mStateBreak = new EnemyStateSwoon(this, "SwoonStart", "Swoon", "SwoonEnd", false, true);
80 mStateBreak->initParams(initParam: swoonParam);
81 al::initNerveState(user: this, state: mStateBreak, nerve: &NrvKaronWing.Break, hostName: "壊れ");
82
83 EnemyStateReviveInsideScreen* stateRevive = new EnemyStateReviveInsideScreen(this);
84 al::initNerveState(user: this, state: stateRevive, nerve: &NrvKaronWing.Revive, hostName: "画面内復活");
85 mSpawnTrans.set(al::getTrans(actor: this));
86
87 mStateHackStart = new EnemyStateHackStart(this, nullptr, nullptr);
88 al::initNerveState(user: this, state: mStateHackStart, nerve: &NrvKaronWing.HackStart, hostName: "キャプチャー開始");
89
90 mStateWander = new FlyerStateWander(this, &cWanderParam);
91 al::initNerveState(user: this, state: mStateWander, nerve: &NrvKaronWing.Wander, hostName: "うろつき");
92
93 mCapTargetInfo = rs::createCapTargetInfoWithPlayerCollider(this, mStateHack, nullptr);
94 if (!al::trySyncStageSwitchAppear(actor: this))
95 makeActorAlive();
96}
97
98void KaronWing::control() {
99 if (al::isNerve(user: this, nerve: &NrvKaronWing.HackStart))
100 return;
101
102 if (!al::isNerve(user: this, nerve: &NrvKaronWing.Revive) &&
103 (al::isCollidedFloorCode(this, "DamageFire") || al::isCollidedFloorCode(this, "Needle") ||
104 al::isCollidedFloorCode(this, "Poison"))) {
105 al::startHitReaction(actor: this, name: "死亡");
106 al::setNerve(user: this, nerve: &NrvKaronWing.Revive);
107 } else if (al::isInDeathArea(actor: this) && !al::isNerve(user: this, nerve: &NrvKaronWing.Revive)) {
108 al::startHitReaction(actor: this, name: "死亡");
109 al::setNerve(user: this, nerve: &NrvKaronWing.Revive);
110 }
111}
112
113void KaronWing::updateCollider() {
114 if (al::isNerve(user: this, nerve: &NrvKaronWing.Hack)) {
115 mStateHack->updateCollider();
116 return;
117 }
118 al::LiveActor::updateCollider();
119}
120
121void KaronWing::attackSensor(al::HitSensor* self, al::HitSensor* other) {
122 if (al::isNerve(user: this, nerve: &NrvKaronWing.Revive))
123 return;
124
125 if (mPlayerHack) {
126 rs::sendMsgHackerNoReaction(mPlayerHack, other, self);
127 mStateHack->attackSensor(self, other);
128 return;
129 }
130
131 if (al::isSensorEnemyAttack(self) && !al::isNerve(user: this, nerve: &NrvKaronWing.HackStart) &&
132 !al::isNerve(user: this, nerve: &NrvKaronWing.Hack) && !al::isNerve(user: this, nerve: &NrvKaronWing.Swoon) &&
133 !al::isNerve(user: this, nerve: &NrvKaronWing.Break)) {
134 if (al::sendMsgEnemyAttack(receiver: other, sender: self)) {
135 al::faceToTarget(actor: this, target: al::getSensorPos(other));
136 setNerve(user: this, nerve: &NrvKaronWing.Attack);
137 return;
138 }
139
140 al::sendMsgPush(receiver: other, sender: self) || rs::sendMsgPushToPlayer(source: other, target: self);
141 }
142}
143
144bool KaronWing::receiveMsg(const al::SensorMsg* message, al::HitSensor* other,
145 al::HitSensor* self) {
146 if (al::isNerve(user: this, nerve: &NrvKaronWing.Revive)) {
147 return rs::isMsgPlayerDisregardHomingAttack(message) ||
148 rs::isMsgPlayerDisregardTargetMarker(message) || al::isMsgPlayerDisregard(msg: message);
149 }
150
151 if (rs::tryReceiveMsgInitCapTargetAndSetCapTargetInfo(message, mCapTargetInfo))
152 return true;
153
154 if (al::isNerve(user: this, nerve: &NrvKaronWing.Hack) && mStateHack->receiveMsg(message, other, self)) {
155 if (mStateHack->isEndCancel())
156 al::setNerve(user: this, nerve: &NrvKaronWing.Swoon);
157 else if (mStateHack->isEndReset()) {
158 al::startHitReaction(actor: this, name: "消滅");
159 al::setNerve(user: this, nerve: &NrvKaronWing.Revive);
160 } else if (mStateHack->isEndDamage())
161 kill();
162 return true;
163 }
164
165 if (rs::tryReceiveMsgNpcScareByEnemyIgnoreTargetHack(message, mCapTargetInfo))
166 return true;
167
168 if (!al::isNerve(user: this, nerve: &NrvKaronWing.Revive) && !al::isNerve(user: this, nerve: &NrvKaronWing.HackStart) &&
169 !al::isNerve(user: this, nerve: &NrvKaronWing.Hack)) {
170 if (rs::isMsgCapEnableLockOn(message))
171 return !rs::isOnEnemyCap(mEnemyCap);
172 if (rs::isMsgCapCancelLockOn(message))
173 return true;
174 if (mStateSwoon->tryReceiveMsgStartLockOn(message))
175 return !rs::isOnEnemyCap(mEnemyCap);
176 if (mStateSwoon->tryReceiveMsgStartHack(message)) {
177 al::invalidateClipping(actor: this);
178 al::setNerve(user: this, nerve: &NrvKaronWing.HackStart);
179 mPlayerHack = mStateHackStart->tryStart(message, other, self);
180 return true;
181 }
182 }
183
184 if (al::isNerve(user: this, nerve: &NrvKaronWing.Swoon) || al::isNerve(user: this, nerve: &NrvKaronWing.Break)) {
185 if (mStateSwoon->tryReceiveMsgEnableLockOn(message))
186 return true;
187 if (mStateSwoon->tryReceiveMsgStartHack(message)) {
188 al::setNerve(user: this, nerve: &NrvKaronWing.HackStart);
189 mPlayerHack = mStateHackStart->tryStart(message, other, self);
190 return true;
191 }
192 if (mStateSwoon->tryReceiveMsgEndSwoon(message))
193 return true;
194 if (rs::isMsgTankExplosion(message))
195 (al::isNerve(user: this, nerve: &NrvKaronWing.Swoon) ? mStateSwoon : mStateBreak)->requestTrampled();
196 }
197
198 if (al::isNerve(user: this, nerve: &NrvKaronWing.ReviveAppear) || al::isNerve(user: this, nerve: &NrvKaronWing.Wait) ||
199 al::isNerve(user: this, nerve: &NrvKaronWing.Turn) || al::isNerve(user: this, nerve: &NrvKaronWing.Wander) ||
200 al::isNerve(user: this, nerve: &NrvKaronWing.Find) || al::isNerve(user: this, nerve: &NrvKaronWing.Chase)) {
201 if (mStateSwoon->tryReceiveMsgAttack(message)) {
202 if (rs::tryStartEnemyCapBlowDown(mEnemyCap, other)) {
203 mDamageStartY = al::getTrans(actor: this).y;
204 al::setVelocityBlowAttackAndTurnToTarget(actor: this, target: rs::getPlayerBodyPos(this), speedH: 7.0f,
205 speedV: 25.0f);
206 al::setNerve(user: this, nerve: &NrvKaronWing.DamageCap);
207 }
208 return true;
209 }
210
211 if (rs::isMsgPressDown(message) || rs::isMsgBlowDown(message)) {
212 rs::tryStartEnemyCapBlowDown(mEnemyCap, other);
213 rs::requestHitReactionToAttacker(message, self, other);
214 al::setNerve(user: this, nerve: &NrvKaronWing.Break);
215 return true;
216 }
217 }
218
219 if (!mPlayerHack && !al::isNerve(user: this, nerve: &NrvKaronWing.Swoon) &&
220 !al::isNerve(user: this, nerve: &NrvKaronWing.Break) &&
221 al::tryReceiveMsgPushAndAddVelocity(this, message, other, self, 3.0f))
222 return true;
223
224 return false;
225}
226
227void KaronWing::exeWait() {
228 if (al::isFirstStep(user: this)) {
229 al::validateClipping(actor: this);
230 al::startAction(actor: this, actionName: "FlyWait");
231 }
232
233 al::scaleVelocity(actor: this, factor: 0.7f);
234 if (al::isGreaterEqualStep(user: this, step: 60))
235 al::setNerve(user: this, nerve: &NrvKaronWing.Wander);
236}
237
238void KaronWing::exeWander() {
239 al::updateNerveState(user: this);
240 if (al::isNearPlayer(this, 1500.0f))
241 al::setNerve(user: this, nerve: &NrvKaronWing.Turn);
242}
243
244void KaronWing::exeTurn() {
245 if (al::isFirstStep(user: this))
246 al::startAction(actor: this, actionName: "EnemyFly");
247
248 sead::Vector3f playerPos = al::getPlayerPos(this, 0);
249 al::turnToTarget(actor: this, target: playerPos, deg: 2.3f);
250 if (al::isFaceToTargetDegreeH(actor: this, target: playerPos, face: al::getFront(actor: this), degH: 2.3f)) {
251 al::setNerve(user: this, nerve: &NrvKaronWing.Find);
252 return;
253 }
254
255 al::scaleVelocity(actor: this, factor: 0.7f);
256}
257
258void KaronWing::exeFind() {
259 if (al::isFirstStep(user: this))
260 al::startAction(actor: this, actionName: "Find");
261
262 if (al::isActionEnd(actor: this)) {
263 al::setNerve(user: this, nerve: &NrvKaronWing.Chase);
264 return;
265 }
266
267 al::scaleVelocity(actor: this, factor: 0.7f);
268}
269
270void KaronWing::exeChase() {
271 if (al::isFirstStep(user: this))
272 al::startAction(actor: this, actionName: "FlyChase");
273
274 if (!al::isNearPlayer(this, 2000.0f)) {
275 al::setNerve(user: this, nerve: &NrvKaronWing.Wait);
276 return;
277 }
278
279 al::flyAndTurnToTarget(actor: this, target: al::getPlayerPos(this, 0), forceFront: 4.0f, forceGravity: 0.0f, decay: 0.7f, deg: 2.3f);
280}
281
282void KaronWing::exeRevive() {
283 if (al::updateNerveStateAndNextNerve(user: this, nerve: &NrvKaronWing.ReviveAppear)) {
284 mStateHack->resetFlyLimit(mSpawnTrans);
285 rs::tryAppearEnemyCap(mEnemyCap);
286 al::startVisAnim(this, "CapOn");
287 }
288}
289
290void KaronWing::exeReviveAppear() {
291 if (al::isFirstStep(user: this))
292 al::startAction(actor: this, actionName: "AppearStart");
293
294 if (al::isActionEnd(actor: this))
295 al::setNerve(user: this, nerve: &NrvKaronWing.Wait);
296}
297
298void KaronWing::exeAttack() {
299 if (al::isFirstStep(user: this)) {
300 al::startAction(actor: this, actionName: "AttackHit");
301 al::setVelocityZero(this);
302 }
303
304 if (al::isActionEnd(actor: this)) {
305 al::setNerve(user: this, nerve: &NrvKaronWing.Wait);
306 return;
307 }
308
309 if (!al::isOnGround(this, 0))
310 al::addVelocityToGravity(actor: this, force: 1.0f);
311 al::scaleVelocity(actor: this, factor: al::isOnGround(this, 0) ? 0.7f : 0.97f);
312}
313
314void KaronWing::exeSwoon() {
315 if (al::isFirstStep(user: this))
316 al::invalidateClipping(actor: this);
317
318 if (al::updateNerveState(user: this)) {
319 al::startHitReaction(actor: this, name: "消滅");
320 al::setNerve(user: this, nerve: &NrvKaronWing.Revive);
321 return;
322 }
323
324 if (!al::isOnGround(this, 0))
325 al::addVelocityToGravity(actor: this, force: 1.0f);
326 al::scaleVelocity(actor: this, factor: al::isOnGround(this, 0) ? 0.7f : 0.97f);
327}
328
329void KaronWing::exeBreak() {
330 if (al::isFirstStep(user: this))
331 al::invalidateClipping(actor: this);
332
333 if (al::updateNerveState(user: this)) {
334 al::setNerve(user: this, nerve: &NrvKaronWing.Wait);
335 return;
336 }
337
338 if (!al::isOnGround(this, 0))
339 al::addVelocityToGravity(actor: this, force: 1.0f);
340 al::scaleVelocity(actor: this, factor: al::isOnGround(this, 0) ? 0.7f : 0.97f);
341}
342
343void KaronWing::exeHackStart() {
344 al::updateNerveStateAndNextNerve(user: this, nerve: &NrvKaronWing.Hack);
345}
346
347void KaronWing::exeHack() {
348 al::updateNerveStateAndNextNerve(user: this, nerve: &NrvKaronWing.Swoon);
349}
350
351void KaronWing::exeDamageCap() {
352 if (al::isFirstStep(user: this)) {
353 al::invalidateClipping(actor: this);
354 al::startAction(actor: this, actionName: "DamageCap");
355 }
356
357 if (!al::isOnGround(this, 0))
358 al::addVelocityToGravity(actor: this, force: 1.0f);
359 al::scaleVelocity(actor: this, factor: al::isOnGround(this, 0) ? 0.7f : 0.97f);
360
361 if (al::isActionEnd(actor: this)) {
362 const sead::Vector3f& trans = al::getTrans(actor: this);
363 const sead::Vector3f& velocity = al::getVelocity(actor: this);
364 if (trans.y + velocity.y <= mDamageStartY || al::isOnGround(this, 0))
365 al::setNerve(user: this, nerve: &NrvKaronWing.Wait);
366 }
367}
368