diff --git a/data/file_list.yml b/data/file_list.yml index 02b1515138..19165cd36a 100644 --- a/data/file_list.yml +++ b/data/file_list.yml @@ -77229,266 +77229,266 @@ MapObj/Motorcycle.o: - offset: 0x2c6dec size: 328 label: _ZN10MotorcycleC2EPKc - status: NotDecompiled + status: Matching - offset: 0x2c6f34 size: 324 label: _ZN10MotorcycleC1EPKc - status: NotDecompiled + status: Matching - offset: 0x2c7078 size: 1636 label: _ZN10Motorcycle4initERKN2al13ActorInitInfoE - status: NotDecompiled + status: NonMatchingMajor - offset: 0x2c76dc size: 24 label: _ZN10Motorcycle18initAfterPlacementEv - status: NotDecompiled + status: Matching - offset: 0x2c76f4 size: 380 label: _ZN10Motorcycle12attackSensorEPN2al9HitSensorES2_ - status: NotDecompiled + status: Matching - offset: 0x2c7870 size: 184 - label: '' - status: NotDecompiled + label: _Z12isAnyRideRunP10Motorcycle + status: Matching - offset: 0x2c7928 size: 2492 label: _ZN10Motorcycle10receiveMsgEPKN2al9SensorMsgEPNS0_9HitSensorES5_ - status: NotDecompiled + status: Matching - offset: 0x2c82e4 size: 240 - label: '' - status: NotDecompiled + label: _Z7endRideP10MotorcycleP24MotorcyclePlayerAnimatorPN2al16CameraTargetBaseEPNS3_20TransCameraSubTargetE + status: Matching - offset: 0x2c83d4 size: 400 - label: '' - status: NotDecompiled + label: _Z14syncPuppetPoseP16IUsePlayerPuppetP10MotorcycleRKN4sead4QuatIfEERKNS3_7Vector3IfEE + status: Matching - offset: 0x2c8564 size: 544 - label: '' - status: NotDecompiled + label: _Z11startGetOffP10MotorcyclePP16IUsePlayerPuppetPiP24MotorcyclePlayerAnimatorPN2al16CameraTargetBaseEPNS7_20TransCameraSubTargetE + status: Matching - offset: 0x2c8784 size: 3588 label: _ZN10Motorcycle8movementEv - status: NotDecompiled + status: NonMatchingMajor - offset: 0x2c9588 size: 44 label: _ZN10Motorcycle4killEv - status: NotDecompiled + status: Matching - offset: 0x2c95b4 size: 412 label: _ZN10Motorcycle14updateColliderEv - status: NotDecompiled + status: Matching - offset: 0x2c9750 size: 72 label: _ZN10Motorcycle8calcAnimEv - status: NotDecompiled + status: Matching - offset: 0x2c9798 size: 440 label: _ZN10Motorcycle7exeWaitEv - status: NotDecompiled + status: Matching - offset: 0x2c9950 size: 1232 - label: '' - status: NotDecompiled + label: _Z17updateOrientationP10Motorcyclef + status: Matching - offset: 0x2c9e20 size: 384 label: _ZN10Motorcycle8exeCreepEv - status: NotDecompiled + status: Matching - offset: 0x2c9fa0 size: 320 label: _ZN10Motorcycle7exeFallEv - status: NotDecompiled + status: Matching - offset: 0x2ca0e0 size: 208 - label: '' - status: NotDecompiled + label: _Z24tryHandleGroundCollisionP10Motorcycle + status: Matching - offset: 0x2ca1b0 size: 396 label: _ZN10Motorcycle7exeJumpEv - status: NotDecompiled + status: Matching - offset: 0x2ca33c size: 40 label: _ZN10Motorcycle7endJumpEv - status: NotDecompiled + status: Matching - offset: 0x2ca364 size: 164 label: _ZN10Motorcycle11exeReactionEv - status: NotDecompiled + status: Matching - offset: 0x2ca408 size: 256 label: _ZN10Motorcycle8exeResetEv - status: NotDecompiled + status: Matching - offset: 0x2ca508 size: 136 label: _ZN10Motorcycle12exeRideStartEv - status: NotDecompiled + status: Matching - offset: 0x2ca590 size: 120 label: _ZN10Motorcycle14exeRideStartOnEv - status: NotDecompiled + status: Matching - offset: 0x2ca608 size: 444 label: _ZN10Motorcycle11exeRideWaitEv - status: NotDecompiled + status: Matching - offset: 0x2ca7c4 size: 180 - label: '' - status: NotDecompiled + label: _Z7TryJumpP10MotorcycleP16IUsePlayerPuppet + status: Matching - offset: 0x2ca878 size: 1056 - label: '' - status: NotDecompiled + label: _Z10tryParkingP10MotorcycleP16IUsePlayerPuppetP13ParkingParamsP14MotorcyclePose + status: Matching - offset: 0x2cac98 size: 1032 label: _ZN10Motorcycle15exeRideWaitJumpEv - status: NotDecompiled + status: Matching - offset: 0x2cb0a0 size: 756 - label: '' - status: NotDecompiled + label: _Z14updateSeRumbleP13SeRumbleStateP10MotorcycleP16IUsePlayerPuppetfb + status: Matching - offset: 0x2cb394 size: 172 - label: '' - status: NotDecompiled + label: _Z22handleCeilingCollisionP10Motorcycle + status: Matching - offset: 0x2cb440 size: 868 - label: '' - status: NotDecompiled + label: _Z24updateSteerAngleAndAccelP10MotorcycleP16IUsePlayerPuppetP17AccelerationStatePf + status: Matching - offset: 0x2cb7a4 size: 572 - label: '' - status: NotDecompiled + label: _Z14calcInputStickPN4sead7Vector2IfEEP10MotorcycleP16IUsePlayerPuppet + status: Matching - offset: 0x2cb9e0 size: 40 label: _ZN10Motorcycle15endRideWaitJumpEv - status: NotDecompiled + status: Matching - offset: 0x2cba08 size: 428 label: _ZN10Motorcycle15exeRideWaitLandEv - status: NotDecompiled + status: Matching - offset: 0x2cbbb4 size: 880 label: _ZN10Motorcycle15exeRideRunStartEv - status: NotDecompiled + status: Matching - offset: 0x2cbf24 size: 392 - label: '' - status: NotDecompiled + label: _Z31tryHandleHighSpeedWallCollisionP10MotorcycleRKN4sead7Vector3IfEE + status: Matching - offset: 0x2cc0ac size: 1352 - label: '' - status: NotDecompiled + label: _Z21updateRunningVelocityP10Motorcyclef + status: Matching - offset: 0x2cc5f4 size: 228 - label: '' - status: NotDecompiled + label: _Z23TryJumpWithCameraAbsorbP10MotorcycleP16IUsePlayerPuppet + status: Matching - offset: 0x2cc6d8 size: 404 - label: '' - status: NotDecompiled + label: _Z16TryWheelieOrFallP10Motorcycle + status: Matching - offset: 0x2cc86c size: 1000 label: _ZN10Motorcycle10exeRideRunEv - status: NotDecompiled + status: Matching - offset: 0x2ccc54 size: 880 label: _ZN10Motorcycle17exeRideRunCollideEv - status: NotDecompiled + status: Matching - offset: 0x2ccfc4 size: 572 label: _ZN10Motorcycle14exeRideRunFallEv - status: NotDecompiled + status: Matching - offset: 0x2cd200 size: 360 - label: '' - status: NotDecompiled + label: _Z15applyAirPhysicsP10MotorcycleP16IUsePlayerPuppetf + status: Matching - offset: 0x2cd368 size: 452 - label: '' - status: NotDecompiled + label: _Z8tryBoundP10Motorcycle + status: Matching - offset: 0x2cd52c size: 512 - label: '' - status: NotDecompiled + label: _Z20updateAirOrientationP10Motorcycle + status: Matching - offset: 0x2cd72c size: 508 - label: '' - status: NotDecompiled + label: _Z7tryLandP10Motorcycle + status: Matching - offset: 0x2cd928 size: 604 label: _ZN10Motorcycle17exeRideRunWheelieEv - status: NotDecompiled + status: Matching - offset: 0x2cdb84 size: 40 label: _ZN10Motorcycle17endRideRunWheelieEv - status: NotDecompiled + status: Matching - offset: 0x2cdbac size: 724 label: _ZN10Motorcycle14exeRideRunLandEv - status: NotDecompiled + status: Matching - offset: 0x2cde80 size: 1208 label: _ZN10Motorcycle14exeRideRunJumpEv - status: NotDecompiled + status: Matching - offset: 0x2ce338 size: 40 label: _ZN10Motorcycle14endRideRunJumpEv - status: NotDecompiled + status: Matching - offset: 0x2ce360 size: 244 label: _ZN10Motorcycle20exeRideRunBoundStartEv - status: NotDecompiled + status: Matching - offset: 0x2ce454 size: 644 - label: '' - status: NotDecompiled + label: _Z18updateBoundPhysicsP10MotorcycleP16IUsePlayerPuppetPfP13SeRumbleStateb + status: Matching - offset: 0x2ce6d8 size: 224 label: _ZN10Motorcycle15exeRideRunBoundEv - status: NotDecompiled + status: Matching - offset: 0x2ce7b8 size: 328 label: _ZN10Motorcycle15exeRideRunClashEv - status: NotDecompiled + status: Matching - offset: 0x2ce900 size: 252 label: _ZN10Motorcycle18exeRideParkingSnapEv - status: NotDecompiled + status: Matching - offset: 0x2ce9fc size: 64 label: _ZN10Motorcycle19exeRideParkingStartEv - status: NotDecompiled + status: Matching - offset: 0x2cea3c size: 184 label: _ZN10Motorcycle14exeRideParkingEv - status: NotDecompiled + status: Matching - offset: 0x2ceaf4 size: 292 label: _ZN10Motorcycle19exeRideParkingAfterEv - status: NotDecompiled + status: Matching - offset: 0x2cec18 size: 8 label: _ZNK10Motorcycle17getPlayerColliderEv - status: NotDecompiled + status: Matching lazy: true - offset: 0x2cec20 size: 8 label: _ZThn264_NK10Motorcycle17getPlayerColliderEv - status: NotDecompiled + status: Matching lazy: true - offset: 0x2cec28 size: 8 label: _ZNK12_GLOBAL__N_117MotorcycleNrvWait7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2cec30 size: 24 - label: '' - status: NotDecompiled + label: _ZNK30MotorcycleColliderCameraTarget9calcTransEPN4sead7Vector3IfEE + status: Matching - offset: 0x2cec48 size: 8 label: _ZNK12_GLOBAL__N_117MotorcycleNrvJump7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2cec50 size: 40 @@ -77498,7 +77498,7 @@ MapObj/Motorcycle.o: - offset: 0x2cec78 size: 8 label: _ZNK12_GLOBAL__N_125MotorcycleNrvRideWaitJump7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2cec80 size: 40 @@ -77508,12 +77508,12 @@ MapObj/Motorcycle.o: - offset: 0x2ceca8 size: 8 label: _ZNK12_GLOBAL__N_124MotorcycleNrvRideRunFall7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2cecb0 size: 8 label: _ZNK12_GLOBAL__N_127MotorcycleNrvRideRunWheelie7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2cecb8 size: 40 @@ -77523,7 +77523,7 @@ MapObj/Motorcycle.o: - offset: 0x2cece0 size: 8 label: _ZNK12_GLOBAL__N_124MotorcycleNrvRideRunJump7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2cece8 size: 40 @@ -77533,115 +77533,115 @@ MapObj/Motorcycle.o: - offset: 0x2ced10 size: 8 label: _ZNK12_GLOBAL__N_130MotorcycleNrvRideRunBoundStart7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2ced18 size: 8 label: _ZNK12_GLOBAL__N_127MotorcycleNrvRideRunCollide7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2ced20 size: 8 label: _ZNK12_GLOBAL__N_124MotorcycleNrvRideRunLand7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2ced28 size: 8 label: _ZNK12_GLOBAL__N_117MotorcycleNrvFall7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2ced30 size: 8 label: _ZNK12_GLOBAL__N_128MotorcycleNrvResetNoReaction7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2ced38 size: 8 label: _ZNK12_GLOBAL__N_126MotorcycleNrvRideStartLeft7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2ced40 size: 8 label: _ZNK12_GLOBAL__N_127MotorcycleNrvRideStartRight7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2ced48 size: 8 label: _ZNK12_GLOBAL__N_124MotorcycleNrvRideStartOn7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2ced50 size: 8 label: _ZNK12_GLOBAL__N_121MotorcycleNrvRideWait7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2ced58 size: 8 label: _ZNK12_GLOBAL__N_118MotorcycleNrvCreep7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2ced60 size: 8 label: _ZNK12_GLOBAL__N_121MotorcycleNrvReaction7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2ced68 size: 8 label: _ZNK12_GLOBAL__N_118MotorcycleNrvReset7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2ced70 size: 8 label: _ZNK12_GLOBAL__N_128MotorcycleNrvRideParkingSnap7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2ced78 size: 8 label: _ZNK12_GLOBAL__N_125MotorcycleNrvRideWaitLand7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2ced80 size: 8 label: _ZNK12_GLOBAL__N_125MotorcycleNrvRideRunStart7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2ced88 size: 408 - label: '' - status: NotDecompiled + label: _Z22tryHandleWallCollisionP10Motorcycle + status: Matching - offset: 0x2cef20 size: 8 label: _ZNK12_GLOBAL__N_125MotorcycleNrvRideRunClash7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2cef28 size: 8 label: _ZNK12_GLOBAL__N_120MotorcycleNrvRideRun7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2cef30 size: 328 - label: '' - status: NotDecompiled + label: _Z14updateVelocityP10MotorcycleRKN4sead7Vector3IfEES5_ + status: Matching - offset: 0x2cf078 size: 8 label: _ZNK12_GLOBAL__N_125MotorcycleNrvRideRunBound7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2cf080 size: 68 label: _ZNK12_GLOBAL__N_129MotorcycleNrvRideParkingStart7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2cf0c4 size: 8 label: _ZNK12_GLOBAL__N_124MotorcycleNrvRideParking7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x2cf0cc size: 8 label: _ZNK12_GLOBAL__N_129MotorcycleNrvRideParkingAfter7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true MapObj/MotorcycleParkingLot.o: '.text': diff --git a/lib/al/Library/Camera/TransCameraSubTarget.h b/lib/al/Library/Camera/TransCameraSubTarget.h new file mode 100644 index 0000000000..ec01860dcc --- /dev/null +++ b/lib/al/Library/Camera/TransCameraSubTarget.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +#include "Library/Camera/CameraSubTargetBase.h" + +namespace al { + +class TransCameraSubTarget : public CameraSubTargetBase { +public: + const char* getTargetName() const override { return mName; } + + void calcTrans(sead::Vector3f* trans) const override { trans->set(*mTrans); } + +private: + const char* mName; + sead::Vector3f* mTrans; +}; + +static_assert(sizeof(TransCameraSubTarget) == 0x28); + +} // namespace al diff --git a/lib/al/Project/Camera/CameraSubTargetTurnParam.h b/lib/al/Project/Camera/CameraSubTargetTurnParam.h index b431b6f054..778574f860 100644 --- a/lib/al/Project/Camera/CameraSubTargetTurnParam.h +++ b/lib/al/Project/Camera/CameraSubTargetTurnParam.h @@ -11,6 +11,14 @@ class CameraSubTargetTurnParam { void init(const ActorInitInfo& info); + f32 getTurnRate1() const { return mTurnSpeedRate1; } + + void setTurnRate1(f32 value) { mTurnSpeedRate1 = value; } + + f32 getTurnRate2() const { return mTurnSpeedRate2; } + + void setTurnRate2(f32 value) { mTurnSpeedRate2 = value; } + private: f32 mTurnSpeedRate1 = 0.3f; f32 mTurnSpeedRate2 = 0.1f; diff --git a/src/Camera/PlayerColliderCameraTarget.h b/src/Camera/PlayerColliderCameraTarget.h new file mode 100644 index 0000000000..0b794f50a5 --- /dev/null +++ b/src/Camera/PlayerColliderCameraTarget.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Library/Camera/ActorCameraTarget.h" +#include "Library/LiveActor/LiveActor.h" + +namespace al { +class LiveActor; +} // namespace al + +class IUsePlayerCollision; + +class PlayerColliderCameraTarget : public al::ActorCameraTarget { +public: + PlayerColliderCameraTarget(const al::LiveActor*, const IUsePlayerCollision*); + + bool isCollideGround() const override; + +private: + char filler_28[8]; +}; + +static_assert(sizeof(PlayerColliderCameraTarget) == 0x30); diff --git a/src/MapObj/Motorcycle.cpp b/src/MapObj/Motorcycle.cpp new file mode 100644 index 0000000000..ffea22e047 --- /dev/null +++ b/src/MapObj/Motorcycle.cpp @@ -0,0 +1,2217 @@ +#include "MapObj/Motorcycle.h" + +#include "Library/Area/AreaObj.h" +#include "Library/Area/AreaObjUtil.h" +#include "Library/Camera/CameraUtil.h" +#include "Library/Camera/TransCameraSubTarget.h" +#include "Library/Collision/Collider.h" +#include "Library/Collision/CollisionParts.h" +#include "Library/Collision/CollisionPartsKeeperUtil.h" +#include "Library/Collision/PartsConnector.h" +#include "Library/Collision/PartsConnectorUtil.h" +#include "Library/Controller/PadRumbleFunction.h" +#include "Library/Effect/EffectSystemInfo.h" +#include "Library/Item/ItemUtil.h" +#include "Library/Joint/JointControllerKeeper.h" +#include "Library/LiveActor/ActorActionFunction.h" +#include "Library/LiveActor/ActorAnimFunction.h" +#include "Library/LiveActor/ActorAreaFunction.h" +#include "Library/LiveActor/ActorClippingFunction.h" +#include "Library/LiveActor/ActorFlagFunction.h" +#include "Library/LiveActor/ActorInitUtil.h" +#include "Library/LiveActor/ActorModelFunction.h" +#include "Library/LiveActor/ActorMovementFunction.h" +#include "Library/LiveActor/ActorPoseUtil.h" +#include "Library/LiveActor/ActorSensorUtil.h" +#include "Library/Math/MathUtil.h" +#include "Library/Nature/NatureUtil.h" +#include "Library/Nerve/NerveSetupUtil.h" +#include "Library/Nerve/NerveUtil.h" +#include "Library/Placement/PlacementFunction.h" +#include "Library/Se/SeFunction.h" +#include "Library/Shadow/ActorShadowUtil.h" +#include "Library/Stage/StageSwitchUtil.h" +#include "Project/Camera/CameraSubTargetTurnParam.h" + +#include "MapObj/MotorcyclePlayerAnimator.h" +#include "Player/CollidedShapeResult.h" +#include "Player/CollisionShapeInfo.h" +#include "Player/CollisionShapeKeeper.h" +#include "Player/PlayerCollider.h" +#include "System/GameDataFunction.h" +#include "System/GameDataUtil.h" +#include "Util/CameraUtil.h" +#include "Util/InputInterruptTutorialUtil.h" +#include "Util/NpcAnimUtil.h" +#include "Util/ObjUtil.h" +#include "Util/PlayerCollisionUtil.h" +#include "Util/PlayerPuppetFunction.h" +#include "Util/PlayerUtil.h" +#include "Util/SensorMsgFunction.h" + +namespace { +NERVE_IMPL(Motorcycle, Wait) +NERVE_IMPL(Motorcycle, Creep) +NERVE_IMPL(Motorcycle, Fall) +NERVE_IMPL(Motorcycle, Jump) +NERVE_IMPL(Motorcycle, Reaction) +NERVE_IMPL(Motorcycle, Reset) +NERVE_IMPL_(Motorcycle, ResetNoReaction, Reset) +NERVE_IMPL_(Motorcycle, RideStartLeft, RideStart) +NERVE_IMPL_(Motorcycle, RideStartRight, RideStart) +NERVE_IMPL(Motorcycle, RideStartOn) +NERVE_IMPL(Motorcycle, RideWait) +NERVE_IMPL(Motorcycle, RideWaitJump) +NERVE_IMPL(Motorcycle, RideWaitLand) +NERVE_IMPL(Motorcycle, RideRunStart) +NERVE_IMPL(Motorcycle, RideRun) +NERVE_IMPL(Motorcycle, RideRunCollide) +NERVE_IMPL(Motorcycle, RideRunFall) +NERVE_IMPL(Motorcycle, RideRunWheelie) +NERVE_IMPL(Motorcycle, RideRunLand) +NERVE_IMPL(Motorcycle, RideRunJump) +NERVE_IMPL(Motorcycle, RideRunBoundStart) +NERVE_IMPL(Motorcycle, RideRunBound) +NERVE_IMPL(Motorcycle, RideRunClash) +NERVE_IMPL(Motorcycle, RideParkingSnap) +NERVE_IMPL(Motorcycle, RideParkingStart) +NERVE_IMPL(Motorcycle, RideParking) +NERVE_IMPL(Motorcycle, RideParkingAfter) + +NERVES_MAKE_STRUCT(Motorcycle, Wait, Jump, RideWaitJump, RideRunFall, RideRunWheelie, RideRunJump, + RideRunBoundStart, RideRunCollide, RideRunLand, Fall, ResetNoReaction, + RideStartLeft, RideStartRight, RideStartOn, RideWait, Creep, Reaction, Reset, + RideWaitLand, RideRunStart, RideRun, RideRunBound) +NERVES_MAKE_NOSTRUCT(Motorcycle, RideRunClash, RideParking, RideParkingAfter, RideParkingStart, + RideParkingSnap) +} // namespace + +const sead::Vector3f forceField5 = {0.0f, 140.0f, -85.0f}; +const sead::Vector3f sFrontArrowBase = {0.0f, -70.0f, 0.0f}; +const sead::Vector3f sBackSphere = {0.0f, 80.0f, -120.0f}; +const sead::Vector3f sCenterFaceSphere = {0.0f, 80.0f, -60.0f}; +const sead::Vector3f sFrontCenterArrow = {0.0f, 50.0f, 10.0f}; +const sead::Vector3f sFrontLeftArrow = {-25.0f, 50.0f, -150.0f}; +const sead::Vector3f sFrontRightArrow = {25.0f, 50.0f, -150.0f}; +const sead::Vector3f sActorOffset = {0.0f, 50.0f, -80.0f}; +const sead::Vector3f sGroundNormal = {0.0f, -1.0f, 0.0f}; +const sead::Vector3f sItemSpawnOffset = {0.0f, 150.0f, -90.0f}; + +static inline void reset(Motorcycle* actor) { + al::setVelocityZero(actor); + al::invalidateHitSensors(actor); + al::hideModelIfShow(actor); + al::setNerve(actor, &NrvMotorcycle.Reset); +} + +bool tryHandleGroundCollision(Motorcycle* actor) { + if (rs::isOnGround(actor, actor)) { + if (al::isInWater(actor)) { + reset(actor); + return true; + } + + if (rs::isCollisionCodeJump(actor)) { + al::HitSensor* sensor = rs::tryGetCollidedGroundSensor(actor); + if (sensor) + rs::sendMsgPlayerTouchFloorJumpCode(sensor, al::getHitSensor(actor, "PlayerBody")); + al::setNerve(actor, &NrvMotorcycle.Jump); + return true; + } + + al::setNerve(actor, &NrvMotorcycle.Creep); + return true; + } + + return false; +} + +void handleCeilingCollision(Motorcycle* actor) { + if (rs::isCollidedCeiling(actor) && al::getVelocity(actor).dot(sGroundNormal) < 0.0f) { + sead::Vector3f* velocity = al::getVelocityPtr(actor); + al::verticalizeVec(velocity, sGroundNormal, *velocity); + + al::HitSensor* sensor = rs::tryGetCollidedCeilingSensor(actor); + if (sensor) + al::sendMsgPlayerUpperPunch(sensor, al::getHitSensor(actor, "PlayerAttack")); + } +} + +bool TryJump(Motorcycle* actor, IUsePlayerPuppet* playerPuppet) { + if (!rs::isOnGround(actor, actor)) + return false; + + if (rs::isCollisionCodeJump(actor)) { + al::HitSensor* sensor = rs::tryGetCollidedGroundSensor(actor); + if (sensor) + rs::sendMsgPlayerTouchFloorJumpCode(sensor, al::getHitSensor(actor, "PlayerBody")); + al::setNerve(actor, &NrvMotorcycle.RideRunJump); + return true; + } + + if (rs::isPuppetTrigJumpButton(playerPuppet)) { + al::setNerve(actor, &NrvMotorcycle.RideWaitJump); + return true; + } + + return false; +} + +bool TryJumpWithCameraAbsorb(Motorcycle* actor, IUsePlayerPuppet* playerPuppet) { + if (!rs::isOnGround(actor, actor)) + return false; + + if (rs::isCollisionCodeJump(actor)) { + al::HitSensor* sensor = rs::tryGetCollidedGroundSensor(actor); + if (sensor) + rs::sendMsgPlayerTouchFloorJumpCode(sensor, al::getHitSensor(actor, "PlayerBody")); + + al::setNerve(actor, &NrvMotorcycle.RideRunJump); + return true; + } + + if (rs::isPuppetTrigJumpButton(playerPuppet)) { + if (sead::Mathf::rad2deg(sead::Mathf::asin(al::getFront(actor).y)) > 17.5f) + al::requestStopCameraVerticalAbsorb(actor); + al::setNerve(actor, &NrvMotorcycle.RideRunJump); + return true; + } + + return false; +} + +void endRide(Motorcycle* actor, MotorcyclePlayerAnimator* playerAnimator, + al::CameraTargetBase* cameraTarget, al::TransCameraSubTarget* cameraSubTarget) { + playerAnimator->endBind(); + alPadRumbleFunction::stopPadRumbleDirectValue(actor, -1); + rs::tryCloseBindTutorial(actor); + al::invalidateHitSensors(actor); + al::validateHitSensorBindableAll(actor); + al::validateHitSensorRideAll(actor); + al::hideSilhouetteModel(actor); + al::validateOcclusionQuery(actor); + al::showModelIfHide(actor); + al::resetCameraTarget(actor, cameraTarget); + al::offCameraRideObj(actor); + al::invalidateDepthShadowMap(actor); + al::onDepthShadowModel(actor); + if (al::isActiveCameraSubTarget(cameraSubTarget)) + al::resetCameraSubTarget(actor, cameraSubTarget); + + if (!tryHandleGroundCollision(actor)) + al::setNerve(actor, &NrvMotorcycle.Fall); +} + +void syncPuppetPose(IUsePlayerPuppet* playerPuppet, Motorcycle* actor, const sead::Quatf& startQuat, + const sead::Vector3f& startTrans) { + sead::Quatf jointQuat = sead::Quatf::unit; + sead::Vector3f trans = {0.0f, 0.0f, 0.0f}; + sead::Matrix34f* jointMtx = al::getJointMtxPtr(actor, "JointRoot"); + + jointMtx->toQuat(jointQuat); + jointQuat.normalize(); + jointMtx->getTranslation(trans); + + if (al::isNerve(actor, &NrvMotorcycle.RideStartLeft) || + al::isNerve(actor, &NrvMotorcycle.RideStartRight)) { + f32 rate = al::calcNerveRate(actor, rs::getPuppetAnimFrameMax(playerPuppet)); + al::slerpQuat(&jointQuat, startQuat, jointQuat, rate); + al::lerpVec(&trans, startTrans, trans, rate); + } + + rs::setPuppetTrans(playerPuppet, trans); + rs::setPuppetQuat(playerPuppet, jointQuat); +} + +// NOTE: This might be a sead function +inline f32 getAngleBetweenTwoVectors(const sead::Vector3f& a, const sead::Vector3f& b) { + f32 x = a.dot(b); + f32 y = a.cross(b).length(); + return sead::Mathf::atan2(y, x); +} + +static inline bool isTouchingGround(MotorcycleParams* params) { + return al::isNormalize(params->groundNormalAvg) && params->isTouchingGround; +} + +void updateOrientation(Motorcycle* actor, f32 steerAngle) { + sead::Vector3f normal; + if (rs::isCollidedGround(actor)) + normal = -rs::getCollidedGroundNormal(actor); + else + normal = -actor->getParams()->groundNormal; + + if (!al::isParallelDirection(al::getGravity(actor), normal)) { + f32 startAngle = 1.0f; + if (rs::isCollidedGround(actor)) { + startAngle = al::lerpValue( + 1.0f, 0.3f, + al::normalize(sead::Mathf::rad2deg(sead::Mathf::sin(al::getFront(actor).y)), 11.0f, + 15.0f)); + } + steerAngle = al::lerpValue(startAngle, 1.0, al::normalize(steerAngle, 10.0f, 40.0f)); + sead::Vector3f normalGravity = al::getGravity(actor).cross(normal); + al::normalize(&normalGravity); + + sead::Quatf steerQuat; + steerQuat.setAxisRadian(normalGravity, + 0.05f * getAngleBetweenTwoVectors(al::getGravity(actor), normal) * + steerAngle); + + al::getGravityPtr(actor)->setRotated(steerQuat, al::getGravity(actor)); + al::normalize(al::getGravityPtr(actor)); + + sead::Vector3f* frontPtr = al::getFrontPtr(actor); + al::verticalizeVec(frontPtr, al::getGravity(actor), *frontPtr); + al::normalize(al::getFrontPtr(actor)); + } + MotorcycleParams* params = actor->getParams(); + + if (!isTouchingGround(params) && params->isTouchingWall) { + sead::Vector3f front = al::getFront(actor); + al::verticalizeVec(&front, sGroundNormal, front); + if (al::tryNormalizeOrZero(&front)) { + if (!(al::getFront(actor).y > 0.0f)) { + al::rotateVectorDegree(&front, front, front.cross({0.0f, -1.0f, 0.0f}), 45.0f); + al::normalize(&front); + } + + al::turnVecToVecDegree(&front, al::getFront(actor), front, 2.0f); + sead::Vector3f* frontPtr = al::getFrontPtr(actor); + al::turnVecToVecRate(frontPtr, *frontPtr, front, 0.15f); + al::normalize(al::getFrontPtr(actor)); + + sead::Vector3f* gravityPtr = al::getGravityPtr(actor); + al::verticalizeVec(gravityPtr, al::getFront(actor), *gravityPtr); + al::normalize(al::getGravityPtr(actor)); + } + } + + if (isTouchingGround(params) && params->isTouchingWall) + rs::tryFollowRotateFrontAxisUpIfCollidedGround(actor, actor); +} + +void updateVelocity(Motorcycle* actor, const sead::Vector3f& horizontalAxis, + const sead::Vector3f& verticalAxis) { + sead::Vector3f velH = {0.0f, 0.0f, 0.0f}; + sead::Vector3f velV = {0.0f, 0.0f, 0.0f}; + al::separateVectorHV(&velH, &velV, horizontalAxis, al::getVelocity(actor)); + + sead::Vector3f front = al::getFront(actor); + al::verticalizeVec(&front, verticalAxis, front); + al::tryNormalizeOrZero(&front); + + al::setVelocity(actor, (velH.length() * front) + (velV.length() * verticalAxis)); +} + +void calcInputStick(sead::Vector2f* outStick, Motorcycle* actor, IUsePlayerPuppet* playerPuppet) { + if (!actor->isStickWorldPose()) { + outStick->set(rs::getPuppetMoveStick(playerPuppet)); + return; + } + + outStick->set(0.0f, 0.0f); + sead::Vector3f vecView = {0.0f, 0.0f, 0.0f}; + al::calcVecViewInput(&vecView, rs::getPuppetMoveStick(playerPuppet), -al::getGravity(actor), + al::getViewMtxPtr(actor, 0)); + + sead::Vector3f sideDir = {0.0f, 0.0f, 0.0f}; + al::calcSideDir(&sideDir, actor); + al::verticalizeVec(&sideDir, sead::Vector3f::ey, sideDir); + al::normalize(&sideDir); + + sead::Vector3f front = al::getFront(actor); + al::verticalizeVec(&front, sead::Vector3f::ey, front); + al::normalize(&front); + + sead::Vector3f parallelVec = {0.0f, 0.0f, 0.0f}; + al::parallelizeVec(¶llelVec, sideDir, vecView); + sead::Vector3f diff = vecView - parallelVec; + + if (!al::isNearZero(parallelVec)) { + f32 signX = (parallelVec.dot(sideDir) > 0.0f) ? -1.0f : 1.0f; + outStick->x = signX * parallelVec.length(); + } + + if (!al::isNearZero(diff)) { + f32 signY = (diff.dot(front) > 0.0f) ? 1.0f : -1.0f; + outStick->y = signY * diff.length(); + } +} + +void applyAirPhysics(Motorcycle* actor, IUsePlayerPuppet* playerPuppet, f32 gravityAccel) { + sead::Vector2f stick = {0.0f, 0.0f}; + calcInputStick(&stick, actor, playerPuppet); + + al::rotateVectorDegreeY(al::getFrontPtr(actor), stick.x * -0.5f); + al::normalize(al::getFrontPtr(actor)); + + sead::Vector3f front = al::getFront(actor); + front.y = 0; + al::tryNormalizeOrZero(&front); + const sead::Vector3f& vel = al::getVelocity(actor); + sead::Vector3f velocity = + front * sead::Mathf::sqrt(sead::Mathf::pow(vel.x, 2) + sead::Mathf::pow(vel.z, 2)); + + f32 friction = 0.95f; + if (rs::isPuppetHoldActionButton(playerPuppet)) + friction = 1.0f; + + al::setVelocity(actor, velocity * friction + (al::getVelocity(actor).y - gravityAccel) * + sead::Vector3f::ey * 0.95f); +} + +bool tryHandleWallCollision(Motorcycle* actor) { + if (rs::isCollisionCodePoleClimbWall(actor)) + return false; + + s32 hitNum = rs::getWallHitInfoNum(actor); + s32 i = 0; + bool isCollision = false; + for (; i < hitNum; i++) { + isCollision |= rs::sendMsgMotorcycleDashCollide(rs::getWallHitInfoSensor(actor, i), + al::getHitSensor(actor, "PlayerBody")); + } + if (isCollision) { + al::LiveActor* host = al::getSensorHost(rs::tryGetCollidedWallSensor(actor)); + if (al::isDead(host)) + return false; + al::CollisionParts* parts = host->getCollisionParts(); + if (parts && !parts->isValidCollision()) + return false; + } + + sead::Vector3f front = {al::getFront(actor).x, 0.0f, al::getFront(actor).z}; + if (!al::tryNormalizeOrZero(&front)) + return false; + + if (sead::Mathf::cos(sead::Mathf::deg2rad(15.0f)) < + front.dot(-rs::getCollidedWallNormal(actor))) { + al::setVelocityZeroX(actor); + al::setVelocityZeroZ(actor); + al::setNerve(actor, &RideRunClash); + return true; + } + + return false; +} + +bool tryHandleHighSpeedWallCollision(Motorcycle* actor, const sead::Vector3f& transDelta) { + if (!rs::isCollidedWallVelocity(actor, actor)) + return false; + + s32 hitNum = rs::getWallHitInfoNum(actor); + bool isCollision = false; + for (s32 i = 0; i < hitNum; i++) { + isCollision |= rs::sendMsgMotorcycleDashCollide(rs::getWallHitInfoSensor(actor, i), + al::getHitSensor(actor, "PlayerBody")); + } + if (isCollision) + return false; + + if (al::calcSpeedMax(1.8f, 0.95f) * 0.9f < al::calcSpeedH(actor) && + tryHandleWallCollision(actor)) + return true; + + sead::Vector3f delta = transDelta; + al::verticalizeVec(&delta, al::getGravity(actor), delta); + if (delta.length() >= 10.0f) { + al::setNerve(actor, &NrvMotorcycle.RideRunCollide); + return true; + } + return false; +} + +void updateSteerAngleAndAccel(Motorcycle* actor, IUsePlayerPuppet* playerPuppet, + AccelerationState* accelerationState, f32* steerAngle) { + f32 accelRate = + al::lerpValue(1.0f, 0.75f, al::normalize(actor->getParams()->framesInAir, 0, 15)); + f32 speed = al::normalize(al::calcSpeedH(actor), 0.0f, al::calcSpeedMax(1.8f, 0.95f)); + f32 lerpSpeed; + if (actor->isStickWorldPose()) + lerpSpeed = al::lerpValue(3.4f, 2.6f, speed); + else + lerpSpeed = al::lerpValue(3.6f, 2.8f, speed); + + if (!al::isNearZero(rs::getPuppetMoveStick(playerPuppet).x)) { + accelRate *= lerpSpeed * al::normalizeAbs(*steerAngle, 0.0f, 55.0f); + if (rs::isCollidedWall(actor)) { + sead::Vector3f front = al::getFront(actor); + al::rotateVectorDegree(&front, front, al::getGravity(actor), accelRate); + sead::Vector3f diff = front - al::getFront(actor); + if (al::tryNormalizeOrZero(&diff) && actor->getParams()->isInFront(diff) && + actor->getParams()->isInBack(diff)) + accelRate = 0.0; + } + } else { + al::AreaObj* frontSnapArea = al::tryFindAreaObj(actor, "MotorcycleFrontSnapArea"); + if (frontSnapArea) { + sead::Vector3f front = {0.0f, 0.0f, 0.0f}; + al::getAreaObjDirFront(&front, frontSnapArea); + accelRate = + al::calcAngleOnPlaneDegree(front, al::getFront(actor), -al::getGravity(actor)) * + sead::Mathf::deg2rad(2.0f); + } else { + accelRate = 0.0f; + } + } + + sead::Vector3f* frontPtr = al::getFrontPtr(actor); + al::rotateVectorDegree(frontPtr, *frontPtr, al::getGravity(actor), accelRate); + al::normalize(al::getFrontPtr(actor)); + accelerationState->isAccelerating = rs::isPuppetHoldActionButton(playerPuppet); + if (accelerationState->isAccelerating) + accelRate = + al::lerpValue(1.0f, 0.3f, al::normalize(sead::Mathf::abs(accelRate), 0.0f, 0.7f)); + else + accelRate = -0.75f; + + accelerationState->accelRate += accelRate; + accelerationState->accelRate = sead::Mathf::clamp(accelerationState->accelRate, 0.0f, 5.0f); +} + +bool TryWheelieOrFall(Motorcycle* actor) { + if (rs::isCollidedGround(actor)) + return false; + if (actor->getParams()->framesInAir > 1) { + if (sead::Mathf::rad2deg(sead::Mathf::sin(al::getFront(actor).y)) > 17.5f) { + sead::Vector3f trans = {0.0f, 0.0f, 0.0f}; + al::calcTransLocalOffset(&trans, actor, sFrontCenterArrow); + + if (!alCollisionUtil::getFirstPolyOnArrow(actor, nullptr, nullptr, trans, + al::getGravity(actor) * 125.0f, nullptr, + nullptr)) { + al::limitVelocityDirSign(actor, al::getGravity(actor), 0.0f); + al::setNerve(actor, &NrvMotorcycle.RideRunWheelie); + return true; + } + } + } + + MotorcycleParams* params = actor->getParams(); + if (params->framesInAir >= 6) { + updateVelocity(actor, -params->groundNormal, sGroundNormal); + al::limitVelocityDirSign(actor, sGroundNormal, 3.0f); + al::setNerve(actor, &NrvMotorcycle.RideRunFall); + return true; + } + return false; +} + +static inline f32 getGroundDistance(Motorcycle* actor, MotorcycleParams* params) { + sead::Vector3f distance = al::getTrans(actor) - params->lastGroundPos; + al::parallelizeVec(&distance, al::getGravity(actor), distance); + return distance.dot(al::getGravity(actor)); +} + +bool tryBound(Motorcycle* actor) { + if (!rs::isOnGround(actor, actor)) + return false; + + MotorcycleParams* params = actor->getParams(); + if (!rs::isCollidedGround(actor) || params->isOnGround) + return false; + + if (getGroundDistance(actor, params) > 700.0f) { + al::startHitReaction(actor, "バウンド"); + f32 distance = al::normalize(getGroundDistance(actor, actor->getParams()), 700.0f, 5000.0f); + al::setVelocityY(actor, al::lerpValue(25.0f, 40.0f, distance)); + al::setNerve(actor, &NrvMotorcycle.RideRunBoundStart); + return true; + } + return false; +} + +bool tryLand(Motorcycle* actor) { + if (rs::isOnGround(actor, actor)) { + updateVelocity(actor, sGroundNormal, -rs::getCollidedGroundNormal(actor)); + al::limitVelocityDirSign(actor, -rs::getCollidedGroundNormal(actor), 5.0f); + + if (al::isGreaterEqualStep(actor, 15)) { + MotorcycleParams* params = actor->getParams(); + if (rs::isCollidedGround(actor) && !params->isOnGround) { + if (getGroundDistance(actor, params) > 150.0f) { + updateVelocity(actor, sGroundNormal, -rs::getCollidedGroundNormal(actor)); + al::limitVelocityDirSign(actor, -rs::getCollidedGroundNormal(actor), 5.0f); + al::setNerve(actor, &NrvMotorcycle.RideRunLand); + return true; + } + } + } + al::setNerve(actor, &NrvMotorcycle.RideRun); + return true; + } + + return false; +} + +void startGetOff(Motorcycle* actor, IUsePlayerPuppet** playerPuppet, s32* mGetOffDelay, + MotorcyclePlayerAnimator* playerAnimator, al::CameraTargetBase* cameraTarget, + al::TransCameraSubTarget* cameraSubTarget) { + const sead::Vector3f groundNormal = {0.0f, -1.0f, 0.0f}; + + al::limitVelocityDirSign(actor, -groundNormal, 0.0f); + + rs::showPuppetShadow(*playerPuppet); + rs::validatePuppetReceivePush(*playerPuppet); + + bool actorIsNull = actor == nullptr; // Hack early load for getViewMtxPtr + MotorcycleParams* params = actor->getParams(); + if (params->frontContactPoints.size() > 0 && params->backContactPoints.size() > 0) { + sead::Vector3f offset = {0.0f, 0.0f, 0.0f}; + al::calcTransLocalOffset(&offset, actor, sActorOffset); + rs::resetPuppetPosition(*playerPuppet, offset); + } + + sead::Vector2f stick = {rs::getPuppetMoveStick(*playerPuppet).x, + -rs::getPuppetMoveStick(*playerPuppet).y}; + + sead::Vector3f front = {0.0f, 0.0f, 0.0f}; + sead::Vector3f jumpDir; + if (al::isNearZero(stick, 0.2f) || + !al::calcDirViewInput(&front, stick, sGroundNormal, al::getViewMtxPtr(actor, 0))) { + al::verticalizeVec(&front, -groundNormal, al::getFront(actor)); + al::normalize(&front); + jumpDir.set(-front); + } else { + jumpDir.set(front); + } + + rs::setPuppetFront(*playerPuppet, front); + rs::setPuppetUp(*playerPuppet, -groundNormal); + + rs::endBindJumpAndPuppetNull(playerPuppet, sead::Vector3f::ey * 20.0f + jumpDir * 9.0f, 3); + + *mGetOffDelay = 5; + endRide(actor, playerAnimator, cameraTarget, cameraSubTarget); +} + +bool tryParking(Motorcycle* actor, IUsePlayerPuppet* playerPuppet, ParkingParams* params, + MotorcyclePose* pose) { + if (rs::isPuppetHoldActionButton(playerPuppet) || al::calcSpeedH(actor) > 8.0f || + !rs::isCollidedGround(actor)) + return false; + + al::HitSensor* groundSensor = rs::tryGetCollidedGroundSensor(actor); + if (!groundSensor) + return false; + + sead::Vector3f backDir = {0.0f, 0.0f, 0.0f}; + al::calcBackDir(&backDir, al::getSensorHost(groundSensor)); + if (!(sead::Mathf::abs(al::getFront(actor).dot(backDir)) < + sead::Mathf::cos(sead::Mathf::deg2rad(25.0f)))) { + al::parallelizeVec(&backDir, backDir, al::getFront(actor)); + al::normalize(&backDir); + + sead::Quatf quat = sead::Quatf::unit; + sead::Vector3f up = {0.0f, 0.0f, 0.0f}; + sead::Vector3f side = {0.0f, 0.0f, 0.0f}; + al::calcUpDir(&up, al::getSensorHost(groundSensor)); + al::makeQuatFrontUp(&quat, backDir, up); + al::calcQuatSide(&side, quat); + + sead::Vector3f targetPos = {0.0f, 30.0f, 75.0f}; + targetPos.rotate(quat); + targetPos += al::getActorTrans(groundSensor); + + sead::Vector3f distanceH = {0.0f, 0.0f, 0.0f}; + al::parallelizeVec(&distanceH, side, al::getTrans(actor) - targetPos); + + if (distanceH.length() > 100.0f) + return false; + + sead::Vector3f distanceV = {0.0f, 0.0f, 0.0f}; + al::parallelizeVec(&distanceV, -backDir, al::getTrans(actor) - targetPos); + + if (distanceV.length() > 100.0f) + return false; + + if (rs::sendMsgMotorcycleCollideParkingLot(groundSensor, + al::getHitSensor(actor, "PlayerBody"))) { + params->actor = al::getSensorHost(groundSensor); + params->pose = *pose; + + al::calcQuat(¶ms->quatA, actor); + params->quatB.set(quat); + params->mCameraSubTargetPos.set(al::getTrans(actor)); + params->vectorB.set(targetPos); + + al::offCollide(actor); + al::setVelocityZero(actor); + al::setNerve(actor, &RideParkingSnap); + return true; + } + } + + return false; +} + +void updateSeRumble(SeRumbleState* valueB, Motorcycle* actor, IUsePlayerPuppet* playerPuppet, + f32 steerAngle, bool isJumping) { + f32 throttleScale; + if (rs::isPuppetHoldActionButton(playerPuppet)) { + throttleScale = 1.0f; + valueB->rumble = sead::Mathi::clampMax(valueB->rumble + 1, 240); + } else { + throttleScale = 0.6f; + valueB->rumble = sead::Mathi::clampMin(valueB->rumble - 15, 0); + } + + valueB->volume = al::calcSpeedH(actor); + f32 rumbleFade = sead::Mathf::clamp(valueB->rumble / -240.0f + 1.0f, 0.25f, 1.0f); + f32 baseRumbleAmp = throttleScale * rumbleFade * 0.35f; + + f32 vibrationWave = 0.0f; + if (valueB->rumble < 40) { + f32 phase = (valueB->rumble / 40.0f - 1.0f) * sead::Mathf::pi(); + vibrationWave = (phase * sinf(phase)) / 1.8f; + } + + f32 rumbleScale = baseRumbleAmp * (vibrationWave * 1.45f + 1.0f); + f32 lowRumble = rumbleScale; + f32 speedOffset = sead::Mathf::clampMin(valueB->volume - 5.0f, 0.0f); + f32 baseFrequency = (vibrationWave * 0.08f + 1.0f) * ((speedOffset / 70.0f + 1.0f) * 168.0f); + f32 highRumble = baseFrequency; + f32 frequency = 160.0f; + if (isJumping) { + frequency = 272.0f; + highRumble = baseFrequency * 1.7f; + lowRumble = rumbleScale * 0.6f; + } + + al::holdSeWithParam(actor, "CurveLv", valueB->volume * steerAngle, "回転角(Degree)"); + + f32 leftWeight = steerAngle > 0.0f ? 0.0f : 0.01f; + leftWeight *= steerAngle; + leftWeight = 0.7f - leftWeight; + f32 rightWeight = (steerAngle > 0.0f ? 0.01f : 0.0f) * steerAngle; + + alPadRumbleFunction::startPadRumbleDirectValue(actor, frequency, highRumble, lowRumble, + lowRumble, leftWeight * 0.7f, + (rightWeight + 0.7f) * 0.7f, -1); + + f32 throttleVolume = rs::isPuppetHoldActionButton(playerPuppet) ? 9.0f : 0.0f; + if (rs::isPuppetHoldActionButton(playerPuppet)) + al::holdSeWithParam(actor, "MoveStartLv", throttleVolume + valueB->volume, ""); + else + al::holdSeWithParam(actor, "MoveEndLv", throttleVolume + valueB->volume, ""); + al::holdSeWithParam(actor, "MoveLv", throttleVolume + valueB->volume, ""); + al::holdSeWithParam(actor, "IdleLv", throttleVolume + valueB->volume, ""); +} + +static inline f32 getJointDistance(Motorcycle* actor, const char* jointNameA, + const char* jointNameB) { + sead::Vector3f frontTirePos = {0.0f, 0.0f, 0.0f}; + sead::Vector3f backTirePos = {0.0f, 0.0f, 0.0f}; + al::calcJointPos(&frontTirePos, actor, jointNameA); + al::calcJointPos(&backTirePos, actor, jointNameB); + return (frontTirePos - backTirePos).length(); +} + +void updateAirOrientation(Motorcycle* actor) { + if (al::getVelocity(actor).y > 0.0f) + return; + + sead::Vector3f hitPos = {0.0f, 0.0f, 0.0f}; + sead::Vector3f checkPos = + (al::getTrans(actor) - + al::getFront(actor) * getJointDistance(actor, "FrontTire", "BackTire")) - + al::getGravity(actor) * 150.0f; + if (!alCollisionUtil::getFirstPolyOnArrow(actor, &hitPos, nullptr, checkPos, + al::getGravity(actor) * 150.0f, nullptr, nullptr)) { + return; + } + + sead::Vector3f front = al::getTrans(actor) - hitPos; + al::normalize(&front); + al::setFront(actor, front); + + sead::Vector3f* gravity = al::getGravityPtr(actor); + al::verticalizeVec(gravity, front, *gravity); + al::normalize(al::getGravityPtr(actor)); +} + +static inline void updateSteerAngle(Motorcycle* actor, f32* steerAngle, + IUsePlayerPuppet* playerPuppet, f32 stickScale, f32 tiltScale, + f32 turnRate) { + sead::Vector2f stick = {0.0f, 0.0f}; + calcInputStick(&stick, actor, playerPuppet); + f32 stickH = stick.x * stickScale; + f32 leftAngle = al::diffNearAngleDegree(0.0f, rs::getPuppetPoseRotZDegreeLeft(playerPuppet)); + f32 rigthAngle = al::diffNearAngleDegree(0.0f, rs::getPuppetPoseRotZDegreeRight(playerPuppet)); + + f32 puppetAngle = al::normalizeAbs( + sead::Mathf::abs(leftAngle) < sead::Mathf::abs(rigthAngle) ? rigthAngle : leftAngle, 45.0f, + 135.0f); + + f32 target = stickH + puppetAngle * tiltScale; + *steerAngle = al::lerpValue(*steerAngle, target, turnRate); +} + +void updateBoundPhysics(Motorcycle* actor, IUsePlayerPuppet* playerPuppet, f32* steerAngle, + SeRumbleState* seRumbleState, bool wasInWater) { + updateSteerAngle(actor, steerAngle, playerPuppet, 12.5f, -7.5f, 0.1f); + sead::Vector2f stick = {0.0f, 0.0f}; + calcInputStick(&stick, actor, playerPuppet); + al::rotateVectorDegreeY(al::getFrontPtr(actor), stick.x * -0.5f); + al::normalize(al::getFrontPtr(actor)); + + sead::Vector3f velocity = {al::getVelocity(actor).x, 0.0f, al::getVelocity(actor).z}; + f32 velocityY = al::getVelocity(actor).y; + if (!rs::isPuppetHoldActionButton(playerPuppet)) + velocity *= 0.95f; + + sead::Vector3f front = {al::getFront(actor).x, 0.0f, al::getFront(actor).z}; + al::tryNormalizeOrZero(&front); + al::setVelocity(actor, velocity.length() * front + sead::Vector3f{0.0f, velocityY, 0.0f}); + al::addVelocityY(actor, -2.0f); + updateSeRumble(seRumbleState, actor, playerPuppet, *steerAngle, true); + + if (tryBound(actor)) + return; + + if (rs::isCollidedWallVelocity(actor, actor) && tryHandleWallCollision(actor)) + return; + + updateAirOrientation(actor); + if (tryLand(actor)) + return; + + if (al::isInWater(actor) && !wasInWater) + al::startHitReaction(actor, "着水"); +} + +// TODO: might be moved into `sead` +inline f32 normalize2(sead::Vector3f* v, f32 scalar) { + const f32 len = v->length(); + if (len > 0) { + const f32 inv_len = scalar / len; + v->x *= inv_len; + v->y *= inv_len; + v->z *= inv_len; + } + + return len; +} + +void updateRunningVelocity(Motorcycle* actor, f32 value) { + const sead::Vector3f groundNormal = {0.0f, -1.0f, 0.0f}; + rs::reboundVelocityPart(actor, actor, 0.0f, -0.5f, 0.0f, 0.0f); + sead::Vector3f velH = {0.0f, 0.0f, 0.0f}; + sead::Vector3f velV = {0.0f, 0.0f, 0.0f}; + al::separateVectorHV(&velH, &velV, actor->getParams()->groundNormal, al::getVelocity(actor)); + + sead::Vector3f normal = {0.0f, 0.0f, 0.0f}; + if (rs::isCollidedGround(actor)) { + if (!isTouchingGround(actor->getParams())) + normal.set(rs::getCollidedGroundNormal(actor)); + else + normal.set(actor->getParams()->groundNormalAvg); + } else { + normal.set(-groundNormal); + al::verticalizeVec(&normal, al::getFront(actor), normal); + if (!al::tryNormalizeOrZero(&normal)) + normal.set(-groundNormal); + } + + velV = -normal * (velV.length() + 3.0f); + al::limitLength(&velV, velV, rs::isCollidedGround(actor) ? 5.0f : 35.0f); + + if (!al::isNearZero(velH)) { + f32 length = velH.length(); + al::parallelizeVec(&velH, al::getFront(actor), velH); + if (!al::isNearZero(velH)) + normalize2(&velH, length); + sead::Vector3f front = al::getFront(actor); + al::verticalizeVec(&front, normal, front); + al::normalize(&front); + if (!al::isParallelDirection(al::getFront(actor), velH)) { + sead::Vector3f faceDir = velH; + al::normalize(&faceDir); + al::turnVecToVecRate(&front, faceDir, al::getFront(actor), 0.15f); + al::normalize(&front); + } + velH = (front * value + velH) * 0.95f; + } else { + velH = (al::getFront(actor) * value) * 0.95f; + } + + if (rs::isCollidedGround(actor)) { + const al::CollisionParts* parts = rs::getCollidedGroundCollisionParts(actor); + + sead::Vector3f transformedVelH = velH; + transformedVelH.rotate(parts->getPrevBaseInvMtx()); + transformedVelH.rotate(parts->getBaseMtx()); + + al::verticalizeVec(&transformedVelH, al::getGravity(actor), transformedVelH); + normalize2(&transformedVelH, velH.length()); + velH.set(transformedVelH); + } + al::setVelocity(actor, velH + velV); +} + +Motorcycle::Motorcycle(const char* name) : al::LiveActor(name) {} + +// NON_MATCHING: Uses ldr instead of add https://decomp.me/scratch/TZRB7 +void Motorcycle::init(const al::ActorInitInfo& info) { + al::tryGetArg(&mIsOnLight, info, "IsOnLight"); + al::initActorSuffix(this, info, mIsOnLight ? "Night" : nullptr); + al::initNerve(this, &NrvMotorcycle.Wait, 0); + al::hideSilhouetteModel(this); + al::invalidateDepthShadowMap(this); + al::invalidateHitSensors(this); + al::validateHitSensorBindableAll(this); + al::validateHitSensorRideAll(this); + al::startAction(this, "Wait"); + if (mIsOnLight) + al::startMclAnim(this, "FrontOnTailOff"); + else + al::startMclAnim(this, "FrontOffTailOff"); + al::offCollide(this); + al::calcQuat(&mBaseQuat, this); + + mBaseTrans.set(al::getTrans(this)); + mPlayerAnimator = new MotorcyclePlayerAnimator(); + mAccelerationState = new AccelerationState; + mParams = new MotorcycleParams; + mSeRumbleState = new SeRumbleState; + mParkingParams = new ParkingParams; + + al::initJointControllerKeeper(this, 6); + al::initJointLocalZRotator(this, &mPose.steerAngle, "AllRoot"); + al::initJointLocalXRotator(this, &mPose.handleAngle, "Handle"); + al::initJointLocalYRotator(this, &mPose.handleAngle, "FrontWheel"); + al::initJointLocalYRotator(this, &mPose.handleAngle, "Cowl"); + al::initJointLocalXRotator(this, &mPose.leanAngle, "AllRoot"); + al::initJointLocalXRotator(this, &mPose.jumpAngle, "JointRoot"); + + CollisionShapeKeeper* collision = new CollisionShapeKeeper(7, 0x40, 0x10); + collision->createShapeArrow("FrontCenter", sFrontCenterArrow, sFrontArrowBase, 20.0f, 0); + collision->createShapeArrow("FrontLeft", sFrontLeftArrow, sFrontArrowBase, 20.0f, 1); + collision->createShapeArrow("FrontRight", sFrontRightArrow, sFrontArrowBase, 20.0f, 2); + + collision->createShapeSphereSupportGround("FrontFace", 60.0f, {0.0f, 80.0f, -35.0f}, + sead::Vector3f::ey, 20.0f); + collision->createShapeSphereSupportGround("BackFace", 60.0f, sBackSphere, sead::Vector3f::ey, + 20.0f); + collision->createShapeSphereSupportGround("CenterFace", 60.0f, sCenterFaceSphere, + sead::Vector3f::ey, 20.0f); + + collision->createShapeSphereIgnoreGround("Head", 40.0f, {0.0f, 155.0f, -80.0f}); + collision->updateShape(); + + PlayerCollider* collider = new PlayerCollider(getCollisionDirector(), getBaseMtx(), + al::getTransPtr(this), &sGroundNormal, false); + collider->setCollisionShapeKeeper(collision); + collider->onInvalidate(); + mPlayerCollider = collider; + rs::createAndSetColliderFilterSpecialPurpose(this, "MoveLimit"); + + mColliderCameraTarget = new MotorcycleColliderCameraTarget(this); + mTransCameraSubTarget = al::createTransCameraSubTarget("バイク進行方向", &mCameraSubTargetPos); + + mCameraSubTargetTurnParam = new al::CameraSubTargetTurnParam(); + al::initCameraSubTargetTurnParam(mTransCameraSubTarget, mCameraSubTargetTurnParam); + + mCollisionPartsConnector = al::tryCreateCollisionPartsConnector(this, info, mBaseQuat); + mBindKeepDemoInfo = rs::initBindKeepDemoInfo(); + al::tryGetArg(&mIsStickWorldPose, info, "IsStickWorldPose"); + + mCoursePointSize = al::calcLinkChildNum(info, "CoursePoint"); + if (mCoursePointSize > 0) { + mCoursePoints = new sead::Vector3f[mCoursePointSize]; + for (s32 i = 0; i < mCoursePointSize; i++) + al::getChildLinkT(&mCoursePoints[i], info, "CoursePoint", i); + } + + al::setEffectNamedMtxPtr(this, "WaterSurface", &mWaterSurfaceMtx); + al::setSeUserSyncParamPtr(this, &mSeRumbleState->volume, "スピード"); + makeActorAlive(); +} + +void Motorcycle::initAfterPlacement() { + if (mCollisionPartsConnector) + al::attachCollisionPartsConnectorToGround(mCollisionPartsConnector, this); +} + +bool isAnyRideRun(Motorcycle* actor) { + return al::isNerve(actor, &NrvMotorcycle.RideRunStart) || + al::isNerve(actor, &NrvMotorcycle.RideRun) || + al::isNerve(actor, &NrvMotorcycle.RideRunCollide) || + al::isNerve(actor, &NrvMotorcycle.RideRunFall) || + al::isNerve(actor, &NrvMotorcycle.RideRunWheelie) || + al::isNerve(actor, &NrvMotorcycle.RideRunLand) || + al::isNerve(actor, &NrvMotorcycle.RideRunJump) || + al::isNerve(actor, &NrvMotorcycle.RideRunBoundStart) || + al::isNerve(actor, &NrvMotorcycle.RideRunBound); +} + +void Motorcycle::attackSensor(al::HitSensor* self, al::HitSensor* other) { + if (mPlayerPuppet && al::isSensorPlayerAttack(self)) { + if (al::isNerve(this, &NrvMotorcycle.Jump) || + al::isNerve(this, &NrvMotorcycle.RideWaitJump) || + al::isNerve(this, &NrvMotorcycle.RideRunFall) || + al::isNerve(this, &NrvMotorcycle.RideRunWheelie) || + al::isNerve(this, &NrvMotorcycle.RideRunJump) || + al::isNerve(this, &NrvMotorcycle.RideRunBoundStart) || + al::isNerve(this, &NrvMotorcycle.RideRunBound)) { + if (al::sendMsgPlayerAttackTrample(other, self, nullptr)) + return; + } + + if (isAnyRideRun(this)) { + if (rs::sendMsgMotorcycleDashAttack(other, self) || + rs::sendMsgMotorcycleAttack(other, self) || rs::sendMsgBirdFlyAway(other, self) || + mPlayerPuppet) + return; + } + } + + if (al::isSensorBindableAll(self) || al::isSensorPlayerAll(self) || + rs::sendMsgIgnorePushMotorcycle(other, self) || al::sendMsgPush(other, self)) { + return; + } + + if (mGetOffDelay <= 0) { + if (rs::sendMsgPushToPlayer(other, self)) + return; + } + + rs::sendMsgPushToMotorcycle(other, self); +} + +bool Motorcycle::receiveMsg(const al::SensorMsg* message, al::HitSensor* other, + al::HitSensor* self) { + if (rs::isMsgTargetMarkerPosition(message)) { + sead::Vector3f position = {0.0f, 0.0f, 0.0f}; + al::calcTransLocalOffset(&position, this, forceField5); + rs::setMsgTargetMarkerPosition(message, position); + return true; + } + + if (rs::isMsgIgnoredByRunawayNpc(message)) + return true; + + if (al::isSensorPlayer(self)) { + if (rs::isMsgPushToMotorcycle(message)) { + rs::calcNpcPushVecBetweenSensors(&mExternalPushVelocity, other, self); + return true; + } + + if (al::isSensorNpc(other) && + (al::isMsgPushAll(message) || rs::isMsgPushToPlayer(message))) { + sead::Vector3f sideDir = {0.0f, 0.0f, 0.0f}; + al::calcSideDir(&sideDir, this); + sead::Vector3f sensorDistance = al::getSensorPos(self) - al::getSensorPos(other); + al::parallelizeVec(&sensorDistance, sideDir, sensorDistance); + f32 radius = sead::Mathf::max(al::getSensorRadius(other), al::getSensorRadius(self)) - + sensorDistance.length(); + if (radius > 0.0f && al::tryNormalizeOrZero(&sensorDistance)) { + f32 pushRate = sead::Mathf::min(radius, 10.0f); + mExternalPushVelocity += pushRate * sensorDistance; + al::limitLength(&mExternalPushVelocity, mExternalPushVelocity, radius); + + return true; + } + } + } + + if (rs::isMsgKillByShineGet(message) || rs::isMsgKillByHomeDemo(message)) { + if (mPlayerPuppet) { + rs::endBindAndPuppetNull(&mPlayerPuppet); + endRide(this, mPlayerAnimator, mColliderCameraTarget, mTransCameraSubTarget); + } + reset(this); + al::setNerve(this, &NrvMotorcycle.ResetNoReaction); + return true; + } + + if (mPlayerPuppet) { + if (rs::isActiveBindKeepDemo(mBindKeepDemoInfo)) { + if (rs::isMsgBindKeepDemoExecute(message)) { + mPlayerAnimator->tryStartBindRideRunIfNotPlaying(); + mPlayerAnimator->update(0.0f, 1.0f, 0.0f); + if (mPlayerPuppet) + syncPuppetPose(mPlayerPuppet, this, mPuppetQuat, mPuppetTrans); + mIsExecutingBindDemo = false; + return true; + } + + if (rs::tryEndBindKeepDemo(mBindKeepDemoInfo, message, mPlayerPuppet)) { + mPlayerAnimator->tryStartBindRideRunIfNotPlaying(); + mIsExecutingBindDemo = false; + if (al::isNerve(this, &NrvMotorcycle.RideStartLeft) || + al::isNerve(this, &NrvMotorcycle.RideStartRight) || + al::isNerve(this, &NrvMotorcycle.RideStartOn)) { + al::setNerve(this, &NrvMotorcycle.RideWait); + return true; + } + return true; + } + } else if (rs::tryStartBindKeepDemo(mBindKeepDemoInfo, message, mPlayerPuppet)) { + mPlayerAnimator->startBindKeepDemo(); + mIsExecutingBindDemo = true; + return true; + } + + if (al::isMsgPlayerDisregard(message)) + return true; + + if (rs::isMsgEnableMapCheckPointWarp(message) || rs::isMsgBindCollidedGround(message)) + return rs::isCollidedGround(this); + + if (!al::isSensorName(self, "Pushed") && al::isSensorPlayer(self) && + (al::isMsgExplosion(message) || rs::isMsgEnemyAttackTRex(message) || + (!isAnyRideRun(this) && al::isMsgEnemyAttack(message)))) { + return rs::requestDamage(mPlayerPuppet); + } + + if (rs::tryReceiveBindCancelMsgAndPuppetNull(&mPlayerPuppet, message)) { + endRide(this, mPlayerAnimator, mColliderCameraTarget, mTransCameraSubTarget); + return true; + } + if (al::isMsgBindEnd(message)) { + startGetOff(this, &mPlayerPuppet, &mGetOffDelay, mPlayerAnimator, mColliderCameraTarget, + mTransCameraSubTarget); + return true; + } + return rs::isMsgBindRecoveryLife(message) && al::isNerve(this, &NrvMotorcycle.RideWait); + } + + if (rs::isMsgCheckCarObstacle(message)) + return true; + + if (rs::isMsgFireSwitchFire(message)) { + reset(this); + return true; + } + + if (al::isSensorRide(self)) { + if (rs::isMsgPlayerObjectWallHit(message)) + return false; + + if (rs::isMsgNpcCapReactionAll(message) || al::isMsgExplosion(message) || + (rs::isPlayerHack(this) && + rs::checkMsgNpcTrampleReactionAll(message, other, self, false))) { + rs::requestHitReactionToAttacker(message, self, other); + + if (al::isNerve(this, &NrvMotorcycle.Wait) || al::isNerve(this, &NrvMotorcycle.Creep) || + al::isNerve(this, &NrvMotorcycle.Reaction)) { + if (al::isNerve(this, &NrvMotorcycle.Reaction) && al::isLessStep(this, 7)) + return false; + + if (mItemSpawnCount > 0) { + sead::Vector3f itemPos = sItemSpawnOffset; + al::multVecPose(&itemPos, this, itemPos); + sead::Quatf itemQuat = sead::Quatf::unit; + al::calcQuat(&itemQuat, this); + al::appearItem(this, itemPos, itemQuat, nullptr); + mItemSpawnCount--; + } + al::setNerve(this, &NrvMotorcycle.Reaction); + } + return true; + } + + return false; + } + + if (!al::isSensorBindableAll(self) || rs::isPlayer2D(this)) + return false; + + if (al::isMsgBindStart(message) && + (al::isNerve(this, &NrvMotorcycle.Wait) || al::isNerve(this, &NrvMotorcycle.Creep) || + al::isNerve(this, &NrvMotorcycle.Reaction))) { + if (rs::isPlayerOnGround(this)) { + if (!rs::isPlayerInputTriggerRide(this)) + return false; + } else { + if (al::getActorVelocity(other).y > -6.0f) + return false; + + sead::Vector3f velocity = al::getActorVelocity(other); + if (!al::tryNormalizeOrZero(&velocity) || velocity.y > 0.0f) { + if (mGetOffDelay > 0) + mGetOffDelay = 5; + return false; + } + + if (mGetOffDelay > 0) + return false; + + sead::Vector3f dits = al::getActorTrans(other) - al::getTrans(this); + al::parallelizeVec(&dits, al::getFront(this), dits); + f32 frontDot = al::getFront(this).dot(dits); + f32 distance = dits.length(); + if (frontDot > 0.0f) { + if (distance > 50.0f) + return false; + } else if (distance > 200.0f) { + return false; + } + if (al::calcDistanceV(al::getGravity(this), other, self) > 100.0f) + return false; + } + return true; + } + + if (al::isMsgBindInit(message)) { + mPlayerPuppet = rs::startPuppet(self, other); + rs::validateRecoveryArea(mPlayerPuppet); + mPlayerAnimator->startBind(mPlayerPuppet); + rs::setPuppetVelocity(mPlayerPuppet, sead::Vector3f::zero); + bool isCollidedPuppetGround = rs::isCollidedPuppetGround(mPlayerPuppet); + al::validateDepthShadowMap(this); + al::offDepthShadowModel(this); + rs::hidePuppetShadow(mPlayerPuppet); + rs::invalidatePuppetCollider(mPlayerPuppet); + rs::invalidatePuppetReceivePush(mPlayerPuppet); + rs::invalidateWaterSurfaceShadow(mPlayerPuppet); + al::setCameraTarget(this, mColliderCameraTarget); + al::onCameraRideObj(this); + if (mIsStickWorldPose == false) { + al::setCameraSubTarget(this, mTransCameraSubTarget); + mCameraSubTargetAngle = 0.0f; + mCoursePointFollowTimer = 0; + } + al::invalidateHitSensors(this); + al::validateHitSensorPlayerAll(this); + al::validateHitSensorEnemyBodyAll(this); + al::invalidateClipping(this); + al::onCollide(this); + al::showSilhouetteModel(this); + al::invalidateOcclusionQuery(this); + mAccelerationState->reset(); + mAutoResetTimer = 1200; + rs::calcPuppetQuat(&mPuppetQuat, mPlayerPuppet); + mPuppetTrans.set(rs::getPuppetTrans(mPlayerPuppet)); + BindInfo bindInfo; + bindInfo.name = "Motorcycle"; + rs::tryAppearBindTutorial(this, bindInfo); + rs::rideMotorcycle(this); + mPuppetRotZLeft = rs::getPuppetPoseRotZDegreeLeft(mPlayerPuppet); + mPuppetRotZRight = rs::getPuppetPoseRotZDegreeRight(mPlayerPuppet); + if (!isCollidedPuppetGround) { + al::setNerve(this, &NrvMotorcycle.RideStartOn); + return true; + } + + sead::Vector3f dirH = {0.0f, 0.0f, 0.0f}; + if (al::calcDirBetweenSensorsH(&dirH, self, other)) { + sead::Vector3f sideDir = {0.0f, 0.0f, 0.0f}; + al::calcSideDir(&sideDir, this); + if (sideDir.dot(dirH) > 0.0f) { + al::setNerve(this, &NrvMotorcycle.RideStartLeft); + return true; + } + } + al::setNerve(this, &NrvMotorcycle.RideStartRight); + return true; + } + + return false; +} + +static inline bool isCollidedGround(Motorcycle* actor) { + return rs::isCollidedGround(actor); +} + +// NON_MATCHING: Major optimization differences https://decomp.me/scratch/B2j7L +void Motorcycle::movement() { + if (rs::isActiveBindKeepDemo(mBindKeepDemoInfo)) { + mPlayerAnimator->update(0.0f, 1.0f, 0.0f); + return; + } + + mAccelerationState->isAccelerating = false; + al::LiveActor::movement(); + + if (mPlayerPuppet) { + rs::syncPuppetVisibility(this, mPlayerPuppet); + rs::copyPuppetDitherAlpha(mPlayerPuppet, this); + al::AreaObj* areaObj = + al::tryFindAreaObj(this, "MotorcycleInCheckArea", al::getTrans(this)); + if (areaObj) { + al::tryOnStageSwitch(areaObj, "SwitchAreaOn"); + areaObj->invalidate(); + } + if (mParams->framesInAir >= 60) + rs::requestUpToTargetCameraAngleBySpeed(this, 50.0f, 3.0f, 0); + rs::requestUpdateSafetyPoint(mPlayerPuppet, this, this); + if (rs::isCollidedGround(this)) + rs::sendPlayerCollisionTouchMsg(this, al::getHitSensor(this, "PlayerBody"), this); + if (rs::isCollidedGround(this)) + al::holdSe(this, "GroundNoiseLv"); + } + + if (al::isActiveCameraSubTarget(mTransCameraSubTarget)) { + f32 distRate = 0.0f; + f32 angleRate = 0.0f; + + if (mCoursePointSize < 1 || !al::isOnStageSwitch(this, "SwitchValidCoursePoint")) { + mCameraSubTargetAngle = al::lerpValue(mCameraSubTargetAngle, 0.0f, 0.045f); + mCoursePointFollowTimer = sead::Mathi::max(0, mCoursePointFollowTimer - 1); + } else { + f32 bestAngle = 180.0f; + s32 bestIdx = -1; + for (s32 i = 0; i < mCoursePointSize; i++) { + f32 distance = (al::getTrans(this) - mCoursePoints[i]).length(); + if (distance < 750.0f || distance > 3000.0f) + continue; + sead::Vector3f lookDir = {0.0f, 0.0f, 0.0f}; + al::calcCameraLookDir(&lookDir, this, 0); + f32 angle = al::calcAngleDegree(lookDir, mCoursePoints[i] - al::getTrans(this)); + if (angle > 120.0f || !(bestIdx < 0 || angle < bestAngle)) + continue; + + distRate = al::normalize(distance, 750.0f, 3000.0f); + angleRate = al::normalize(angle, 0.0f, 120.0f); + bestIdx = i; + bestAngle = angle; + } + + if (bestIdx < 0) { + mCameraSubTargetAngle = al::lerpValue(mCameraSubTargetAngle, 0.0f, 0.045f); + mCoursePointFollowTimer = sead::Mathi::clampMin(mCoursePointFollowTimer - 1, 0); + } else { + f32 angleOnPlane = al::calcAngleOnPlaneDegree( + al::getFront(this), mCoursePoints[bestIdx] - al::getTrans(this), + al::getGravity(this)); + f32 targetAngle = sead::Mathf::clamp(angleOnPlane, -50.0f, 50.0f); + targetAngle = al::lerpValue(mCameraSubTargetAngle, targetAngle, 0.3f); + mCameraSubTargetAngle = al::lerpValue(mCameraSubTargetAngle, targetAngle, 0.15f); + mCoursePointFollowTimer = sead::Mathi::clampMax(mCoursePointFollowTimer + 1, 30); + } + } + + if (al::isNerve(this, &NrvMotorcycle.RideWait)) { + mCameraSubTargetTurnParam->setTurnRate1(mCameraSubTargetTurnParam->getTurnRate1() * + 0.98f); + mCameraSubTargetTurnParam->setTurnRate2(mCameraSubTargetTurnParam->getTurnRate2() * + 0.98f); + } else { + f32 normSpeed = + al::normalize(al::calcSpeedH(this), 0.0f, al::calcSpeedMax(1.8f, 0.95f)); + f32 combinedRate = distRate * al::normalize(mCoursePointFollowTimer, 0, 30) * angleRate; + f32 turnRate1 = + al::lerpValue(al::lerpValue(0.3f, 0.1f, normSpeed), 0.275f, combinedRate); + mCameraSubTargetTurnParam->setTurnRate1(turnRate1); + f32 turnRate2 = + al::lerpValue(al::lerpValue(0.15f, 0.075f, normSpeed), 0.1f, combinedRate); + mCameraSubTargetTurnParam->setTurnRate2(turnRate2); + } + + sead::Vector3f subTargetFront = al::getFront(this); + al::rotateVectorDegree(&subTargetFront, subTargetFront, al::getGravity(this), + mCameraSubTargetAngle); + mCameraSubTargetPos = al::getTrans(this) + subTargetFront * 250.0f; + } + + MotorcycleParams* params = mParams; + params->frontContactPoints.clear(); + params->backContactPoints.clear(); + params->groundNormalAvg = {0.0f, 0.0f, 0.0f}; + + if (!al::isNoCollide(this)) { + params->isTouchingGround = false; + params->isTouchingWall = false; + params->isTouchingFront = false; + params->isTouchingBack = false; + + if (isCollidedGround(this)) { + CollisionShapeKeeper* shapeKeeper = getPlayerCollider()->getCollisionShapeKeeper(); + const s32 numResults = shapeKeeper->getNumCollideResult(); + for (s32 i = 0; i < numResults; i++) { + const CollidedShapeResult* result = shapeKeeper->getCollidedShapeResult(i); + if (result->isArrow()) { + const al::ArrowHitInfo& arrowHitInfo = result->getArrowHitInfo(); + const sead::Vector3f& normal = arrowHitInfo.hitInfo->triangle.getNormal(0); + if (al::isFloorPolygon(normal, sGroundNormal)) { + if (result->getShapeInfoArrow()->get_18() == 0) { + params->isTouchingGround = true; + params->groundNormalAvg += + result->getArrowHitInfo().hitInfo->triangle.getNormal(0); + } else { + params->isTouchingWall = true; + } + } + } else if (result->isSphere()) { + const al::SphereHitInfo& sphereHitInfo = result->getSphereHitInfo(); + sead::Vector3f normal = sphereHitInfo.hitInfo->triangle.getNormal(0); + if (al::isEqualString("FrontFace", result->getShapeInfoSphere()->getName())) { + params->frontContactPoints.emplaceBack(normal); + if (al::isFloorPolygon(normal, sGroundNormal)) + params->isTouchingFront = true; + } else if (al::isEqualString("BackFace", + result->getShapeInfoSphere()->getName())) { + params->backContactPoints.emplaceBack(normal); + if (al::isFloorPolygon(normal, sGroundNormal)) + params->isTouchingBack = true; + } + } + } + } + al::tryNormalizeOrZero(¶ms->groundNormalAvg); + } + + if (rs::isCollidedGround(this)) { + if (isTouchingGround(mParams) && !mParams->isTouchingWall) { + sead::Vector3f pos = {0.0f, 0.0f, 0.0f}; + const sead::Vector3f& front = al::getFront(this); + sead::Vector3f frontOffset = front * -300.0f * 0.5f; + sead::Vector3f arrowPos; + sead::Vector3f arrowDir; + al::Triangle triangle; + const al::IUseCollision* collision = this; + arrowPos = frontOffset + al::getTrans(this) - al::getGravity(this) * 100.0f; + const sead::Vector3f& gravity = al::getGravity(this); + arrowDir = gravity * 100.0f + gravity * 100.0f; + bool foundFloor = + alCollisionUtil::getFirstPolyOnArrow(collision, &pos, &triangle, arrowPos, arrowDir, + nullptr, nullptr) && + al::isFloorPolygon(triangle.getNormal(0), sGroundNormal); + + if (foundFloor) { + if (!al::isNear(pos, al::getTrans(this), 0.001f)) { + arrowPos = pos - al::getTrans(this); + al::normalize(&arrowPos); + sead::Vector3f* frontPtr = al::getFrontPtr(this); + arrowDir = -arrowPos; + al::turnVecToVecRate(frontPtr, *frontPtr, arrowDir, 0.3f); + al::normalize(al::getFrontPtr(this)); + sead::Vector3f* gravityPtr = al::getGravityPtr(this); + al::verticalizeVec(gravityPtr, al::getFront(this), *gravityPtr); + al::normalize(al::getGravityPtr(this)); + } + } + } + } + + f32 weightC = 0.0f; + mPose.handleAngle = al::normalizeAbs(mPose.steerAngle, 0.0f, 55.0f) * -32.5f; + f32 weightL = 1.0f; + f32 weightR = 0.0f; + if (mPlayerPuppet) { + weightR = 0.0f; + if (mPose.handleAngle > 0.0f) + weightL = al::normalize(mPose.handleAngle, 0.0f, 32.5f); + else + weightL = 0.0f; + if (mPose.handleAngle < 0.0f) + weightR = 1.0f - al::normalize(mPose.handleAngle, -32.5f, 0.0f); + weightC = 1.0f - (weightL + weightR); + } + mPlayerAnimator->update(weightL, weightC, weightR); + + al::setMaterialCode(this, + rs::isCollidedGround(this) ? rs::getMaterialCodeGround(this) : "NoCollide"); + al::makeMtxSRT(&mWaterSurfaceMtx, this); + + if (mPlayerPuppet && al::isInWater(this)) { + sead::Vector3f flatSurface = {0.0f, 0.0f, 0.0f}; + sead::Vector3f waterSurface = {0.0f, 0.0f, 0.0f}; + sead::Vector3f waterSurfaceNormal = {0.0f, 0.0f, 0.0f}; + sead::Vector3f actorUp = {0.0f, 0.0f, 0.0f}; + al::calcUpDir(&actorUp, this); + bool foundFlat = al::calcFindWaterSurfaceFlat( + &flatSurface, nullptr, this, al::getTrans(this), sead::Vector3f::ey, 121.0f); + bool foundWater = al::calcFindWaterSurface(&waterSurface, &waterSurfaceNormal, this, + al::getTrans(this), sead::Vector3f::ey, 121.0f); + + sead::Vector3f surface = {0.0f, 0.0f, 0.0}; + bool foundSurface = false; + if (foundFlat || foundWater) { + if (foundFlat && foundWater) + if ((flatSurface - al::getTrans(this)).length() > + (waterSurface - al::getTrans(this)).length()) + surface.set(flatSurface); + else + surface.set(waterSurface); + else if (foundFlat) + surface.set(flatSurface); + else + surface.set(waterSurface); + foundSurface = true; + } + + if (foundSurface) { + al::updateMaterialCodePuddle(this, true); + al::updateMaterialCodeWater(this, false); + mWaterSurfaceMtx.setTranslation(surface); + } else { + startGetOff(this, &mPlayerPuppet, &mGetOffDelay, mPlayerAnimator, mColliderCameraTarget, + mTransCameraSubTarget); + reset(this); + } + } else { + al::updateMaterialCodePuddle(this, false); + al::updateMaterialCodeWater(this, false); + } + + mGetOffDelay--; + + bool wasAccelerating = mIsAccelerating; + bool isAccelerating = mPlayerPuppet && !mAccelerationState->isAccelerating; + + mIsAccelerating = isAccelerating; + if (wasAccelerating != isAccelerating) { + const char* anim = mIsOnLight ? (isAccelerating ? "FrontOnTailOn" : "FrontOnTailOff") : + (isAccelerating ? "FrontOffTailOn" : "FrontOffTailOff"); + al::startMclAnim(this, anim); + } +} + +void Motorcycle::kill() { + al::LiveActor::kill(); + al::onDepthShadowModel(this); + al::invalidateDepthShadowMap(this); +} + +void Motorcycle::updateCollider() { + if (rs::isActiveBindKeepDemo(mBindKeepDemoInfo)) + return; + MotorcycleParams* params = mParams; + if (al::isNoCollide(this)) { + params->isOnGround = false; + params->framesInAir = -1; + params->isOnJump = false; + } else { + params->isOnGround = rs::isCollidedGround(this); + params->isOnJump = rs::isCollisionCodeJump(this); + if (params->isOnGround) { + params->framesInAir = 0; + params->lastGroundPos.set(al::getTrans(this)); + params->groundNormal.set(rs::getCollidedGroundNormal(this)); + } else + params->framesInAir++; + } + + mIsInWater = al::isInWater(this); + mPrevTransDelta = -al::getTrans(this); + + rs::updateCollider(this, this, + al::getVelocity(this) + mExternalPushVelocity + mSteerShiftVelocity); + mExternalPushVelocity = {0.0f, 0.0f, 0.0f}; + mSteerShiftVelocity = {0.0f, 0.0f, 0.0f}; + mPrevTransDelta += al::getTrans(this); +} + +void Motorcycle::calcAnim() { + al::LiveActor::calcAnim(); + if (mPlayerPuppet && !mIsExecutingBindDemo) + syncPuppetPose(mPlayerPuppet, this, mPuppetQuat, mPuppetTrans); +} + +void Motorcycle::exeWait() { + if (al::isFirstStep(this)) { + al::tryStartActionIfNotPlaying(this, "Wait"); + al::setVelocityZero(this); + } + mPose.steerAngle = al::lerpValue(mPose.steerAngle, 0.0f, 0.2f); + mPose.handleAngle = al::lerpValue(mPose.handleAngle, 0.0f, 0.2f); + mPose.leanAngle = al::lerpValue(mPose.leanAngle, 0.0f, 0.2f); + mPose.jumpAngle = al::lerpValue(mPose.jumpAngle, 0.0f, 0.2f); + + if (al::isNoCollide(this)) { + if (mCollisionPartsConnector) { + if (!al::isMtxConnectorConnecting(mCollisionPartsConnector)) { + kill(); + return; + } + sead::Quatf quat = sead::Quatf::unit; + sead::Vector3f trans = {0.0f, 0.0f, 0.0f}; + al::calcConnectQT(&quat, &trans, mCollisionPartsConnector); + al::resetQuatPosition(this, quat, trans); + } + } else { + updateOrientation(this, mPose.steerAngle); + al::setVelocityToGravity(this, 2.0f); + if (!rs::isCollidedGround(this) && al::isGreaterEqualStep(this, 2)) { + al::setNerve(this, &NrvMotorcycle.Fall); + return; + } + } + if (mAutoResetTimer > 0 && mAutoResetTimer-- == 1) + reset(this); +} + +void Motorcycle::exeCreep() { + if (al::isFirstStep(this)) + al::tryStartActionIfNotPlaying(this, "Wait"); + + mPose.steerAngle = al::lerpValue(mPose.steerAngle, 0.0f, 0.2f); + mPose.handleAngle = al::lerpValue(mPose.handleAngle, 0.0f, 0.2f); + mPose.leanAngle = al::lerpValue(mPose.leanAngle, 0.0f, 0.2f); + mPose.jumpAngle = al::lerpValue(mPose.jumpAngle, 0.0f, 0.2f); + updateOrientation(this, mPose.steerAngle); + + al::addVelocityToGravity(this, 2.0f); + al::scaleVelocity(this, 0.9f); + al::limitVelocityDir(this, al::getGravity(this), 360.0f); + + if (!isCollidedGround(this) && al::isGreaterEqualStep(this, 2)) { + al::setNerve(this, &NrvMotorcycle.Fall); + return; + } + + if (al::isInDeathArea(this) || al::isGreaterEqualStep(this, 360)) { + reset(this); + return; + } + + if (al::isVelocitySlowH(this, 3.0f)) + al::setNerve(this, &NrvMotorcycle.Wait); +} + +void Motorcycle::exeFall() { + bool isInWater = al::isInWater(this); + al::addVelocityY(this, isInWater ? -2.0f : -1.0f); + if (isInWater) + al::scaleVelocityParallelVertical(this, sGroundNormal, 0.8f, 0.925f); + else + al::scaleVelocityExceptDirection(this, sGroundNormal, 0.95f); + + if (al::getVelocity(this).dot({0.0f, -1.0f, 0.0f}) > 0.0f) + al::limitVelocityDir(this, sGroundNormal, 35.0f); + + if (al::isInDeathArea(this)) { + reset(this); + return; + } + + if (tryHandleGroundCollision(this)) + return; + + if (al::isGreaterEqualStep(this, 360)) { + reset(this); + return; + } + + if (al::isInWater(this) && !mIsInWater) + al::startHitReaction(this, "着水"); +} + +void Motorcycle::exeJump() { + if (al::isFirstStep(this)) + al::setVelocity(this, 0.0f, 50.0f, 0.0f); + + bool isInWater = al::isInWater(this); + al::addVelocityY(this, isInWater ? -2.0f : -1.0f); + if (!isInWater) + al::scaleVelocityExceptDirection(this, sGroundNormal, 0.95f); + else + al::scaleVelocityParallelVertical(this, sGroundNormal, 0.8f, 0.925f); + + if (al::getVelocity(this).dot({0.0f, -1.0f, 0.0f}) > 0.0f) + al::limitVelocityDir(this, sGroundNormal, 35.0f); + + if (al::getVelocity(this).dot({0.0f, -1.0f, 0.0f}) > 0.0f || al::isInDeathArea(this)) { + reset(this); + return; + } + + if (!tryHandleGroundCollision(this) && al::isInWater(this) && !mIsInWater) + al::startHitReaction(this, "着水"); +} + +void Motorcycle::endJump() { + mPlayerAnimator->tryStartBindRideLandIfJump(); + mPose.jumpAngle = 0.0f; +} + +void Motorcycle::exeReaction() { + if (al::isFirstStep(this)) + al::startAction(this, "Reaction"); + + mPose.steerAngle = al::lerpValue(mPose.steerAngle, 0.0f, 0.2f); + mPose.handleAngle = al::lerpValue(mPose.handleAngle, 0.0f, 0.2f); + mPose.leanAngle = al::lerpValue(mPose.leanAngle, 0.0f, 0.2f); + mPose.jumpAngle = al::lerpValue(mPose.jumpAngle, 0.0f, 0.2f); + al::setNerveAtActionEnd(this, &NrvMotorcycle.Wait); +} + +void Motorcycle::exeReset() { + if (al::isFirstStep(this)) { + al::tryKillEmitterAndParticleAll(this); + al::stopAction(this); + if (al::isNerve(this, &NrvMotorcycle.Reset)) + al::startHitReaction(this, "リセット"); + al::offCollide(this); + mPose.steerAngle = 0.0f; + mPose.handleAngle = 0.0f; + mPose.leanAngle = 0.0f; + mPose.jumpAngle = 0.0f; + mAutoResetTimer = -1; + } + + if (al::isStep(this, 1)) { + al::resetQuatPosition(this, mBaseQuat, mBaseTrans); + rs::resetCollision(this); + } + + if (al::isGreaterEqualStep(this, 60)) { + al::validateClipping(this); + al::restartAction(this); + al::showModelIfHide(this); + al::invalidateHitSensors(this); + al::validateHitSensorBindableAll(this); + al::validateHitSensorRideAll(this); + al::startHitReaction(this, "出現"); + al::setNerve(this, &NrvMotorcycle.Wait); + } +} + +void Motorcycle::exeRideStart() { + if (al::isFirstStep(this)) { + al::tryStartActionIfNotPlaying(this, "Wait"); + if (al::isNerve(this, &NrvMotorcycle.RideStartLeft)) + mPlayerAnimator->startBindRideStartL(); + else + mPlayerAnimator->startBindRideStartR(); + } + + if (rs::isPuppetActionEnd(mPlayerPuppet)) + al::setNerve(this, &NrvMotorcycle.RideStartOn); +} + +void Motorcycle::exeRideStartOn() { + if (al::isFirstStep(this)) { + al::startAction(this, "RideOn"); + mPlayerAnimator->startBindRideOn(); + rs::requestResetCameraAngleV(this, 0); + } + + if (rs::isPuppetActionEnd(mPlayerPuppet)) { + rs::validatePuppetLookAt(mPlayerPuppet); + al::setNerve(this, &NrvMotorcycle.RideWait); + } +} + +static inline bool tryTriggerGetOff(Motorcycle* actor, IUsePlayerPuppet** puppy, + MotorcyclePlayerAnimator* animator, + al::CameraTargetBase* camera, + al::TransCameraSubTarget* subCamera) { + if (!rs::isActiveBindKeepDemo(actor->getBindKeepDemoInfo()) && rs::isTriggerGetOff(*puppy)) { + if (!al::isInAreaObj(actor, "MotorcycleInvalidGetOffArea")) { + startGetOff(actor, puppy, actor->getOffDelay(), animator, camera, subCamera); + return true; + } + al::startHitReaction(actor, "降車キャンセル"); + } + return false; +} + +void Motorcycle::exeRideWait() { + if (al::isFirstStep(this)) { + al::tryStartActionIfNotPlaying(this, "Wait"); + mPlayerAnimator->startBindWait(); + } + if (tryTriggerGetOff(this, &mPlayerPuppet, mPlayerAnimator, mColliderCameraTarget, + mTransCameraSubTarget)) + return; + + mPose.steerAngle = al::lerpValue(mPose.steerAngle, 0.0f, 0.025f); + alPadRumbleFunction::startPadRumbleDirectValue(this, 160.0f, 168.0f, sead::Mathf::deg2rad(2.0f), + sead::Mathf::deg2rad(2.0f), 0.7f, 0.7f, -1); + updateOrientation(this, mPose.steerAngle); + al::addVelocityToGravity(this, 2.0f); + al::scaleVelocity(this, 0.95f); + + if (rs::isCollidedGround(this)) + al::limitVelocityDirSign(this, al::getGravity(this), 3.0f); + + if (rs::isPuppetHoldActionButton(mPlayerPuppet)) { + al::setNerve(this, &NrvMotorcycle.RideRunStart); + return; + } + + if (!TryJump(this, mPlayerPuppet)) + tryParking(this, mPlayerPuppet, mParkingParams, &mPose); +} + +void Motorcycle::exeRideWaitJump() { + if (al::isFirstStep(this)) { + mParams->groundNormal = {0.0f, 1.0f, 0.0f}; + + // TODO: Check mParams->isOnJump + bool isOnJump = mParams->isOnJump == 0; + f32 velocity = isOnJump ? 18.5f : 50.0f; + mIsOnJump = isOnJump; + + al::setVelocity(this, 0.0f, velocity, 0.0f); + al::startHitReaction(this, "ジャンプ開始"); + mPlayerAnimator->startBindRideJump(); + } + + if (tryTriggerGetOff(this, &mPlayerPuppet, mPlayerAnimator, mColliderCameraTarget, + mTransCameraSubTarget)) + return; + + updateSeRumble(mSeRumbleState, this, mPlayerPuppet, mPose.steerAngle, true); + handleCeilingCollision(this); + updateSteerAngleAndAccel(this, mPlayerPuppet, mAccelerationState, &mPose.steerAngle); + sead::Vector2f stick = {0.0f, 0.0f}; + calcInputStick(&stick, this, mPlayerPuppet); + f32 angle = stick.x * 2.5f; + if (angle * angle > 0.1f) { + sead::Vector3f front = al::getFront(this); + sead::Vector3f front2 = al::getFront(this); + al::rotateVectorDegree(&front2, front2, al::getGravity(this), angle); + al::normalize(&front2); + + sead::Vector3f diff = front2 - front; + al::normalize(&diff); + al::setFront(this, front2); + + f32 fVar10 = getJointDistance(this, "AllRoot", "JointRoot") * + sead::Mathf::sin(sead::Mathf::deg2rad(sead::Mathf::abs(angle * 0.5f))) * 2; + f32 x = diff.x * fVar10; + f32 z = diff.z * fVar10; + mSteerShiftVelocity.x = x; + mSteerShiftVelocity.z = z; + } + + al::addVelocityToDirection( + this, al::getFront(this), + al::lerpValue(0.0f, 1.8f, + al::easeIn(al::normalize(mAccelerationState->accelRate, 0.0f, 5.0f)))); + if (!mIsOnJump || !rs::isPuppetHoldJumpButton(mPlayerPuppet) || al::isGreaterStep(this, 6)) { + mIsOnJump = false; + al::addVelocityY(this, -2.0f); + } + + al::scaleVelocityExceptDirection(this, sGroundNormal, 0.95f); + if (al::getVelocity(this).dot({0.0f, -1.0f, 0.0f}) > 0.0f) + al::limitVelocityDir(this, sGroundNormal, 35.0f); + + if (rs::isOnGround(this, this)) { + al::startHitReaction(this, "着地"); + if (!TryJump(this, mPlayerPuppet)) { + mPose.leanAngle = 0.0f; + al::setNerve(this, &NrvMotorcycle.RideWaitLand); + } else { + al::startAction(this, "WaitLand"); + } + } +} + +void Motorcycle::endRideWaitJump() { + mPlayerAnimator->tryStartBindRideLandIfJump(); + mPose.jumpAngle = 0.0f; +} + +void Motorcycle::exeRideWaitLand() { + if (al::isFirstStep(this)) + al::startAction(this, "WaitLand"); + + if (TryJump(this, mPlayerPuppet)) + return; + + if (tryTriggerGetOff(this, &mPlayerPuppet, mPlayerAnimator, mColliderCameraTarget, + mTransCameraSubTarget)) + return; + + mPose.steerAngle = al::lerpValue(mPose.steerAngle, 0.0f, 0.025f); + alPadRumbleFunction::startPadRumbleDirectValue(this, 160.0f, 168.0f, sead::Mathf::deg2rad(2.0f), + sead::Mathf::deg2rad(2.0f), 0.7f, 0.7f, -1); + updateOrientation(this, mPose.steerAngle); + al::addVelocityToGravity(this, 2.0f); + al::scaleVelocity(this, 0.95f); + + if (rs::isCollidedGround(this)) + al::limitVelocityDirSign(this, al::getGravity(this), 3.0f); + + if (rs::isPuppetHoldActionButton(mPlayerPuppet)) { + al::setNerve(this, &NrvMotorcycle.RideRunStart); + return; + } + + al::setNerveAtActionEnd(this, &NrvMotorcycle.RideWait); +} + +void Motorcycle::exeRideRunStart() { + if (al::isFirstStep(this)) { + al::startAction(this, "RunStart"); + mPlayerAnimator->startBindRideRunStart(); + mSeRumbleState->reset(); + } + if (tryTriggerGetOff(this, &mPlayerPuppet, mPlayerAnimator, mColliderCameraTarget, + mTransCameraSubTarget)) + return; + if (tryHandleHighSpeedWallCollision(this, mPrevTransDelta)) + return; + + updateSteerAngle(this, &mPose.steerAngle, mPlayerPuppet, 40.0f, -15.0f, 0.3f); + updateOrientation(this, mPose.steerAngle); + updateSteerAngleAndAccel(this, mPlayerPuppet, mAccelerationState, &mPose.steerAngle); + + updateRunningVelocity( + this, al::lerpValue(0.0f, 1.8f, + al::easeIn(al::normalize(mAccelerationState->accelRate, 0.0f, 5.0f)))); + updateSeRumble(mSeRumbleState, this, mPlayerPuppet, mPose.steerAngle, false); + + if (TryJumpWithCameraAbsorb(this, mPlayerPuppet)) + return; + + if (TryWheelieOrFall(this)) + return; + + MotorcycleParams* params = mParams; + if (rs::isCollidedGround(this) && !params->isOnGround) { + if (150.0f < getGroundDistance(this, params)) { + updateVelocity(this, sGroundNormal, -rs::getCollidedGroundNormal(this)); + al::limitVelocityDirSign(this, -rs::getCollidedGroundNormal(this), 5.0f); + al::setNerve(this, &NrvMotorcycle.RideRunLand); + return; + } + } + if (al::isActionEnd(this)) + al::setNerve(this, &NrvMotorcycle.RideRun); +} + +void Motorcycle::exeRideRun() { + if (al::isFirstStep(this)) { + al::tryStartActionIfNotPlaying(this, "Run"); + mPlayerAnimator->tryStartBindRideRunIfNotPlaying(); + mHighSpeedCameraTimer = 0; + } + if (tryTriggerGetOff(this, &mPlayerPuppet, mPlayerAnimator, mColliderCameraTarget, + mTransCameraSubTarget)) + return; + if (tryHandleHighSpeedWallCollision(this, mPrevTransDelta)) + return; + updateSteerAngle(this, &mPose.steerAngle, mPlayerPuppet, 40.0f, -15.0f, 0.3f); + updateOrientation(this, mPose.steerAngle); + updateSteerAngleAndAccel(this, mPlayerPuppet, mAccelerationState, &mPose.steerAngle); + + updateRunningVelocity( + this, al::lerpValue(0.0f, 1.8f, + al::easeIn(al::normalize(mAccelerationState->accelRate, 0.0f, 5.0f)))); + updateSeRumble(mSeRumbleState, this, mPlayerPuppet, mPose.steerAngle, false); + if (!mAccelerationState->isAccelerating) + mHighSpeedCameraTimer = 0; + else if (mHighSpeedCameraTimer++ > 13) + rs::requestDownToDefaultCameraAngleBySpeed(this, 6.0f, 0); + + if (TryJumpWithCameraAbsorb(this, mPlayerPuppet) || TryWheelieOrFall(this)) + return; + + MotorcycleParams* params = mParams; + if (rs::isCollidedGround(this) && !params->isOnGround) { + if (getGroundDistance(this, params) > 150.0f) { + updateVelocity(this, sGroundNormal, -rs::getCollidedGroundNormal(this)); + al::limitVelocityDirSign(this, -rs::getCollidedGroundNormal(this), 5.0f); + al::setNerve(this, &NrvMotorcycle.RideRunLand); + return; + } + } + + if (!tryParking(this, mPlayerPuppet, mParkingParams, &mPose) && + al::isVelocitySlowH(this, 3.0f) && mAccelerationState->accelRate < 0.01f && + rs::isOnGround(this, this)) { + al::setNerve(this, &NrvMotorcycle.RideWait); + } +} + +void Motorcycle::exeRideRunCollide() { + if (al::isFirstStep(this)) + al::startAction(this, "RunCollide"); + if (tryTriggerGetOff(this, &mPlayerPuppet, mPlayerAnimator, mColliderCameraTarget, + mTransCameraSubTarget)) + return; + updateSteerAngle(this, &mPose.steerAngle, mPlayerPuppet, 40.0f, -15.0f, 0.3f); + updateOrientation(this, mPose.steerAngle); + updateSteerAngleAndAccel(this, mPlayerPuppet, mAccelerationState, &mPose.steerAngle); + + updateRunningVelocity( + this, al::lerpValue(0.0f, 1.8f, + al::easeIn(al::normalize(mAccelerationState->accelRate, 0.0f, 5.0f)))); + updateSeRumble(mSeRumbleState, this, mPlayerPuppet, mPose.steerAngle, false); + + if (TryJumpWithCameraAbsorb(this, mPlayerPuppet)) + return; + + if (TryWheelieOrFall(this)) + return; + + MotorcycleParams* params = mParams; + if (rs::isCollidedGround(this) && !params->isOnGround) { + if (150.0f < getGroundDistance(this, params)) { + updateVelocity(this, sGroundNormal, -rs::getCollidedGroundNormal(this)); + al::limitVelocityDirSign(this, -rs::getCollidedGroundNormal(this), 5.0f); + al::setNerve(this, &NrvMotorcycle.RideRunLand); + return; + } + } + + if (rs::isCollidedWallVelocity(this, this)) + return; + if (mParams->frontContactPoints.size() > 0 && mParams->backContactPoints.size() > 0) + return; + al::setNerve(this, &NrvMotorcycle.RideRun); +} + +void Motorcycle::exeRideRunFall() { + if (tryTriggerGetOff(this, &mPlayerPuppet, mPlayerAnimator, mColliderCameraTarget, + mTransCameraSubTarget)) + return; + + mParams->groundNormal = {0.0f, 1.0f, 0.0f}; + updateSteerAngle(this, &mPose.steerAngle, mPlayerPuppet, 12.5f, -7.5f, 0.1f); + + applyAirPhysics(this, mPlayerPuppet, al::isInWater(this) ? 1.0f : 2.0f); + updateSeRumble(mSeRumbleState, this, mPlayerPuppet, mPose.steerAngle, true); + if (!tryBound(this) && + (!rs::isCollidedWallVelocity(this, this) || !tryHandleWallCollision(this))) { + updateAirOrientation(this); + if (!tryLand(this) && al::isInWater(this) && !mIsInWater) { + al::startHitReaction(this, "着水"); + return; + } + } + return; +} + +void Motorcycle::exeRideRunWheelie() { + if (al::isFirstStep(this)) + mPlayerAnimator->startBindRideJump(); + + if (tryTriggerGetOff(this, &mPlayerPuppet, mPlayerAnimator, mColliderCameraTarget, + mTransCameraSubTarget)) + return; + mParams->groundNormal = {0.0f, 1.0f, 0.0f}; + + updateSteerAngle(this, &mPose.steerAngle, mPlayerPuppet, 12.5f, -7.5f, 0.1f); + + applyAirPhysics(this, mPlayerPuppet, + al::calcNerveEaseInValue(this, 30, 0.0f, al::isInWater(this) ? 1.0f : 2.0f)); + updateSeRumble(mSeRumbleState, this, mPlayerPuppet, mPose.steerAngle, true); + if (!tryBound(this) && + (!rs::isCollidedWallVelocity(this, this) || !tryHandleWallCollision(this))) { + updateAirOrientation(this); + if (!tryLand(this) && al::isInWater(this) && !mIsInWater) { + al::startHitReaction(this, "着水"); + return; + } + } +} + +void Motorcycle::endRideRunWheelie() { + mPlayerAnimator->tryStartBindRideLandIfJump(); + mPose.jumpAngle = 0.0f; +} + +void Motorcycle::exeRideRunLand() { + if (al::isFirstStep(this)) + al::startAction(this, "RunLand"); + + if (tryTriggerGetOff(this, &mPlayerPuppet, mPlayerAnimator, mColliderCameraTarget, + mTransCameraSubTarget) || + tryHandleHighSpeedWallCollision(this, mPrevTransDelta)) + return; + + updateSteerAngle(this, &mPose.steerAngle, mPlayerPuppet, 40.0f, -15.0f, 0.3f); + updateOrientation(this, mPose.steerAngle); + updateSteerAngleAndAccel(this, mPlayerPuppet, mAccelerationState, &mPose.steerAngle); + + updateRunningVelocity( + this, al::lerpValue(0.0f, 1.8f, + al::easeIn(al::normalize(mAccelerationState->accelRate, 0.0f, 5.0f)))); + updateSeRumble(mSeRumbleState, this, mPlayerPuppet, mPose.steerAngle, al::isLessStep(this, 3)); + if (TryJumpWithCameraAbsorb(this, mPlayerPuppet)) + return; + + if (!rs::isCollidedGround(this) && mParams->framesInAir >= 6) { + updateVelocity(this, -mParams->groundNormal, sGroundNormal); + al::limitVelocityDirSign(this, sGroundNormal, 3.0f); + al::setNerve(this, &NrvMotorcycle.RideRunFall); + return; + } + + al::setNerveAtActionEnd(this, &NrvMotorcycle.RideRun); +} + +void Motorcycle::exeRideRunJump() { + if (al::isFirstStep(this)) { + al::tryStartActionIfNotPlaying(this, "Run"); + al::startHitReaction(this, "ジャンプ開始"); + + f32 velocity = 55.0f; + if (!mParams->isOnJump) { + velocity = 21.5f; + mIsOnJump = true; + } else { + al::requestStopCameraVerticalAbsorb(this); + mIsOnJump = false; + } + + al::setVelocityY(this, velocity); + mPlayerAnimator->startBindRideJump(); + } + mParams->groundNormal = {0.0f, 1.0f, 0.0f}; + if (tryTriggerGetOff(this, &mPlayerPuppet, mPlayerAnimator, mColliderCameraTarget, + mTransCameraSubTarget)) + return; + + updateSteerAngle(this, &mPose.steerAngle, mPlayerPuppet, 40.0f, -15.0f, 0.3f); + updateSeRumble(mSeRumbleState, this, mPlayerPuppet, mPose.steerAngle, true); + updateOrientation(this, mPose.steerAngle); + handleCeilingCollision(this); + updateSteerAngleAndAccel(this, mPlayerPuppet, mAccelerationState, &mPose.steerAngle); + mPose.jumpAngle = al::lerpValue(0.0f, -20.0f, al::calcNerveRate(this, 4)); + + if (!mIsOnJump || !rs::isPuppetHoldJumpButton(mPlayerPuppet) || + al::isGreaterEqualStep(this, 6)) { + mIsOnJump = 0; + sead::Vector3f front = al::getFront(this); + al::verticalizeVec(&front, sGroundNormal, front); + al::tryNormalizeOrZero(&front); + al::addVelocity( + this, front * al::lerpValue(0.0f, 1.8f, + al::easeIn(al::normalize(mAccelerationState->accelRate, + 0.0f, 5.0f))) + + (al::isInWater(this) ? 1.0f : 2.0f) * sead::Vector3f(0.0f, -1.0f, 0.0f)); + } + + al::scaleVelocityExceptDirection(this, sGroundNormal, 0.95f); + if (0.0f < al::getVelocity(this).dot({0.0f, -1.0f, 0.0f})) + al::limitVelocityDir(this, sGroundNormal, 35.0f); + + if (tryBound(this)) + return; + + if (!rs::isOnGround(this, this)) { + if (al::isInWater(this) && !mIsInWater) + al::startHitReaction(this, "着水"); + return; + } + + if (al::isVelocitySlowH(this, 3.0f) && 0.01f > mAccelerationState->accelRate) { + if (!TryJump(this, mPlayerPuppet)) + al::setNerve(this, &NrvMotorcycle.RideWaitLand); + return; + } + + if (TryJumpWithCameraAbsorb(this, mPlayerPuppet)) { + al::startAction(this, "RunLand"); + return; + } + + updateVelocity(this, sGroundNormal, -rs::getCollidedGroundNormal(this)); + al::limitVelocityDirSign(this, -rs::getCollidedGroundNormal(this), 5.0f); + al::setNerve(this, &NrvMotorcycle.RideRunLand); + return; +} + +void Motorcycle::endRideRunJump() { + mPlayerAnimator->tryStartBindRideLandIfJump(); + mPose.jumpAngle = 0.0f; +} + +void Motorcycle::exeRideRunBoundStart() { + if (al::isFirstStep(this)) + al::startAction(this, "RunLand"); + + if (tryTriggerGetOff(this, &mPlayerPuppet, mPlayerAnimator, mColliderCameraTarget, + mTransCameraSubTarget)) + return; + + mParams->groundNormal.set(0.0f, 1.0f, 0.0f); + updateBoundPhysics(this, mPlayerPuppet, &mPose.steerAngle, mSeRumbleState, mIsInWater); + al::setNerveAtActionEnd(this, &NrvMotorcycle.RideRunBound); +} + +void Motorcycle::exeRideRunBound() { + if (al::isFirstStep(this)) + al::startAction(this, "Run"); + + if (tryTriggerGetOff(this, &mPlayerPuppet, mPlayerAnimator, mColliderCameraTarget, + mTransCameraSubTarget)) + return; + mParams->groundNormal.set(0.0f, 1.0f, 0.0f); + updateBoundPhysics(this, mPlayerPuppet, &mPose.steerAngle, mSeRumbleState, mIsInWater); +} + +void Motorcycle::exeRideRunClash() { + if (al::isFirstStep(this)) { + al::startAction(this, "Clash"); + mPlayerAnimator->startBindRideClash(); + } + + if (tryTriggerGetOff(this, &mPlayerPuppet, mPlayerAnimator, mColliderCameraTarget, + mTransCameraSubTarget)) + return; + + mPose.steerAngle = al::lerpValue(mPose.steerAngle, 0.0f, 0.025f); + updateSeRumble(mSeRumbleState, this, mPlayerPuppet, mPose.steerAngle, true); + al::addVelocityY(this, -2.0f); + al::scaleVelocityY(this, 0.95f); + if (al::isActionEnd(this) && rs::isOnGround(this, this)) + al::setNerve(this, &NrvMotorcycle.RideWait); +} + +void Motorcycle::exeRideParkingSnap() { + if (al::isFirstStep(this)) { + mSeRumbleState->reset(); + alPadRumbleFunction::stopPadRumbleDirectValue(this, -1); + } + + f32 rate = al::calcNerveRate(this, 6); + ParkingParams* params = mParkingParams; + f32 invRate = 1.0f - rate; + mPose.steerAngle = invRate * params->pose.steerAngle; + mPose.handleAngle = invRate * params->pose.handleAngle; + mPose.leanAngle = invRate * params->pose.leanAngle; + mPose.jumpAngle = invRate * params->pose.jumpAngle; + + sead::Quatf quat = sead::Quatf::unit; + al::slerpQuat(&quat, params->quatA, params->quatB, rate); + + sead::Vector3f pos = {0.0f, 0.0f, 0.0f}; + al::lerpVec(&pos, mParkingParams->mCameraSubTargetPos, mParkingParams->vectorB, rate); + al::resetQuatPosition(this, quat, pos); + al::setNerveAtGreaterEqualStep(this, &RideParkingStart, 6); +} + +void Motorcycle::exeRideParkingStart() { + if (al::isFirstStep(this)) + al::startAction(this, "ParkingStart"); + al::setNerveAtActionEnd(this, &RideParking); +} + +void Motorcycle::exeRideParking() { + if (al::isFirstStep(this)) { + al::startAction(this, "Parking"); + al::tryOnStageSwitch(mParkingParams->actor, "SwitchMotorcycleFloorTouchBothWheelOn"); + al::onCollide(this); + } + + updateOrientation(this, mPose.steerAngle); + al::addVelocityToGravity(this, 2.0f); + al::scaleVelocity(this, 0.95f); + + if (rs::isCollidedGround(this)) + al::limitVelocityDirSign(this, al::getGravity(this), 3.0f); + + al::setNerveAtGreaterEqualStep(this, &RideParkingAfter, 8); +} + +void Motorcycle::exeRideParkingAfter() { + if (tryTriggerGetOff(this, &mPlayerPuppet, mPlayerAnimator, mColliderCameraTarget, + mTransCameraSubTarget)) + return; + + updateOrientation(this, mPose.steerAngle); + al::addVelocityToGravity(this, 2.0f); + al::scaleVelocity(this, 0.95f); + + if (rs::isCollidedGround(this)) + al::limitVelocityDirSign(this, al::getGravity(this), 3.0f); + + if (rs::isPuppetHoldActionButton(mPlayerPuppet)) { + al::setNerve(this, &NrvMotorcycle.RideRunStart); + return; + } + + TryJump(this, mPlayerPuppet); +} diff --git a/src/MapObj/Motorcycle.h b/src/MapObj/Motorcycle.h new file mode 100644 index 0000000000..9bc9724fcf --- /dev/null +++ b/src/MapObj/Motorcycle.h @@ -0,0 +1,222 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "Library/LiveActor/ActorPoseUtil.h" +#include "Library/LiveActor/LiveActor.h" + +#include "Camera/PlayerColliderCameraTarget.h" +#include "Player/IUsePlayerCollision.h" + +namespace al { +class CollisionPartsConnector; +class CameraTargetBase; +class TransCameraSubTarget; +class CameraSubTargetTurnParam; + +} // namespace al +class IUsePlayerPuppet; +class PlayerCollider; +class MotorcyclePlayerAnimator; +class BindKeepDemoInfo; +class MotorcycleColliderCameraTarget; + +struct MotorcycleParams { + bool isInBack(const sead::Vector3f pos) { + for (s32 i = 0; i < backContactPoints.size(); i++) { + const sead::Vector3f& contact = *backContactPoints[i]; + const f32 value = -pos.y * contact.y - pos.x * contact.x - pos.z * contact.z; + if (value < 0.0f) + return true; + } + return false; + } + + bool isInFront(const sead::Vector3f pos) { + for (s32 i = 0; i < frontContactPoints.size(); i++) + if (pos.dot(*frontContactPoints[i]) < 0.0f) + return true; + return false; + } + + bool isOnGround = false; + char isOnJump = false; + bool isTouchingGround = false; + bool isTouchingWall = false; + bool isTouchingFront = false; + bool isTouchingBack = false; + sead::Vector3f groundNormalAvg = {0.0f, 0.0f, 0.0f}; + sead::FixedObjArray frontContactPoints; + sead::FixedObjArray backContactPoints; + s32 framesInAir = 0; + sead::Vector3f lastGroundPos = {0.0f, 0.0f, 0.0f}; + sead::Vector3f groundNormal = {0.0f, 1.0f, 0.0f}; +}; + +static_assert(sizeof(MotorcycleParams) == 0xc78); + +struct MotorcyclePose { + f32 steerAngle = 0.0f; + f32 handleAngle = 0.0f; + f32 leanAngle = 0.0f; + f32 jumpAngle = 0.0f; +}; + +static_assert(sizeof(MotorcyclePose) == 0x10); + +struct ParkingParams { + al::LiveActor* actor = nullptr; + MotorcyclePose pose; + sead::Quatf quatA = sead::Quatf::unit; + sead::Quatf quatB = sead::Quatf::unit; + sead::Vector3f mCameraSubTargetPos = {0.0f, 0.0f, 0.0f}; + sead::Vector3f vectorB = {0.0f, 0.0f, 0.0f}; +}; + +static_assert(sizeof(ParkingParams) == 0x50); + +struct AccelerationState { + void reset() { + isAccelerating = false; + accelRate = 0.0f; + } + + bool isAccelerating = false; + f32 accelRate = 0.0f; +}; + +static_assert(sizeof(AccelerationState) == 0x8); + +struct SeRumbleState { + void reset() { + volume = 0.0f; + rumble = 0; + } + + f32 volume = 0.0f; + s32 rumble = 0; +}; + +static_assert(sizeof(SeRumbleState) == 0x8); + +class Motorcycle : public al::LiveActor, public IUsePlayerCollision { +public: + Motorcycle(const char* name); + + void init(const al::ActorInitInfo& info) override; + void initAfterPlacement() override; + void attackSensor(al::HitSensor* self, al::HitSensor* other) override; + bool receiveMsg(const al::SensorMsg* message, al::HitSensor* other, + al::HitSensor* self) override; + void movement() override; + void kill() override; + void updateCollider() override; + void calcAnim() override; + + PlayerCollider* getPlayerCollider() const override { return mPlayerCollider; } + + void exeWait(); + void exeCreep(); + void exeFall(); + void exeJump(); + void endJump(); + void exeReaction(); + void exeReset(); + void exeRideStart(); + void exeRideStartOn(); + void exeRideWait(); + void exeRideWaitJump(); + void endRideWaitJump(); + void exeRideWaitLand(); + void exeRideRunStart(); + void exeRideRun(); + void exeRideRunCollide(); + void exeRideRunFall(); + void exeRideRunWheelie(); + void endRideRunWheelie(); + void exeRideRunLand(); + void exeRideRunJump(); + void endRideRunJump(); + void exeRideRunBoundStart(); + void exeRideRunBound(); + void exeRideRunClash(); + void exeRideParkingSnap(); + void exeRideParkingStart(); + void exeRideParking(); + void exeRideParkingAfter(); + + MotorcycleParams* getParams() const { return mParams; } + + bool isStickWorldPose() const { return mIsStickWorldPose; } + + BindKeepDemoInfo* getBindKeepDemoInfo() const { return mBindKeepDemoInfo; } + + s32* getOffDelay() { return &mGetOffDelay; } + + IUsePlayerPuppet** getPuppy() { return &mPlayerPuppet; } + +private: + IUsePlayerPuppet* mPlayerPuppet = nullptr; + PlayerCollider* mPlayerCollider = nullptr; + MotorcycleParams* mParams = nullptr; + MotorcyclePlayerAnimator* mPlayerAnimator = nullptr; + AccelerationState* mAccelerationState = nullptr; + MotorcyclePose mPose; + SeRumbleState* mSeRumbleState = nullptr; + ParkingParams* mParkingParams = nullptr; + BindKeepDemoInfo* mBindKeepDemoInfo = nullptr; + al::CollisionPartsConnector* mCollisionPartsConnector = nullptr; + MotorcycleColliderCameraTarget* mColliderCameraTarget = nullptr; + + al::TransCameraSubTarget* mTransCameraSubTarget = nullptr; + al::CameraSubTargetTurnParam* mCameraSubTargetTurnParam = nullptr; + sead::Vector3f mCameraSubTargetPos = {0.0f, 0.0f, 0.0f}; + f32 mCameraSubTargetAngle = 0.0f; + + sead::Vector3f* mCoursePoints = nullptr; + s32 mCoursePointSize = 0; + s32 mCoursePointFollowTimer = 0; + + sead::Quatf mBaseQuat = sead::Quatf::unit; + sead::Vector3f mBaseTrans = {0.0f, 0.0f, 0.0f}; + sead::Quatf mPuppetQuat = sead::Quatf::unit; + sead::Vector3f mPuppetTrans = {0.0f, 0.0f, 0.0f}; + + sead::Vector3f mExternalPushVelocity = {0.0f, 0.0f, 0.0f}; + sead::Vector3f mSteerShiftVelocity = {0.0f, 0.0f, 0.0f}; + sead::Vector3f mPrevTransDelta = {0.0f, 0.0f, 0.0f}; + + f32 mPuppetRotZLeft = 0; + f32 mPuppetRotZRight = 0; + sead::Matrix34f mWaterSurfaceMtx = sead::Matrix34f::ident; + s32 mHighSpeedCameraTimer = 0; + s32 mAutoResetTimer = -1; + s32 mGetOffDelay = -1; + s32 mItemSpawnCount = 3; + + bool mIsOnLight = false; + bool mIsAccelerating = false; + bool mIsStickWorldPose = false; + bool mIsOnJump = false; + bool mIsInWater = false; + bool mIsExecutingBindDemo = false; +}; + +static_assert(sizeof(Motorcycle) == 0x250); + +const sead::Vector3f sColliderTrans = {0.0f, 0.0f, -85.0f}; + +class MotorcycleColliderCameraTarget : public PlayerColliderCameraTarget { +public: + MotorcycleColliderCameraTarget(const Motorcycle* actor) + : PlayerColliderCameraTarget(actor, actor) {} + + void calcTrans(sead::Vector3f* offset) const override { + al::calcTransLocalOffset(offset, getActor(), sColliderTrans); + } +}; diff --git a/src/Player/CollisionShapeInfo.h b/src/Player/CollisionShapeInfo.h index 2616e46718..022223ddfd 100644 --- a/src/Player/CollisionShapeInfo.h +++ b/src/Player/CollisionShapeInfo.h @@ -56,8 +56,10 @@ class CollisionShapeInfoArrow : public CollisionShapeInfoBase { void calcWorldShapeInfo(const sead::Matrix34f&, f32) override; void calcRelativeShapeInfo(const sead::Matrix34f&) override; + s32 get_18() const { return _18; } + private: - u8 _18[0x1c - 0x18]; + s32 _18; sead::Vector3f mBoundingCenter; f32 mBoundingRadius; sead::Vector3f mBoundingCenterWorld; diff --git a/src/Player/CollisionShapeKeeper.h b/src/Player/CollisionShapeKeeper.h index ff2e519d54..9bba561b25 100644 --- a/src/Player/CollisionShapeKeeper.h +++ b/src/Player/CollisionShapeKeeper.h @@ -54,6 +54,8 @@ class CollisionShapeKeeper { void set54(f32 value) { _54 = value; } + s32 getNumCollideResult() const { return mNumCollideResult; } + private: sead::Vector3f mBoundingCenter = {0.0f, 0.0f, 0.0f}; f32 mBoundingRadius = 0.0f; diff --git a/src/Player/PlayerCollider.h b/src/Player/PlayerCollider.h index d5604473ab..e3f14dde75 100644 --- a/src/Player/PlayerCollider.h +++ b/src/Player/PlayerCollider.h @@ -59,6 +59,8 @@ class PlayerCollider : public al::HioNode, public al::IUseCollision { void set1b0(f32 value) { _1b0 = value; } + CollisionShapeKeeper* getCollisionShapeKeeper() const { return mCollisionShapeKeeper; } + private: al::CollisionDirector* mCollisionDirector; const sead::Matrix34f* mMtxPtr; diff --git a/src/Util/PlayerPuppetFunction.h b/src/Util/PlayerPuppetFunction.h index 7bde4603b9..905672583c 100644 --- a/src/Util/PlayerPuppetFunction.h +++ b/src/Util/PlayerPuppetFunction.h @@ -58,7 +58,7 @@ void invalidatePuppetCollider(IUsePlayerPuppet* playerPuppet); bool isCollidedPuppetGround(IUsePlayerPuppet* playerPuppet); const sead::Vector3f& getCollidedPuppetGroundNormal(IUsePlayerPuppet* playerPuppet); void validatePuppetCollider(IUsePlayerPuppet* playerPuppet); -void requestDamage(IUsePlayerPuppet* playerPuppet); +bool requestDamage(IUsePlayerPuppet* playerPuppet); void clearStainEffect(IUsePlayerPuppet* playerPuppet); void copyPuppetDitherAlpha(IUsePlayerPuppet* playerPuppet, const al::LiveActor* actor); void validatePuppetLookAt(IUsePlayerPuppet* playerPuppet); @@ -95,7 +95,7 @@ bool isPuppetHoldAnyButton(const IUsePlayerPuppet* playerPuppet); bool isPuppetMoveStickOn(const IUsePlayerPuppet* playerPuppet); bool isPuppetStartWorldWarp(const IUsePlayerPuppet* playerPuppet); bool isPuppetCancelWorldWarp(const IUsePlayerPuppet* playerPuppet); -const sead::Vector2f& getPuppetMoveStick(const IUsePlayerPuppet* playerPuppet); +sead::Vector2f getPuppetMoveStick(const IUsePlayerPuppet* playerPuppet); bool isTriggerGetOff(const IUsePlayerPuppet* playerPuppet); f32 getPuppetPoseRotZDegreeLeft(const IUsePlayerPuppet* playerPuppet); f32 getPuppetPoseRotZDegreeRight(const IUsePlayerPuppet* playerPuppet);