1#include "MapObj/HackFork.h"
2
3#include "Library/Camera/CameraTicket.h"
4#include "Library/Camera/CameraUtil.h"
5#include "Library/Collision/PartsConnector.h"
6#include "Library/Controller/PadRumbleFunction.h"
7#include "Library/Event/EventFlowUtil.h"
8#include "Library/Joint/JointControllerKeeper.h"
9#include "Library/Joint/JointLocalAxisRotator.h"
10#include "Library/Layout/LayoutActorUtil.h"
11#include "Library/LiveActor/ActorActionFunction.h"
12#include "Library/LiveActor/ActorAnimFunction.h"
13#include "Library/LiveActor/ActorClippingFunction.h"
14#include "Library/LiveActor/ActorFlagFunction.h"
15#include "Library/LiveActor/ActorInitUtil.h"
16#include "Library/LiveActor/ActorModelFunction.h"
17#include "Library/LiveActor/ActorPoseUtil.h"
18#include "Library/LiveActor/ActorResourceFunction.h"
19#include "Library/LiveActor/ActorSensorUtil.h"
20#include "Library/Math/MathUtil.h"
21#include "Library/Nerve/NerveSetupUtil.h"
22#include "Library/Nerve/NerveUtil.h"
23#include "Library/Placement/PlacementFunction.h"
24#include "Library/Play/Camera/ActorMatrixCameraTarget.h"
25#include "Library/Se/SeFunction.h"
26#include "Library/Yaml/ByamlUtil.h"
27
28#include "Player/CapTargetInfo.h"
29#include "Player/IUsePlayerHack.h"
30#include "Player/PlayerHackStartShaderCtrl.h"
31#include "Scene/GuidePosInfoHolder.h"
32#include "System/GameDataUtil.h"
33#include "Util/Hack.h"
34#include "Util/NpcAnimUtil.h"
35#include "Util/NpcEventFlowUtil.h"
36#include "Util/PlayerUtil.h"
37#include "Util/SensorMsgFunction.h"
38
39namespace {
40NERVE_IMPL(HackFork, Wait);
41NERVE_IMPL(HackFork, HackStartWait);
42NERVE_IMPL(HackFork, Damping);
43NERVE_IMPL(HackFork, HackStart);
44NERVE_IMPL(HackFork, HackWait);
45NERVE_IMPL(HackFork, HackBend);
46NERVE_IMPL(HackFork, HackShoot);
47
48NERVES_MAKE_STRUCT(HackFork, Wait, HackStartWait, Damping, HackStart, HackWait, HackBend,
49 HackShoot);
50} // namespace
51
52PlayerHackStartShaderParam sPlayerHackStartShaderParam(true, 100.0f, 10, 20);
53
54HackFork::HackFork(const char* name) : al::LiveActor(name) {}
55
56// NON_MATCHING: Regswap and missing instructions https://decomp.me/scratch/NeUHJ
57void HackFork::init(const al::ActorInitInfo& initInfo) {
58 const char* modelName = nullptr;
59 if (alPlacementFunction::tryGetModelName(modelName: &modelName, initInfo))
60 al::initActorWithArchiveName(actor: this, initInfo, archiveName: modelName, suffix: nullptr);
61 else
62 al::initActor(actor: this, initInfo);
63
64 al::calcSideDir(side: &mSideDir, actor: this);
65 al::initJointControllerKeeper(this, 10);
66 mJointRotationHolder.allocBuffer(ptrNumMax: 5, heap: nullptr);
67
68 const char* jointNames[5] = {"Stick01", "Stick02", "Stick03", "Stick04", "Stick05"};
69 for (s32 i = 0; i < 5; i++) {
70 mJointRotationHolder.pushBack(
71 ptr: al::initJointLocalAxisRotator(this, mSideDir, &mDampingForce, jointNames[i], false));
72 al::initJointLocalXRotator(this, &mDampingStrength, jointNames[i]);
73 }
74
75 al::initNerve(actor: this, nerve: &NrvHackFork.Wait, maxStates: 0);
76 al::tryGetArg(arg: &mIsLimitterFree, initInfo, key: "LimitterFree");
77 bool hasCamera = false;
78 bool isValid = al::tryGetArg(arg: &hasCamera, initInfo, key: "Camera");
79 if (hasCamera && isValid)
80 mCameraTicket = al::initObjectCamera(user: this, actorInitInfo: initInfo, nullptr, nullptr);
81
82 mMatrixCameraTarget = al::createActorMatrixCameraTarget(actor: this, matrix: &mCameraTargetMtx);
83 if (!al::isEqualString(str1: modelName, str2: "HackBoard")) {
84 mCameraOffset = {0.0f, 0.0f, 100.0f};
85 mIsHackBoard = true;
86 }
87
88 bool hasBallon = false;
89 bool isBallonValid = al::tryGetArg(arg: &hasBallon, initInfo, key: "Balloon");
90 if (hasBallon && isBallonValid && !rs::isSequenceTimeBalloonOrRace(actor: this)) {
91 mEventFlowExecutor = rs::initEventFlow(this, initInfo, nullptr, nullptr);
92 rs::startEventFlow(mEventFlowExecutor, "Init");
93 }
94 if (al::isMtpAnimExist(this)) {
95 rs::setNpcMaterialAnimFromPlacementInfo(this, initInfo);
96 al::tryStartMclAnimIfExist(this, al::getPlayingMtpAnimName(this));
97 }
98 mMtxConnector = al::tryCreateMtxConnector(actor: this, info: initInfo);
99 makeActorAlive();
100 mCapTargetInfo = rs::createCapTargetInfo(this, nullptr);
101
102 al::ByamlIter iter = {al::getModelResourceYaml(this, "InitHackCap", nullptr)};
103 mJointName = al::tryGetByamlKeyStringOrNULL(iter, "JointName");
104 sead::Vector3f localTrans = {0.0f, 0.0f, 0.0f};
105 al::tryGetByamlV3f(&localTrans, iter, "LocalTrans");
106 sead::Vector3f localRotate = {0.0f, 0.0f, 0.0f};
107 al::tryGetByamlV3f(&localRotate, iter, "LocalRotate");
108
109 sead::Matrix34f rotationMatrix;
110 sead::Vector3f rotateRad(sead::Mathf::deg2rad(deg: localRotate.x),
111 sead::Mathf::deg2rad(deg: localRotate.y),
112 sead::Mathf::deg2rad(deg: localRotate.z));
113 rotationMatrix.makeR(r: rotateRad);
114
115 sead::Matrix34f translationMatrix;
116 translationMatrix.makeRT(r: {0.0f, 0.0f, 0.0f}, t: localTrans * al::getScaleY(actor: this));
117
118 mStartingPoseMtx = rotationMatrix * translationMatrix;
119
120 sead::Matrix34f jointMtx = *al::getJointMtxPtr(actor: this, mJointName);
121 al::normalize(mtx: &jointMtx);
122 mInvJointMtx.setInverse(jointMtx);
123
124 mCapTargetInfo->setPoseMatrix(&mCapPoseMtx);
125 mUpsideDownInitialHackDir.inverse(q: &mInvInitialHackDir);
126
127 initBasicPoseInfo();
128 mHackStartShaderCtrl = new PlayerHackStartShaderCtrl(this, &sPlayerHackStartShaderParam);
129}
130
131void HackFork::attackSensor(al::HitSensor* self, al::HitSensor* other) {
132 if (al::isSensorName(self, "Push") && !al::sendMsgPush(receiver: other, sender: self) && !mIsHorizontal) {
133 const sead::Vector3f& velocity = al::getActorVelocity(other);
134 if (velocity.x * velocity.x + velocity.z * velocity.z < 4.999696f)
135 rs::sendMsgPushToPlayer(source: other, target: self);
136 }
137}
138
139bool HackFork::receiveMsg(const al::SensorMsg* message, al::HitSensor* other, al::HitSensor* self) {
140 if (rs::isMsgEnableMapCheckPointWarp(message))
141 return false;
142
143 if (rs::isMsgMotorcycleDashAttack(message))
144 return tryTouch(force: 5.0f, reactionName: "タッチ(強)");
145 if (al::isMsgPlayerObjTouch(msg: message) || al::isMsgKickStoneAttackReflect(msg: message) ||
146 rs::isMsgRadishReflect(message) || rs::isMsgSeedReflect(message)) {
147 return tryTouch(force: 2.0f, reactionName: "タッチ(弱)");
148 }
149 if (rs::isMsgHammerBrosHammerHackAttack(message) || al::isMsgPlayerFireBallAttack(msg: message)) {
150 tryTouch(force: 2.0f, reactionName: "タッチ(弱)");
151 rs::requestHitReactionToAttacker(message, self, other);
152 return true;
153 }
154 if (rs::tryReceiveMsgInitCapTargetAndSetCapTargetInfo(message, mCapTargetInfo)) {
155 resetCapMtx(sensor: self);
156 return true;
157 }
158 if (rs::isMsgPlayerDisregardTargetMarker(message))
159 return mEventFlowExecutor && al::isActive(mEventFlowExecutor);
160 if (rs::isMsgTargetMarkerPosition(message)) {
161 sead::Vector3f position = al::getSensorPos(this, "Stick05") + 50.0f * sead::Vector3f::ey;
162 rs::setMsgTargetMarkerPosition(message, position);
163 return true;
164 }
165 if (isNerveHackable()) {
166 if (rs::isMsgCapEnableLockOn(message))
167 return true;
168 if (rs::isMsgStartHack(message)) {
169 al::invalidateClipping(actor: this);
170 mPlayerHack = rs::startHack(self, other, nullptr);
171 rs::startHackStartDemo(mPlayerHack, this);
172 mDampingStrength = 0.0f;
173 mDampingForce = 0.0f;
174 mTouchForce = 0.0f;
175 resetCapMtx(sensor: self);
176 rs::setRouteHeadGuidePosPtr(this, &mHeadGuidePos);
177 al::setNerve(user: this, nerve: &NrvHackFork.HackStartWait);
178 al::startHitReaction(actor: this, name: "ひょうい開始");
179 return true;
180 }
181 if (rs::isMsgCapCancelLockOn(message))
182 return true;
183 }
184 if (isHack()) {
185 if (rs::isMsgHackerDamageAndCancel(message)) {
186 if (al::isSensorName(self, "Body"))
187 return rs::requestDamage(mPlayerHack);
188 return false;
189 }
190 if (rs::isMsgHackSyncDamageVisibility(message)) {
191 rs::syncDamageVisibility(this, mPlayerHack);
192 return true;
193 }
194
195 if (rs::receiveMsgRequestTransferHack(message, mPlayerHack, other))
196 return true;
197 if (rs::isMsgCancelHack(message)) {
198 rs::tryEndHackStartDemo(mPlayerHack, this);
199 if (mIsHorizontal) {
200 rs::endHack(&mPlayerHack);
201 rs::resetRouteHeadGuidePosPtr(this);
202 al::tryStartAction(actor: this, actionName: "HackEnd");
203 al::setNerve(user: this, nerve: &NrvHackFork.Damping);
204 return true;
205 }
206
207 if (mIsHackBoard) {
208 sead::Vector3f upDir;
209 al::calcUpDir(up: &upDir, actor: this);
210 rs::endHackDir(&mPlayerHack, upDir);
211 } else {
212 sead::Vector3f front;
213 if (al::isNearZero(vec: mPullDirection))
214 front.set(-mPullDirection2);
215 else
216 front.set(mPullDirection);
217 front.y = 0.0f;
218 al::tryNormalizeOrDirZ(vec: &front);
219 sead::Quatf quat;
220 al::makeQuatUpFront(outQuat: &quat, up: sead::Vector3f::ey, front);
221 rs::endHackTargetQuat(&mPlayerHack, quat, front);
222 }
223 rs::resetRouteHeadGuidePosPtr(this);
224 al::tryStartAction(actor: this, actionName: "HackEnd");
225 al::setNerve(user: this, nerve: &NrvHackFork.Damping);
226 return true;
227 }
228
229 if (rs::isMsgHackMarioDemo(message) || rs::isMsgHackMarioDead(message)) {
230 rs::endHack(&mPlayerHack);
231 rs::resetRouteHeadGuidePosPtr(this);
232 al::tryStartAction(actor: this, actionName: "HackEnd");
233 al::setNerve(user: this, nerve: &NrvHackFork.Damping);
234 return true;
235 }
236 }
237 return false;
238}
239
240void HackFork::initBasicPoseInfo() {
241 al::calcUpDir(up: &mUpDir, actor: this);
242
243 mHackDir.set(al::getQuat(actor: this));
244
245 sead::Vector3f frontDir;
246 al::calcFrontDir(front: &frontDir, actor: this);
247
248 sead::Quatf rotation;
249 sead::QuatCalcCommon<f32>::setAxisAngle(q&: rotation, axis: frontDir, angle: 180.0f);
250 mUpsideDownInitialHackDir = rotation * mHackDir;
251
252 sead::Vector3f frontDir2;
253 al::calcFrontDir(front: &frontDir2, actor: this);
254 if (sead::Mathf::abs(x: frontDir2.y) > 0.5f) {
255 al::invalidateShadow(actor: this);
256 mIsHorizontal = false;
257 } else {
258 mIsHorizontal = true;
259 }
260}
261
262void HackFork::initAfterPlacement() {
263 if (mMtxConnector) {
264 sead::Vector3f frontDir;
265 al::calcFrontDir(front: &frontDir, actor: this);
266 sead::Vector3f pos = al::getTrans(actor: this) + frontDir * 100.0f;
267 frontDir *= -200.0f;
268 al::attachMtxConnectorToCollision(mtxConnector: mMtxConnector, actor: this, pos, frontDir);
269 }
270}
271
272bool HackFork::tryTouch(f32 force, const char* reactionName) {
273 if (mTouchDelay != 0) {
274 mTouchDelay = 30;
275 return false;
276 }
277
278 if (isNerveHackable()) {
279 mTouchForce = force;
280 mTouchDelay = 30;
281 al::setNerve(user: this, nerve: &NrvHackFork.Damping);
282 al::startHitReaction(actor: this, name: reactionName);
283 return true;
284 }
285 return false;
286}
287
288void HackFork::resetCapMtx(al::HitSensor* sensor) {
289 calcHackDir(sensor);
290 sead::Matrix34f jointMtx = *al::getJointMtxPtr(actor: this, mJointName);
291 al::normalize(mtx: &jointMtx);
292
293 sead::Matrix34f poseMtx = jointMtx * mStartingPoseMtx;
294 sead::Vector3f up = poseMtx.getBase(axis: 1);
295
296 sead::Vector3f normal;
297 if (mIsHorizontal)
298 normal.set({0.0f, 1.0f, 0.0f});
299 else
300 normal = -mPullDirection2;
301
302 al::verticalizeVec(out: &normal, vertical: up, vec: normal);
303
304 if (!al::tryNormalizeOrZero(out: &normal)) {
305 mNextCapPoseMtx = mStartingPoseMtx;
306 } else {
307 sead::Matrix34f invPoseMtx;
308 invPoseMtx.setInverse(poseMtx);
309
310 normal.rotate(m: invPoseMtx);
311
312 sead::Quatf quatRotation;
313 al::makeQuatRotationRate(&quatRotation, sead::Vector3f::ez, normal, 1.0f);
314 sead::Matrix34f quatRotationMtx;
315 quatRotationMtx.fromQuat(q: quatRotation);
316
317 sead::Matrix34f invJointMtx;
318 invJointMtx.setInverse(jointMtx);
319
320 mNextCapPoseMtx = (invJointMtx * poseMtx) * quatRotationMtx;
321 }
322 updateCapMtx();
323}
324
325bool HackFork::isNerveHackable() const {
326 return al::isNerve(user: this, nerve: &NrvHackFork.Wait) || al::isNerve(user: this, nerve: &NrvHackFork.Damping);
327}
328
329bool HackFork::isHack() const {
330 return al::isNerve(user: this, nerve: &NrvHackFork.HackStartWait) ||
331 al::isNerve(user: this, nerve: &NrvHackFork.HackStart) || al::isNerve(user: this, nerve: &NrvHackFork.HackWait) ||
332 al::isNerve(user: this, nerve: &NrvHackFork.HackBend) || al::isNerve(user: this, nerve: &NrvHackFork.HackShoot);
333}
334
335void HackFork::controlSpring() {
336 f32 dampedForce = mDampingForce * 0.92f;
337 mTouchForce += dampedForce * -0.5f;
338 mDampingForce = dampedForce + mTouchForce;
339}
340
341void HackFork::checkSwing() {
342 if (rs::isTriggerHackSwing(mPlayerHack)) {
343 mIsHackSwing = true;
344 mTimeSinceSwingStart = 0;
345 return;
346 }
347
348 mTimeSinceSwingStart++;
349}
350
351// NON_MATCHING: Wrong loading order https://decomp.me/scratch/VW7ZF
352bool HackFork::trySwingJump() {
353 if (rs::isTriggerHackSwing(mPlayerHack)) {
354 mIsHackSwing = true;
355 mTimeSinceSwingStart = 0;
356 } else {
357 bool isHackSwing = mIsHackSwing;
358 mTimeSinceSwingStart++;
359 if (!isHackSwing || 10 < mTimeSinceSwingStart)
360 return false;
361 }
362
363 if (mIsHorizontal) {
364 mPullDirection.set({0.0f, -1.0f, 0.0f});
365 } else {
366 sead::Vector3f pulldirection = mPullDirection2;
367 pulldirection.rotate(q: mHackDir * mInvInitialHackDir);
368 pulldirection.y = 0;
369 al::tryNormalizeOrDirZ(vec: &pulldirection);
370 mPullDirection.set(pulldirection);
371 }
372 mIsPullDown = mPullDirection.dot(t: mUpDir) < 0.0f;
373
374 mDampingForce = 22.5f;
375 sead::Vector3f frontDir;
376 al::calcFrontDir(front: &frontDir, actor: this);
377 bendAndTwist(direction: mPullDirection, front: frontDir);
378 mIsSwingJump = true;
379 al::setNerve(user: this, nerve: &NrvHackFork.HackShoot);
380 return true;
381}
382
383bool HackFork::updateInput(sead::Vector3f* pullDirection, sead::Vector3f frontDir) {
384 sead::Vector3f lookdir;
385 al::calcCameraLookDir(lookDir: &lookdir, user: this, viewIdx: 0);
386
387 if (frontDir.dot(t: sead::Vector3f::ey) < sead::Mathf::cos(t: sead::Mathf::deg2rad(deg: 45.0f)) &&
388 frontDir.dot(t: lookdir) > 0.0f)
389 frontDir = -frontDir;
390
391 sead::Vector3f moveDir = {0.0f, 0.0f, 0.0f};
392 bool isMovePossible = rs::calcHackerMoveDir(&moveDir, mPlayerHack, frontDir);
393
394 mInputBuffer.forcePushBack(item: moveDir);
395
396 *pullDirection = {0.0f, 0.0f, 0.0f};
397 for (s32 i = 0; i < mInputBuffer.size(); i++)
398 *pullDirection += mInputBuffer[i];
399
400 if (!al::tryNormalizeOrZero(out: pullDirection))
401 al::calcUpDir(up: pullDirection, actor: this);
402 return isMovePossible;
403}
404
405f32 HackFork::getJumpRange() const {
406 return mIsLimitterFree ? 180.0f : 45.0f;
407}
408
409void HackFork::bendAndTwist(const sead::Vector3f& direction, const sead::Vector3f& front) {
410 mDampingForce = sead::Mathf::clampMax(val: mDampingForce + 1.0f, max_: 22.5f);
411 mTouchForce = 0.0f;
412
413 mSideDir.setCross(a: front, b: direction);
414 if (!al::tryNormalizeOrZero(out: &mSideDir))
415 al::calcSideDir(side: &mSideDir, actor: this);
416
417 for (s32 i = 0; i < mJointRotationHolder.size(); i++)
418 mJointRotationHolder[i]->setVector28(mSideDir);
419}
420
421// NON_MATCHING: Wrong loading order and registers https://decomp.me/scratch/8ReuA
422void HackFork::shoot() {
423 sead::Vector3f frontDir;
424 al::calcFrontDir(front: &frontDir, actor: this);
425 sead::Quatf quat;
426 al::makeQuatUpFront(outQuat: &quat, up: frontDir, front: -mPullDirection);
427
428 sead::Quatf rotationQuat;
429 rotationQuat.makeVectorRotation(from: frontDir, to: sead::Vector3f::ey);
430 quat = rotationQuat * quat;
431 quat.normalize();
432
433 sead::Vector3f jointPos;
434 al::calcJointPos(&jointPos, actor: this, "Stick05");
435
436 sead::Vector3f launchDir;
437 launchDir = mPullDirection;
438 if (launchDir.y < -sead::Mathf::cos(t: sead::Mathf::deg2rad(deg: 110.0f)))
439 launchDir += -sead::Vector3f::ey;
440 al::normalize(vec: &launchDir);
441
442 f32 scale = mPullDirection.dot(t: -sead::Vector3f::ey);
443 f32 cos = sead::Mathf::cos(t: sead::Mathf::deg2rad(deg: 45.0f));
444 if (scale > cos) {
445 scale = sead::Mathf::clamp(value: (scale - cos) / (1.0f - cos), low: 0.0f, high: 1.0f);
446 scale = (1.0f - sead::Mathf::square(t: 1.0f - scale)) * -8.0f;
447 } else {
448 scale = -0.0f;
449 }
450
451 sead::Vector3f frontDir2;
452 al::calcFrontDir(front: &frontDir2, actor: this);
453 frontDir2 *= scale;
454 f32 launchSpeed = mIsSwingJump ? 61.27f : 55.7f;
455 sead::Vector3f direction = frontDir2 - launchSpeed * launchDir;
456
457 rs::endHackAirVelocity(&mPlayerHack, jointPos, quat, direction, mAirVel);
458 rs::resetRouteHeadGuidePosPtr(this);
459 al::startHitReaction(actor: this, name: "ジャンプ");
460 al::tryStartAction(actor: this, actionName: "HackEnd");
461}
462
463void HackFork::control() {
464 mHackStartShaderCtrl->update();
465 if (mMtxConnector) {
466 al::connectPoseQT(actor: this, mtxConnector: mMtxConnector);
467 initBasicPoseInfo();
468 }
469 if (!isHack()) {
470 if (mCameraTicket && al::isActiveCamera(ticket: mCameraTicket))
471 al::endCamera(user: this, ticket: mCameraTicket, -1, false);
472 if (al::isActiveCameraTarget(target: mMatrixCameraTarget))
473 al::resetCameraTarget(user: this, target: mMatrixCameraTarget);
474 }
475 if (mTouchDelay != 0)
476 mTouchDelay--;
477 if (isHack()) {
478 if (mIsHorizontal) {
479 sead::Vector3f frontDir;
480 al::calcFrontDir(front: &frontDir, actor: this);
481 frontDir.y = 0.0f;
482 al::tryNormalizeOrDirZ(vec: &frontDir);
483 frontDir.negate();
484 sead::Vector3f sideDir;
485 sideDir.setCross(a: sead::Vector3f::ey, b: frontDir);
486 mCameraTargetMtx.setBase(axis: 0, v: sideDir);
487 mCameraTargetMtx.setBase(axis: 1, v: sead::Vector3f::ey);
488 mCameraTargetMtx.setBase(axis: 2, v: frontDir);
489 } else {
490 sead::Vector3f upDir;
491 al::calcUpDir(up: &upDir, actor: this);
492 upDir.y = 0.0;
493 al::tryNormalizeOrDirZ(vec: &upDir);
494 sead::Vector3f sideDir;
495 sideDir.setCross(a: sead::Vector3f::ey, b: upDir);
496 if ((al::getCameraPos(user: this, viewIdx: 0) - al::getTrans(actor: this)).dot(t: upDir) >= 0.0f) {
497 upDir = -upDir;
498 sideDir = -sideDir;
499 }
500 mCameraTargetMtx.setBase(axis: 0, v: sideDir);
501 mCameraTargetMtx.setBase(axis: 1, v: sead::Vector3f::ey);
502 mCameraTargetMtx.setBase(axis: 2, v: upDir);
503 }
504
505 sead::Vector3f cameraOffset;
506 cameraOffset.setRotated(q: al::getQuat(actor: this), a: mCameraOffset);
507 if (mIsHorizontal) {
508 sead::Vector3f frontDir;
509 al::calcFrontDir(front: &frontDir, actor: this);
510 cameraOffset += frontDir * 100.0f;
511 }
512 mCameraTargetMtx.setBase(axis: 3, v: cameraOffset + al::getTrans(actor: this));
513 al::calcJointPos(&mHeadGuidePos, actor: this, "Stick05");
514 mHeadGuidePos.y = sead::Mathf::max(a: mHeadGuidePos.y, b: al::getTrans(actor: this).y) + 100.0f;
515 }
516}
517
518void HackFork::calcAnim() {
519 al::LiveActor::calcAnim();
520 if (isHack())
521 updateCapMtx();
522}
523
524void HackFork::updateCapMtx() {
525 sead::Matrix34f mtx = *al::getJointMtxPtr(actor: this, mJointName);
526 al::normalize(mtx: &mtx);
527 mCapPoseMtx = mtx * mNextCapPoseMtx;
528}
529
530void HackFork::calcHackDir(al::HitSensor* sensor) {
531 if (mIsHorizontal)
532 mPullDirection2.set({0.0f, -1.0f, 0.0f});
533 else
534 mPullDirection2 = rs::getPlayerPos(this) - al::getSensorPos(sensor);
535
536 sead::Vector3f frontDir;
537 al::calcFrontDir(front: &frontDir, actor: this);
538 al::verticalizeVec(out: &mPullDirection2, vertical: frontDir, vec: mPullDirection2);
539 if (!al::tryNormalizeOrZero(out: &mPullDirection2))
540 al::calcUpDir(up: &mPullDirection2, actor: this);
541
542 mHackDir.inverse(q: &mInvInitialHackDir);
543}
544
545void HackFork::exeWait() {
546 if (al::isFirstStep(user: this)) {
547 al::startAction(actor: this, actionName: "Wait");
548 al::showModelIfHide(actor: this);
549 }
550 if (mEventFlowExecutor)
551 rs::updateEventFlow(mEventFlowExecutor);
552}
553
554void HackFork::exeHackStartWait() {
555 if (al::isFirstStep(user: this)) {
556 if (mCameraTicket) {
557 al::startCamera(user: this, ticket: mCameraTicket, unk: -1);
558 al::requestStopCameraVerticalAbsorb(user: this);
559 }
560 al::setCameraTarget(user: this, target: mMatrixCameraTarget);
561 mIsHackSwing = false;
562 mTimeSinceSwingStart = 0;
563 mIsSwingJump = false;
564 mAirVel = 0;
565 }
566
567 checkSwing();
568
569 if (rs::isHackStartDemoEnterMario(mPlayerHack))
570 al::setNerve(user: this, nerve: &NrvHackFork.HackStart);
571}
572
573void HackFork::exeDamping() {
574 if (al::isFirstStep(user: this))
575 al::showModelIfHide(actor: this);
576
577 if (al::isNearZero(value: mTouchForce) && al::isNearZero(value: mDampingForce)) {
578 if (al::isNearZero(value: mDampingStrength)) {
579 mDampingStrength = 0.0f;
580 mDampingForce = 0.0f;
581 mTouchForce = 0.0f;
582 al::validateClipping(actor: this);
583 al::setNerve(user: this, nerve: &NrvHackFork.Wait);
584 return;
585 }
586 mDampingStrength *= 0.9f;
587 return;
588 }
589 controlSpring();
590}
591
592void HackFork::exeHackStart() {
593 if (al::isFirstStep(user: this)) {
594 al::startAction(actor: this, actionName: "HackStart");
595 mHackStartShaderCtrl->start();
596 mPullDirection = {0.0f, 0.0f, 0.0f};
597 }
598 checkSwing();
599 if (al::isActionEnd(actor: this)) {
600 rs::endHackStartDemo(mPlayerHack, this);
601 al::setNerve(user: this, nerve: &NrvHackFork.HackWait);
602 }
603}
604
605void HackFork::exeHackWait() {
606 controlSpring();
607 mInputBuffer.clear();
608 sead::Vector3f frontDir;
609 al::calcFrontDir(front: &frontDir, actor: this);
610 sead::Vector3f pullDirection;
611 if (!trySwingJump() && updateInput(pullDirection: &pullDirection, frontDir) &&
612 sead::Mathf::abs(x: pullDirection.dot(t: mUpDir)) >
613 sead::Mathf::cos(t: sead::Mathf::deg2rad(deg: getJumpRange()))) {
614 mPullDirection.set(pullDirection);
615 mIsPullDown = mPullDirection.dot(t: mUpDir) < 0.0f;
616 al::setNerve(user: this, nerve: &NrvHackFork.HackBend);
617 }
618}
619
620void HackFork::exeHackBend() {
621 if (al::isFirstStep(user: this))
622 mIsReadyToShoot = false;
623
624 if (al::isLessEqualStep(user: this, step: 15) && trySwingJump())
625 return;
626 sead::Vector3f frontDir;
627 al::calcFrontDir(front: &frontDir, actor: this);
628
629 sead::Vector3f pullDirection;
630 bool isInput = updateInput(pullDirection: &pullDirection, frontDir);
631 if (!isInput || rs::isTriggerHackSwing(mPlayerHack)) {
632 if (mIsReadyToShoot) {
633 if (isInput) {
634 mAirVel = 60;
635 mIsSwingJump = true;
636 }
637 al::setNerve(user: this, nerve: &NrvHackFork.HackShoot);
638 } else {
639 al::setNerve(user: this, nerve: &NrvHackFork.HackWait);
640 }
641 }
642 sead::Vector3f oldPullDirection = mPullDirection;
643 f32 jumpDir = mIsPullDown ? -1.0f : 1.0f;
644
645 f32 angle = sead::Mathf::clamp(value: (jumpDir * mUpDir).dot(t: pullDirection), low: -1.0f, high: 1.0f);
646 if (sead::Mathf::rad2deg(rad: sead::Mathf::cos(t: angle)) < getJumpRange())
647 mPullDirection = mPullDirection * 0.5f + pullDirection * 0.5f;
648 al::normalize(vec: &mPullDirection);
649 f32 oldDampingForce = mDampingForce;
650 bendAndTwist(direction: mPullDirection, front: frontDir);
651 f32 rumbleVolume = (mDampingForce - oldDampingForce) * 0.5f +
652 (mPullDirection - oldPullDirection).length() * 3.3f;
653 if (!al::isNearZero(value: rumbleVolume)) {
654 al::holdSe(this, "PgBendLv");
655 sead::Vector3f jointPos;
656 al::calcJointPos(&jointPos, actor: this, "Stick03");
657 al::PadRumbleParam param = al::PadRumbleParam(0.0f, 1300.0f, rumbleVolume, rumbleVolume);
658 alPadRumbleFunction::startPadRumbleWithParam(actor: this, position: jointPos, name: "パルス(中)", rumbleParam: param, port: -1);
659 }
660 if (mDampingForce >= 4.5f)
661 mIsReadyToShoot = true;
662}
663
664void HackFork::exeHackShoot() {
665 controlSpring();
666 if (mDampingForce < 0.0f) {
667 shoot();
668 al::setNerve(user: this, nerve: &NrvHackFork.Damping);
669 }
670}
671