OdysseyDecomp/src/MapObj/HackFork.cpp
2026-01-09 00:43:34 +01:00

677 lines
23 KiB
C++

#include "MapObj/HackFork.h"
#include "Library/Camera/CameraTicket.h"
#include "Library/Camera/CameraUtil.h"
#include "Library/Collision/PartsConnectorUtil.h"
#include "Library/Controller/PadRumbleFunction.h"
#include "Library/Event/EventFlowUtil.h"
#include "Library/Joint/JointControllerKeeper.h"
#include "Library/Joint/JointLocalAxisRotator.h"
#include "Library/Layout/LayoutActorUtil.h"
#include "Library/LiveActor/ActorActionFunction.h"
#include "Library/LiveActor/ActorAnimFunction.h"
#include "Library/LiveActor/ActorClippingFunction.h"
#include "Library/LiveActor/ActorFlagFunction.h"
#include "Library/LiveActor/ActorInitUtil.h"
#include "Library/LiveActor/ActorModelFunction.h"
#include "Library/LiveActor/ActorPoseUtil.h"
#include "Library/LiveActor/ActorResourceFunction.h"
#include "Library/LiveActor/ActorSensorUtil.h"
#include "Library/Math/MathUtil.h"
#include "Library/Nerve/NerveSetupUtil.h"
#include "Library/Nerve/NerveUtil.h"
#include "Library/Placement/PlacementFunction.h"
#include "Library/Play/Camera/ActorMatrixCameraTarget.h"
#include "Library/Se/SeFunction.h"
#include "Library/Yaml/ByamlUtil.h"
#include "Player/CapTargetInfo.h"
#include "Player/IUsePlayerHack.h"
#include "Player/PlayerHackStartShaderCtrl.h"
#include "Scene/GuidePosInfoHolder.h"
#include "System/GameDataUtil.h"
#include "Util/Hack.h"
#include "Util/NpcAnimUtil.h"
#include "Util/NpcEventFlowUtil.h"
#include "Util/PlayerUtil.h"
#include "Util/SensorMsgFunction.h"
namespace {
NERVE_IMPL(HackFork, Wait);
NERVE_IMPL(HackFork, HackStartWait);
NERVE_IMPL(HackFork, Damping);
NERVE_IMPL(HackFork, HackStart);
NERVE_IMPL(HackFork, HackWait);
NERVE_IMPL(HackFork, HackBend);
NERVE_IMPL(HackFork, HackShoot);
// TODO: Find a memory layout that fits these globals perfectly
struct {
NERVES_MAKE_STRUCT(HackFork, Wait, HackStartWait, Damping, HackStart, HackWait, HackBend,
HackShoot);
PlayerHackStartShaderParam sPlayerHackStartShaderParam = {true, 100.0f, 10, 20};
} HackForkData;
} // namespace
HackFork::HackFork(const char* name) : al::LiveActor(name) {}
void HackFork::init(const al::ActorInitInfo& info) {
const char* modelName = nullptr;
if (alPlacementFunction::tryGetModelName(&modelName, info))
al::initActorWithArchiveName(this, info, modelName, nullptr);
else
al::initActor(this, info);
al::calcSideDir(&mSideDir, this);
al::initJointControllerKeeper(this, 10);
mJointRotationHolder.allocBuffer(5, nullptr);
const char* jointNames[5] = {"Stick01", "Stick02", "Stick03", "Stick04", "Stick05"};
for (s32 i = 0; i < 5; i++) {
mJointRotationHolder.pushBack(
al::initJointLocalAxisRotator(this, mSideDir, &mDampingForce, jointNames[i], false));
al::initJointLocalXRotator(this, &mDampingStrength, jointNames[i]);
}
al::initNerve(this, &HackForkData.NrvHackFork.Wait, 0);
al::tryGetArg(&mIsLimitterFree, info, "LimitterFree");
bool hasCamera = false;
bool isValid = al::tryGetArg(&hasCamera, info, "Camera");
if (hasCamera && isValid)
mCameraTicket = al::initObjectCamera(this, info, nullptr, nullptr);
mMatrixCameraTarget = al::createActorMatrixCameraTarget(this, &mCameraTargetMtx);
if (!al::isEqualString(modelName, "HackBoard")) {
mCameraOffset = {0.0f, 0.0f, 100.0f};
mIsHackBoard = true;
}
bool hasBallon = false;
bool isBallonValid = al::tryGetArg(&hasBallon, info, "Balloon");
if (hasBallon && isBallonValid && !rs::isSequenceTimeBalloonOrRace(this)) {
mEventFlowExecutor = rs::initEventFlow(this, info, nullptr, nullptr);
rs::startEventFlow(mEventFlowExecutor, "Init");
}
if (al::isMtpAnimExist(this)) {
rs::setNpcMaterialAnimFromPlacementInfo(this, info);
al::tryStartMclAnimIfExist(this, al::getPlayingMtpAnimName(this));
}
mMtxConnector = al::tryCreateMtxConnector(this, info);
makeActorAlive();
mCapTargetInfo = rs::createCapTargetInfo(this, nullptr);
al::ByamlIter iter = {al::getModelResourceYaml(this, "InitHackCap", nullptr)};
mJointName = al::tryGetByamlKeyStringOrNULL(iter, "JointName");
sead::Vector3f localTrans = {0.0f, 0.0f, 0.0f};
al::tryGetByamlV3f(&localTrans, iter, "LocalTrans");
sead::Vector3f localRotate = {0.0f, 0.0f, 0.0f};
al::tryGetByamlV3f(&localRotate, iter, "LocalRotate");
sead::Matrix34f rotationMatrix;
sead::Vector3f rotateRad(sead::Mathf::deg2rad(localRotate.x),
sead::Mathf::deg2rad(localRotate.y),
sead::Mathf::deg2rad(localRotate.z));
rotationMatrix.makeR(rotateRad);
sead::Matrix34f translationMatrix;
translationMatrix.makeRT({0.0f, 0.0f, 0.0f}, localTrans * al::getScaleY(this));
mStartingPoseMtx = rotationMatrix * translationMatrix;
sead::Matrix34f jointMtx = *al::getJointMtxPtr(this, mJointName);
al::normalize(&jointMtx);
mInvJointMtx.setInverse(jointMtx);
mCapTargetInfo->setPoseMatrix(&mCapPoseMtx);
mInvInitialHackDir.setInverse(mUpsideDownInitialHackDir);
initBasicPoseInfo();
mHackStartShaderCtrl =
new PlayerHackStartShaderCtrl(this, &HackForkData.sPlayerHackStartShaderParam);
}
void HackFork::attackSensor(al::HitSensor* self, al::HitSensor* other) {
if (al::isSensorName(self, "Push") && !al::sendMsgPush(other, self) && !mIsHorizontal) {
const sead::Vector3f& velocity = al::getActorVelocity(other);
if (velocity.x * velocity.x + velocity.z * velocity.z < 4.999696f)
rs::sendMsgPushToPlayer(other, self);
}
}
bool HackFork::receiveMsg(const al::SensorMsg* message, al::HitSensor* other, al::HitSensor* self) {
if (rs::isMsgEnableMapCheckPointWarp(message))
return false;
if (rs::isMsgMotorcycleDashAttack(message))
return tryTouch(5.0f, "タッチ(強)");
if (al::isMsgPlayerObjTouch(message) || al::isMsgKickStoneAttackReflect(message) ||
rs::isMsgRadishReflect(message) || rs::isMsgSeedReflect(message)) {
return tryTouch(2.0f, "タッチ(弱)");
}
if (rs::isMsgHammerBrosHammerHackAttack(message) || al::isMsgPlayerFireBallAttack(message)) {
tryTouch(2.0f, "タッチ(弱)");
rs::requestHitReactionToAttacker(message, self, other);
return true;
}
if (rs::tryReceiveMsgInitCapTargetAndSetCapTargetInfo(message, mCapTargetInfo)) {
resetCapMtx(self);
return true;
}
if (rs::isMsgPlayerDisregardTargetMarker(message))
return mEventFlowExecutor && al::isActive(mEventFlowExecutor);
if (rs::isMsgTargetMarkerPosition(message)) {
sead::Vector3f position = al::getSensorPos(this, "Stick05") + 50.0f * sead::Vector3f::ey;
rs::setMsgTargetMarkerPosition(message, position);
return true;
}
if (isNerveHackable()) {
if (rs::isMsgCapEnableLockOn(message))
return true;
if (rs::isMsgStartHack(message)) {
al::invalidateClipping(this);
mPlayerHack = rs::startHack(self, other, nullptr);
rs::startHackStartDemo(mPlayerHack, this);
mDampingStrength = 0.0f;
mDampingForce = 0.0f;
mTouchForce = 0.0f;
resetCapMtx(self);
rs::setRouteHeadGuidePosPtr(this, &mHeadGuidePos);
al::setNerve(this, &HackForkData.NrvHackFork.HackStartWait);
al::startHitReaction(this, "ひょうい開始");
return true;
}
if (rs::isMsgCapCancelLockOn(message))
return true;
}
if (isHack()) {
if (rs::isMsgHackerDamageAndCancel(message)) {
if (al::isSensorName(self, "Body"))
return rs::requestDamage(mPlayerHack);
return false;
}
if (rs::isMsgHackSyncDamageVisibility(message)) {
rs::syncDamageVisibility(this, mPlayerHack);
return true;
}
if (rs::receiveMsgRequestTransferHack(message, mPlayerHack, other))
return true;
if (rs::isMsgCancelHack(message)) {
rs::tryEndHackStartDemo(mPlayerHack, this);
if (mIsHorizontal) {
rs::endHack(&mPlayerHack);
rs::resetRouteHeadGuidePosPtr(this);
al::tryStartAction(this, "HackEnd");
al::setNerve(this, &HackForkData.NrvHackFork.Damping);
return true;
}
if (mIsHackBoard) {
sead::Vector3f upDir;
al::calcUpDir(&upDir, this);
rs::endHackDir(&mPlayerHack, upDir);
} else {
sead::Vector3f front;
if (al::isNearZero(mPullDirection))
front.set(-mPullDirection2);
else
front.set(mPullDirection);
front.y = 0.0f;
al::tryNormalizeOrDirZ(&front);
sead::Quatf quat;
al::makeQuatUpFront(&quat, sead::Vector3f::ey, front);
rs::endHackTargetQuat(&mPlayerHack, quat, front);
}
rs::resetRouteHeadGuidePosPtr(this);
al::tryStartAction(this, "HackEnd");
al::setNerve(this, &HackForkData.NrvHackFork.Damping);
return true;
}
if (rs::isMsgHackMarioDemo(message) || rs::isMsgHackMarioDead(message)) {
rs::endHack(&mPlayerHack);
rs::resetRouteHeadGuidePosPtr(this);
al::tryStartAction(this, "HackEnd");
al::setNerve(this, &HackForkData.NrvHackFork.Damping);
return true;
}
}
return false;
}
void HackFork::initBasicPoseInfo() {
al::calcUpDir(&mUpDir, this);
mHackDir.set(al::getQuat(this));
sead::Vector3f frontDir;
al::calcFrontDir(&frontDir, this);
sead::Quatf rotation;
rotation.setAxisAngle(frontDir, 180.0f);
mUpsideDownInitialHackDir = rotation * mHackDir;
sead::Vector3f frontDir2;
al::calcFrontDir(&frontDir2, this);
if (sead::Mathf::abs(frontDir2.y) > 0.5f) {
al::invalidateShadow(this);
mIsHorizontal = false;
} else {
mIsHorizontal = true;
}
}
void HackFork::initAfterPlacement() {
if (mMtxConnector) {
sead::Vector3f frontDir;
al::calcFrontDir(&frontDir, this);
sead::Vector3f pos = al::getTrans(this) + frontDir * 100.0f;
frontDir *= -200.0f;
al::attachMtxConnectorToCollision(mMtxConnector, this, pos, frontDir);
}
}
bool HackFork::tryTouch(f32 force, const char* reactionName) {
if (mTouchDelay != 0) {
mTouchDelay = 30;
return false;
}
if (isNerveHackable()) {
mTouchForce = force;
mTouchDelay = 30;
al::setNerve(this, &HackForkData.NrvHackFork.Damping);
al::startHitReaction(this, reactionName);
return true;
}
return false;
}
void HackFork::resetCapMtx(al::HitSensor* sensor) {
calcHackDir(sensor);
sead::Matrix34f jointMtx = *al::getJointMtxPtr(this, mJointName);
al::normalize(&jointMtx);
sead::Matrix34f poseMtx = jointMtx * mStartingPoseMtx;
sead::Vector3f up = poseMtx.getBase(1);
sead::Vector3f normal;
if (mIsHorizontal)
normal.set({0.0f, 1.0f, 0.0f});
else
normal = -mPullDirection2;
al::verticalizeVec(&normal, up, normal);
if (!al::tryNormalizeOrZero(&normal)) {
mNextCapPoseMtx = mStartingPoseMtx;
} else {
sead::Matrix34f invPoseMtx;
invPoseMtx.setInverse(poseMtx);
normal.rotate(invPoseMtx);
sead::Quatf quatRotation;
al::makeQuatRotationRate(&quatRotation, sead::Vector3f::ez, normal, 1.0f);
sead::Matrix34f quatRotationMtx;
quatRotationMtx.fromQuat(quatRotation);
sead::Matrix34f invJointMtx;
invJointMtx.setInverse(jointMtx);
mNextCapPoseMtx = (invJointMtx * poseMtx) * quatRotationMtx;
}
updateCapMtx();
}
bool HackFork::isNerveHackable() const {
return al::isNerve(this, &HackForkData.NrvHackFork.Wait) ||
al::isNerve(this, &HackForkData.NrvHackFork.Damping);
}
bool HackFork::isHack() const {
return al::isNerve(this, &HackForkData.NrvHackFork.HackStartWait) ||
al::isNerve(this, &HackForkData.NrvHackFork.HackStart) ||
al::isNerve(this, &HackForkData.NrvHackFork.HackWait) ||
al::isNerve(this, &HackForkData.NrvHackFork.HackBend) ||
al::isNerve(this, &HackForkData.NrvHackFork.HackShoot);
}
void HackFork::controlSpring() {
f32 dampedForce = mDampingForce * 0.92f;
mTouchForce += dampedForce * -0.5f;
mDampingForce = dampedForce + mTouchForce;
}
void HackFork::checkSwing() {
if (rs::isTriggerHackSwing(mPlayerHack)) {
mIsHackSwing = true;
mTimeSinceSwingStart = 0;
return;
}
mTimeSinceSwingStart++;
}
bool HackFork::trySwingJump() {
if (rs::isTriggerHackSwing(mPlayerHack)) {
mIsHackSwing = true;
mTimeSinceSwingStart = 0;
} else {
bool isHackSwing = mIsHackSwing;
mTimeSinceSwingStart++;
if (!isHackSwing || 10 < mTimeSinceSwingStart)
return false;
}
if (mIsHorizontal) {
mPullDirection.set({0.0f, -1.0f, 0.0f});
} else {
sead::Vector3f pulldirection = mPullDirection2;
pulldirection.rotate(mHackDir * mInvInitialHackDir);
pulldirection.y = 0;
al::tryNormalizeOrDirZ(&pulldirection);
mPullDirection.set(pulldirection);
}
mIsPullDown = mPullDirection.dot(mUpDir) < 0.0f;
mDampingForce = 22.5f;
sead::Vector3f frontDir;
al::calcFrontDir(&frontDir, this);
bendAndTwist(mPullDirection, frontDir);
mIsSwingJump = true;
al::setNerve(this, &HackForkData.NrvHackFork.HackShoot);
return true;
}
bool HackFork::updateInput(sead::Vector3f* pullDirection, sead::Vector3f frontDir) {
sead::Vector3f lookdir;
al::calcCameraLookDir(&lookdir, this, 0);
if (frontDir.dot(sead::Vector3f::ey) < sead::Mathf::cos(sead::Mathf::deg2rad(45.0f)) &&
frontDir.dot(lookdir) > 0.0f)
frontDir = -frontDir;
sead::Vector3f moveDir = {0.0f, 0.0f, 0.0f};
bool isMovePossible = rs::calcHackerMoveDir(&moveDir, mPlayerHack, frontDir);
mInputBuffer.forcePushBack(moveDir);
*pullDirection = {0.0f, 0.0f, 0.0f};
for (s32 i = 0; i < mInputBuffer.size(); i++)
*pullDirection += mInputBuffer[i];
if (!al::tryNormalizeOrZero(pullDirection))
al::calcUpDir(pullDirection, this);
return isMovePossible;
}
f32 HackFork::getJumpRange() const {
return mIsLimitterFree ? 180.0f : 45.0f;
}
void HackFork::bendAndTwist(const sead::Vector3f& direction, const sead::Vector3f& front) {
mDampingForce = sead::Mathf::clampMax(mDampingForce + 1.0f, 22.5f);
mTouchForce = 0.0f;
mSideDir.setCross(front, direction);
if (!al::tryNormalizeOrZero(&mSideDir))
al::calcSideDir(&mSideDir, this);
for (s32 i = 0; i < mJointRotationHolder.size(); i++)
mJointRotationHolder[i]->setVector28(mSideDir);
}
// NON_MATCHING: Wrong loading order and registers https://decomp.me/scratch/8ReuA
void HackFork::shoot() {
sead::Vector3f frontDir;
al::calcFrontDir(&frontDir, this);
sead::Quatf quat;
al::makeQuatUpFront(&quat, frontDir, -mPullDirection);
sead::Quatf rotationQuat;
rotationQuat.makeVectorRotation(frontDir, sead::Vector3f::ey);
quat = rotationQuat * quat;
quat.normalize();
sead::Vector3f jointPos;
al::calcJointPos(&jointPos, this, "Stick05");
sead::Vector3f launchDir;
launchDir = mPullDirection;
if (launchDir.y < -sead::Mathf::cos(sead::Mathf::deg2rad(110.0f)))
launchDir += -sead::Vector3f::ey;
al::normalize(&launchDir);
f32 scale = mPullDirection.dot(-sead::Vector3f::ey);
f32 cos = sead::Mathf::cos(sead::Mathf::deg2rad(45.0f));
if (scale > cos) {
scale = sead::Mathf::clamp((scale - cos) / (1.0f - cos), 0.0f, 1.0f);
scale = (1.0f - sead::Mathf::square(1.0f - scale)) * -8.0f;
} else {
scale = -0.0f;
}
sead::Vector3f frontDir2;
al::calcFrontDir(&frontDir2, this);
frontDir2 *= scale;
f32 launchSpeed = mIsSwingJump ? 61.27f : 55.7f;
sead::Vector3f direction = frontDir2 - launchSpeed * launchDir;
rs::endHackAirVelocity(&mPlayerHack, jointPos, quat, direction, mAirVel);
rs::resetRouteHeadGuidePosPtr(this);
al::startHitReaction(this, "ジャンプ");
al::tryStartAction(this, "HackEnd");
}
void HackFork::control() {
mHackStartShaderCtrl->update();
if (mMtxConnector) {
al::connectPoseQT(this, mMtxConnector);
initBasicPoseInfo();
}
if (!isHack()) {
if (mCameraTicket && al::isActiveCamera(mCameraTicket))
al::endCamera(this, mCameraTicket, -1, false);
if (al::isActiveCameraTarget(mMatrixCameraTarget))
al::resetCameraTarget(this, mMatrixCameraTarget);
}
if (mTouchDelay != 0)
mTouchDelay--;
if (isHack()) {
if (mIsHorizontal) {
sead::Vector3f frontDir;
al::calcFrontDir(&frontDir, this);
frontDir.y = 0.0f;
al::tryNormalizeOrDirZ(&frontDir);
frontDir.negate();
sead::Vector3f sideDir;
sideDir.setCross(sead::Vector3f::ey, frontDir);
mCameraTargetMtx.setBase(0, sideDir);
mCameraTargetMtx.setBase(1, sead::Vector3f::ey);
mCameraTargetMtx.setBase(2, frontDir);
} else {
sead::Vector3f upDir;
al::calcUpDir(&upDir, this);
upDir.y = 0.0;
al::tryNormalizeOrDirZ(&upDir);
sead::Vector3f sideDir;
sideDir.setCross(sead::Vector3f::ey, upDir);
if ((al::getCameraPos(this, 0) - al::getTrans(this)).dot(upDir) >= 0.0f) {
upDir = -upDir;
sideDir = -sideDir;
}
mCameraTargetMtx.setBase(0, sideDir);
mCameraTargetMtx.setBase(1, sead::Vector3f::ey);
mCameraTargetMtx.setBase(2, upDir);
}
sead::Vector3f cameraOffset;
cameraOffset.setRotated(al::getQuat(this), mCameraOffset);
if (mIsHorizontal) {
sead::Vector3f frontDir;
al::calcFrontDir(&frontDir, this);
cameraOffset += frontDir * 100.0f;
}
mCameraTargetMtx.setBase(3, cameraOffset + al::getTrans(this));
al::calcJointPos(&mHeadGuidePos, this, "Stick05");
mHeadGuidePos.y = sead::Mathf::max(mHeadGuidePos.y, al::getTrans(this).y) + 100.0f;
}
}
void HackFork::calcAnim() {
al::LiveActor::calcAnim();
if (isHack())
updateCapMtx();
}
void HackFork::updateCapMtx() {
sead::Matrix34f mtx = *al::getJointMtxPtr(this, mJointName);
al::normalize(&mtx);
mCapPoseMtx = mtx * mNextCapPoseMtx;
}
void HackFork::calcHackDir(al::HitSensor* sensor) {
if (mIsHorizontal)
mPullDirection2.set({0.0f, -1.0f, 0.0f});
else
mPullDirection2 = rs::getPlayerPos(this) - al::getSensorPos(sensor);
sead::Vector3f frontDir;
al::calcFrontDir(&frontDir, this);
al::verticalizeVec(&mPullDirection2, frontDir, mPullDirection2);
if (!al::tryNormalizeOrZero(&mPullDirection2))
al::calcUpDir(&mPullDirection2, this);
mInvInitialHackDir.setInverse(mHackDir);
}
void HackFork::exeWait() {
if (al::isFirstStep(this)) {
al::startAction(this, "Wait");
al::showModelIfHide(this);
}
if (mEventFlowExecutor)
rs::updateEventFlow(mEventFlowExecutor);
}
void HackFork::exeHackStartWait() {
if (al::isFirstStep(this)) {
if (mCameraTicket) {
al::startCamera(this, mCameraTicket, -1);
al::requestStopCameraVerticalAbsorb(this);
}
al::setCameraTarget(this, mMatrixCameraTarget);
mIsHackSwing = false;
mTimeSinceSwingStart = 0;
mIsSwingJump = false;
mAirVel = 0;
}
checkSwing();
if (rs::isHackStartDemoEnterMario(mPlayerHack))
al::setNerve(this, &HackForkData.NrvHackFork.HackStart);
}
void HackFork::exeDamping() {
if (al::isFirstStep(this))
al::showModelIfHide(this);
if (al::isNearZero(mTouchForce) && al::isNearZero(mDampingForce)) {
if (al::isNearZero(mDampingStrength)) {
mDampingStrength = 0.0f;
mDampingForce = 0.0f;
mTouchForce = 0.0f;
al::validateClipping(this);
al::setNerve(this, &HackForkData.NrvHackFork.Wait);
return;
}
mDampingStrength *= 0.9f;
return;
}
controlSpring();
}
void HackFork::exeHackStart() {
if (al::isFirstStep(this)) {
al::startAction(this, "HackStart");
mHackStartShaderCtrl->start();
mPullDirection = {0.0f, 0.0f, 0.0f};
}
checkSwing();
if (al::isActionEnd(this)) {
rs::endHackStartDemo(mPlayerHack, this);
al::setNerve(this, &HackForkData.NrvHackFork.HackWait);
}
}
void HackFork::exeHackWait() {
controlSpring();
mInputBuffer.clear();
sead::Vector3f frontDir;
al::calcFrontDir(&frontDir, this);
sead::Vector3f pullDirection;
if (!trySwingJump() && updateInput(&pullDirection, frontDir) &&
sead::Mathf::abs(pullDirection.dot(mUpDir)) >
sead::Mathf::cos(sead::Mathf::deg2rad(getJumpRange()))) {
mPullDirection.set(pullDirection);
mIsPullDown = mPullDirection.dot(mUpDir) < 0.0f;
al::setNerve(this, &HackForkData.NrvHackFork.HackBend);
}
}
void HackFork::exeHackBend() {
if (al::isFirstStep(this))
mIsReadyToShoot = false;
if (al::isLessEqualStep(this, 15) && trySwingJump())
return;
sead::Vector3f frontDir;
al::calcFrontDir(&frontDir, this);
sead::Vector3f pullDirection;
bool isInput = updateInput(&pullDirection, frontDir);
if (!isInput || rs::isTriggerHackSwing(mPlayerHack)) {
if (mIsReadyToShoot) {
if (isInput) {
mAirVel = 60;
mIsSwingJump = true;
}
al::setNerve(this, &HackForkData.NrvHackFork.HackShoot);
} else {
al::setNerve(this, &HackForkData.NrvHackFork.HackWait);
}
}
sead::Vector3f oldPullDirection = mPullDirection;
f32 jumpDir = mIsPullDown ? -1.0f : 1.0f;
f32 cos = sead::Mathf::clamp((jumpDir * mUpDir).dot(pullDirection), -1.0f, 1.0f);
if (sead::Mathf::rad2deg(sead::Mathf::acos(cos)) < getJumpRange())
mPullDirection = mPullDirection * 0.5f + pullDirection * 0.5f;
al::normalize(&mPullDirection);
f32 oldDampingForce = mDampingForce;
bendAndTwist(mPullDirection, frontDir);
f32 rumbleVolume = (mDampingForce - oldDampingForce) * 0.5f +
(mPullDirection - oldPullDirection).length() * 3.3f;
if (!al::isNearZero(rumbleVolume)) {
al::holdSe(this, "PgBendLv");
sead::Vector3f jointPos;
al::calcJointPos(&jointPos, this, "Stick03");
al::PadRumbleParam param = al::PadRumbleParam(0.0f, 1300.0f, rumbleVolume, rumbleVolume);
alPadRumbleFunction::startPadRumbleWithParam(this, jointPos, "パルス(中)", param, -1);
}
if (mDampingForce >= 4.5f)
mIsReadyToShoot = true;
}
void HackFork::exeHackShoot() {
controlSpring();
if (mDampingForce < 0.0f) {
shoot();
al::setNerve(this, &HackForkData.NrvHackFork.Damping);
}
}