1#include "MapObj/AnagramAlphabetCharacter.h"
2
3#include "Library/LiveActor/ActorActionFunction.h"
4#include "Library/LiveActor/ActorClippingFunction.h"
5#include "Library/LiveActor/ActorCollisionFunction.h"
6#include "Library/LiveActor/ActorFlagFunction.h"
7#include "Library/LiveActor/ActorInitFunction.h"
8#include "Library/LiveActor/ActorInitUtil.h"
9#include "Library/LiveActor/ActorMovementFunction.h"
10#include "Library/LiveActor/ActorPoseUtil.h"
11#include "Library/LiveActor/ActorSensorUtil.h"
12#include "Library/LiveActor/LiveActorFunction.h"
13#include "Library/Math/MathUtil.h"
14#include "Library/Nerve/NerveSetupUtil.h"
15#include "Library/Nerve/NerveUtil.h"
16
17#include "MapObj/AnagramAlphabet.h"
18#include "MapObj/CapTargetParts.h"
19#include "Player/HackerJudge.h"
20#include "Player/PlayerHackStartShaderCtrl.h"
21#include "Util/Hack.h"
22#include "Util/JudgeUtil.h"
23#include "Util/SensorMsgFunction.h"
24
25namespace {
26NERVE_IMPL(AnagramAlphabetCharacter, Wait);
27NERVE_IMPL(AnagramAlphabetCharacter, WaitHack);
28NERVE_IMPL(AnagramAlphabetCharacter, HackEnd);
29NERVE_IMPL(AnagramAlphabetCharacter, HackStart);
30NERVE_IMPL(AnagramAlphabetCharacter, WaitHackStart);
31NERVE_IMPL(AnagramAlphabetCharacter, Complete);
32NERVE_IMPL(AnagramAlphabetCharacter, HackWait);
33NERVE_IMPL(AnagramAlphabetCharacter, HackMove);
34NERVE_IMPL(AnagramAlphabetCharacter, HackFall);
35NERVE_IMPL(AnagramAlphabetCharacter, HackGoal);
36NERVE_IMPL(AnagramAlphabetCharacter, Set);
37
38NERVES_MAKE_NOSTRUCT(AnagramAlphabetCharacter, HackStart);
39NERVES_MAKE_STRUCT(AnagramAlphabetCharacter, Wait, WaitHack, HackEnd, WaitHackStart, Complete,
40 HackWait, HackMove, HackFall, HackGoal, Set);
41} // namespace
42
43AnagramAlphabetCharacter::AnagramAlphabetCharacter(const char* name) : al::LiveActor(name) {}
44
45void AnagramAlphabetCharacter::init(const al::ActorInitInfo& info) {
46 al::initActorChangeModel(actor: this, initInfo: info);
47 al::initNerve(actor: this, nerve: &NrvAnagramAlphabetCharacter.Wait, maxStates: 0);
48 al::initSubActorKeeperNoFile(this, info, 1);
49
50 mCapTargetInfo = rs::createCapTargetInfo(this, 0);
51 mCapTargetParts = new CapTargetParts(this, info);
52 al::invalidateClipping(actor: mCapTargetParts);
53 al::registerSubActorSyncClipping(this, mCapTargetParts);
54 al::onSyncAlphaMaskSubActor(actor: this, subActor: mCapTargetParts);
55
56 al::createAndSetColliderSpecialPurpose(this, "Alphabet");
57
58 mHackerJudgeNormalFall = new HackerJudgeNormalFall(this, 5);
59 mHackerJudgeStartRun = new HackerJudgeStartRun(this, &mHackerParent);
60 mPlayerHackStartShaderCtrl = new PlayerHackStartShaderCtrl(this, 0);
61
62 al::startAction(actor: this, actionName: "Wait");
63 al::offCollide(actor: this);
64 makeActorAlive();
65}
66
67void AnagramAlphabetCharacter::attackSensor(al::HitSensor* self, al::HitSensor* other) {
68 if (mHackerParent) {
69 rs::sendMsgHackerNoReaction(mHackerParent, other, self);
70 return;
71 }
72 if (!al::isSensorNpc(self))
73 return;
74 if (al::isSensorPlayer(other) && al::isSensorName(other, "Pushed"))
75 al::sendMsgPush(receiver: other, sender: self);
76 else if (al::isSensorNpc(other))
77 al::sendMsgPush(receiver: other, sender: self);
78}
79
80bool AnagramAlphabetCharacter::isHack() {
81 return al::isNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackWait) ||
82 al::isNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackMove) ||
83 al::isNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackFall) ||
84 al::isNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackGoal);
85}
86
87bool AnagramAlphabetCharacter::receiveMsg(const al::SensorMsg* message, al::HitSensor* other,
88 al::HitSensor* self) {
89 if (rs::tryReceiveMsgInitCapTargetAndSetCapTargetInfo(message, mCapTargetInfo))
90 return true;
91
92 if (rs::isMsgPlayerDisregardHomingAttack(message) ||
93 rs::isMsgPlayerDisregardTargetMarker(message))
94 return al::isSensorName(self, "Body");
95
96 if (rs::isMsgTargetMarkerPosition(message) && al::isSensorName(self, "PossessedHitMark")) {
97 const sead::Vector3f& sensorPos = al::getSensorPos(this, "PossessedHitMark");
98 rs::setMsgTargetMarkerPosition(message, sead::Vector3f::ey * 60.0f + sensorPos);
99 return true;
100 }
101
102 if (rs::isMsgCapKeepLockOn(message))
103 return al::isNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.WaitHack);
104
105 if (rs::isMsgCapCancelLockOn(message)) {
106 if (al::isNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.WaitHack)) {
107 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.Wait);
108 return true;
109 }
110 return false;
111 }
112
113 if (rs::isMsgCancelHack(message)) {
114 if (isHack()) {
115 endHack();
116 return true;
117 }
118 return false;
119 }
120 if (rs::isMsgHackMarioDead(message) || rs::isMsgHackMarioDemo(message) ||
121 rs::isMsgHackMarioInWater(message) || rs::isMsgHackMarioCheckpointFlagWarp(message)) {
122 endHack();
123 return true;
124 }
125
126 if (rs::isMsgHackerDamageAndCancel(message)) {
127 if (isHack()) {
128 sead::Vector3f dir;
129 al::calcDirBetweenSensorsH(&dir, other, self);
130 endHackDir(dir);
131 return true;
132 }
133 return false;
134 }
135
136 if (rs::isMsgStartHack(message)) {
137 if (al::isNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.WaitHack)) {
138 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.WaitHackStart);
139 al::invalidateClipping(actor: this);
140 mHackerParent = rs::startHack(self, other, nullptr);
141 rs::startHackStartDemo(mHackerParent, this);
142 return true;
143 } else
144 return false;
145 }
146
147 if (rs::isMsgCapStartLockOn(message)) {
148 if (al::isNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.Complete))
149 return false;
150
151 if (al::isSensorName(self, "PossessedHitMark")) {
152 if (al::isNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.Wait))
153 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.WaitHack);
154
155 return true;
156 }
157
158 return false;
159 }
160
161 if (rs::isMsgEnableMapCheckPointWarpCollidedGround(message, this))
162 return true;
163
164 if ((al::isNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackWait) ||
165 al::isNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackMove) ||
166 al::isNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackFall)) &&
167 al::tryReceiveMsgPushAndAddVelocityH(this, message, other, self, 10.0f)) {
168 if (al::isCollidedWall(this))
169 al::limitVelocityDirSign(actor: this, dir: -al::getCollidedWallNormal(this), limit: 0.0f);
170
171 return true;
172 }
173
174 return false;
175}
176
177void AnagramAlphabetCharacter::setComplete() {
178 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.Complete);
179}
180
181void AnagramAlphabetCharacter::killCapTarget() {
182 mCapTargetParts->kill();
183 al::invalidateHitSensors(this);
184}
185
186void AnagramAlphabetCharacter::exeWait() {
187 if (al::isFirstStep(user: this)) {
188 al::setVelocityZero(this);
189 al::startAction(actor: this, actionName: "Wait");
190 mCapTargetParts->startNormal();
191 }
192}
193
194void AnagramAlphabetCharacter::exeWaitHack() {
195 if (al::isFirstStep(user: this))
196 mCapTargetParts->startSwoon();
197}
198
199void AnagramAlphabetCharacter::exeWaitHackStart() {
200 if (rs::isHackStartDemoEnterMario(mHackerParent))
201 al::setNerve(user: this, nerve: &HackStart);
202}
203
204void AnagramAlphabetCharacter::exeHackStart() {
205 if (al::isFirstStep(user: this)) {
206 mCapTargetParts->startHack();
207 al::startAction(actor: this, actionName: "HackStart");
208 }
209
210 if (al::isStep(user: this, step: 10))
211 mPlayerHackStartShaderCtrl->start();
212 mPlayerHackStartShaderCtrl->update();
213
214 if (al::isGreaterEqualStep(user: this, step: 60)) {
215 mPlayerHackStartShaderCtrl->end();
216 rs::endHackStartDemo(mHackerParent, this);
217 al::onCollide(actor: this);
218 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackWait);
219 }
220}
221
222void AnagramAlphabetCharacter::exeHackWait() {
223 if (al::isFirstStep(user: this)) {
224 al::startAction(actor: this, actionName: "Wait");
225 rs::resetJudge(judge: mHackerJudgeNormalFall);
226 }
227
228 al::addVelocityToGravity(actor: this, force: 1.5f);
229 al::reboundVelocityFromCollision(actor: this, reboundStrength: 0.0f, reboundMin: 0.0f, friction: 0.0f);
230 al::scaleVelocity(actor: this, factor: 0.7f);
231
232 if (rs::updateJudgeAndResult(judge: mHackerJudgeNormalFall))
233 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackFall);
234
235 else if (mParent->testBase(this))
236 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackGoal);
237
238 else if (rs::updateJudgeAndResult(judge: mHackerJudgeStartRun))
239 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackMove);
240}
241
242void AnagramAlphabetCharacter::exeHackMove() {
243 if (al::isFirstStep(user: this)) {
244 rs::resetJudge(judge: mHackerJudgeNormalFall);
245 mSwingTimer = 0;
246 }
247
248 if (rs::isTriggerHackSwing(mHackerParent))
249 mSwingTimer = 60;
250 else if (mSwingTimer >= 1)
251 mSwingTimer--;
252
253 al::addVelocityToGravity(actor: this, force: 1.5f);
254 al::scaleVelocity(actor: this, factor: 0.7f);
255
256 sead::Vector3f dir = sead::Vector3f(0.0f, 0.0f, 0.0f);
257 f32 runningAccel = rs::isHoldHackAction(mHackerParent) ? 4.0f : 2.0f;
258
259 f32 accel = mSwingTimer > 0 ? runningAccel + 2.0f : runningAccel;
260
261 if (accel != 2.0f) {
262 if (!al::isActionPlaying(actor: this, actionName: "Dash"))
263 al::startAction(actor: this, actionName: "Dash");
264 } else {
265 if (!al::isActionPlaying(actor: this, actionName: "Walk"))
266 al::startAction(actor: this, actionName: "Walk");
267 }
268
269 rs::addHackActorAccelStick(this, mHackerParent, &dir, accel, sead::Vector3f::ey);
270 al::turnToDirection(actor: this, dir, deg: 5.0f);
271 al::reboundVelocityFromCollision(actor: this, reboundStrength: 0.0f, reboundMin: 0.0f, friction: 1.0f);
272
273 if (rs::updateJudgeAndResult(judge: mHackerJudgeNormalFall))
274 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackFall);
275
276 else if (mParent->testBase(this))
277 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackGoal);
278
279 else if (rs::isHackerStopMove(this, mHackerParent, 1.5f))
280 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackWait);
281}
282
283void AnagramAlphabetCharacter::exeHackFall() {
284 if (al::isFirstStep(user: this)) {
285 } // unused call
286
287 al::addVelocityToGravity(actor: this, force: 1.5f);
288 al::scaleVelocity(actor: this, factor: 0.99f);
289
290 if (al::isOnGround(this, 0)) {
291 al::startHitReaction(actor: this, name: "着地");
292 al::reboundVelocityFromCollision(actor: this, reboundStrength: 0.0f, reboundMin: 0.0f, friction: 1.0f);
293 if (rs::updateJudgeAndResult(judge: mHackerJudgeStartRun))
294 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackMove);
295 else
296 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackWait);
297 }
298}
299
300void AnagramAlphabetCharacter::exeHackEnd() {
301 al::addVelocityToGravity(actor: this, force: 1.5f);
302 al::scaleVelocity(actor: this, factor: 0.99f);
303
304 if (al::isOnGround(this, 0)) {
305 al::offCollide(actor: this);
306 al::startHitReaction(actor: this, name: "着地");
307 al::setVelocityZero(this);
308 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.Wait);
309 }
310}
311
312void AnagramAlphabetCharacter::exeHackGoal() {
313 if (al::isFirstStep(user: this)) {
314 al::startAction(actor: this, actionName: "Wait");
315 al::offCollide(actor: this);
316 al::setVelocityZero(this);
317 al::startHitReaction(actor: this, name: "ゴールデモ開始");
318 }
319
320 sead::Vector3f front = {mPoseMatrix->m[0][2], mPoseMatrix->m[1][2], mPoseMatrix->m[2][2]};
321
322 sead::Vector3f pos;
323 mPoseMatrix->getTranslation(o&: pos);
324
325 al::turnToDirection(actor: this, dir: front, deg: 20.0f);
326 al::lerpVec(al::getTransPtr(actor: this), al::getTrans(actor: this), pos, 0.5f);
327
328 if (al::isGreaterEqualStep(user: this, step: 0)) {
329 al::updatePoseMtx(actor: this, mtx: mPoseMatrix);
330
331 endHackDir(dir: front);
332
333 mParent->testEndHack();
334 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.Set);
335 al::startHitReaction(actor: this, name: "ゴールデモ終了");
336 }
337}
338
339void AnagramAlphabetCharacter::exeSet() {
340 if (al::isFirstStep(user: this))
341 al::startAction(actor: this, actionName: "Set");
342
343 if (al::isActionEnd(actor: this)) {
344 if (!mParent->testComplete())
345 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.Wait);
346 }
347}
348
349void AnagramAlphabetCharacter::exeComplete() {}
350
351void AnagramAlphabetCharacter::endHack() {
352 CapTargetParts* capTargetParts = mCapTargetParts;
353 rs::endHack(&mHackerParent);
354 al::validateClipping(actor: this);
355 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackEnd);
356 capTargetParts->startNormal();
357}
358
359void AnagramAlphabetCharacter::endHackDir(const sead::Vector3f& dir) {
360 CapTargetParts* capTargetParts = mCapTargetParts;
361 rs::endHackDir(&mHackerParent, dir);
362 al::validateClipping(actor: this);
363 al::setNerve(user: this, nerve: &NrvAnagramAlphabetCharacter.HackEnd);
364 capTargetParts->startNormal();
365}
366