diff --git a/include/Camera/Camera.hpp b/include/Camera/Camera.hpp index fb64d583..330be67b 100644 --- a/include/Camera/Camera.hpp +++ b/include/Camera/Camera.hpp @@ -98,7 +98,7 @@ class CPolarSubCamera : public JDrama::TLookAtCamera { void ctrlGameCamera_(); void perform(u32, JDrama::TGraphics*); void getOffsetAngleX() const; - void getOffsetAngleY() const; + s16 getOffsetAngleY() const; void getFinalAngleZ() const; ~CPolarSubCamera(); void controlByCameraCode_(int*); diff --git a/include/MSound/MSound.hpp b/include/MSound/MSound.hpp index c858090f..5888b545 100644 --- a/include/MSound/MSound.hpp +++ b/include/MSound/MSound.hpp @@ -126,9 +126,43 @@ class MSound : public JAIBasic { } // TODO: startSoundActor was also very likely here - void startForceJumpSound(Vec*, u32, f32, u32) { } + void startForceJumpSound(Vec*, u32, f32, u32); }; +#pragma dont_inline on +void MSound::startForceJumpSound(Vec* pos, u32 groundType, f32 height, + u32 dist) +{ + u32 soundID; + u8 type = (u8)groundType; + + switch (type) { + case 21: + case 23: + case 29: + soundID = 0x180A; + break; + case 30: + default: + if (dist < 6000) { + soundID = 0x1810; + } else if (dist < 12000) { + soundID = 0x1811; + } else { + soundID = 0x1812; + } + break; + } + + if (gateCheck(soundID)) { + if (gateCheck(soundID)) { + MSoundSESystem::MSoundSE::startSoundActor(soundID, pos, 0, + (JAISound**)NULL, 0, 4); + } + } +} +#pragma dont_inline off + extern MSound* MSGMSound; extern JAIBasic* MSGBasic; extern MSound* gpMSound; diff --git a/include/Map/PollutionManager.hpp b/include/Map/PollutionManager.hpp index 060b0eac..97d29737 100644 --- a/include/Map/PollutionManager.hpp +++ b/include/Map/PollutionManager.hpp @@ -28,7 +28,7 @@ class TPollutionManager : public TJointModelManager { void stamp(u16, f32 x, f32 y, f32 z, f32 range); void clean(f32, f32, f32, f32); void stampGround(u16, f32, f32, f32, f32); - u16 getPollutionType(f32, f32, f32) const; + u32 getPollutionType(f32, f32, f32) const; u32 getPollutionDegree() const; void isProhibit(f32, f32, f32) const; bool isPolluted(f32, f32, f32) const; diff --git a/include/Player/MarioMain.hpp b/include/Player/MarioMain.hpp index 2203ec08..66c69fde 100644 --- a/include/Player/MarioMain.hpp +++ b/include/Player/MarioMain.hpp @@ -84,7 +84,14 @@ class TMarioGamePad; class TMario : public TTakeActor, public TDrawSyncCallback { public: - struct JumpSlipRecord; + struct JumpSlipRecord { + s16 mTimer; + u16 _pad; + u32 mStatus; + u32 mJumpStatus; + u32 mFallbackStatus; + u32 _unk10; + }; class TOptionParams : public TParams { public: @@ -610,7 +617,7 @@ class TMario : public TTakeActor, public TDrawSyncCallback { virtual MtxPtr getTakingMtx(); virtual bool moveRequest(const JGeometry::TVec3&); - virtual void drawSyncCallback(u16); + void drawSyncCallback(u16); virtual void initValues(); virtual void checkReturn(); virtual void checkController(JDrama::TGraphics*); @@ -715,7 +722,7 @@ class TMario : public TTakeActor, public TDrawSyncCallback { Mtx* getRootAnmMtx(); void getHeadRot(); void getJumpIntoWaterModelData(); - void jumpMain(); + BOOL jumpMain(); void fallDead(); void diving(); void hipAttacking(); @@ -723,7 +730,7 @@ class TMario : public TTakeActor, public TDrawSyncCallback { void wireJumping(); void rotateJumping(); void rocketing(); - void rocketCheck(); + BOOL rocketCheck(); void boardJumping(); void rotateBroadJumping(); void broadJumping(); @@ -789,11 +796,11 @@ class TMario : public TTakeActor, public TDrawSyncCallback { void checkRideMovement(); void getActorMtx(const THitActor&, f32 (*)[4]); void checkCurrentPlane(); - void getDmgMapCode(int) const; - void checkGroundPlane(f32, f32, f32, f32*, const TBGCheckData**); + TEParams* getDmgMapCode(int code) const; + BOOL checkGroundPlane(f32 x, f32 y, f32 z, f32* outHeight, const TBGCheckData** outPlane); void makeHistory(); void checkStickSmash(); - void checkStickRotate(int*); + BOOL checkStickRotate(int*); void getLRLevel(unsigned char); void getDizzyPower(); void getDizzyAngle(); @@ -811,14 +818,14 @@ class TMario : public TTakeActor, public TDrawSyncCallback { void checkGraffitoFire(); void checkGraffitoDamage(); void makeGraffitoDamage(const TMario::TEParams&); - void checkAllMotions(); + BOOL checkAllMotions(); BOOL changePlayerDropping(u32, u32); BOOL changePlayerJumping(u32, u32); - void changePlayerTriJump(); + BOOL changePlayerTriJump(); BOOL changePlayerStatus(u32, u32, bool); void throwMario(const JGeometry::TVec3&, f32); void setStatusToRunning(u32, u32); - void setStatusToJumping(u32, u32); + u32 setStatusToJumping(u32, u32); void setPlayerJumpSpeed(f32, f32); void setMissJumping(); void isTurnning(); @@ -826,13 +833,13 @@ class TMario : public TTakeActor, public TDrawSyncCallback { void checkPlayerAround(int, f32); void isJumpMiss(); void isSlipLimit(); - void getSlideStopCatch(); - void getSlideStopNormal(); - void canSlipJump(); - void isSlipStart(); - void isFrontSlip(int); - void checkRoofPlane(const Vec&, f32, const TBGCheckData**); - void checkWallPlane(Vec*, f32, f32); + f32 getSlideStopCatch(); + f32 getSlideStopNormal(); + BOOL canSlipJump(); + BOOL isSlipStart(); + bool isFrontSlip(int); + f32 checkRoofPlane(const Vec&, f32, const TBGCheckData**); + const TBGCheckData* checkWallPlane(Vec*, f32, f32); void setPlayerVelocity(f32); void setNormalAttackArea(); void changePos(const Vec&); @@ -845,36 +852,36 @@ class TMario : public TTakeActor, public TDrawSyncCallback { void windMove(const JGeometry::TVec3&); void flowMove(const JGeometry::TVec3&); void warpRequest(const JGeometry::TVec3&, f32); - void isForceSlip(); + BOOL isForceSlip(); void getRidingMtx(f32 (*)[4]); bool isWallInFront() const; bool isInvincible() const; bool isUnderWater() const; - void canSquat() const; - void getJumpSlideControl() const; - void getJumpAccelControl() const; - BOOL jumpProcess(int); + BOOL canSquat() const; + f32 getJumpSlideControl() const; + f32 getJumpAccelControl() const; + int jumpProcess(int); void fallProcess(); void isFallCancel(); - void checkGroundAtJumping(const Vec&, int); - void hangonCheck(const TBGCheckData*, const Vec&, const Vec&); - void barProcess(); - void walkProcess(); - void waitProcess(); + int checkGroundAtJumping(const Vec&, int); + int hangonCheck(const TBGCheckData*, const Vec&, const Vec&); + int barProcess(); + int walkProcess(); + int waitProcess(); void stopProcess(); - void checkGroundAtWalking(Vec*); + int checkGroundAtWalking(Vec*); void checkDescent(); void keepDistance(const THitActor&, f32); void keepDistance(const JGeometry::TVec3&, f32, f32); void playerRefrection(int); - void moveMain(); + BOOL moveMain(); void broadJumpSlip(); void ultraJumpSlip(); void uTurnJumpSlip(); void secJumpSlip(); void landSlip(); void jumpSlip(); - void jumpSlipEvents(TMario::JumpSlipRecord*); + BOOL jumpSlipEvents(TMario::JumpSlipRecord*); void jumpSlipCommon(short, u32); void loserDown(); void catchDown(); @@ -884,7 +891,7 @@ class TMario : public TTakeActor, public TDrawSyncCallback { void shortBackDown(); void foreDown(); void backDown(); - void downingCommon(int, f32, int); + f32 downingCommon(int, f32, int); void oilSlope(); void oilSlip(); void oilRun(); @@ -909,31 +916,31 @@ class TMario : public TTakeActor, public TDrawSyncCallback { void changePlayerWaiting(); void doBraking(f32); void doSurfing(); - void getSurfingParamsGround(); // UNUSED - void getSurfingParamsWater(); + TSurfingParams& getSurfingParamsGround(); // UNUSED + TSurfingParams& getSurfingParamsWater(); void doRunning(); void doStopping(); void doSlipping(f32); void slopeProcess(); - void doSliding(f32); + int doSliding(f32); void slideProcess(f32, f32); - void getSlideStickMult(); - void getChangeAngleSpeed(); + f32 getSlideStickMult(); + f32 getChangeAngleSpeed(); void getSlopeSlideAccele(f32*, f32*); void getSlopeNormalAccele(f32*, f32*); void doRunningAnimation(); void getRunningInWaterBrake(); - void isRunningInWater(); + BOOL isRunningInWater(); void changePlayerCatching(); - void isRunningTurnning(); - void isRunningSlipStart(); + BOOL isRunningTurnning(); + BOOL isRunningSlipStart(); void changePlayerPower(f32, u32, u32); void clashStandard(u32, u32); void postureControl(); - void isThrowStart(); - void considerRotateStart(); - void specMain(); - void fencePunch(); + BOOL isThrowStart(); + BOOL considerRotateStart(); + BOOL specMain(); + BOOL fencePunch(); void fenceMove(); void fenceJumpCatch(); void fenceCatch(); @@ -954,7 +961,7 @@ class TMario : public TTakeActor, public TDrawSyncCallback { void wireWaitToSWaitL(); void wireSWait(); void wireWait(); - void wireMove(f32); + int wireMove(f32); void getOnWirePosAngle(JGeometry::TVec3*, short*); void taken(); void hangJumping(); @@ -970,16 +977,16 @@ class TMario : public TTakeActor, public TDrawSyncCallback { void moveRoof(); void waitRoof(); void hangRoof(); - void roofCommonEvents(); + BOOL roofCommonEvents(); void doRoofWaitingProcess(); - void doRoofMovingProcess(); - void hangingCheckRoof(JGeometry::TVec3*); + int doRoofMovingProcess(); + int hangingCheckRoof(JGeometry::TVec3*); void barHang(); void barClimb(); void barWait(); void barJumpSetting(); void stateMachineUpper(); - void checkPumpEnable(); + BOOL checkPumpEnable(); void checkPumping(); bool askJumpIntoWaterEffectExist() const; void sinkInSandEffect(); @@ -1034,7 +1041,7 @@ class TMario : public TTakeActor, public TDrawSyncCallback { void emitParticle(int); void moveParticle(); void initParticle(); - void waitMain(); + BOOL waitMain(); void slipEnd(); void brakeEnd(); void hipAttackEnd(); @@ -1046,25 +1053,25 @@ class TMario : public TTakeActor, public TDrawSyncCallback { void landEnd(); void secJumpEnd(); void jumpEnd(); - void jumpEndEvents(u32); - void jumpEndCommon(int, int); + BOOL jumpEndEvents(u32); + BOOL jumpEndCommon(int, int); void pullEnd(); - void squatStandup(); + BOOL squatStandup(); void squatStart(); - void squating(); + BOOL squating(); void getSideWalkValues(E_SIDEWALK_TYPE*, f32*, f32*); void wakeup(); - void sleeping(); - void sleepily(); - void waiting(); + BOOL sleeping(); + BOOL sleepily(); + BOOL waiting(); void changeMontemanWaitingAnim(); void stopCommon(int, int); - void waitingCommonEvents(); + BOOL waitingCommonEvents(); void checkPutStart(); - void canPut(); - void canSleep(); - void startTalking(); - void swimMain(); + BOOL canPut(); + BOOL canSleep(); + BOOL startTalking(); + BOOL swimMain(); void swimPDown(); void swimDown(); void swimPDamage(); @@ -1077,8 +1084,8 @@ class TMario : public TTakeActor, public TDrawSyncCallback { void swimWaitToPaddle(); void swimWait(); void swimStart(); - void checkSwimToHangFence(); - void checkSwimJump(); + BOOL checkSwimToHangFence(); + BOOL checkSwimJump(); void doSwimming(); void isSwimWaiting(); void setGamePad(TMarioGamePad*); @@ -1152,7 +1159,7 @@ class TMario : public TTakeActor, public TDrawSyncCallback { // Fabricated bool checkFlag(u32 attribute) const { - return unk118 & attribute ? true : false; + return mState & attribute ? true : false; } // Fabricated @@ -1168,15 +1175,15 @@ class TMario : public TTakeActor, public TDrawSyncCallback { } // Fabricated - bool checkUnk380(u32 message) const + bool checkPumpState(u32 state) const { - return unk380 == message ? true : false; + return mPumpState == state ? true : false; } // Fabricated - bool fabricatedUnk380Inline() const + bool isPumpIdle() const { - if (unk380 == 0 || unk380 == 1) { + if (mPumpState == 0 || mPumpState == 1) { return true; } return false; @@ -1206,7 +1213,7 @@ class TMario : public TTakeActor, public TDrawSyncCallback { /* 0x94 */ JGeometry::TVec3 mFaceAngle; /* 0x9A */ s16 mModelFaceAngle; /* 0x9C */ s16 unk9C; - /* 0x9E */ s16 unk9E; + /* 0x9E */ s16 mSlideAngle; // direction of sliding velocity (from matan(velZ, velX)) /* 0xA0 */ u32 unkA0; /* 0xA4 */ JGeometry::TVec3 mVel; @@ -1214,7 +1221,8 @@ class TMario : public TTakeActor, public TDrawSyncCallback { /* 0xB4 */ f32 mSlideVelX; /* 0xB8 */ f32 mSlideVelZ; - /* 0xBC */ char unkBC[0x1C]; + /* 0xBC */ f32 unkBC; + /* 0xC0 */ char unkC0[0x18]; /* 0xD8 */ const TBGCheckData* mWallPlane; // TBGCheckData 0xD8 /* 0xDC */ const TBGCheckData* mRoofPlane; // TBGCheckData 0xDC @@ -1240,11 +1248,11 @@ class TMario : public TTakeActor, public TDrawSyncCallback { /* 0x10C */ u32 unk10C; /* 0x110 */ u32 unk110; - /* 0x114 */ u16 unk114; - /* 0x116 */ u16 unk116; - /* 0x118 */ u32 unk118; // some flag / attribute + /* 0x114 */ u16 mSubState; + /* 0x116 */ u16 mSubStateTimer; + /* 0x118 */ u32 mState; // E_MARIO_FLAG bitfield, tested via checkFlag() - /* 0x11C */ u32 unk11C; + /* 0x11C */ u32 mPrevState; // previous frame's mState /* 0x120 */ s16 mHealth; @@ -1264,8 +1272,8 @@ class TMario : public TTakeActor, public TDrawSyncCallback { /* 0x14E */ u16 unk14E; /* 0x150 */ u32 unk150; /* 0x154 */ TWaterEmitInfo* unk154; - /* 0x158 */ u32 unk158; - /* 0x15C */ u32 unk15C; + /* 0x158 */ TWaterEmitInfo* unk158; + /* 0x15C */ f32 unk15C; /* 0x160 */ JGeometry::TVec3 unk160[4]; // Bone position, probably larger array /* 0x190 */ u32 unk190; @@ -1282,22 +1290,29 @@ class TMario : public TTakeActor, public TDrawSyncCallback { /* 0x1E4 */ u32 unk1E4; /* 0x1E8 */ u32 unk1E8; /* 0x1EC */ f32 unk1EC; - /* 0x1F0 */ char unk1F0[0x29C - 0x1F0]; + /* 0x1F0 */ char unk1F0[0x250 - 0x1F0]; + /* 0x250 */ Mtx mGroundMtx; + /* 0x280 */ char unk280[0x29C - 0x280]; /* 0x29C */ JGeometry::TVec3 unk29C; - /* 0x2A8 */ char unk2A8[0x2BC - 0x2A8]; + /* 0x2A8 */ JGeometry::TVec3 unk2A8; + /* 0x2B4 */ u32 unk2B4; + /* 0x2B8 */ u16 unk2B8; + /* 0x2BA */ s16 unk2BA; /* 0x2BC */ f32 unk2BC; - /* 0x2C0 */ char unk2C0[0x348 - 0x2C0]; + /* 0x2C0 */ TLiveActor* mRidingActor; + /* 0x2C4 */ char unk2C4[0x348 - 0x2C4]; /* 0x348 */ f32 unk348; /* 0x34C */ u16 unk34C; /* 0x34E */ u16 unk34E; /* 0x350 */ s32 unk350; - /* 0x354 */ char unk354[0x370 - 0x354]; - /* 0x370 */ u32 unk370; + /* 0x354 */ char unk354[0x36C - 0x354]; + /* 0x36C */ f32 unk36C; + /* 0x370 */ f32 unk370; /* 0x374 */ u32 unk374; /* 0x378 */ u32 unk378; /* 0x37C */ u16 unk37C; /* 0x37E */ u16 unk37E; - /* 0x380 */ u32 unk380; // pump state? + /* 0x380 */ u32 mPumpState; // FLUDD pump phase (0=idle, 1=requested, 2=active, 3=holding) /* 0x384 */ THitActor* unk384; // Last receiveMessage sender /* 0x388 */ u8 unk388; // TODO: Make enum (0 = red, 1 = yellow, 2 = green) diff --git a/include/Player/Watergun.hpp b/include/Player/Watergun.hpp index f59650b8..18e20518 100644 --- a/include/Player/Watergun.hpp +++ b/include/Player/Watergun.hpp @@ -198,7 +198,7 @@ class TWaterGun { /* 0x1C85 */ u8 mSecondNozzle; /* 0x1C86 */ bool mIsEmitWater; /* 0x1C87 */ u8 unk1C87; - /* 0x1C88 */ u32 unk1C88; + /* 0x1C88 */ f32 unk1C88; /* 0x1C8C */ u8 mCurrentPressure; /* 0x1C8D */ u8 mPreviousPressure; /* 0x1C8E */ u8 unk1C8E; @@ -235,4 +235,25 @@ class TWaterGun { /* 0x1D14 */ TWaterGunParams mWatergunParams; }; +bool TWaterGun::isEmitting() +{ + if (mCurrentWater == 0) { + return false; + } + s32 kind = getCurrentNozzle()->getNozzleKind(); + if (kind == 1) { + TNozzleTrigger* triggerNozzle + = (TNozzleTrigger*)getCurrentNozzle(); + if (triggerNozzle->unk385 == TNozzleTrigger::ACTIVE) { + return true; + } else { + return false; + } + } else if (getCurrentNozzle()->unk378 > 0.0f) { + return true; + } else { + return false; + } +} + #endif diff --git a/include/Player/Yoshi.hpp b/include/Player/Yoshi.hpp index 47eb6894..affffdf4 100644 --- a/include/Player/Yoshi.hpp +++ b/include/Player/Yoshi.hpp @@ -28,7 +28,7 @@ class TYoshi { void initInLoadAfter(); void kill(); void movement(); - bool onYoshi(); + BOOL onYoshi(); void ride(); void setEggYoshiPtr(void*); // TEggYoshi* void thinkAnimation(); @@ -73,4 +73,8 @@ class TYoshi { extern JUtility::TColor bodyColor[4]; +#pragma dont_inline on +BOOL TYoshi::onYoshi() { return (u8)mState == MOUNTED ? 1 : 0; } +#pragma dont_inline off + #endif diff --git a/src/Enemy/hamukuri.cpp b/src/Enemy/hamukuri.cpp index 95e6f17d..d0bb2ad6 100644 --- a/src/Enemy/hamukuri.cpp +++ b/src/Enemy/hamukuri.cpp @@ -759,7 +759,7 @@ bool THamuKuri::isFindMario(f32 param_1) if (unk198) return false; - if (gpMarioOriginal->unk380 == 0 + if (gpMarioOriginal->mPumpState == 0 && !*(int*)((u8*)gpMarioOriginal->mWaterGun + 0x1C80) /* TODO: */) { unk194 = unk1F4->mSLGiveUpLength.get(); unk194 *= 3.0f; diff --git a/src/GC2D/CardLoad.cpp b/src/GC2D/CardLoad.cpp index b5563ac3..c16c1297 100644 --- a/src/GC2D/CardLoad.cpp +++ b/src/GC2D/CardLoad.cpp @@ -733,7 +733,7 @@ void TCardLoad::perform(u32 param_1, JDrama::TGraphics* param_2) case 10: MSBgm::startBGM(0x80010010); unk38->onFlag(0x1); - gpMarioOriginal->unk118 &= ~0x400; + gpMarioOriginal->mState &= ~0x400; unk14 = 9; unk18 = 1; unkF0->getPane()->setAlpha(0); diff --git a/src/Map/PollutionManager.cpp b/src/Map/PollutionManager.cpp index 5ac53b00..66ac4b77 100644 --- a/src/Map/PollutionManager.cpp +++ b/src/Map/PollutionManager.cpp @@ -42,7 +42,7 @@ void TPollutionManager::stampGround(u16 param_1, f32 param_2, f32 param_3, getLayer(i)->stamp(param_1, param_2, param_3, param_4, param_5); } -u16 TPollutionManager::getPollutionType(f32 param_1, f32 param_2, +u32 TPollutionManager::getPollutionType(f32 param_1, f32 param_2, f32 param_3) const { for (int i = 0; i < getJointModelNum(); ++i) diff --git a/src/Map/Shimmer.cpp b/src/Map/Shimmer.cpp index 8d29e454..da5578f2 100644 --- a/src/Map/Shimmer.cpp +++ b/src/Map/Shimmer.cpp @@ -27,7 +27,7 @@ void TShimmer::far() { } void TShimmer::perform(u32 param_1, JDrama::TGraphics* param_2) { - if (gpMarioOriginal->unk118 & 0x4000 ? true : false) + if (gpMarioOriginal->mState & 0x4000 ? true : false) return; if (param_1 & 1) { diff --git a/src/Player/MarioAccess.cpp b/src/Player/MarioAccess.cpp index fce8f49d..5ce0a0de 100644 --- a/src/Player/MarioAccess.cpp +++ b/src/Player/MarioAccess.cpp @@ -86,7 +86,7 @@ bool SMS_IsMarioDashing() { bool ret; - if ((gpMarioOriginal->unk118 & 0x4000) != 0) { + if ((gpMarioOriginal->mState & 0x4000) != 0) { ret = true; } else { ret = false; @@ -253,7 +253,7 @@ void SMS_SetMarioAccessParams() gpMarioSpeedZ = speed + 2; gpMarioLightID = &gpMarioOriginal->mLightID; - gpMarioFlag = &gpMarioOriginal->unk118; + gpMarioFlag = &gpMarioOriginal->mState; gpMarioThrowPower = &gpMarioOriginal->mDeParams.mThrowPower.value; gpMarioGroundPlane = &gpMarioOriginal->mGroundPlane; diff --git a/src/Player/MarioAction.cpp b/src/Player/MarioAction.cpp index 944a3ea2..dae9ec21 100644 --- a/src/Player/MarioAction.cpp +++ b/src/Player/MarioAction.cpp @@ -17,7 +17,7 @@ BOOL TMario::taking() unk384 = nullptr; } if (isLast1AnimeFrame()) { - unk380 = 2; + mPumpState = 2; unk37E = 0; return changePlayerStatus(0xC400201, 0, false); } else { diff --git a/src/Player/MarioAutodemo.cpp b/src/Player/MarioAutodemo.cpp index 3146bfb0..687cc5fd 100644 --- a/src/Player/MarioAutodemo.cpp +++ b/src/Player/MarioAutodemo.cpp @@ -134,7 +134,7 @@ BOOL TMario::warpIn() gpMarDirector->setNextStage(nextStage, mHolder); } - unk114 |= 2; + mSubState |= 2; J3DFrameCtrl& frameCtrl = getMotionFrameCtrl(); frameCtrl.setRate(0.0f); @@ -160,7 +160,7 @@ BOOL TMario::warpIn() } case 1: { if ((f32)mActionTimer > mAutoDemoParams.mWarpInBallsDispTime.get()) { - unk114 &= ~(1 << 1); + mSubState &= ~(1 << 1); rumbleStart(0x15, 0x14); } if (mAutoDemoParams.mWarpInBallsTime.get() > (f32)mActionTimer) { @@ -172,11 +172,11 @@ BOOL TMario::warpIn() break; } case 2: - unk114 &= ~(1 << 1); + mSubState &= ~(1 << 1); rumbleStart(0x14, mMotorParams.mMotorWall.get() / 2); if ((f32)mActionTimer > mAutoDemoParams.mWarpInCapturedTime.get()) { - unk114 &= ~(1 << 1); + mSubState &= ~(1 << 1); mActionTimer = 0; mHolder->receiveMessage(this, HIT_MESSAGE_ATTACK); mActionState = 3; @@ -184,7 +184,7 @@ BOOL TMario::warpIn() break; case 3: - unk118 |= MARIO_FLAG_IS_PERFORMING; + mState |= MARIO_FLAG_IS_PERFORMING; break; } @@ -200,7 +200,7 @@ BOOL TMario::isUnUsualStageStart() if ((gpMarDirector->mMap == 0x3A) && (gpMarDirector->unk7D == 0 || gpMarDirector->unk7D == 1)) { changePlayerStatus(0x800447, 0, true); - unk114 |= 2; + mSubState |= 2; if (mPinaRail != nullptr) { mPinaRail->setBckFromIndex(0); mPinaRail->getFrameCtrl(0)->setRate(0.5f); @@ -215,13 +215,13 @@ BOOL TMario::isUnUsualStageStart() } if (SMS_isDivingMap()) { - unk114 |= 2; + mSubState |= 2; // I suspect some inline stuff here, weird to check right after you set // it - unk118 |= MARIO_FLAG_HELMET_FLW_CAMERA; - unk118 |= MARIO_FLAG_HELMET; - unk118 |= MARIO_FLAG_HAS_FLUDD; + mState |= MARIO_FLAG_HELMET_FLW_CAMERA; + mState |= MARIO_FLAG_HELMET; + mState |= MARIO_FLAG_HAS_FLUDD; if (checkFlag(MARIO_FLAG_HAS_FLUDD)) { mWaterGun->changeNozzle(2, true); @@ -247,7 +247,7 @@ BOOL TMario::rollingStart(const JGeometry::TVec3* warpPos, f32 rotation) return TRUE; } else { if (mAction == 0x133f) { - unk114 &= ~2; + mSubState &= ~2; if (warpPos != nullptr) { warpRequest(*warpPos, rotation); mFaceAngle.set(0, DEG2SHORTANGLE(rotation), 0); @@ -270,7 +270,7 @@ BOOL TMario::returnStart(const JGeometry::TVec3* warpPos, f32 rotation, if (mAction == 0x133f) { int offsetPlayerStatus = playerStatus << 8; if (flag == TRUE) { - unk114 &= ~2; + mSubState &= ~2; if (warpPos != nullptr) { warpRequest(*warpPos, rotation); mFaceAngle.set(0, DEG2SHORTANGLE(rotation), 0); @@ -281,7 +281,7 @@ BOOL TMario::returnStart(const JGeometry::TVec3* warpPos, f32 rotation, setAnimation(0xC3, 1.0f); changePlayerStatus(0x1337, offsetPlayerStatus | 2, true); } else { - unk114 &= ~2; + mSubState &= ~2; if (warpPos != nullptr) { f32 flippedAngle = rotation + 180.0f; warpRequest(*warpPos, flippedAngle); @@ -304,7 +304,7 @@ BOOL TMario::waitingStart(const JGeometry::TVec3* warpPos, f32 rotation) if (result != 0) { return TRUE; } else { - unk114 &= ~2; + mSubState &= ~2; if (warpPos != nullptr) { warpRequest(*warpPos, rotation); mFaceAngle.set(0, DEG2SHORTANGLE(rotation), 0); @@ -314,7 +314,7 @@ BOOL TMario::waitingStart(const JGeometry::TVec3* warpPos, f32 rotation) &mFloorPosition.y, &mGroundPlane); unk2BC = mFloorPosition.y; setAnimation(0xC3, 1.0f); - unk114 |= 2; + mSubState |= 2; changePlayerStatus(0xC400201, 0, true); return TRUE; } @@ -324,7 +324,7 @@ BOOL TMario::waitingStart(const JGeometry::TVec3* warpPos, f32 rotation) BOOL TMario::toroccoStart() { changePlayerStatus(0x800447, 0, true); - unk114 |= 2; + mSubState |= 2; if (mPinaRail != nullptr) { mPinaRail->setBckFromIndex(0); mPinaRail->getFrameCtrl(0)->setRate(0.5f); @@ -344,10 +344,10 @@ BOOL TMario::warpOut() // volatile u32 padding[4]; mActionTimer += 1; - unk114 |= 2; + mSubState |= 2; switch (mActionState) { case 0: - unk114 |= 2; + mSubState |= 2; if ((mActionArg & 0xff) == 2) { setAnimation(0x13B, 1.0f); } else { @@ -368,14 +368,14 @@ BOOL TMario::warpOut() } if (mActionTimer >= unkDelay) { if (checkFlag(MARIO_FLAG_HELMET_FLW_CAMERA)) { - unk114 |= 2; + mSubState |= 2; return changePlayerStatus(0x891, 0, true); } mActionState = 2; } break; case 2: - unk114 |= 2; + mSubState |= 2; if ((mActionArg & 0xff) == 2) { setAnimation(0x13C, 1.0f); } else { @@ -386,7 +386,7 @@ BOOL TMario::warpOut() } break; case 3: - unk114 |= 2; + mSubState |= 2; switch (mActionArg & 0xff) { case 0: return changePlayerStatus(0xC000230, 0, true); @@ -557,7 +557,7 @@ BOOL TMario::demoMain() result = FALSE; break; case 0x133F: - unk114 &= ~2; + mSubState &= ~2; result = FALSE; break; } diff --git a/src/Player/MarioCheckCol.cpp b/src/Player/MarioCheckCol.cpp index 8b137891..7664bbd8 100644 --- a/src/Player/MarioCheckCol.cpp +++ b/src/Player/MarioCheckCol.cpp @@ -1 +1,333 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// MarioCheckCol: -inline deferred, functions in REVERSE address order. + +// hitNormal: 0x80161A78, size 0x210 +void TMario::hitNormal(THitActor* actor) +{ + u32 action = mAction; + + // Check action bit 12 (grounded/walking) + if (action & 0x1000) { + // Check speed < 0 + if (mVel.y < 0.0f) { + // Check actor below mario + if (actor->mPosition.y < mPosition.y) { + // Check action range + if ((action - 0x80000000) == 0x8A9) { + // Call virtual receiveMessage(this, 1) + u32* vtbl = *(u32**)actor; + typedef BOOL (*MsgFunc)(THitActor*, THitActor*, u32); + BOOL result = ((MsgFunc)vtbl[0x28])(actor, this, 1); + if (result) { + u32 actorType = *(u32*)((u8*)actor + 0x4C); + if ((actorType - 0x08000000) <= 1) { + setPlayerVelocity(0.0f); + unk78 = unk78 & ~(1 << 8); + } + } + return; + } + + // Normal grounded hit + keepDistance(*actor, 0.0f); + return; + } + } + } + + // Check flag bit 10 + if (checkFlag(0x400)) { + if (actor->mPosition.y > mPosition.y) { + u32* vtbl = *(u32**)actor; + typedef void (*MsgFunc)(THitActor*, THitActor*, u32); + ((MsgFunc)vtbl[0x28])(actor, this, 3); + return; + } + } + + // Check action ranges for kick/trample + u32 act = mAction; + if ((act - 0x80000000) == 0x456 || + (act - 0x7C000000) == 0x45D || + (act - 0x80000000) == 0x88A) { + u32* vtbl = *(u32**)actor; + typedef void (*MsgFunc)(THitActor*, THitActor*, u32); + ((MsgFunc)vtbl[0x28])(actor, this, 12); + ((MsgFunc)vtbl[0x28])(actor, this, 0); + } + + // Check watergun nozzle state + TWaterGun* wg = mWaterGun; + u8 nozzleState = *(u8*)((u8*)wg + 0x1C84); + if (nozzleState != 0) + return; + + u8 emitState = *(u8*)((u8*)wg + 0x1C86); + if (emitState == 0) + return; + + // Create a hit position copy and push via receiveMessage(actor, 15) + JGeometry::TVec3 hitPos; + hitPos.x = mPosition.x; + hitPos.y = mPosition.y; + hitPos.z = mPosition.z; + hitPos.y += *(f32*)((u8*)0 + 0); // height offset + // Store position to a temp area and call virtual + u32* vtbl2 = *(u32**)actor; + typedef void (*MsgFunc)(THitActor*, THitActor*, u32); + ((MsgFunc)vtbl2[0x28])(actor, this, 15); +} + +// hangPole: 0x801617E4, size 0x294 +void TMario::hangPole(THitActor* actor) +{ + // Check action bit 20 + if (checkActionFlag(0x100000)) + return; + + // Check conditions for grabbing pole + u8 canGrab = 0; + if (*(u32*)((u8*)this + 0x6C) == 0) { + if (!isMario()) { + canGrab = 1; + } + } + + u8 shouldGrab; + if (!canGrab) { + shouldGrab = 0; + } else { + u32 action = mAction; + u32 low9 = action & 0x1FF; + if (low9 >= 0x80 && low9 <= 0x9F) { + shouldGrab = 1; + } else if (action & 0x200000) { + shouldGrab = 1; + } else { + shouldGrab = 0; + } + } + + if ((u8)shouldGrab != 1) { + // Normal hit - call virtual for height and keepDistance + u32* vtbl = *(u32**)actor; + typedef void (*HeightFunc)(THitActor*, f32); + ((HeightFunc)vtbl[0x2C])(actor, mPosition.y); + keepDistance(actor->mPosition, *(f32*)((u8*)0 + 0), 0.0f); + return; + } + + // Distance check in XZ plane + f32 actorZ = actor->mPosition.z; + f32 marioZ = mPosition.z; + f32 actorX = actor->mPosition.x; + f32 dz = actorZ - marioZ; + f32 marioX = mPosition.x; + f32 dx = actorX - marioX; + + f32 distSqXZ = dx * dx + dz * dz; + f32 distXZ = 0.0f; + if (distSqXZ > 0.0f) { + distXZ = sqrtf(distSqXZ); + } + + f32 safeDistXZ = distXZ; + if (0.0f == distXZ) + safeDistXZ = 1.0f; + + f32 normZ = dz / safeDistXZ; + f32 normX = dx / safeDistXZ; + + // Angle-based check + u32 prevAction = mPrevAction; + u16 faceAngle = (u16)mModelFaceAngle; + u8 canCatch = 1; + + // sin/cos lookup + u32 sinTbl = *(u32*)((u8*)0 + 0); + u32 cosTbl = *(u32*)((u8*)0 + 0); + s16 halfAngle = faceAngle >> *(u32*)((u8*)0 + 0); + f32 sinVal = *(f32*)(sinTbl + (u32)((u16)halfAngle << 2)); + f32 cosVal = *(f32*)(cosTbl + (u32)((u16)halfAngle << 2)); + f32 catchRadius = *(f32*)((u8*)actor + 0x58); + f32 dot = cosVal * normZ + sinVal * normX; + f32 poleRadius = mBarParams.mCatchRadius.value; + f32 poleHeight = mBarParams.mCatchAngle.value; + + if (prevAction & 0x100000) + canCatch = 0; + + if (dot < poleHeight) + canCatch = 0; + + if (safeDistXZ > catchRadius + poleRadius) + canCatch = 0; + + // Check Y position + f32 yCheck = *(f32*)((u8*)0 + 0); + if (mPosition.y < yCheck + actor->mPosition.y) + canCatch = 0; + + if ((u8)canCatch != 1) { + // Fall through to normal hit + u32* vtbl = *(u32**)actor; + typedef void (*HeightFunc)(THitActor*, f32); + ((HeightFunc)vtbl[0x2C])(actor, mPosition.y); + keepDistance(actor->mPosition, *(f32*)((u8*)0 + 0), 0.0f); + return; + } + + // Grab pole + setPlayerVelocity(0.0f); + *(u32*)((u8*)this + 0x68) = (u32)actor; + mVel.y = 0.0f; + mForwardVel = 0.0f; + changePlayerStatus(0x10100341, 0, false); + + // Virtual receiveMessage(actor, mario, 5) + u32* vtbl2 = *(u32**)actor; + typedef void (*MsgFunc)(THitActor*, THitActor*, u32); + ((MsgFunc)vtbl2[0x28])(actor, this, 5); + + mHolderHeightDiff = mPosition.y - actor->mPosition.y; +} + +// checkCollision: 0x80160480, size 0x135C +void TMario::checkCollision() +{ + // Check action bit 19 + if (checkActionFlag(0x80000)) + return; + + // Check yoshi state + TYoshi* yoshi = mYoshi; + u8 yoshiState = *(u8*)yoshi; + if (yoshiState == 6 || yoshiState == 2) { + f32 marioY = mPosition.y; + f32 yoshiFloorY = *(f32*)((u8*)yoshi + 0x24); + if (yoshiFloorY <= marioY) { + f32 extra = *(f32*)((u8*)0 + 0); + if (marioY < extra + yoshiFloorY) { + // XZ distance + f32 yoshiZ = *(f32*)((u8*)yoshi + 0x28); + f32 marioZ = mPosition.z; + f32 yoshiX = *(f32*)((u8*)yoshi + 0x20); + f32 marioX = mPosition.x; + f32 dz = yoshiZ - marioZ; + f32 dx = yoshiX - marioX; + f32 distSq = dx * dx + dz * dz; + f32 dist = 0.0f; + if (distSq > 0.0f) + dist = sqrtf(distSq); + + // Additional checks + u32 action = mAction; + if (action & 0x1000) { + if (*(u32*)((u8*)this + 0x6C) == 0 + && mVel.y < 0.0f + && *(f32*)((u8*)yoshi + 0x24) < mPosition.y + && action != 0x89C + && (action - 0x02000000) != 0x8B8 + && action != 0x883 + && dist < *(f32*)((u8*)0 + 0)) + { + // Copy yoshi pos + mPosition.x = *(f32*)((u8*)yoshi + 0x20); + mPosition.y = *(f32*)((u8*)yoshi + 0x24); + mPosition.z = *(f32*)((u8*)yoshi + 0x28); + + TYoshi* yoshi2 = mYoshi; + s16 yoshiAngle = *(s16*)((u8*)yoshi2 + 0x70); + mModelFaceAngle = yoshiAngle; + mFaceAngle.z = mModelFaceAngle; + + // HAS_FLUDD check + if (checkFlag(MARIO_FLAG_HAS_FLUDD)) { + TWaterGun* wg = mWaterGun; + u8 ntype = wg->mCurrentNozzle; + *(u32*)((u8*)this + 0x3E8) = ntype; + + TWaterGun* wg2 = mWaterGun; + TNozzleBase* nozzle = wg2->getCurrentNozzle(); + u32 waterA = *(u32*)((u8*)nozzle + 0xCC); + u32 waterB = *(u32*)((u8*)nozzle + 0xBC); + f32 ratio = (f32)((s32)waterA / (s32)waterB); + *(f32*)((u8*)this + 0x3EC) = ratio; + } + + TYoshi* yoshi3 = mYoshi; + *(u32*)((u8*)yoshi3 + 0) = 0; // ride + mState |= 0x8000; + + if (checkFlag(MARIO_FLAG_HAS_FLUDD)) { + mWaterGun->changeNozzle(3, true); + } + + changePlayerStatus(0x0C400201, 0, false); + return; + } + } + } + } + } + + // Set attack area + setNormalAttackArea(); + + // Iterate over collision actors + u16 numActors = *(u16*)((u8*)this + 0x48); + for (u16 i = 0; i < numActors; i++) { + THitActor** actorList = *(THitActor***)((u8*)this + 0x44); + THitActor* actor = actorList[i]; + + u32 actorFlags = *(u32*)((u8*)actor + 0x4C); + + // Check actor flag bit 26 + if (!(actorFlags & 0x04000000)) + continue; + + // Check mario flags for special collision handling + if (!checkFlag(0x80000)) { + if (!checkActionFlag(0x2000)) { + if (checkActionFlag(0x1000)) { + if ((mAction - 0x80000000) != 0x8A9) { + if (mVel.y < 0.0f) { + if (actor->mPosition.y < mPosition.y) { + if (trampleExec(actor)) { + keepDistance(*actor, 0.0f); + } + } + } + } + } + } + } + + // Hit normal processing + keepDistance(actor->mPosition, 0.0f, 0.0f); + + // Check hit result flags + u32 hitFlags = *(u32*)((u8*)actor + 0xF0); + if (!(hitFlags & 0x100000)) + continue; + + // Process hit reaction + hitNormal(actor); + // Store actor and change status + *(THitActor**)((u8*)this + 0x384) = actor; + changePlayerStatus(0x383, 0, false); + } +} diff --git a/src/Player/MarioCollision.cpp b/src/Player/MarioCollision.cpp index 24ab787f..7f25d39c 100644 --- a/src/Player/MarioCollision.cpp +++ b/src/Player/MarioCollision.cpp @@ -99,7 +99,7 @@ bool TMario::isTakeSituation(THitActor* object) } // Probably an inline - if (!checkUnk380(5)) { + if (!checkPumpState(5)) { return false; } @@ -212,7 +212,7 @@ void TMario::loserExec() // volatile u32 padding[2]; if (mAction != 0x224e0 && mAction != 0x21313 && mAction != 0x224e1 && mAction != 0x1000192a) { - unk118 |= MARIO_FLAG_GAME_OVER; + mState |= MARIO_FLAG_GAME_OVER; mHealth = 0; gpMSound->startSoundSystemSE(0x480c, 0, nullptr, 0); @@ -407,11 +407,11 @@ void TMario::considerTake() // volatile u32 missingStack[6]; bool check = false; - if (checkUnk380(2)) { + if (checkPumpState(2)) { check = true; } - if (checkUnk380(3)) { + if (checkPumpState(3)) { check = true; } diff --git a/src/Player/MarioDraw.cpp b/src/Player/MarioDraw.cpp index c6ad7424..eab35283 100644 --- a/src/Player/MarioDraw.cpp +++ b/src/Player/MarioDraw.cpp @@ -1561,7 +1561,7 @@ void TMario::initModel() mKoopaRail->getModel()->setBaseTRMtx( mTorocco->getModel()->getAnmMtx(0)); } - unk118 |= MARIO_FLAG_HAS_FLUDD; + mState |= MARIO_FLAG_HAS_FLUDD; mTorocco->calcAnm(); MtxPtr toroccoMtx = mTorocco->getModel()->getAnmMtx(2); mPosition.x = toroccoMtx[0][3]; @@ -1642,7 +1642,7 @@ void TMario::finalDrawInitialize() bool TMario::isUpperPumpingStyle() const { - if (unk380 == 0 || unk380 == 1) { + if (mPumpState == 0 || mPumpState == 1) { return true; } return false; @@ -1797,7 +1797,7 @@ void TMario::calcBaseMtx(MtxPtr mtx) mFaceAngle.x = 0; } // Probably another checkFlag inline - if ((unk114 & 8) ? true : false) { + if ((mSubState & 8) ? true : false) { // This is too many inlines for me to try even figure out, // so i won't even attempt it rn... // Keeping my working draft, but it is 100% wrong @@ -1878,7 +1878,7 @@ void TMario::addCallBack(JDrama::TGraphics* graphics) modelData->getJointNodePointer(mBoneIDs[1])->setCallBack(MarioWaistCtrl); - if (0x4B0 > gpMarDirector->unk58 || fabricatedUnk380Inline()) { + if (0x4B0 > gpMarDirector->unk58 || isPumpIdle()) { if (mMultiMtxEffect != nullptr) { mMultiMtxEffect->flagOff(); } @@ -1891,7 +1891,7 @@ void TMario::addCallBack(JDrama::TGraphics* graphics) mCap->mtxEffectHide(); } } else { - if ((gMarioAnimeData[mAnimationId].unk6 & 2) != 0 && unk380 == 5) { + if ((gMarioAnimeData[mAnimationId].unk6 & 2) != 0 && mPumpState == 5) { if (mMultiMtxEffect != nullptr) { mMultiMtxEffect->flagOn(); } @@ -1906,7 +1906,7 @@ void TMario::addCallBack(JDrama::TGraphics* graphics) } } - if ((gMarioAnimeData[mAnimationId].unk6 & 4) != 0 && unk380 == 5) { + if ((gMarioAnimeData[mAnimationId].unk6 & 4) != 0 && mPumpState == 5) { if (checkFlag(MARIO_FLAG_HAS_FLUDD)) { mWaterGun->unk1CDC->mMtxEffectTbl[1]->mFlags |= 1; } @@ -1940,15 +1940,15 @@ void TMario::setUpperDamageRun() frameCtrl.setFrame(frameCtrl.getStart()); frameCtrl.setRate(1.0f); frameCtrl.setRate(0.5f); - unk380 = 4; + mPumpState = 4; } void TMario::addUpper() { // volatile u32 padding[17]; J3DFrameCtrl& frameCtrl = mModel->getFrameCtrl(1); - if (unk380 != 4) { - switch (unk380) { + if (mPumpState != 4) { + switch (mPumpState) { case 0: case 1: if (onYoshi()) { @@ -1970,7 +1970,7 @@ void TMario::addUpper() break; } - if (unk380 == 0) { + if (mPumpState == 0) { frameCtrl.setAttribute(J3DFrameCtrl::ATTR_PING_PONG_LOOP); unk348 = mGamePad->mCompSPos[3] * mUpperBodyParams.mPumpAnmSpeed.get(); @@ -2150,10 +2150,10 @@ void TMario::drawSpecial(JDrama::TGraphics* graphics) { if (mAction == 0x20338) { unk4EC = 1; - unk114 |= 0x20; + mSubState |= 0x20; } else { unk4EC = 0; - unk114 &= ~0x20; + mSubState &= ~0x20; } // Probably some enum? I see no reason why this is a switch... switch (unk4EC) { @@ -2190,7 +2190,7 @@ void TMario::drawLogic() unk398->draw(); GXSetColorUpdate(GX_TRUE); GXSetAlphaUpdate(GX_FALSE); - if (unk114 & 0x20 ? true : false) { + if (mSubState & 0x20 ? true : false) { unk394->draw(); unk398->draw(); } diff --git a/src/Player/MarioInit.cpp b/src/Player/MarioInit.cpp index bb5fe00f..a7eba654 100644 --- a/src/Player/MarioInit.cpp +++ b/src/Player/MarioInit.cpp @@ -1,9 +1,25 @@ #include +#include +#include +#include +#include +#include // rogue includes needed for matching sinit & bss #include #include +// Forward declarations for types not fully included +class CPolarSubCamera; +class MSound; +class JSUMemoryInputStream; + +extern CPolarSubCamera* gpCamera; +extern MSound* gpMSound; + +// TMarioEffect forward declaration - init called via symbol +extern "C" void init__12TMarioEffectFP6TMario(void*, TMario*); + // TODO: stuff from other rogue includes static JGeometry::TVec3 cDeformedTerrainCenter = JGeometry::TVec3(0.0f, 5000.0f, 0.0f); @@ -527,13 +543,205 @@ TMario::TDeParams::TDeParams() void TMario::setGamePad(TMarioGamePad* pad) { mGamePad = pad; } -void TMario::resetHistory() { } +void TMario::resetHistory() +{ + for (int i = 0; i < 60; i++) { + unk530[i] = 0; + } + + unk534 = 0; + unk536 = 0; + unk538 = 0; + unk53A = 0; + unk53B = 0; +} + +void TMario::initValues() +{ + mHealth = mDeParams.mHpMax.get(); + unk134 = 0.0f; + *(f32*)&unk138 = 1.0f; + unk13C = 0; + *(f32*)&unk140 = 0.0f; + + // Allocate and init TMarioControllerWork (36 bytes) + unk108 = (u32)operator new(0x24); + { + u8* p = (u8*)unk108; + *(s16*)(p + 0x00) = 0; + *(s16*)(p + 0x02) = 0; + *(f32*)(p + 0x10) = 0.0f; + *(f32*)(p + 0x14) = 0.0f; + *(f32*)(p + 0x18) = 0.0f; + *(u32*)(p + 0x04) = 0; + *(u32*)(p + 0x08) = 0; + *(u8*)(p + 0x0C) = 0; + *(u8*)(p + 0x0D) = 0; + } + *(f32*)&unk10C = 0.0f; + *(f32*)&unk110 = 0.0f; + + // Allocate TWaterEmitInfo for damage and wet + TWaterEmitInfo* damageEmit = new TWaterEmitInfo("/Mario/DamageWaterEmit.prm"); + unk154 = damageEmit; + + TWaterEmitInfo* wetEmit = new TWaterEmitInfo("/Mario/WetWaterEmit.prm"); + unk158 = wetEmit; + + unk388 = 0; + unk389 = 0; + mHolderHeightDiff = 0.0f; + + initModel(); + + unk3D8 = 0.0f; + unk3DC = 0.0f; + + // Allocate and init TMarioCap + TMarioCap* cap = new TMarioCap(this); + mCap = cap; + + // Allocate and init TWaterGun + TWaterGun* gun = new TWaterGun(this); + mWaterGun = gun; + mWaterGun->init(); + mWaterGun->setAmountToRate((f32)*(u32*)&unk280[0x18] / 100.0f); + + // Allocate and init TYoshi + TYoshi* yoshi = (TYoshi*)operator new(0x124); + if (yoshi) { + J3DFrameCtrl* frameCtrl = new ((void*)((u8*)yoshi + 0x5C)) J3DFrameCtrl(0); + } + mYoshi = yoshi; + mYoshi->init(this); + + // Allocate and init TMarioEffect (THitActor subclass, 0x84 bytes) + void* marioEffect = operator new(0x84); + if (marioEffect) { + new (marioEffect) THitActor("マリオエフェクト"); + // In original code, TMarioEffect vtable is set here + } + mMarioEffect = marioEffect; + init__12TMarioEffectFP6TMario(mMarioEffect, this); + + // Init unk414 and related float vectors + unk414.x = 0.0f; + unk414.y = 0.0f; + unk414.z = 1.0f; + mMarioScreenPos.x = 0.0f; + mMarioScreenPos.y = 0.0f; + mMarioScreenPos.z = 0.0f; + mWarpInDir.x = 0.0f; + mWarpInDir.y = 0.0f; + mWarpInDir.z = 0.0f; + unk468 = 0.0f; + unk46C = 0.0f; + + // Allocate and init MAnmSound + MAnmSound* anmSound = new MAnmSound(gpMSound); + mAnmSound = anmSound; + mAnmSound->initAnmSound(nullptr, 1, 0.0f); + + unk4EC = 0; + mBlendLogicOp = 10; + mWaterWakeAlpha = 0; + + // Allocate history buffer (60 s16 entries = 120 bytes) + unk530 = (s16*)operator new[](120); + + resetHistory(); + + // Init this as a hit actor + initHitActor(0x80000001, 5, -1024, + mDeParams.mTrampleRadius.get(), + mDeParams.mAttackHeight.get(), + mDeParams.mDamageRadius.get(), + mDeParams.mDamageHeight.get()); + + // Allocate TMBindShadowBody (0x1C = 28 bytes) + TMBindShadowBody* shadow = (TMBindShadowBody*)operator new(0x1C); + if (shadow) { + new (shadow) TMBindShadowBody(this, mModel->unk8, 1.0f); + } + unk390 = (u32)shadow; + + // Set default identification/magic values for various fields + unk92 = 0x11; // 0x92 sth + *(s16*)((u8*)this + 0xA2) = 0xAD; // 0xA2 sth + *(s16*)((u8*)this + 0xC6) = 0x22; // 0xC6 sth + *(s16*)((u8*)this + 0xD6) = 0x33; // 0xD6 sth + *(s16*)((u8*)this + 0x102) = 0xAD; // 0x102 sth + *(s16*)((u8*)this + 0x12A) = 0x44; // 0x12A sth + *(s16*)((u8*)this + 0x13E) = 0x55; // 0x13E sth + unk37C = 0x99; // 0x37C sth + unk3D1 = 0xAA; // 0x3D1 stb + *(s16*)((u8*)this + 0x3D2) = 0xBB; // 0x3D2 sth + unk535 = 0xCC; // 0x535 stb + unk556 = 0xEE; // 0x556 sth +} + +void TMario::loadAfter() +{ + u8 hasFludd; + if (mState & MARIO_FLAG_HAS_FLUDD) { + hasFludd = 1; + } else { + hasFludd = 0; + } + if (hasFludd) { + mWaterGun->initInLoadAfter(); + } + + if (mYoshi) { + mYoshi->initInLoadAfter(); + } + + if (SMS_isMultiPlayerMap()) { + gpCamera->addMultiPlayer(&mPosition, 60.0f, 80.0f); + } + + initParticle(); + + if (isMario()) { + MtxPtr mtx = (MtxPtr)((u8*)mModel->unk8->mNodeMatrices + 0x30); + gpMSound->setPlayerInfo(&mPosition, &unk29C, mtx, true); + } else { + MtxPtr mtx = (MtxPtr)((u8*)mModel->unk8->mNodeMatrices + 0x30); + gpMSound->setPlayerInfo(&mPosition, &unk29C, mtx, false); + } + + finalDrawInitialize(); + initMirrorModel(); +} + +void TMario::load(JSUMemoryInputStream& stream) +{ + JDrama::TActor::load(stream); + + mFaceAngle.x = 0; -void TMario::initValues() { } + mFaceAngle.y = (s16)(mRotation.y * (65536.0f / 360.0f)); + mFaceAngle.z = 0; + mModelFaceAngle = mFaceAngle.y; + unk9C = mFaceAngle.y; + mSlideAngle = mFaceAngle.y; -void TMario::loadAfter() { } + stream.read(&unk280[0x18], 4); -void TMario::load(JSUMemoryInputStream&) { } + u32 flags; + stream.read(&flags, 4); + + mState = 0; + if (flags & 1) { + mState &= ~MARIO_FLAG_HAS_FLUDD; + } else { + mState |= MARIO_FLAG_HAS_FLUDD; + } + + SMS_SetMarioAccessParams(); + + initValues(); +} TMario::TMario() : TTakeActor("HitActor") diff --git a/src/Player/MarioJump.cpp b/src/Player/MarioJump.cpp index 8b137891..6fefa12f 100644 --- a/src/Player/MarioJump.cpp +++ b/src/Player/MarioJump.cpp @@ -1 +1,705 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// NOTE: -inline deferred means functions must be in REVERSE address order. + +void TMario::startJumpWall() +{ + if (mWallPlane != nullptr) { + s16 wallAngle = matan(mWallPlane->getNormal().z, mWallPlane->getNormal().x); + s16 newAngle = wallAngle + 0x8000; + emitParticle(24, newAngle); + emitParticle(25, newAngle); + } + mVel.y = 0.0f; + mFaceAngle.y = mFaceAngle.y + 0x8000; + f32 ceiling = mJumpParams.mJumpJumpCatchSp.value; + if (mVel.y + ceiling + mPosition.y >= mFloorPosition.y) + mVel.y = 0.0f; + changePlayerStatus(0x02000886, 0, false); +} + +void TMario::doJumping() +{ + mForwardVel = mForwardVel * mJumpParams.mJumpSpeedBrake.value; + f32 sideVel = 0.0f; + if (mInput & 1) { + s16 intendedYaw = mIntendedYaw; + s16 faceY = mFaceAngle.y; + f32 intendedMag = mIntendedMag; + s16 angleDiff = intendedYaw - faceY; + if (mAction == 0x088B) { + u8 hasFludd; + if (mState & 0x10000) hasFludd = 1; else hasFludd = 0; + if (hasFludd) { + TWaterGun* gun = mWaterGun; + if (gun->mCurrentWater != 0) { + s32 kind = gun->getCurrentNozzle()->getNozzleKind(); + if (kind == 1) { + TNozzleTrigger* t = (TNozzleTrigger*)gun->getCurrentNozzle(); + if (t->unk385 == 1) {} + } else { + if (gun->getCurrentNozzle()->unk378 > 0.0f) {} + } + } + intendedMag = mDivingParams.mAccelControl.value * intendedMag; + } + } + u32 actionBase = mAction - 0xFE000000; + if (actionBase <= 2182) { + if (mVel.y > 0.0f) { + s16 d = (s16)angleDiff; + if (d < -16384 || d > 16384) intendedMag = 0.0f; + } + } + f32 accel; + if (onYoshi()) { + TYoshi* y = mYoshi; + u8 sw; if (y->mFlutterState == 1) sw = 1; else sw = 0; + if (sw) { + s16 d = (s16)angleDiff; + if (d > -16384 && d < 16384) accel = mYoshiParams.mHoldOutAccCtrlF.value; + else accel = mYoshiParams.mHoldOutAccCtrlB.value; + } else accel = getJumpAccelControl(); + } else accel = getJumpAccelControl(); + u16 au = (u16)angleDiff; + mForwardVel = accel * intendedMag * JMASSin(au) + mForwardVel; + sideVel = intendedMag * intendedMag * JMASCos(au); + } + if (mForwardVel > 0.0f) mForwardVel -= mJumpParams.mJumpAccelControl.value; + if (mForwardVel < 0.0f) mForwardVel += mJumpParams.mJumpAccelControl.value; + u16 fa = mFaceAngle.y; + mSlideVelX = mForwardVel * JMASSin(fa); + mSlideVelZ = mForwardVel * JMASCos(fa); + u16 pa = (u16)(mFaceAngle.y + 16384); + mSlideVelX += sideVel * JMASSin(pa); + mSlideVelZ += sideVel * JMASCos(pa); + mVel.x = mSlideVelX; + mVel.z = mSlideVelZ; + if (mVel.y < 0.0f) { + *(f32*)((u8*)this + 0x50) = mDeParams.mTrampleRadius.value; + calcEntryRadius(); + *(f32*)((u8*)this + 0x54) = mDeParams.mAttackHeight.value; + calcEntryRadius(); + } else { + *(f32*)((u8*)this + 0x50) = mDeParams.mPushupRadius.value; + calcEntryRadius(); + *(f32*)((u8*)this + 0x54) = mDeParams.mPushupHeight.value; + calcEntryRadius(); + } +} + +void TMario::jumpingBasic(int statusId, int anmId, int groundCheck) +{ + doJumping(); + int result = jumpProcess(groundCheck); + switch (result) { + case 0: + setAnimation(anmId, 1.0f); + break; + case 1: { + if (mGroundPlane->getActor()) + ((TLiveActor*)mGroundPlane->getActor())->receiveMessage(this, 0); + u8 canCatch = 0, shouldCatch = 1; + u8 hy; if (mSubState & 0x100) hy = 1; else hy = 0; + if (hy) shouldCatch = 0; + if (*(f32*)((u8*)this + 0x02AC) - mPosition.y <= mDeParams.mDamageFallHeight.value) + shouldCatch = 0; + if (onYoshi()) shouldCatch = 0; + u16 bg = *(u16*)((u8*)mGroundPlane); + if (bg == 0x0A || bg == 0x800A || bg == 0x0108) shouldCatch = 0; + else { + u8 r; if (bg == 0x07 || bg == 0x8007) r = 1; else r = 0; + if (r) shouldCatch = 0; + else { + if (bg == 0x08 || bg == 0x8008) shouldCatch = 0; + else { u8 w; if (bg == 0x09 || bg == 0x8009) w = 1; else w = 0; if (w) shouldCatch = 0; } + } + } + if (mVel.y > 0.0f) shouldCatch = 0; + if (shouldCatch) { + u8 cf; if (mState & 0x80000) cf = 1; else cf = 0; + if (cf) { + u8 fe; if (mState & 0x10000) fe = 1; else fe = 0; + if (fe) { + if (*(u8*)((u8*)mWaterGun + 0x1C84) == 2) break; + jumpProcess(0); + changePlayerStatus(0x0479, 0, false); + rumbleStart(21, mMotorParams.mMotorHipDrop.value); + startVoice(0x789E); + canCatch = 1; + if (gpMSound->gateCheck(0x193E)) + MSoundSESystem::MSoundSE::startSoundActor(0x193E, (const Vec*)&mPosition, 0, nullptr, 0, 4); + strongTouchDownEffect(); + floorDamageExec(1, 3, 0, mMotorParams.mMotorReturn.value); + break; + } + } + } + *(f32*)((u8*)this + 0x02AC) = mPosition.y; + changePlayerStatus(statusId, 0, false); + if (!canCatch) { rumbleStart(20, mMotorParams.mMotorWall.value / 2); stopVoice(); } + u32 pa = mPrevAction; + if (pa == 0x0887 || (pa - 0x0895 <= 1)) strongTouchDownEffect(); + else smallTouchDownEffect(); + break; + } + case 2: { + if (mAction == 0x0893) break; + if (mForwardVel > mDeParams.mClashSpeed.value) { + emitParticle(12); + changePlayerDropping(0x000208B0, 0); + break; + } + setAnimation(anmId, 1.0f); + if (onYoshi()) { setPlayerVelocity(0.0f); break; } + if (mWallPlane) { + u16 wt = *(u16*)((u8*)mWallPlane); + u8 rp; if (wt == 5 || wt == 0x8005) rp = 1; else rp = 0; + if (rp) { changePlayerStatus(0x088D, 0, false); setPlayerVelocity(0.0f); break; } + } + if (mWallPlane) { + u16 wt = *(u16*)((u8*)mWallPlane); + u8 fc; if (wt == 0x010A) fc = 1; else fc = 0; + if (fc) { + s16 wa = matan(mWallPlane->getNormal().z, mWallPlane->getNormal().x); + mFaceAngle.y = wa + 0x8000; + mModelFaceAngle = mFaceAngle.y; + if (mAction == 0x0887) mModelFaceAngle = mFaceAngle.y - 0x8000; + rumbleStart(21, mMotorParams.mMotorWall.value); + changePlayerStatus(0x3000036C, 0, false); + break; + } + } + playerRefrection(0); + if (mWallPlane) { + changePlayerStatus(0x08A7, 0, false); + if (isMario()) { + rumbleStart(21, mMotorParams.mMotorWall.value); + gpCameraShake->startShake((EnumCamShakeMode)1, 0.0f); + u32 sid = gpMSound->getWallSound(0, mForwardVel); + if (gpMSound->gateCheck(sid)) + MSoundSESystem::MSoundSE::startSoundActor(sid, (const Vec*)&mPosition, 0, nullptr, 0, 4); + } + break; + } + if (mVel.y > 0.0f) mVel.y = 0.0f; + changePlayerStatus(0x000208B0, 0, false); + break; + } + case 3: + setAnimation(0x33, 1.0f); + changePlayerDropping(0x3800034B, 0); + break; + case 4: + rumbleStart(21, mMotorParams.mMotorWall.value); + changePlayerStatus(0x08200348, 0, false); + break; + } +} + +void TMario::considerJumpRotate() +{ + int dir; + if (checkStickRotate(&dir) != 1) return; + switch (dir) { + case 2: mAction = 0x0896; break; + case 3: mAction = 0x0895; break; + } +} + +void TMario::checkBackTrig() +{ + if (!(mInput & 0x10000)) return; + TMarioGamePad* pad = mGamePad; + if (pad->mEnabledFrameMeaning & 0x4000) { changePlayerStatus(0x008008A9, 0, false); return; } + if (!onYoshi()) { + setPlayerVelocity(mJumpParams.mJumpJumpCatchSp.value); + changePlayerStatus(0x0080088A, 0, false); + } +} + +void TMario::landing() +{ + if (mVel.y < 0.0f) { + u16 t = mActionTimer; mActionTimer = t + 1; + if (t > 240) { mActionTimer = 240; startSoundActor(0x786B); mActionArg = 3; } + } + if (mInput & 0x10000) { + TMarioGamePad* pad = mGamePad; + if (pad->mEnabledFrameMeaning & 0x4000) changePlayerStatus(0x008008A9, 0, false); + else if (!onYoshi()) { setPlayerVelocity(mJumpParams.mJumpJumpCatchSp.value); changePlayerStatus(0x0080088A, 0, false); } + } + if (rocketCheck()) return; + s32 anm = 86; + switch (mActionArg) { + case 0: anm = 86; break; + case 1: anm = 144; break; + case 3: anm = 288; break; + } + jumpingBasic(0x04000471, anm, 3); +} + +void TMario::jumpCatch() +{ + if (mInput & 0x10000) { + TMarioGamePad* pad = mGamePad; + if (pad->mEnabledFrameMeaning & 0x4000) { changePlayerStatus(0x008008A9, 0, false); return; } + } + setAnimation(136, 1.0f); + doJumping(); + int r = jumpProcess(0); + switch (r) { + case 1: { + u8 cc = 1; + u8 hy; if (mSubState & 0x100) hy = 1; else hy = 0; + if (hy) cc = 0; + if (*(f32*)((u8*)this + 0x02AC) - mPosition.y <= mDeParams.mDamageFallHeight.value) cc = 0; + if (onYoshi()) cc = 0; + u16 bg = *(u16*)((u8*)mGroundPlane); + if (bg == 0x0A || bg == 0x800A || bg == 0x0108) cc = 0; + else { + u8 t; if (bg == 0x07 || bg == 0x8007) t = 1; else t = 0; + if (t) cc = 0; + else { if (bg == 0x08 || bg == 0x8008) cc = 0; + else { u8 w; if (bg == 0x09 || bg == 0x8009) w = 1; else w = 0; if (w) cc = 0; } } + } + if (mVel.y > 0.0f) cc = 0; + if (cc) { + u8 cf; if (mState & 0x80000) cf = 1; else cf = 0; + if (cf) { sinkInSandEffect(); changePlayerStatus(0x0002033C, 1, false); break; } + } + changePlayerStatus(0x00800456, 0, false); + break; + } + case 2: case 3: { + if (mWallPlane) { + u8 fc; if (*(u16*)((u8*)mWallPlane) == 0x010A) fc = 1; else fc = 0; + if (fc) { changePlayerDropping(0x3000036C, 0); break; } + } + playerRefrection(1); + if (mVel.y > 0.0f) mVel.y = 0.0f; + emitParticle(12); + changePlayerDropping(0x000208B0, 0); + break; + } + } +} + +void TMario::jumpDownCommon(int statusId, int anmId, f32 velY) +{ + setPlayerVelocity(velY); + int r = jumpProcess(0); + switch (r) { + case 0: setAnimation(anmId, 1.0f); break; + case 1: changePlayerStatus(statusId, mActionArg, false); break; + case 2: case 3: + setAnimation(0, 1.0f); + playerRefrection(0); + if (mVel.y > 0.0f) mVel.y = 0.0f; + setPlayerVelocity(-velY); + break; + } +} + +void TMario::stayWall() +{ + u16 t = mActionTimer; mActionTimer = t + 1; + if (mActionTimer > 60) mActionTimer = 60; + if (mInput & 2) { + if (mWallPlane) { + s16 wa = matan(mWallPlane->getNormal().z, mWallPlane->getNormal().x); + s16 na = wa + 0x8000; + emitParticle(24, na); emitParticle(25, na); + } + mVel.y = 0.0f; + mFaceAngle.y = mFaceAngle.y + 0x8000; + f32 c = mJumpParams.mJumpJumpCatchSp.value; + if (mVel.y + c + mPosition.y >= mFloorPosition.y) mVel.y = 0.0f; + changePlayerStatus(0x02000886, 0, false); + return; + } + if (mInput & 0x10000) { + TMarioGamePad* pad = mGamePad; + if (pad->mEnabledFrameMeaning & 0x4000) { changePlayerStatus(0x008008A9, 0, false); return; } + if (!onYoshi()) { setPlayerVelocity(mJumpParams.mJumpJumpCatchSp.value); changePlayerStatus(0x0080088A, 0, false); return; } + } + if (mActionTimer < 20) { + mActionTimer = mActionTimer + 1; + mVel.x = 0.0f; mVel.y = 0.0f; mVel.z = 0.0f; + } else { + mVel.y = -(f32)mActionTimer * mJumpParams.mJumpAccelControl.value; + } + if (mWallPlane) { + mPosition.x -= mWallPlane->mNormal.x; + mPosition.z -= mWallPlane->mNormal.z; + } + int jr = jumpProcess(0); + if (jr == 1) { mFaceAngle.y += 0x8000; changePlayerStatus(0x088C, 0, false); return; } + if (!mWallPlane) { + mFaceAngle.y += 0x8000; + setPlayerVelocity(mJumpParams.mJumpJumpCatchSp.value); + mVel.y = 0.0f; + changePlayerStatus(0x088C, 0, false); + return; + } + setAnimation(204, 1.0f); + if (mVel.y < 0.0f) { + wallSlipEffect(); + if (gpMSound->gateCheck(0x113F)) + MSoundSESystem::MSoundSE::startSoundActor(0x113F, (const Vec*)&mPosition, 0, nullptr, 0, 4); + } +} + +void TMario::catchStop() +{ + if (mActionState == 0) { mVel.y = 0.0f; mActionState = 1; } + doJumping(); + int r = jumpProcess(0); + switch (r) { + case 0: + if (mActionState == 1) setAnimation(111, 1.0f); + else setAnimation(86, 1.0f); + break; + case 1: changePlayerStatus(0x0C000232, 0, false); break; + case 2: case 3: setPlayerVelocity(0.0f); break; + } + if (mActionState == 1 && isLast1AnimeFrame()) mActionState = 2; +} + +void TMario::slipFalling() +{ + u16 t = mActionTimer; mActionTimer = t + 1; + if (mActionTimer > 120 && mPosition.y - mFloorPosition.y > 0.0f) { + changePlayerStatus(0x088C, 1, false); return; + } + mForwardVel *= mJumpParams.mJumpSpeedBrake.value; + if (mInput & 1) { + s16 ad = mIntendedYaw - mFaceAngle.y; + u16 au = (u16)ad; + f32 im = mIntendedMag; + f32 cr = *(f32*)((u8*)this + 0x0B8C); + f32 ac = im * cr; + mForwardVel += ac * JMASSin(au); + mFaceAngle.y = (s16)(ac * JMASCos(au) + (f32)mFaceAngle.y); + } + if (mForwardVel > 0.0f) mForwardVel -= mJumpParams.mJumpAccelControl.value; + if (mForwardVel < 0.0f) mForwardVel += mJumpParams.mJumpAccelControl.value; + u16 fa = mFaceAngle.y; + f32 vx = mForwardVel * JMASSin(fa); mSlideVelX = vx; mVel.x = vx; + f32 vz = mForwardVel * JMASCos(fa); mSlideVelZ = vz; mVel.z = vz; + int jr = jumpProcess(0); + switch (jr) { + case 1: + if (mActionState == 0 && mVel.y < 0.0f && mGroundPlane->getNormal().y >= 0.0f) { + mVel.y = -mVel.y * *(f32*)((u8*)this + 0x0BA0); mActionState = 1; + } else changePlayerStatus(0x00840452, 0, false); + break; + case 2: + if (mVel.y > 0.0f) mVel.y = 0.0f; + rumbleStart(21, mMotorParams.mMotorWall.value); + changePlayerStatus(0x000208B0, 0, false); + break; + } + setAnimation(145, 1.0f); +} + +void TMario::fireDowning() +{ + if (mActionTimer == 1) startVoice(0x7849); + u16 t2 = mActionTimer; mActionTimer = t2 + 1; + if (!(mInput & 1)) mForwardVel = FConverge(mForwardVel, 0.0f, 0.0f, mJumpParams.mFireDownControl.value); + if (mInput & 1) { + s16 ad = mIntendedYaw - mFaceAngle.y; u16 au = (u16)ad; + f32 ac = mIntendedMag * *(f32*)((u8*)this + 0x0B8C) * mJumpParams.mFireDownControl.value; + mForwardVel += ac * JMASSin(au); + mFaceAngle.y = (s16)(ac * JMASCos(au) + (f32)mFaceAngle.y); + if (mForwardVel < 0.0f) { mFaceAngle.y += 0x8000; mForwardVel *= mJumpParams.mFireBackVelocity.value; } + if (mForwardVel > 0.0f) mForwardVel -= mJumpParams.mJumpAccelControl.value; + } + u16 fa = mFaceAngle.y; + f32 vx = mForwardVel * JMASSin(fa); mSlideVelX = vx; mVel.x = vx; + f32 vz = mForwardVel * JMASCos(fa); mSlideVelZ = vz; mVel.z = vz; + int jr = jumpProcess(0); + if (jr == 1) { + if (mActionState < 2 && mVel.y < 0.0f) { + mVel.y = -mVel.y * mJumpParams.mBroadJumpForce.value; + setPlayerVelocity(mJumpParams.mBroadJumpForce.value * mForwardVel); + mActionState = mActionState + 1; + } else { startVoice(0x7852); changePlayerStatus(0x08000239, 0, false); } + } else if (jr == 2) playerRefrection(0); + setAnimation(41, 1.0f); +} + +void TMario::thrownDowning() +{ + s16 ad = mIntendedYaw - mFaceAngle.y; u16 au = (u16)ad; + f32 ac = mIntendedMag * *(f32*)((u8*)this + 0x0B8C); + f32 ta = mJumpParams.mThrownAccel.value; + mForwardVel += ac * JMASSin(au) * ta; + f32 ts = mJumpParams.mThrownSlide.value; + mFaceAngle.y = (s16)(ac * JMASCos(au) * ts + (f32)mFaceAngle.y); + mForwardVel *= mJumpParams.mThrownBrake.value; + u16 fa = mFaceAngle.y; + f32 vx = mForwardVel * JMASSin(fa); mSlideVelX = vx; mVel.x = vx; + f32 vz = mForwardVel * JMASCos(fa); mSlideVelZ = vz; mVel.z = vz; + int jr = jumpProcess(0); + if (jr == 1) { + if (mActionState < 2 && mVel.y < 0.0f) { + mVel.y = -mVel.y * mJumpParams.mBroadJumpForce.value; + setPlayerVelocity(mJumpParams.mBroadJumpForce.value * mForwardVel); + mActionState = mActionState + 1; + } else changePlayerStatus(0x0C000223, 0, false); + } else if (jr == 2) playerRefrection(0); + setAnimation(288, 1.0f); +} + +void TMario::boardJumping() +{ + setAnimation(109, 1.0f); + if (mVel.y < 0.0f) { + *(f32*)((u8*)this + 0x50) = mDeParams.mTrampleRadius.value; calcEntryRadius(); + *(f32*)((u8*)this + 0x54) = mDeParams.mAttackHeight.value; calcEntryRadius(); + } else { + *(f32*)((u8*)this + 0x50) = mDeParams.mPushupRadius.value; calcEntryRadius(); + *(f32*)((u8*)this + 0x54) = mDeParams.mPushupHeight.value; calcEntryRadius(); + } + int r = jumpProcess(0); + if (r == 1 && mVel.y < 0.0f) changePlayerStatus(0x00810446, 0, false); + else if (r == 2) { + if (!mWallPlane) { setPlayerVelocity(0.0f); loserExec(); } + else { + s16 wa = matan(mWallPlane->getNormal().z, mWallPlane->getNormal().x); + s16 d = wa - mFaceAngle.y; + s16 mx = mSurfingParamsWaterRed.mClashAngle.value; + if ((s16)d < -mx || (s16)d > mx) { + if (mForwardVel > mSurfingParamsWaterRed.mClashSpeed.value) startJumpWall(); + else setPlayerVelocity(0.0f); + } else setPlayerVelocity(0.0f); + } + } +} + +BOOL TMario::rocketCheck() +{ + u8 cr = 1; + if (mAction == 0x088B) cr = 0; + if (mAction == 0x088D) cr = 0; + u8 hf; if (mState & 0x10000) hf = 1; else hf = 0; + if (hf) { + if (*(u8*)((u8*)mWaterGun->getCurrentNozzle() + 0x18) != 1) cr = 0; + u8 nw; if (mPumpState == 0) nw = 1; else nw = 0; + if (nw) cr = 0; + TWaterGun* g = mWaterGun; + if (g->mCurrentWater == 0) cr = 0; + else { + s32 k = g->getCurrentNozzle()->getNozzleKind(); + if (k == 1) { TNozzleTrigger* t = (TNozzleTrigger*)g->getCurrentNozzle(); if (t->unk385 != 1) cr = 0; } + else { if (g->getCurrentNozzle()->unk378 <= 0.0f) cr = 0; } + } + } else cr = 0; + if ((u8)cr == 1) { + *(f32*)((u8*)this + 0x0314) = mPosition.y + *(f32*)((u8*)mWaterGun + 0x1D40); + changePlayerStatus(0x088B, 0, false); + } + return 0; +} + +void TMario::rocketing() +{ + u8 hf; if (mState & 0x10000) hf = 1; else hf = 0; + if (!hf) { changePlayerStatus(0x088D, 0, false); return; } + if (*(u8*)((u8*)mWaterGun->getCurrentNozzle() + 0x18) != 1) { changePlayerStatus(0x088D, 0, false); return; } + u8 nw; if (mPumpState == 0) nw = 1; else nw = 0; + if (nw) { changePlayerStatus(0x088D, 0, false); return; } + if (mInput & 1) { + u8 rp = *(u8*)((u8*)mWaterGun + 0x1C84); + if (rp == 4) { + s16 ad = mIntendedYaw - mFaceAngle.y; + f32 im = mIntendedMag; + s16 ade = (s16)ad; + if (ade > -5461 && ade < 5461) { + } else if (ade >= -27306 && ade <= 27306) { + s16 fa2 = *(s16*)((u8*)mWaterGun->getCurrentNozzle() + 0x0310); + f32 ac = *(f32*)((u8*)this + 0x0B8C) * (-im); + u16 au = (u16)ad; + s16 ra = (s16)(ac * (f32)fa2 * JMASSin(au)); + u8 fo; if (mState & 0x10000) fo = 1; else fo = 0; + if (fo) { + mWaterGun->unk1CC2 = -ra; + mWaterGun->unk1CC4 = ra; + IConverge((int)mFaceAngle.y, (int)mIntendedYaw, (int)mHoverParams.mRotSp.value, (int)mHoverParams.mRotSp.value); + mFaceAngle.y = mIntendedYaw - ra; + } + } else { + s16 ta; + if (ade >= -16384 && ade <= 16384) ta = *(s16*)((u8*)mWaterGun->getCurrentNozzle() + 0x0324); + else ta = *(s16*)((u8*)mWaterGun->getCurrentNozzle() + 0x0338); + f32 ac = *(f32*)((u8*)this + 0x0B8C) * (-im); + u16 au = (u16)ad; + s16 ra = (s16)(ac * (f32)ta * JMASSin(au)); + mWaterGun->unk1CC2 = ra; + mWaterGun->unk1CC4 = ra; + mForwardVel += im * JMASCos(au) * mDivingParams.mAccelControl.value; + } + } + } else { mWaterGun->unk1CC2 = 0; mWaterGun->unk1CC4 = 0; } + u16 fa = mFaceAngle.y; + mSlideVelX = mForwardVel * JMASSin(fa); mSlideVelZ = mForwardVel * JMASCos(fa); + mVel.x = mSlideVelX; mVel.z = mSlideVelZ; + u8 rp2 = *(u8*)((u8*)mWaterGun + 0x1C84); + if (rp2 == 4) { + mVel.y = (*(f32*)((u8*)this + 0x0314) - mPosition.y) * mHoverParams.mAccelRate.value; + mForwardVel *= mHoverParams.mBrake.value; + } + int res = jumpProcess(2); + if (res >= 3 && res < 5) { rumbleStart(21, mMotorParams.mMotorWall.value); changePlayerStatus(0x08200348, 0, false); } + if (mRoofPlane) { + f32 c = mJumpParams.mJumpJumpCatchSp.value; + if (c + mPosition.y > mFloorPosition.y) mPosition.y = mFloorPosition.y - c; + } + setAnimation(86, 1.0f); +} + +void TMario::hipAttacking() +{ + s32 i = 0; f32 md = 0.0f; + while (i < *(u16*)((u8*)this + 0x48)) { + THitActor* a = ((THitActor**)*(u32*)((u8*)this + 0x44))[i]; + u32 at = *(u32*)((u8*)a + 0x4C); + u8 it; if (at - 0xC0000000 <= 11) it = 1; else it = 0; + if (it) { + f32 dx = *(f32*)((u8*)a + 0x10) - mPosition.x; + f32 dy = *(f32*)((u8*)a + 0x14) - mPosition.y; + f32 dz = *(f32*)((u8*)a + 0x18) - mPosition.z; + f32 d = JGeometry::TUtil::sqrt(dx*dx + dy*dy + dz*dz); + if (d > md) { mPosition.x = *(f32*)((u8*)a + 0x10); mPosition.z = *(f32*)((u8*)a + 0x18); } + } + i++; + } + switch (mActionState) { + case 0: startVoice(0x788F); mActionState = 1; + case 1: { + if (mFloorPosition.y > mPosition.y) { mPosition.y = 10.0f + mFloorPosition.y; changePlayerStatus(0x0080023C, 0, false); break; } + if (mActionTimer < 40) { + f32 f = (f32)(40 - mActionTimer) * mJumpParams.mHipAttackSpeedY.value; + if (mJumpParams.mJumpJumpCatchSp.value + mPosition.y + f < mFloorPosition.y) { + mPosition.y += f * mJumpParams.mHipAttackSpeedY.value; + *(f32*)((u8*)this + 0x104) = mPosition.y; + } + } + setPlayerVelocity(0.0f); + *(f32*)((u8*)this + 0x50) = 0.0f; calcEntryRadius(); + setAnimation(60, 1.0f); + u16 tt = mActionTimer; mActionTimer = tt + 1; + if (mActionTimer >= 60) { mActionTimer = 0; mActionState = 2; } + mVel.y = 0.0f; + int r = jumpProcess(0); + if (r == 1) { changePlayerStatus(0x0C000230, 0, false); break; } + if (r == 2) { setPlayerVelocity(0.0f); if (mVel.y > 0.0f) mVel.y = 0.0f; changePlayerStatus(0x000208B0, 0, false); break; } + break; + } + case 2: case 3: { + setAnimation(61, 1.0f); + u16 tt = mActionTimer; mActionTimer = tt + 1; + if ((s16)mActionTimer > mJumpParams.mSuperHipAttackCt.value) mActionState = 3; + if (mActionState == 2) { mVel.y = mJumpParams.mHipAttackSpeedY.value; emitBlurHipDrop(); } + else { mVel.y = mJumpParams.mSuperHipAttackSpeedY.value; emitBlurHipDropSuper(); } + *(f32*)((u8*)this + 0x50) = mDeParams.mHipdropRadius.value; calcEntryRadius(); + *(f32*)((u8*)this + 0x54) = mDeParams.mAttackHeight.value; calcEntryRadius(); + int r = jumpProcess(0); + if (r == 1) { + if (isMario()) { + if (mActionState == 2) { SMSRumbleMgr->start(0, (f32*)nullptr); gpCameraShake->startShake((EnumCamShakeMode)0, 0.0f); } + else { rumbleStart(21, 30); gpCameraShake->startShake((EnumCamShakeMode)39, 0.0f); } + } + if (mGroundPlane->getActor()) { + if (!onYoshi() && *(u32*)((u8*)mGroundPlane->getActor() + 0x4C) - 0xC0000000 <= 106) { + emitParticle(57, (const JGeometry::TVec3*)&mPosition); + mPosition.y -= 5.0f; + ((TLiveActor*)mGroundPlane->getActor())->receiveMessage(this, 3); + startVoice(0x78D3); changePlayerStatus(0x00200346, 0, false); break; + } + if (mActionState == 2) ((TLiveActor*)mGroundPlane->getActor())->receiveMessage(this, 1); + else { ((TLiveActor*)mGroundPlane->getActor())->receiveMessage(this, 3); ((TLiveActor*)mGroundPlane->getActor())->receiveMessage(this, 1); } + } + if (mActionState == 2) { emitParticle(20); emitParticle(19); emitParticle(18); } + else { emitParticle(67); emitParticle(68); emitParticle(69); emitParticle(70); } + changePlayerStatus(0x0080023C, 0, false); + } else if (r == 2) { + setPlayerVelocity(0.0f); if (mVel.y > 0.0f) mVel.y = 0.0f; + changePlayerStatus(0x000208B0, 0, false); + rumbleStart(21, mMotorParams.mMotorWall.value); + if (gpMSound->gateCheck(0x180E)) MSoundSESystem::MSoundSE::startSoundActor(0x180E, (const Vec*)&mPosition, 0, nullptr, 0, 4); + } + break; + } + } + mModelFaceAngle = mFaceAngle.y; +} + +void TMario::diving() +{ + jumpProcess(0); +} + +BOOL TMario::jumpMain() +{ + if (*(u32*)((u8*)this + 0x6C) != 0) { + u8 s; if (mInput & 0x4000) s = 1; else s = 0; + if (s) changePlayerStatus(0x820008AB, 0, false); + } + BOOL result = 0; + switch (mAction) { + case 0x000208B4: checkBackTrig(); rocketCheck(); considerJumpRotate(); jumpingBasic(0x000208B4, 86, 0); break; + case 0x02000882: checkBackTrig(); doJumping(); jumpProcess(0); break; + case 0x08000883: checkThrowObject(); doJumping(); jumpProcess(0); break; + case 0x0884: checkBackTrig(); rocketCheck(); considerJumpRotate(); jumpingBasic(0x0884, 86, 0); break; + case 0x02000885: setPlayerVelocity(0.0f); jumpProcess(0); break; + case 0x02000886: stayWall(); break; + case 0x08000887: checkBackTrig(); rocketCheck(); considerJumpRotate(); jumpingBasic(0x08000887, 191, 1); break; + case 0x0888: jumpingBasic(0x0888, 86, 0); break; + case 0x02000889: setPlayerVelocity(0.0f); jumpProcess(0); break; + case 0x088B: rocketing(); break; + case 0x088C: slipFalling(); break; + case 0x088D: checkBackTrig(); rocketCheck(); considerJumpRotate(); jumpingBasic(0x088D, 86, 0); break; + case 0x088E: jumpingBasic(0x088E, 86, 0); break; + case 0x0891: jumpingBasic(0x0891, 86, 0); break; + case 0x0892: jumpingBasic(0x0892, 86, 0); break; + case 0x0893: jumpingBasic(0x0893, 51, 0); break; + case 0x0894: checkBackTrig(); rocketCheck(); considerJumpRotate(); jumpingBasic(0x0894, 86, 0); break; + case 0x0895: case 0x0896: jumpingBasic(mAction, 86, 0); break; + case 0x0897: emitBlurSpinJump(); jumpingBasic(0x0897, 86, 0); break; + case 0x089C: jumpingBasic(0x089C, 86, 0); break; + case 0x000208B0: fireDowning(); break; + case 0x000208B1: thrownDowning(); break; + case 0x000208B3: boardJumping(); break; + case 0x000208B6: jumpDownCommon(0, 0, 0.0f); break; + case 0x000208B8: catchStop(); break; + case 0x000208BA: jumpDownCommon(0, 0, 0.0f); break; + case 0x02000880: case 0x02000890: diving(); break; + case 0x008008A6: jumpCatch(); break; + case 0x008008A9: hipAttacking(); break; + case 0x0080088A: checkBackTrig(); rocketCheck(); considerJumpRotate(); jumpingBasic(0x0080088A, 86, 0); break; + case 0x00810446: boardJumping(); break; + case 0x0281089A: jumpingBasic(0x0281089A, 86, 0); break; + case 0x0081089B: jumpingBasic(0x0081089B, 86, 0); break; + } + return result; +} diff --git a/src/Player/MarioMove.cpp b/src/Player/MarioMove.cpp index 8b137891..00980a89 100644 --- a/src/Player/MarioMove.cpp +++ b/src/Player/MarioMove.cpp @@ -1 +1,4775 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void TMario::addVelocity(f32 vel) +{ + mForwardVel += vel; + if (mForwardVel > 99.0f) + mForwardVel = 99.0f; +} + +void TMario::windMove(const JGeometry::TVec3& wind) +{ + mPosition.x += wind.x; + mPosition.y += wind.y; + mPosition.z += wind.z; +} + +void TMario::flowMove(const JGeometry::TVec3& flow) +{ + u8 inWater; + if (mAction & 0x2000) + inWater = 1; + else + inWater = 0; + + if (inWater != 1) + return; + + mPosition.x += flow.x; + mPosition.y += flow.y; + mPosition.z += flow.z; +} + +bool TMario::moveRequest(const JGeometry::TVec3& pos) +{ + JGeometry::TVec3 localPos(pos); + localPos.sub(mPosition); + JGeometry::TVec3 delta(localPos); + + mPosition = *(JGeometry::TVec3*)&pos; + + // Adjust all position-relative fields by delta + unk160[0].x += delta.x; + unk160[0].y += delta.y; + unk160[0].z += delta.z; + + unk29C.x += delta.x; + unk29C.y += delta.y; + unk29C.z += delta.z; + + mWireStartPos.x += delta.x; + mWireStartPos.y += delta.y; + mWireStartPos.z += delta.z; + + mWireEndPos.x += delta.x; + mWireEndPos.y += delta.y; + mWireEndPos.z += delta.z; + + unk2A8.x += delta.x; + unk2A8.y += delta.y; + unk2A8.z += delta.z; + + unk2BC += delta.y; + + unk1CC += delta.x; + unk1DC += delta.y; + unk1EC += delta.z; + + *(f32*)((u8*)this + 0x1FC) += delta.x; + *(f32*)((u8*)this + 0x20C) += delta.y; + *(f32*)((u8*)this + 0x21C) += delta.z; + + *(f32*)((u8*)this + 0x22C) += delta.x; + *(f32*)((u8*)this + 0x23C) += delta.y; + *(f32*)((u8*)this + 0x24C) += delta.z; + + *(f32*)((u8*)this + 0x324) += delta.x; + *(f32*)((u8*)this + 0x334) += delta.y; + *(f32*)((u8*)this + 0x344) += delta.z; + + if (mRidingActor != NULL) { + Mtx localMtx; + if (mRidingActor->getRootJointMtx() == NULL) { + SMS_GetActorMtx(*mRidingActor, localMtx); + } else { + PSMTXCopy(*(mRidingActor->getRootJointMtx()), localMtx); + } + PSMTXInverse(localMtx, localMtx); + + *(JGeometry::TVec3*)((u8*)this + 0x300) = + *(JGeometry::TVec3*)((u8*)this + 0x2F4); + + PSMTXMultVec(localMtx, (Vec*)&mPosition, + (Vec*)((u8*)this + 0x2F4)); + } + + return true; +} + +void TMario::warpRequest(const JGeometry::TVec3& pos, f32 angle) +{ + JGeometry::TVec3 dir(pos); + dir.sub(mPosition); + JGeometry::TVec3 delta(dir); + + moveRequest(pos); + + mFaceAngle.y = DEG2SHORTANGLE(angle); + mModelFaceAngle = mFaceAngle.y; + + gpCamera->addMoveCameraAndMario(*(const Vec*)&delta); + + if (gpMarDirector->mMap != 7) + mGamePad->onNeutralMarioKey(); + + changePlayerStatus(0x0C400201, 0, true); +} + +BOOL TMario::changePlayerTriJump() +{ + int jumpAmount; + + if ((u8)isForceSlip()) { + jumpAmount = mSlipParamsAll.mMissJump.value; + } else { + const TBGCheckData* ground = mGroundPlane; + u16 bgType = ground->mBGType; + + u8 isSlippery; + if (bgType == 0x0C || bgType == 0x800C || bgType == 0xA00C) + isSlippery = 1; + else + isSlippery = 0; + if (isSlippery) { + jumpAmount = mSlipParamsAllSlider.mMissJump.value; + } else { + u8 isSand; + if (bgType == 0x02 || bgType == 0x8002) + isSand = 1; + else + isSand = 0; + if (isSand && ground->mNormal.y < 0.866025f) { + jumpAmount = mSlipParams45.mMissJump.value; + } else { + u8 isWet; + if (bgType == 0x04 || bgType == 0x4004 + || bgType == 0x8004 || bgType == 0xC004) + isWet = 1; + else + isWet = 0; + if (isWet) { + if (ground->mNormal.y > 0.99f) { + jumpAmount = mSlipParamsWaterGround.mMissJump.value; + } else { + jumpAmount = mSlipParamsWaterSlope.mMissJump.value; + } + } else { + jumpAmount = mSlipParamsNormal.mMissJump.value; + } + } + } + } + + if (jumpAmount != 0) { + mFaceAngle.x = 0; + mModelFaceAngle = mFaceAngle.y; + + if (mForwardVel > 0.0f) { + s16 oppAngle = (s16)(mSlopeAngle + 0x8000); + u16 diff = (u16)(mFaceAngle.y - oppAngle); + + f32 sinDiff = JMASSin(diff); + f32 cosDiff = JMASCos(diff); + + f32 fwd = mForwardVel; + f32 sinComp = fwd * sinDiff; + f32 cosComp = fwd * cosDiff; + f32 scaledSin = 0.75f * sinComp; + f32 sqSum = cosComp * cosComp + scaledSin * scaledSin; + + if (sqSum > 0.0f) { + double guess = __frsqrte((double)sqSum); + guess = .5 * guess * (3.0 - guess * guess * sqSum); + f32 sqrtResult; + sqrtResult = (f32)(sqSum * guess); + sqSum = sqrtResult; + } + mForwardVel = sqSum; + + mSlideVelX = mForwardVel * JMASSin((u16)mFaceAngle.y); + mSlideVelZ = mForwardVel * JMASCos((u16)mFaceAngle.y); + + mVel.x = mSlideVelX; + mVel.z = mSlideVelZ; + + s16 newAngle = matan(scaledSin, cosComp); + mFaceAngle.y = oppAngle + newAngle; + } + + dropObject(); + changePlayerStatus(0x02000885, 0, false); + return TRUE; + } + + int stickDirection; + if (checkStickRotate(&stickDirection)) { + switch (stickDirection) { + case 2: + changePlayerStatus(0x896, 0, false); + break; + case 3: + changePlayerStatus(0x895, 0, false); + break; + } + return TRUE; + } + + changePlayerStatus(0x02000880, 0, false); + return TRUE; +} + +BOOL TMario::changePlayerJumping(u32 status, u32 arg) +{ + int jumpAmount; + + if ((u8)isForceSlip()) { + jumpAmount = mSlipParamsAll.mMissJump.value; + } else { + const TBGCheckData* ground = mGroundPlane; + u16 bgType = ground->mBGType; + + u8 isSlippery; + if (bgType == 0x0C || bgType == 0x800C || bgType == 0xA00C) + isSlippery = 1; + else + isSlippery = 0; + if (isSlippery) { + jumpAmount = mSlipParamsAllSlider.mMissJump.value; + } else { + u8 isSand; + if (bgType == 0x02 || bgType == 0x8002) + isSand = 1; + else + isSand = 0; + if (isSand && ground->mNormal.y < 0.866025f) { + jumpAmount = mSlipParams45.mMissJump.value; + } else { + u8 isWet; + if (bgType == 0x04 || bgType == 0x4004 + || bgType == 0x8004 || bgType == 0xC004) + isWet = 1; + else + isWet = 0; + if (isWet) { + if (ground->mNormal.y > 0.99f) { + jumpAmount = mSlipParamsWaterGround.mMissJump.value; + } else { + jumpAmount = mSlipParamsWaterSlope.mMissJump.value; + } + } else { + jumpAmount = mSlipParamsNormal.mMissJump.value; + } + } + } + } + + if (jumpAmount != 0) { + mFaceAngle.x = 0; + mModelFaceAngle = mFaceAngle.y; + + if (mForwardVel > 0.0f) { + s16 oppAngle = (s16)(mSlopeAngle + 0x8000); + u16 diff = (u16)(mFaceAngle.y - oppAngle); + + f32 sinDiff = JMASSin(diff); + f32 cosDiff = JMASCos(diff); + + f32 fwd = mForwardVel; + f32 sinComp = fwd * sinDiff; + f32 cosComp = fwd * cosDiff; + f32 scaledSin = 0.75f * sinComp; + f32 sqSum = cosComp * cosComp + scaledSin * scaledSin; + + if (sqSum > 0.0f) { + double guess = __frsqrte((double)sqSum); + guess = .5 * guess * (3.0 - guess * guess * sqSum); + f32 sqrtResult; + sqrtResult = (f32)(sqSum * guess); + sqSum = sqrtResult; + } + mForwardVel = sqSum; + + mSlideVelX = mForwardVel * JMASSin((u16)mFaceAngle.y); + mSlideVelZ = mForwardVel * JMASCos((u16)mFaceAngle.y); + + mVel.x = mSlideVelX; + mVel.z = mSlideVelZ; + + s16 newAngle = matan(scaledSin, cosComp); + mFaceAngle.y = oppAngle + newAngle; + } + + dropObject(); + changePlayerStatus(0x02000885, 0, false); + return TRUE; + } + + int stickDirection; + if (checkStickRotate(&stickDirection)) { + switch (stickDirection) { + case 2: + changePlayerStatus(0x896, 0, false); + break; + case 3: + changePlayerStatus(0x895, 0, false); + break; + } + return TRUE; + } + + changePlayerStatus(status, arg, false); + return TRUE; +} + +BOOL TMario::changePlayerDropping(u32 status, u32 arg) +{ + dropObject(); + return changePlayerStatus(status, arg, false); +} + +BOOL TMario::changePlayerStatus(u32 status, u32 arg, bool force) +{ + if (!force) { + if (status == mAction) + return 0; + if (checkActionThing()) + return 0; + } + + if (mAction == 0x20467) + return 0; + + if (SMS_isDivingMap()) { + if ((u32)(status - 0x10020000) != 880 && status != 0x0891 + && status != 0x1302) + return 0; + } + + switch (status & 0x1C0) { + case 0x40: { + f32 speed = mIntendedMag; + if (speed <= 8.0f) + ; + else + speed = 8.0f; + + if (status == 0x04000440) { + if (0.0f <= mForwardVel && mForwardVel < speed) + mForwardVel = speed; + } else if (status == 0x50) { + u8 facing = 0; + s16 angleDiff = (s16)(mSlopeAngle - mFaceAngle.y); + if (angleDiff > -16384 && angleDiff < 16384) + facing = 1; + + if (facing) + status = 0x00840452; + else + status = 0x00840453; + + startVoice(0x78CF); + } + break; + } + case 0x80: + status = setStatusToJumping(status, arg); + break; + } + + mPrevAction = mAction; + mAction = status; + mActionArg = arg; + mActionState = 0; + mActionTimer = 0; + return 1; +} + +void TMario::throwMario(const JGeometry::TVec3& throwVec, f32 speed) +{ + JGeometry::TVec3 dir(throwVec); + + if (dir.squared() <= 0.0f) + dir.y = 0.0f; + + dir.normalize(); + + f32 dirZ = dir.z; + mFaceAngle.y = matan(dirZ, dir.x) + 0x8000; + mModelFaceAngle = mFaceAngle.y; + + f32 hMagSq = dir.x * dir.x + dirZ * dirZ; + f32 hMag = std::sqrtf(hMagSq); + + mForwardVel = speed * -hMag; + mVel.y = dir.y * speed; +} + +void TMario::setPlayerVelocity(f32 speed) +{ + mForwardVel = speed; + mSlideVelX = mForwardVel * JMASSin(mFaceAngle.y); + mSlideVelZ = mForwardVel * JMASCos(mFaceAngle.y); + mVel.x = mSlideVelX; + mVel.z = mSlideVelZ; +} + +void TMario::setNormalAttackArea() +{ + mAttackRadius = mDeParams.mHoldRadius.value; + calcEntryRadius(); + mAttackHeight = mDeParams.mAttackHeight.value; + calcEntryRadius(); +} + +BOOL TMario::canBendBody() +{ + u32 act = mAction & 0x1FF; + if (act >= 0x14B && act <= 0x14F) + return false; + if (act >= 0x140 && act <= 0x143) + return false; + return true; +} + +void TMario::checkThrowObject() +{ + if (mModel->unkC->checkPass(4.0f)) { + startVoice(0x788F); + dropObject(); + } +} + +BOOL TMario::onYoshi() const +{ + u8 result = 0; + if (mYoshi != NULL) { + if (mYoshi->onYoshi()) + result = 1; + } + return result; +} + +BOOL TMario::checkGroundPlane(f32 x, f32 y, f32 z, f32* outHeight, + const TBGCheckData** outPlane) +{ + *outHeight = gpMap->checkGround(x, y, z, outPlane); + + if ((*outPlane)->isMarioThrough()) { + *outHeight = gpMap->checkGround(x, *outHeight - 1.0f, z, outPlane); + } + + u8 isWall; + if ((*outPlane)->mFlags & 0x10) + isWall = 1; + else + isWall = 0; + + u8 isSafe; + if (isWall == 1) + isSafe = 0; + else + isSafe = 1; + + if (isSafe) + return true; + return false; +} + +f32 TMario::checkRoofPlane(const Vec& pos, f32 height, + const TBGCheckData** result) +{ + return gpMap->checkRoof(pos.x, height + 80.0f, pos.z, result); +} + +bool TMario::isFrontSlip(int param) +{ + int angle = mFaceAngle.y; + if (param != 0) { + if (mForwardVel < 0.0f) + angle = angle + 0x8000; + } + s16 diff = (s16)(mSlopeAngle - angle); + bool result = false; + if (diff > -0x4000 && diff < 0x4000) + result = true; + return result; +} + +void TMario::dirtyLimitCheck() +{ + if (unk134 < 0.0f) + unk134 = 0.0f; + f32 maxDirty = mDirtyParams.mDirtyMax.value; + if (maxDirty < unk134) + unk134 = maxDirty; +} + +void TMario::stateMachine() +{ + BOOL result = true; + while (result) { + switch (mAction & 0x1C0) { + case 0x000: + result = waitMain(); + break; + case 0x040: + result = moveMain(); + break; + case 0x080: + result = jumpMain(); + break; + case 0x0C0: + result = swimMain(); + break; + case 0x100: + result = demoMain(); + break; + case 0x140: + result = specMain(); + break; + case 0x180: + result = actnMain(); + break; + } + } +} + +void TMario::calcGroundMtx(const JGeometry::TVec3& inPos) +{ + JGeometry::TVec3 pos(inPos); + const TBGCheckData* check; + pos.y = gpMap->checkGround(pos, &check); + + JGeometry::TVec3 pt1; + pt1.x = pos.x + JMASCos(mFaceAngle.y); + pt1.y = 30.0f + pos.y; + pt1.z = pos.z - JMASSin(mFaceAngle.y); + pt1.y = gpMap->checkGround(pt1, &check); + + JGeometry::TVec3 pt2; + pt2.x = pos.x + JMASSin(mFaceAngle.y); + pt2.y = 30.0f + pos.y; + pt2.z = pos.z + JMASCos(mFaceAngle.y); + pt2.y = gpMap->checkGround(pt2, &check); + + mGroundMtx[0][0] = pt1.x - pos.x; + mGroundMtx[1][0] = pt1.y - pos.y; + mGroundMtx[2][0] = pt1.z - pos.z; + + mGroundMtx[0][1] = 0.0f; + mGroundMtx[1][1] = 1.0f; + mGroundMtx[2][1] = 0.0f; + + mGroundMtx[0][2] = pt2.x - pos.x; + mGroundMtx[1][2] = pt2.y - pos.y; + mGroundMtx[2][2] = pt2.z - pos.z; + + mGroundMtx[0][3] = pos.x; + mGroundMtx[1][3] = 18.0f + pos.y; + mGroundMtx[2][3] = pos.z; +} + +u32 TMario::setStatusToJumping(u32 status, u32 arg) +{ + unk2BC = mPosition.y; + + s16 health = *(s16*)((u8*)this + 0x360); + s32 halfMaxHealth = mDeParams.mFootPrintTimerMax.value / 2; + if (health > halfMaxHealth) { + gpPollution->stamp(1, mPosition.x, mPosition.y, mPosition.z, + mDirtyParams.mPolSizeJump.value); + } + + switch (status) { + case 0x089C: + case 0x02000880: { + // Standard/running jump + mVel.y = mForwardVel * 0.25f + 42.0f; + mForwardVel = mForwardVel * 0.8f; + + const TBGCheckData* ground = mGroundPlane; + u8 hasSlipFlag; + if (ground->mFlags & 0x10) + hasSlipFlag = 1; + else + hasSlipFlag = 0; + if (hasSlipFlag) { + break; + } + + u16 groundType = ground->mBGType; + u8 isBeachGround; + if (groundType == 0x108 || groundType == 0x8 + || groundType == 0x8008) + isBeachGround = 1; + else + isBeachGround = 0; + if (isBeachGround) { + f32 jumpPower = ground->getActiveJumpPower(); + mVel.y = 0.01f * jumpPower; + status = 0x884; + break; + } + + u8 isSlipperyGround; + if (groundType == 0x9 || groundType == 0x8009) + isSlipperyGround = 1; + else + isSlipperyGround = 0; + if (isSlipperyGround) { + f32 jumpAdj; + if (ground != NULL) { + f32 jumpPower = ground->getActiveJumpPower(); + jumpAdj = 0.01f * jumpPower; + } else { + jumpAdj = 0.0f; + } + f32 gravity = unkBC; + mVel.y = mVel.y + (-gravity + jumpAdj); + + TLiveActor* groundActor = + (TLiveActor*)mGroundPlane->mActor; + if (groundActor != NULL) { + ((THitActor*)groundActor)->receiveMessage((THitActor*)this, 0); + } + + startVoice(0x78B9); + status = 0x884; + break; + } + + // Normal ground: check speed for voice + u8 isFast; + if (unk370 > mDeParams.mFeelDeep.value) + isFast = 1; + else + isFast = 0; + if (isFast) { + startVoice(0x78A3); + } else { + startVoice(0x78AB); + } + break; + } + case 0x02000881: { + // Hip-drop jump + mVel.y + = mForwardVel * mJumpParams.mSecJumpSpeedMult.value + + mJumpParams.mSecJumpForce.value; + mForwardVel = mForwardVel * mJumpParams.mSecJumpXZMult.value; + startVoice(0x78B1); + break; + } + case 0x02000886: { + // Special jump (ground pound variant) + mVel.y = mForwardVel * 0.0f + 62.0f; + mForwardVel = 24.0f; + startVoice(0x78B1); + break; + } + case 0x0882: { + // Somersault jump + mVel.y + = mForwardVel * mJumpParams.mUltraJumpSpeedMult.value + + mJumpParams.mUltraJumpForce.value; + mForwardVel = mForwardVel * mJumpParams.mUltraJumpXZMult.value; + startVoice(0x78B6); + break; + } + case 0x0883: { + // Side somersault + mForwardVel = mJumpParams.mBackJumpForce.value; + mVel.y + = mForwardVel * 0.0f + + mJumpParams.mBackJumpForceY.value; + startVoice(0x78B6); + break; + } + case 0x0884: { + // Ground pound bounce + if (mGroundPlane != NULL) { + f32 jumpPower = mGroundPlane->getActiveJumpPower(); + mVel.y = 0.01f * jumpPower; + } else { + mVel.y = 0.0f; + } + startVoice(0x78B1); + break; + } + case 0x0895: + case 0x0896: { + // Backflip + mVel.y + = mForwardVel * 0.25f + mJumpParams.mRotateJumpForceY.value; + mForwardVel = mForwardVel * 0.8f; + startVoice(0x78B6); + break; + } + case 0x0887: { + // Spin jump + mVel.y + = mForwardVel * 0.0f + mJumpParams.mTurnJumpForce.value; + mForwardVel = 8.0f; + mFaceAngle.y + = mIntendedYaw; + startVoice(0x78B6); + break; + } + case 0x0888: { + // Wall kick + startVoice(0x78B1); + mForwardVel = mJumpParams.mBroadJumpForce.value; + mVel.y = mJumpParams.mBroadJumpForceY.value; + break; + } + case 0x02000889: { + // Long jump + startVoice(0x78B1); + mForwardVel = mJumpParams.mRotBroadJumpForce.value; + mVel.y = mJumpParams.mRotBroadJumpForceY.value; + break; + } + case 0x0208B4: { + // Zero velocity + mVel.y = 31.5f; + mForwardVel = 8.0f; + break; + } + case 0x0281089A: { + // Hip-drop-to-slide: check ground type + startVoice(0x78AB); + const TBGCheckData* groundResult; + gpMap->checkGround(mPosition.x, mPosition.y, mPosition.z, + &groundResult); + u16 gType = groundResult->mBGType; + u8 isBeach; + if (gType == 0x100 || gType == 0x101 + || (u16)(gType - 0x102) <= 3 || gType == 0x4104) + isBeach = 1; + else + isBeach = 0; + if (isBeach) { + mVel.y + = mForwardVel * mSurfingParamsWaterRed.mJumpXZRatio.value + + mSurfingParamsWaterRed.mJumpPow.value; + } else { + mVel.y + = mForwardVel * mSurfingParamsGroundRed.mJumpXZRatio.value + + mSurfingParamsGroundRed.mJumpPow.value; + } + break; + } + case 0x000208B7: { + // Wall slide jump + if (mActionArg == 2) + break; + mVel.y = mJumpParams.mFireDownForce.value; + if (mActionArg != 0) + break; + mForwardVel = -mJumpParams.mFireBackVelocity.value; + break; + } + case 0x0080088A: { + // Dive recovery + startVoice(0x7884); + f32 clampedVel = 15.0f + mForwardVel; + if (clampedVel > 48.0f) + clampedVel = 48.0f; + mForwardVel = clampedVel; + + u16 angle = mFaceAngle.y; + mSlideVelX = mForwardVel * jmaSinTable[angle >> jmaSinShift]; + mSlideVelZ = mForwardVel * jmaCosTable[angle >> jmaSinShift]; + mVel.x = mSlideVelX; + mVel.z = mSlideVelZ; + break; + } + case 0x02000885: { + // Jumping from certain state + startVoice(0x78AB); + mVel.y = mForwardVel * 0.25f + 42.0f; + break; + } + case 0x088B: { + // FLUDD-dependent jump + TWaterGun* waterGun = mWaterGun; + if (waterGun == NULL) + break; + + s32 nozzle = waterGun->mCurrentNozzle; + if (nozzle == 1) { + // Rocket + startVoice(0x78B9); + rocketEffectStart(); + } + waterGun = mWaterGun; + if (waterGun->mCurrentNozzle == 5) { + // Turbo + startVoice(0x788F); + } + waterGun = mWaterGun; + if (waterGun->mCurrentNozzle == 4) { + // Hover + startVoice(0x78AB); + } + mVel.y = mForwardVel * 0.0f + 10.0f; + break; + } + case 0x02000890: { + // Multi-bounce/triple jump + u16 animId = mAnimationId; + switch (animId) { + case 0xD2: + // Triple jump + startVoice(0x78B1); + mVel.y + = mForwardVel * 0.25f + + mDeParams.mTramplePowStep2.value; + break; + case 0xD3: + // Double jump (D3) + startVoice(0x78B6); + mVel.y + = mForwardVel * 0.25f + + mDeParams.mTramplePowStep3.value; + break; + default: + // Other + startVoice(0x78AB); + mVel.y + = mForwardVel * 0.25f + + mDeParams.mTramplePowStep1.value; + break; + } + mForwardVel = mForwardVel * 0.8f; + break; + } + case 0x0892: { + // Directional air + mVel.y = mForwardVel * 0.25f + 42.0f; + mForwardVel = 0.0f; + u16 angle = mFaceAngle.y; + mSlideVelX = mForwardVel * jmaSinTable[angle >> jmaSinShift]; + mSlideVelZ = mForwardVel * jmaCosTable[angle >> jmaSinShift]; + mVel.x = mSlideVelX; + mVel.z = mSlideVelZ; + startVoice(0x78B6); + break; + } + case 0x0893: { + // Pole jump + if (arg == 0) { + s16 poleAngle = unkF6; + s32 fixedAngle = -0x2000; + f32 paramSpeed = mWireParams.mJumpRate.value; + f32 fAngle = (f32)poleAngle; + f32 sinVal = jmaSinTable[fixedAngle >> jmaSinShift]; + mVel.y = fAngle * paramSpeed * 1.0f * sinVal; + f32 cosVal = jmaCosTable[fixedAngle >> jmaSinShift]; + mForwardVel = -(fAngle * paramSpeed * 1.0f) * cosVal; + + u16 faceAngle = mFaceAngle.y; + mSlideVelX + = mForwardVel * jmaSinTable[faceAngle >> jmaSinShift]; + mSlideVelZ + = mForwardVel * jmaCosTable[faceAngle >> jmaSinShift]; + mVel.x = mSlideVelX; + mVel.z = mSlideVelZ; + } else { + s16 poleAngle = unkF6; + f32 fAngle = (f32)poleAngle; + s32 fixedAngle = 0x6000; + f32 paramSpeed = mWireParams.mJumpRate.value; + f32 sinVal = jmaSinTable[fixedAngle >> jmaSinShift]; + mVel.y = fAngle * paramSpeed * 1.0f * sinVal; + f32 cosVal = jmaCosTable[fixedAngle >> jmaSinShift]; + mForwardVel = -(fAngle * paramSpeed * 1.0f) * cosVal; + + u16 faceAngle = mFaceAngle.y; + mSlideVelX + = mForwardVel * jmaSinTable[faceAngle >> jmaSinShift]; + mSlideVelZ + = mForwardVel * jmaCosTable[faceAngle >> jmaSinShift]; + mVel.x = mSlideVelX; + mVel.z = mSlideVelZ; + } + startVoice(0x78B9); + break; + } + case 0x0894: { + // Slide jump + mVel.y = mForwardVel * 0.0f + 42.0f; + mForwardVel = 0.0f; + u16 angle = mFaceAngle.y; + mSlideVelX = mForwardVel * jmaSinTable[angle >> jmaSinShift]; + mSlideVelZ = mForwardVel * jmaCosTable[angle >> jmaSinShift]; + mVel.x = mSlideVelX; + mVel.z = mSlideVelZ; + startVoice(0x78AB); + break; + } + default: + break; + } + + // Speed bonus + f32 speedBonus = *(f32*)((u8*)this + 0x368); + int hasSpeedBonus; + if (speedBonus > 0.0f) + hasSpeedBonus = 1; + else + hasSpeedBonus = 0; + if (hasSpeedBonus) { + s16 maxAge = mGraffitoParams.mSinkTime.value; + f32 fMaxAge = (f32)maxAge; + f32 minScale = mGraffitoParams.mSinkJumpRateMin.value; + f32 maxScale = mGraffitoParams.mSinkJumpRateMax.value; + f32 scaleRange = maxScale - minScale; + f32 ratio = 1.0f - speedBonus / fMaxAge; + f32 scale = scaleRange * ratio + minScale; + mVel.y *= scale; + mForwardVel *= scale; + + // Decay speed bonus + f32 decayParam = mGraffitoParams.mSinkRecover.value; + f32 bonus2 = *(f32*)((u8*)this + 0x368); + f32 fAge2 = (f32)maxAge; + f32 ratio2 = bonus2 / fAge2; + f32 invRatio = 1.0f - ratio2; + f32 decay = fAge2 * decayParam; + f32 newBonus = bonus2 - invRatio * decay; + *(f32*)((u8*)this + 0x368) = newBonus; + if (*(f32*)((u8*)this + 0x368) < 0.0f) + *(f32*)((u8*)this + 0x368) = 0.0f; + } + + // Yoshi check + u8 isOnYoshi = 0; + if (mYoshi != NULL) { + if (((TYoshi*)mYoshi)->onYoshi()) + isOnYoshi = 1; + } + if (isOnYoshi) { + mVel.y *= mYoshiParams.mJumpYoshiMult.value; + TYoshi* yoshi = (TYoshi*)mYoshi; + yoshi->mFlutterState = 0; + yoshi->mFlutterTimer = yoshi->mMaxFlutterTimer; + } + + // Store jump-start Y + *(f32*)((u8*)this + 0x104) = mPosition.y; + + // Update flag bit 8 based on status bit 25 + if (status & 0x02000000) { + unk78 |= 0x100; + } else { + unk78 &= ~0x100; + } + + return status; +} + +void TMario::checkPlayerAround(int angleOffset, f32 distance) +{ + u16 angle = mFaceAngle.y + angleOffset; + f32 sinVal = JMASSin(angle) * distance; + f32 cosVal = JMASCos(angle) * distance; + const TBGCheckData* result; + gpMap->checkGround(mPosition.x + sinVal, mPosition.y + 100.0f, + mPosition.z + cosVal, &result); +} + +void TMario::checkRideReCalc() +{ + if (mRidingActor != NULL) { + Mtx localMtx; + if (mRidingActor->getRootJointMtx() == NULL) { + SMS_GetActorMtx(*mRidingActor, localMtx); + } else { + PSMTXCopy(*(mRidingActor->getRootJointMtx()), localMtx); + } + PSMTXInverse(localMtx, localMtx); + + *(JGeometry::TVec3*)((u8*)this + 0x300) = + *(JGeometry::TVec3*)((u8*)this + 0x2F4); + + PSMTXMultVec(localMtx, (Vec*)&mPosition, + (Vec*)((u8*)this + 0x2F4)); + } +} + +// Helper macro to avoid caching ctrl in a register (original reloads from unk108 each time) +#define CTRL ((TMarioControllerWork*)unk108) + +void TMario::checkController(JDrama::TGraphics* gfx) +{ + // Convert gamepad stick positions to controller stick values + CTRL->mStickHS16 = (s16)(128.0f * mGamePad->mCompSPos[0]); + CTRL->mStickVS16 = (s16)(128.0f * mGamePad->mCompSPos[1]); + + // Scale stick values during special movement + f32 timer368 = *(f32*)((u8*)this + 0x368); + int bTimerActive; + if (timer368 > 0.0f) { + bTimerActive = 1; + } else { + bTimerActive = 0; + } + if (bTimerActive) { + s16 maxTime = mGraffitoParams.mSinkTime.value; + f32 minScale = mGraffitoParams.mSinkMoveMin.value; + f32 maxScale = mGraffitoParams.mSinkMoveMax.value; + f32 ratio = timer368 / (f32)maxTime; + f32 scale = (maxScale - minScale) * (1.0f - ratio) + minScale; + CTRL->mStickHS16 = (s16)((f32)CTRL->mStickHS16 * scale); + CTRL->mStickVS16 = (s16)((f32)CTRL->mStickVS16 * scale); + } + + // Clear button flags + CTRL->mInput = (TMarioControllerWork::Buttons)0; + CTRL->mFrameInput = (TMarioControllerWork::Buttons)0; + + // Map gamepad meanings to controller buttons + // A button + if (mGamePad->mMeaning & TMarioGamePad::MEANING_0x80) + CTRL->mInput = (TMarioControllerWork::Buttons)(CTRL->mInput | TMarioControllerWork::A); + if (mGamePad->mEnabledFrameMeaning & TMarioGamePad::MEANING_0x80) + CTRL->mFrameInput + = (TMarioControllerWork::Buttons)(CTRL->mFrameInput | TMarioControllerWork::A); + + // B button + if (mGamePad->mMeaning & TMarioGamePad::MEANING_0x100) + CTRL->mInput = (TMarioControllerWork::Buttons)(CTRL->mInput | TMarioControllerWork::B); + if (mGamePad->mEnabledFrameMeaning & TMarioGamePad::MEANING_0x100) + CTRL->mFrameInput + = (TMarioControllerWork::Buttons)(CTRL->mFrameInput | TMarioControllerWork::B); + + // R trigger + if (mGamePad->mMeaning & TMarioGamePad::MEANING_0x400) + CTRL->mInput = (TMarioControllerWork::Buttons)(CTRL->mInput | TMarioControllerWork::R); + if (mGamePad->mEnabledFrameMeaning & TMarioGamePad::MEANING_0x400) + CTRL->mFrameInput + = (TMarioControllerWork::Buttons)(CTRL->mFrameInput | TMarioControllerWork::R); + + // L trigger (frame only) + if (mGamePad->mEnabledFrameMeaning & TMarioGamePad::MEANING_0x1000) + CTRL->mFrameInput = (TMarioControllerWork::Buttons)(CTRL->mFrameInput | 0x10); + + // Convert analog triggers and get raw values + u8* pZeroVal = (u8*)((u8*)this + 0x2358); + u8* pMidVal = (u8*)((u8*)this + 0x236C); + u8* pMaxVal = (u8*)((u8*)this + 0x2380); + f32* pMidLevel = (f32*)((u8*)this + 0x2394); + + CTRL->mAnalogRU8 = (u8)(s32)mGamePad->mCompSPos[3]; + CTRL->mAnalogLU8 = (u8)(s32)mGamePad->mCompSPos[2]; + + u8 analogR = (u8)(s32)mGamePad->mCompSPos[3]; + u8 analogL = (u8)(s32)mGamePad->mCompSPos[2]; + + // Analog R interpolation -> ctrl->mAnalogR + f32 interpResult; + if (analogR < *pZeroVal) { + interpResult = 0.0f; + } else if (analogR < *pMidVal) { + interpResult + = *pMidLevel * (f32)(s32)(analogR - *pZeroVal) / (f32)(s32)(*pMidVal - *pZeroVal); + } else if (analogR < *pMaxVal) { + interpResult + = *pMidLevel + + (1.0f - *pMidLevel) * (f32)(s32)(analogR - *pMidVal) + / (f32)(s32)(*pMaxVal - *pMidVal); + } else { + interpResult = 1.0f; + } + CTRL->mAnalogR = interpResult; + + // Analog L interpolation -> ctrl->mAnalogL + if (analogL < *pZeroVal) { + interpResult = 0.0f; + } else if (analogL < *pMidVal) { + interpResult + = *pMidLevel * (f32)(s32)(analogL - *pZeroVal) / (f32)(s32)(*pMidVal - *pZeroVal); + } else if (analogL < *pMaxVal) { + interpResult + = *pMidLevel + + (1.0f - *pMidLevel) * (f32)(s32)(analogL - *pMidVal) + / (f32)(s32)(*pMaxVal - *pMidVal); + } else { + interpResult = 1.0f; + } + CTRL->mAnalogL = interpResult; + + // Analog L -> unk10C + if (analogL < *pZeroVal) { + interpResult = 0.0f; + } else if (analogL < *pMidVal) { + interpResult + = *pMidLevel * (f32)(s32)(analogL - *pZeroVal) / (f32)(s32)(*pMidVal - *pZeroVal); + } else if (analogL < *pMaxVal) { + interpResult + = *pMidLevel + + (1.0f - *pMidLevel) * (f32)(s32)(analogL - *pMidVal) + / (f32)(s32)(*pMaxVal - *pMidVal); + } else { + interpResult = 1.0f; + } + *(f32*)((u8*)this + 0x10C) = interpResult; + + // Analog R -> unk110 + if (analogR < *pZeroVal) { + interpResult = 0.0f; + } else if (analogR < *pMidVal) { + interpResult + = *pMidLevel * (f32)(s32)(analogR - *pZeroVal) / (f32)(s32)(*pMidVal - *pZeroVal); + } else if (analogR < *pMaxVal) { + interpResult + = *pMidLevel + + (1.0f - *pMidLevel) * (f32)(s32)(analogR - *pMidVal) + / (f32)(s32)(*pMaxVal - *pMidVal); + } else { + interpResult = 1.0f; + } + *(f32*)((u8*)this + 0x110) = interpResult; + + // Deadzone processing for stick H/V + CTRL->mStickH = 0.0f; + CTRL->mStickV = 0.0f; + + if (CTRL->mStickHS16 < -7) + CTRL->mStickH = (f32)(CTRL->mStickHS16 + 6); + if (CTRL->mStickHS16 > 7) + CTRL->mStickH = (f32)(CTRL->mStickHS16 - 6); + if (CTRL->mStickVS16 < -7) + CTRL->mStickV = (f32)(CTRL->mStickVS16 + 6); + if (CTRL->mStickVS16 > 7) + CTRL->mStickV = (f32)(CTRL->mStickVS16 - 6); + + // Compute stick magnitude + f32 stickH = CTRL->mStickH; + f32 stickV = CTRL->mStickV; + f32 dist2 = stickH * stickH + stickV * stickV; + f32 stickDist = dist2; + + if (dist2 > 0.0f) { + double guess = __frsqrte((double)dist2); + guess = .5 * guess * (3.0 - guess * guess * dist2); + f32 sqrtResult; + sqrtResult = (f32)(dist2 * guess); + stickDist = sqrtResult; + } + + // Apply decay from mLengthMultTimes + s32 times = (s32)mControllerParams.mLengthMultTimes.value; + for (s32 i = 0; i < times; i++) { + stickDist *= mControllerParams.mLengthMult.value; + } + + CTRL->mStickDist = stickDist; + + // Cap stick distance at 64.0f + if (CTRL->mStickDist > 64.0f) { + f32 scale = 64.0f / CTRL->mStickDist; + CTRL->mStickH = CTRL->mStickH * scale; + CTRL->mStickV = CTRL->mStickV * (64.0f / CTRL->mStickDist); + CTRL->mStickDist = 64.0f; + } + + // Compute intended magnitude + f32 normDist = (1.0f / 64.0f) * CTRL->mStickDist; + mIntendedMag = 32.0f * normDist * normDist; + + // Decrement rotation timer + if (*(s16*)((u8*)this + 0xA0) > 0) + *(s16*)((u8*)this + 0xA0) = *(s16*)((u8*)this + 0xA0) - 1; + + // Rotation processing + s32 rotOffset = 0; + if (*(s16*)((u8*)this + 0xA0) > 0) { + s16 rotTimer = *(s16*)((u8*)this + 0xA0); + s16 unk252C = mGraffitoParams.mDizzyAngleY.value; + s16 unk2518 = mGraffitoParams.mDizzyWalkCtMax.value; + f32 unk2540 = mGraffitoParams.mDizzyAngleRate.value; + f32 unk2554 = mGraffitoParams.mDizzyPowerRate.value; + f32 unk2568 = mGraffitoParams.mDizzyPower.value; + + u16 sinAngle = (u16)(s32)((f32)rotTimer * unk2540); + u16 cosAngle = (u16)(s32)((f32)rotTimer * unk2554); + + f32 sinVal = JMASSin(sinAngle); + f32 cosVal = JMASCos(cosAngle); + + rotOffset = (s32)(sinVal * (f32)unk252C * (f32)unk252C / (f32)unk2518); + mIntendedMag = mIntendedMag + cosVal * unk2568; + if (mIntendedMag < 0.0f) + mIntendedMag = 0.0f; + } + + // Set intended yaw from stick direction + if (mIntendedMag > 0.0f) { + s16 stickAngle = matan(-CTRL->mStickV, CTRL->mStickH); + s16 camAngle = *(s16*)((u8*)gpCamera + 0x258); + mIntendedYaw = stickAngle + camAngle + rotOffset; + } else { + mIntendedYaw = mFaceAngle.y; + } + + // Watergun turbo nozzle handling + if (mWaterGun != nullptr) { + if (mWaterGun->mCurrentNozzle == TWaterGun::Turbo) { + u8 hasPump; + if (mPumpState == 0) { + hasPump = 1; + } else { + hasPump = 0; + } + if (hasPump && mGamePad->mCompSPos[3] > 0.0f + && (f32)(s32)mWaterGun->mCurrentWater > 0.0f) { + // Turbo nozzle active + if (0.0f == mIntendedMag) + mIntendedYaw = mFaceAngle.y; + + f32 rotSpeed = mDeParams.mDashAcc.value; + + *(f32*)unkC0 += rotSpeed; + if (*(f32*)unkC0 > 32.0f) { + *(f32*)unkC0 = 32.0f; + *(s16*)(unkC0 + 4) = *(s16*)(unkC0 + 4) + 1; + if ((f32)*(s16*)(unkC0 + 4) + > (f32)mDeParams.mDashStartTime.value) { + *(s16*)(unkC0 + 4) = mDeParams.mDashStartTime.value; + + u8 hasFlag; + if (mState & 0x4000) { + hasFlag = 1; + } else { + hasFlag = 0; + } + if (!hasFlag) { + TNozzleBase* nozzle + = mWaterGun->getCurrentNozzle(); + if (((TNozzleTrigger*)nozzle)->unk385 == 1) { + mState |= 0x4000; + startSoundActor(0x814); + + u8 isRunning; + if (mAction & 0x2000) { + isRunning = 1; + } else { + isRunning = 0; + } + if (isRunning) { + changePlayerStatus( + 0x24D5, 0, false); + } + } + } + } + + // Check special states + if ((mAction - 0x04000000u) == 0x440u + || mAction == 0x24D5) { + // keep going + } else { + *(s16*)(unkC0 + 4) = 0; + mState &= ~0x4000; + } + } else { + *(s16*)(unkC0 + 4) = 0; + mState &= ~0x4000; + } + + mIntendedMag = *(f32*)unkC0; + mWaterGun->rotateProp(*(f32*)unkC0); + } else { + if (*(f32*)unkC0 > 0.1f) { + if (0.0f == mIntendedMag) + mIntendedYaw = mFaceAngle.y; + *(f32*)unkC0 *= mDeParams.mDashBrake.value; + mIntendedMag = *(f32*)unkC0; + } else { + *(f32*)unkC0 = 0.0f; + } + *(s16*)(unkC0 + 4) = 0; + mState &= ~0x4000; + } + } else { + if (*(f32*)unkC0 > 0.1f) { + if (0.0f == mIntendedMag) + mIntendedYaw = mFaceAngle.y; + *(f32*)unkC0 *= mDeParams.mDashBrake.value; + mIntendedMag = *(f32*)unkC0; + } else { + *(f32*)unkC0 = 0.0f; + } + *(s16*)(unkC0 + 4) = 0; + mState &= ~0x4000; + } + + // Check turbo nozzle prop rotation + if (mWaterGun->mCurrentNozzle == TWaterGun::Turbo) { + if ((mAction - 0x0C400000u) == 0x201u + || (mAction - 0x04000000u) == 0x440u) { + f32 propSpeed = mIntendedMag * (1.0f / 32.0f); + TNozzleBase* nozzle = mWaterGun->getCurrentNozzle(); + *(f32*)((u8*)nozzle + 0x714) = propSpeed; + } + } + } + + // Set stick moved flag + if (mIntendedMag > 0.0f) + mInput |= 0x1; + + // B button frame -> input flag 0x2 + if (mGamePad->mEnabledFrameMeaning & TMarioGamePad::MEANING_0x80) + mInput |= 0x2; + + // A button held -> input flag 0x80 + if (mGamePad->mMeaning & TMarioGamePad::MEANING_0x80) + mInput |= 0x80; + + // B button on ctrl -> input flag 0x4000 + if (CTRL->mInput & TMarioControllerWork::B) + mInput |= 0x4000; + + // Check Yoshi + u8 isOnYoshi = 0; + if (mYoshi != nullptr) { + if (mYoshi->onYoshi()) + isOnYoshi = 1; + } + + // B frame when not on Yoshi + if (!isOnYoshi) { + if (CTRL->mFrameInput & TMarioControllerWork::B) { + mInput |= 0x8000; + mInput |= 0x2000; + } + } + + // L trigger / Z button check + if ((mGamePad->mEnabledFrameMeaning & TMarioGamePad::MEANING_0x2000) + || (CTRL->mFrameInput & 0x40)) { + u8 isSpecialAction; + if (mAction & 0x800) { + isSpecialAction = 1; + } else { + isSpecialAction = 0; + } + if (isSpecialAction == 1) + mInput |= 0x8000; + } + + // FLUDD nozzle switch handling + u8 hasFluddFlag; + if (mState & 0x8000) { + hasFluddFlag = 1; + } else { + hasFluddFlag = 0; + } + if (hasFluddFlag) { + u8 isTurboActive; + if (mState & 0x4000) { + isTurboActive = 1; + } else { + isTurboActive = 0; + } + if (!isTurboActive) { + u32 meaning = mGamePad->mMeaning; + if ((meaning & TMarioGamePad::MEANING_0x400) + || (meaning & TMarioGamePad::MEANING_0x2000)) + mInput |= 0x200; + if (mGamePad->mEnabledFrameMeaning & TMarioGamePad::MEANING_0x400) + mInput |= 0x100; + } + } +} + +#undef CTRL + +void TMario::checkPlayerAction(JDrama::TGraphics* gfx) +{ + mInput = 0; + checkController(gfx); + makeHistory(); + checkCurrentPlane(); + checkRideMovement(); + if (!(mInput & 3)) + mInput |= 0x20; +} + +void TMario::makeHistory() +{ + if (mIntendedMag > 0.0f) { + if (unk534 == 0) + *(s16*)(&unk536) = mFaceAngle.y; + + unk530[unk534] = mIntendedYaw; + unk534 = unk534 + 1; + + if ((s32)unk534 >= mControllerParams.mStickRotateTime.value) { + int i = 0; + int offset = 0; + while (i < mControllerParams.mStickRotateTime.value) { + s16* p = (s16*)((u8*)unk530 + offset); + *p = *(s16*)((u8*)p + 2); + i++; + offset += 2; + } + unk534 = (u8)(mControllerParams.mStickRotateTime.value - 1); + } + + s16 diff = (s16)(mIntendedYaw - mFaceAngle.y); + if (diff >= -0x2000 && diff <= 0x2000) { + *(s16*)(&unk538) = *(s16*)(&unk538) + 1; + if (*(s16*)(&unk538) > 0x78) { + unk53B = 6; + *(s16*)(&unk538) = 0x78; + } + } else { + *(s16*)(&unk538) = 0; + } + } else { + unk534 = 0; + *(s16*)(&unk538) = 0; + } + + if (unk53B != 0) { + unk53A = 1; + unk53B = unk53B - 1; + } else { + unk53A = 0; + unk53B = 0; + } +} + +BOOL TMario::checkAllMotions() +{ + u32 flags = mInput; + if (flags & 0x2) { + int rotDir; + BOOL rotResult; + if (checkStickRotate(&rotDir)) { + switch (rotDir) { + case 2: + changePlayerStatus(0x896, 0, false); + break; + case 3: + changePlayerStatus(0x895, 0, false); + break; + } + rotResult = 1; + } else { + rotResult = 0; + } + + if (rotResult) + return true; + + return changePlayerStatus(0x02000880, 0, false); + } + + if (flags & 0x4) + return changePlayerStatus(0x88C, 0, false); + + if (flags & 0x1) + return changePlayerStatus(0x04000440, 0, false); + + if (flags & 0x8) + return changePlayerStatus(0x50, 0, false); + + return false; +} + +void TMario::checkGraffitoFire() +{ + u8 shouldSkip; + if (unk14C > 0) { + shouldSkip = 1; + } else { + u8 flagCheck; + if (mState & 0x8) + flagCheck = 1; + else + flagCheck = 0; + if (flagCheck) { + shouldSkip = 1; + } else if (mAction == 0x89C) { + shouldSkip = 1; + } else { + u8 areaId = *(u8*)((u8*)gpMarDirector + 0x124); + if (areaId == 3 || areaId == 4) { + shouldSkip = 1; + } else { + u8 inArea = 1; + if (areaId != 1) { + if (areaId != 2) + inArea = 0; + } + if (inArea) { + shouldSkip = 1; + } else { + u8 actionBit; + if (mAction & 0x1000) + actionBit = 1; + else + actionBit = 0; + if (actionBit) + shouldSkip = 1; + else + shouldSkip = 0; + } + } + } + } + if (shouldSkip) return; + + u8 waterFlag; + if (mState & 0x400) + waterFlag = 1; + else + waterFlag = 0; + if (waterFlag) return; + + if (mPosition.y - mFloorPosition.y > mGraffitoParams.mFireHeight.value) + return; + + u32 action = mAction; + if (action == 0x208B7 || action == 0x8000239) { + mFaceAngle.y += 0x8000; + } + + f32 savedForwardVel = mForwardVel; + f32 savedVelY = mVel.y; + + u8* fireType = (u8*)this + 0x3930; + u8* fireDamage = (u8*)this + 0x3944; + s16* fireRadius = (s16*)((u8*)this + 0x3980); + + *(f32*)((u8*)this + 0x484) = mPosition.x + JMASSin(mFaceAngle.y); + *(f32*)((u8*)this + 0x48C) = mPosition.z + JMASCos(mFaceAngle.y); + + damageExec((THitActor*)((u8*)this + 0x474), + mDmgParamsGraffitoFire.mDamage.value, + mDmgParamsGraffitoFire.mDownType.value, + *fireType, + mDmgParamsGraffitoFire.mMinSpeed.value, + *fireDamage, + mDmgParamsGraffitoFire.mDirty.value, + *fireRadius); + + if (*(f32*)((u8*)this + 0x55C) > 0.0f) { + mVel.y = -savedVelY; + mForwardVel = savedForwardVel; + } + + unk14C = mGraffitoParams.mFireInvincibleTime.value; + dropObject(); + changePlayerStatus(0x208B7, 1, false); + gpMarioParticleManager->emitAndBindToPosPtr(6, &mPosition, 0, 0); + + if (gpMSound->gateCheck(0x1813)) { + MSoundSESystem::MSoundSE::startSoundActor( + 0x1813, (Vec*)&mPosition, 0, (JAISound**)0, 0, 4); + } +} + +void TMario::checkGraffitoSlip() +{ + u8 onSlipSurface; + if (mPosition.y <= 4.0f + mFloorPosition.y) + onSlipSurface = 1; + else + onSlipSurface = 0; + + if (onSlipSurface) { + *(s16*)((u8*)this + 0x360) = mDeParams.mFootPrintTimerMax.value; + + u32 action = mAction; + if (action == 0x84045D || action == 0x4045E) { + *(f32*)((u8*)this + 0x138) = mDirtyParams.mBrakeStartValSlip.value; + *(s16*)((u8*)this + 0x13C) = mDirtyParams.mDirtyTimeSlip.value; + } + + action = mAction; + if (action - 0x40000 == 0x45C || action - 0x40000 == 0x561) { + *(f32*)((u8*)this + 0x138) = mDirtyParams.mBrakeStartValRun.value; + *(s16*)((u8*)this + 0x13C) = mDirtyParams.mDirtyTimeRun.value; + } + + const TBGCheckData* ground = mGroundPlane; + if (*(f32*)((u8*)ground + 0x38) <= mDirtyParams.mSlopeAngle.value) { + *(f32*)((u8*)this + 0x138) = mDirtyParams.mBrakeStartValSlip.value; + *(s16*)((u8*)this + 0x13C) = mDirtyParams.mDirtyTimeSlip.value; + changePlayerStatus(0x4045E, 0, false); + startVoice(0x78D3); + } else { + action = mAction; + if (action == 0x80088A || action == 0x800456 + || action == 0x84045D || action == 0x4045E) { + *(f32*)((u8*)this + 0x138) = mDirtyParams.mBrakeStartValSlip.value; + *(s16*)((u8*)this + 0x13C) = mDirtyParams.mDirtyTimeSlip.value; + changePlayerStatus(0x84045D, 0, false); + if (mPrevAction != 0x84045D) { + startVoice(0x78D3); + } + } else if (mAction != 0x386) { + *(f32*)((u8*)this + 0x138) = mDirtyParams.mBrakeStartValRun.value; + *(s16*)((u8*)this + 0x13C) = mDirtyParams.mDirtyTimeRun.value; + if (mAction == 0x560) { + changePlayerStatus(0x40561, 0, false); + } else { + changePlayerStatus(0x4045C, 0, false); + } + } + } + + u8 bit25; + if (mState & 0x40) + bit25 = 1; + else + bit25 = 0; + if (!bit25) { + unk34E = mDirtyParams.mFogTimeYellow.value + + mDirtyParams.mFogTimeRed.value; + } + u16 timer = unk34E; + unk34E = timer - 1; + timer = unk34E; + if (timer != 0) { + if (timer == mDirtyParams.mFogTimeRed.value) { + floorDamageExec(1, 3, 0, + mMotorParams.mMotorReturn.value); + } + } else { + unk34E = mDirtyParams.mFogTimeYellow.value + + mDirtyParams.mFogTimeRed.value; + } + } else { + u32 action = mAction; + if (action == 0x84045D || action == 0x4045E) { + *(f32*)((u8*)this + 0x138) = mDirtyParams.mBrakeSlipNoPollute.value; + *(s16*)((u8*)this + 0x13C) = mDirtyParams.mDirtyTimeSlip.value; + } + } +} + +void TMario::checkGraffitoElec() +{ + u8 bit25; + if (mState & 0x40) + bit25 = 1; + else + bit25 = 0; + if (!bit25) { + unk34E = mDeParams.mGraffitoNoDmgTime.value; + } + + u16 timer = unk34E; + if (timer != 0) { + unk34E = timer - 1; + return; + } + + u8 shouldSkip; + if (unk14C > 0) { + shouldSkip = 1; + } else { + u8 flagCheck; + if (mState & 0x8) + flagCheck = 1; + else + flagCheck = 0; + if (flagCheck) { + shouldSkip = 1; + } else if (mAction == 0x89C) { + shouldSkip = 1; + } else { + u8 areaId = *(u8*)((u8*)gpMarDirector + 0x124); + if (areaId == 3 || areaId == 4) { + shouldSkip = 1; + } else { + u8 inArea = 1; + if (areaId != 1) { + if (areaId != 2) + inArea = 0; + } + if (inArea) { + shouldSkip = 1; + } else { + u8 actionBit; + if (mAction & 0x1000) + actionBit = 1; + else + actionBit = 0; + if (actionBit) + shouldSkip = 1; + else + shouldSkip = 0; + } + } + } + } + if (shouldSkip) return; + + u32 motionBits = mAction & 0x1C0; + if (motionBits != 0) { + if (motionBits != 0x40) return; + } + + if (*(s16*)((u8*)this + 0x360) > 0) return; + + changePlayerStatus(0x20338, 0, false); + + if (gpMSound->gateCheck(0x1814)) { + MSoundSESystem::MSoundSE::startSoundActor( + 0x1814, (Vec*)&mPosition, 0, (JAISound**)0, 0, 4); + } + if (gpMSound->gateCheck(0x3806)) { + MSoundSESystem::MSoundSE::startSoundActor( + 0x3806, (Vec*)&mPosition, 0, (JAISound**)0, 0, 4); + } +} + +void TMario::checkGraffito() +{ + // Early exit: ground plane has graffito flag + u8 hasGrafFlag; + if (mGroundPlane->mFlags & 0x10) + hasGrafFlag = 1; + else + hasGrafFlag = 0; + + if (hasGrafFlag) + return; + + // Early exit: on Yoshi + u8 onYoshiCheck = 0; + if (mYoshi != NULL) { + if (((TYoshi*)mYoshi)->onYoshi()) + onYoshiCheck = 1; + } + if (onYoshiCheck) + return; + + // Early exit: unk388 state + if (unk388 == 1) + return; + if (unk388 == 2) + return; + + // Get pollution type at current position + s32 isPolluted = 0; + unk350 = gpPollution->getPollutionType(mPosition.x, mPosition.y, + mPosition.z); + + switch (unk350) { + case 2: + case 5: + case 6: { + // 3x3 grid check + isPolluted = 1; + JGeometry::TVec3 pos(mPosition); + pos.x -= 38.0f; + pos.z -= 38.0f; + + // Row 0: check 3 columns + if (!gpPollution->isPolluted(pos.x, pos.y, pos.z)) + isPolluted = 0; + pos.x += 38.0f; + if (!gpPollution->isPolluted(pos.x, pos.y, pos.z)) + isPolluted = 0; + pos.x += 38.0f; + if (!gpPollution->isPolluted(pos.x, pos.y, pos.z)) + isPolluted = 0; + + // Row 1: z += 38 + pos.z += 38.0f; + if (!gpPollution->isPolluted(pos.x, pos.y, pos.z)) + isPolluted = 0; + pos.x -= 38.0f; + if (!gpPollution->isPolluted(pos.x, pos.y, pos.z)) + isPolluted = 0; + pos.x -= 38.0f; + if (!gpPollution->isPolluted(pos.x, pos.y, pos.z)) + isPolluted = 0; + + // Row 2: z += 38 + pos.z += 38.0f; + if (!gpPollution->isPolluted(pos.x, pos.y, pos.z)) + isPolluted = 0; + pos.x += 38.0f; + if (!gpPollution->isPolluted(pos.x, pos.y, pos.z)) + isPolluted = 0; + pos.x += 38.0f; + if (gpPollution->isPolluted(pos.x, pos.y, pos.z)) + break; + isPolluted = 0; + break; + } + case 0: + case 1: + case 3: + case 7: { + // Cross pattern check (5 points) + isPolluted = 1; + JGeometry::TVec3 pos; + pos.x = mPosition.x; + pos.y = *(f32*)((u8*)this + 0xEC); + pos.z = mPosition.z; + + pos.z -= 38.0f; + if (!gpPollution->isPolluted(pos.x, pos.y, pos.z)) + isPolluted = 0; + pos.x += 38.0f; + pos.z += 38.0f; + if (!gpPollution->isPolluted(pos.x, pos.y, pos.z)) + isPolluted = 0; + pos.x -= 38.0f; + if (!gpPollution->isPolluted(pos.x, pos.y, pos.z)) + isPolluted = 0; + pos.x -= 38.0f; + if (!gpPollution->isPolluted(pos.x, pos.y, pos.z)) + isPolluted = 0; + pos.z += 38.0f; + pos.x += 38.0f; + if (gpPollution->isPolluted(pos.x, pos.y, pos.z)) + break; + isPolluted = 0; + break; + } + case 4: { + // Single point check + JGeometry::TVec3 pos; + pos.x = mPosition.x; + pos.y = *(f32*)((u8*)this + 0xEC); + pos.z = mPosition.z; + + if (gpPollution->isPolluted(pos.x, pos.y, pos.z)) { + isPolluted = 1; + } else { + isPolluted = 0; + } + break; + } + case 8: + case 10: + isPolluted = 0; + break; + case 9: + default: + break; + } + + // If on Yoshi and polluted, get off + u8 onYoshi2 = 0; + if (mYoshi != NULL) { + if (((TYoshi*)mYoshi)->onYoshi()) + onYoshi2 = 1; + } + if (onYoshi2) { + if (isPolluted == 1) { + getOffYoshi(true); + } + } + + // Second switch: call appropriate graffito handler + switch (unk350) { + case 4: + if (isPolluted == 1) + checkGraffitoElec(); + break; + case 1: + case 7: + if (isPolluted == 1) + checkGraffitoFire(); + break; + case 2: + if (isPolluted == 1) + checkGraffitoSlip(); + break; + case 3: + if (isPolluted == 1) { + mPosition.x = *(f32*)((u8*)this + 0x29C); + mPosition.z = *(f32*)((u8*)this + 0x2A4); + } + break; + default: + break; + } + + // Set/clear graffito flag + if (isPolluted == 1) { + mState |= 0x40; + } else { + mState &= ~0x40; + } + + // Check floor proximity for effects + u8 isOnFloor; + if (mPosition.y <= *(f32*)((u8*)this + 0xEC) + 8.0f) + isOnFloor = 1; + else + isOnFloor = 0; + + if (!isOnFloor) + return; + + // Emit pollution effect if standing in it + if (isPolluted == 1) { + JGeometry::TVec3* planeNormal + = (JGeometry::TVec3*)(((u8*)mGroundPlane) + 0x34); + SMS_EmitSinkInPollutionEffect( + *(JGeometry::TVec3*)&mPosition, *planeNormal, false); + } + + // Footprint timer + s16 footTimer = *(s16*)((u8*)this + 0x360); + if (footTimer <= 0) + return; + + *(s16*)((u8*)this + 0x360) = footTimer - 1; + + s16 halfDuration = mDeParams.mFootPrintTimerMax.value; + halfDuration = halfDuration / 2; + if (*(s16*)((u8*)this + 0x360) <= halfDuration) + return; + + // Check ground plane flag (inverted) + u8 groundFlag; + if (mGroundPlane->mFlags & 0x10) + groundFlag = 1; + else + groundFlag = 0; + + u8 notOnGraffito; + if (groundFlag == 1) + notOnGraffito = 0; + else + notOnGraffito = 1; + + if (!notOnGraffito) + return; + + // Check trigger flags + u8 hasTrigger; + if (mState & 0x30000) + hasTrigger = 1; + else + hasTrigger = 0; + + if (hasTrigger) + return; + + emitDirtyFootPrint(); +} + +bool TMario::isInvincible() const +{ + if (unk14C > 0) + return true; + + u8 hasFlag; + if (mState & 0x8) + hasFlag = 1; + else + hasFlag = 0; + + if (hasFlag) + return true; + + if (mAction == 0x89C) + return true; + + u8 areaID = *(u8*)((u8*)gpMarDirector + 0x124); + if (areaID == 3 || areaID == 4) + return true; + + u8 isEvent = 1; + if (areaID != 1) { + if (areaID != 2) + isEvent = 0; + } + + if (isEvent) + return true; + + u8 hasBit; + if (mAction & 0x1000) + hasBit = 1; + else + hasBit = 0; + + if (hasBit) + return true; + + return false; +} + +BOOL TMario::isForceSlip() +{ + u16 code = *(u16*)mGroundPlane; + u8 isIce; + if (code == 0x01 || code == 0x4001 || + code == 0x8001 || code == 0xC001) + isIce = 1; + else + isIce = 0; + + if (isIce) + return true; + + if (*(s32*)((u8*)this + 0x350) == 2) { + u8 hasBit; + if (mState & 0x40) + hasBit = 1; + else + hasBit = 0; + + if (hasBit) { + if (*(f32*)((u8*)mGroundPlane + 0x38) < mDirtyParams.mSlopeAngle.value) + return true; + } + } + + if (*(f32*)((u8*)mGroundPlane + 0x38) < mDeParams.mForceSlipAngle.value) + return true; + + return false; +} + +bool TMario::isUnderWater() const +{ + u8 inWater; + if (mState & 0x30000) + inWater = 1; + else + inWater = 0; + + if (inWater) { + f32 floorZ = mFloorPosition.z; + f32 param = mSwimParams.mCanBreathDepth.value; + f32 val = *(f32*)((u8*)this + 0x170); + if (val < floorZ - param) + return true; + } + return false; +} + +bool TMario::isWallInFront() const +{ + if (mWallPlane != NULL) { + s16 wallAngle = getWallAngle(); + s16 diff = (s16)(wallAngle - mFaceAngle.y); + if (diff < -0x71C7 || diff > 0x71C7) + return true; + } + return false; +} + +void TMario::thinkSand() +{ + u8 inWater; + if (mState & 0x30000) + inWater = 1; + else + inWater = 0; + + if (!inWater) { + u16 code = *(u16*)mGroundPlane; + u8 isSand; + if (code == 0x701 || code == 0x4701 || + code == 0x8701 || code == 0xC701) + isSand = 1; + else + isSand = 0; + + if (isSand == 1) { + mState |= 0x40000; + emitSandEffect(); + return; + } + } + mState &= ~0x40000; +} + +f32 TMario::getJumpAccelControl() const +{ + if (mAction == 0x892) + return mWireParams.mWireJumpAccelControl.value; + return mJumpParams.mJumpAccelControl.value; +} + +f32 TMario::getJumpSlideControl() const +{ + if (mAction == 0x892) + return mWireParams.mWireJumpSlideControl.value; + + u8 riding = 0; + if (mYoshi != NULL) { + if (mYoshi->onYoshi()) + riding = 1; + } + + if (riding) { + u8 fluttering; + if (mYoshi->mFlutterState == 1) + fluttering = 1; + else + fluttering = 0; + + if (fluttering) + return mYoshiParams.mHoldOutSldCtrl.value; + } + + return mJumpParams.mJumpSlideControl.value; +} + +BOOL TMario::considerRotateJumpStart() +{ + int rotDir; + if (checkStickRotate(&rotDir)) { + switch (rotDir) { + case 2: + changePlayerStatus(0x896, 0, false); + break; + case 3: + changePlayerStatus(0x895, 0, false); + break; + } + return true; + } + return false; +} + +BOOL TMario::canSquat() const +{ + u8 hasFludd; + if (mState & 0x8000) + hasFludd = 1; + else + hasFludd = 0; + + if (hasFludd) { + if (mWaterGun != NULL) { + TNozzleBase* nozzle = mWaterGun->getCurrentNozzle(); + if (*(u8*)((u8*)nozzle + 0x18) != 1) { + if ((s32)mWaterGun->mCurrentNozzle != 5) { + if (mInput & 0x200) { + return true; + } + } + } + } + } + return false; +} + +void TMario::thinkDirty() +{ + u8 isDirty; + if (mState & 0x40) + isDirty = 1; + else + isDirty = 0; + + if (isDirty) { + if (mAction == 0x04000440 || mAction == 0x0004045C) { + unk134 += mDirtyParams.mIncRunning.value; + } + if (mAction == 0x00800456 || mAction == 0x0084045D || + mAction == 0x0004045E) { + unk134 += mDirtyParams.mIncCatching.value; + } + if (mAction == 0x50) { + unk134 += mDirtyParams.mIncSlipping.value; + } + } + + u8 inWater; + if (mState & 0x30000) + inWater = 1; + else + inWater = 0; + + if (inWater) { + f32 waterLevel = *(f32*)((u8*)this + 0xF0); + if (mPosition.y > waterLevel - 200.0f) + meltInWaterEffect(); + *(s16*)((u8*)this + 0x360) = 0; + unk134 -= mDirtyParams.mDecSwimming.value; + } + + if (mAction == 0x895 || mAction == 0x896) { + unk134 -= mDirtyParams.mDecRotJump.value; + *(s16*)((u8*)this + 0x360) = 0; + } + + u8 hasShirt; + if (mState & 0x10) + hasShirt = 1; + else + hasShirt = 0; + + if (hasShirt) { + unk134 -= mDirtyParams.mDecWaterHit.value; + *(s16*)((u8*)this + 0x360) = 0; + } + + dirtyLimitCheck(); +} + +void TMario::checkRideMovement() +{ + TLiveActor* rideActor = 0; + + Vec pos; + pos = *(Vec*)&mPosition; + + f32 sinAmt = 10.0f * JMASSin((u16)mFaceAngle.y); + pos.x += 0.8f * sinAmt; + f32 cosAmt = 10.0f * JMASCos((u16)mFaceAngle.y); + pos.z += 0.8f * cosAmt; + + const TBGCheckData* wallPlane = + checkWallPlane(&pos, 10.0f, *(f32*)((u8*)this + 0x15C)); + + TLiveActor* groundActor = + (TLiveActor*)mGroundPlane->mActor; + + if (groundActor != 0) { + u8 actionBit; + if (mAction & 0x800) + actionBit = 1; + else + actionBit = 0; + if (!actionBit) { + u8 nearGround; + if (mPosition.y <= 4.0f + mFloorPosition.y) + nearGround = 1; + else + nearGround = 0; + if (nearGround) + rideActor = groundActor; + } + } + + if (groundActor != 0) { + if (mAction == 0x8008A9) { + u16 subState = *(u16*)((u8*)this + 0x84); + if (subState == 2 || subState == 3) + rideActor = groundActor; + } + } + + { + u8 actionBit2; + if (mAction & 0x20000000) + actionBit2 = 1; + else + actionBit2 = 0; + if (actionBit2) { + if (wallPlane != 0) { + if (wallPlane->mActor != 0) + rideActor = (TLiveActor*)wallPlane->mActor; + } + } + } + + if (rideActor != 0) { + if (*(TLiveActor**)((u8*)this + 0x2C0) != 0 + && *(TLiveActor**)((u8*)this + 0x2C0) == rideActor) { + // sameRide + TLiveActor* cur = *(TLiveActor**)((u8*)this + 0x2C0); + Mtx stackMtx; + if (cur->getRootJointMtx() == 0) { + SMS_GetActorMtx(*cur, stackMtx); + } else { + PSMTXCopy((MtxPtr)cur->getRootJointMtx(), stackMtx); + } + + PSMTXMultVec(stackMtx, + (Vec*)((u8*)this + 0x2F4), + (Vec*)&mPosition); + + TLiveActor* ride = *(TLiveActor**)((u8*)this + 0x2C0); + f32 savedRot = *(f32*)((u8*)this + 0x30C); + f32 currentRot = ride->mRotation.y; + f32 delta = currentRot - savedRot; + s16 faceAngle = mFaceAngle.y; + mFaceAngle.y = + faceAngle + (int)(32768.0f * delta / 180.0f); + + ride = *(TLiveActor**)((u8*)this + 0x2C0); + *(f32*)((u8*)this + 0x30C) = ride->mRotation.y; + } else { + // newRide + *(TLiveActor**)((u8*)this + 0x2C0) = rideActor; + + TLiveActor* ride = *(TLiveActor**)((u8*)this + 0x2C0); + *(f32*)((u8*)this + 0x30C) = ride->mRotation.y; + + ride = *(TLiveActor**)((u8*)this + 0x2C0); + if (ride != 0) { + Mtx stackMtx; + if (ride->getRootJointMtx() == 0) { + SMS_GetActorMtx(*ride, stackMtx); + } else { + PSMTXCopy((MtxPtr)ride->getRootJointMtx(), stackMtx); + } + + PSMTXInverse(stackMtx, stackMtx); + + *(Vec*)((u8*)this + 0x300) = *(Vec*)((u8*)this + 0x2F4); + + PSMTXMultVec(stackMtx, + (Vec*)&mPosition, + (Vec*)((u8*)this + 0x2F4)); + } + } + } else { + *(u32*)((u8*)this + 0x2C0) = 0; + } +} + +void TMario::checkCurrentPlane() +{ + u8 bit19; + if (mAction & 0x1000) { + bit19 = 1; + } else { + bit19 = 0; + } + if (bit19) + return; + + // Check if on Yoshi + u8 r28 = 0; + if (mYoshi != nullptr) { + if (((TYoshi*)mYoshi)->onYoshi()) { + r28 = 1; + } + } + + if (r28) { + unk15C = 80.0f; + } else { + unk15C = 50.0f; + } + + // First wall check + TBGWallCheckRecord wallCheck; + r28 = 0; + wallCheck.mCenter.x = mPosition.x; + wallCheck.mCenter.y = 60.0f + mPosition.y; + wallCheck.mCenter.z = mPosition.z; + wallCheck.mRadius = unk15C; + wallCheck.mMaxResults = 2; + wallCheck.mFlags = 0; + gpMap->isTouchedWallsAndMoveXZ(&wallCheck); + + // First skip check + { + u8 skip; + if (unk14C > 0) { + skip = 1; + } else { + if (mState & 0x8) { + r28 = 1; + } + if (r28) { + skip = 1; + } else { + u32 action = mAction; + if (action == 0x89C) { + skip = 1; + } else { + u8 dirState + = *(u8*)((u8*)gpMarDirector + 0x124); + if (dirState == 3 || dirState == 4) { + skip = 1; + } else { + u8 isDemo; + if (dirState == 1 + || dirState == 2) { + isDemo = 1; + } else { + isDemo = 0; + } + if (isDemo) { + skip = 1; + } else { + u8 b19; + if (action & 0x1000) { + b19 = 1; + } else { + b19 = 0; + } + if (b19) { + skip = 1; + } else { + skip = 0; + } + } + } + } + } + } + if (!skip) { + // Loop over first wall results + for (int i = 0; i < wallCheck.mResultWallsNum; i++) { + TBGCheckData* wall = wallCheck.mResultWalls[i]; + u16 wt = wall->mBGType; + u8 isDmgWall; + if (wt == 0xB || wt == 0x800B || wt == 0x103 || wt == 0x101) { + isDmgWall = 1; + } else { + isDmgWall = 0; + } + if (!isDmgWall) + continue; + + TEParams* dmg = getDmgMapCode(wall->mData); + *(f32*)((u8*)this + 0x484) + = mPosition.x + JMASSin((u16)mFaceAngle.y); + *(f32*)((u8*)this + 0x48C) + = mPosition.z + JMASCos((u16)mFaceAngle.y); + damageExec(&mFloorHitActor, + dmg->mDamage.get(), dmg->mDownType.get(), + dmg->mWaterEmit.get(), dmg->mMinSpeed.get(), + dmg->mMotor.get(), dmg->mDirty.get(), + dmg->mInvincibleTime.get()); + } + + // Two-wall crush check + if (wallCheck.mResultWallsNum == 2) { + TBGCheckData* wall0 = wallCheck.mResultWalls[0]; + TBGCheckData* wall1 = wallCheck.mResultWalls[1]; + f32 dot = wall0->mNormal.y * wall1->mNormal.y + + wall0->mNormal.x * wall1->mNormal.x + + wall0->mNormal.z * wall1->mNormal.z; + if (dot < -0.9f) { + // Copy position + JGeometry::TVec3 pos; + pos.x = mPosition.x; + pos.y = mPosition.y; + pos.z = mPosition.z; + + // Compute plane distances + f32 dist0 = wall0->mNormal.y * pos.y + + wall0->mNormal.x * pos.x + + wall0->mNormal.z * pos.z + + wall0->mPlaneDistance; + f32 dist1 = wall1->mNormal.y * pos.y + + wall1->mNormal.x * pos.x + + wall1->mNormal.z * pos.z + + wall1->mPlaneDistance; + + // Check actor type - either wall must be type 0x2BD + u8 doCrush = 0; + const TLiveActor* actor0 = wall0->getActor(); + if (actor0 != nullptr) { + u32 actorType = *(u32*)((u8*)actor0 + 0x4C); + if ((actorType - 0x40000000) == 0x2BD) + doCrush = 1; + } + + if (!doCrush) { + const TLiveActor* actor1 = wall1->getActor(); + if (actor1 != nullptr) { + u32 actorType1 = *(u32*)((u8*)actor1 + 0x4C); + if ((actorType1 - 0x40000000) == 0x2BD) + doCrush = 1; + } + } + + if (doCrush) { + if (dist0 < 10.0f || dist1 < 10.0f) { + floorDamageExec(mDeParams.mHpMax.value, 3, 0, + mMotorParams.mMotorReturn.value); + } + } + } + } + } + } + // Second wall check (lower, smaller radius) + u8 r26 = 1; + r28 = 0; + wallCheck.mCenter.x = mPosition.x; + wallCheck.mCenter.y = 4.0f + mPosition.y; + wallCheck.mCenter.z = mPosition.z; + wallCheck.mRadius = 0.5f * unk15C; + wallCheck.mMaxResults = 1; + wallCheck.mFlags = 0; + gpMap->isTouchedWallsAndMoveXZ(&wallCheck); + + // Skip check for second walls (slightly different logic) + if (unk14C > 0) { + r26 = 1; + } else { + if (!(*(u32*)((u8*)this + 0x118) & 0x8)) { + r26 = r28; + } + if (!r26) { + u32 action2 = mAction; + if (action2 == 0x89C) { + r26 = 1; + } else { + u8 dirState2 = *(u8*)((u8*)gpMarDirector + 0x124); + if (dirState2 == 3 || dirState2 == 4) { + r26 = 1; + } else { + u8 isDemo2; + if (dirState2 == 1 || dirState2 == 2) { + isDemo2 = 1; + } else { + isDemo2 = 0; + } + if (isDemo2) { + r26 = 1; + } else { + u8 bit19_2; + if (action2 & 0x1000) { + bit19_2 = 1; + } else { + bit19_2 = 0; + } + if (bit19_2) { + r26 = 1; + } else { + r26 = 0; + } + } + } + } + } + } + + if (!r26) { + for (int i = 0; i < wallCheck.mResultWallsNum; i++) { + TBGCheckData* wall = wallCheck.mResultWalls[i]; + u16 wt2 = wall->mBGType; + u8 isDmg2; + if (wt2 == 0xB || wt2 == 0x800B || wt2 == 0x103 + || wt2 == 0x101) { + isDmg2 = 1; + } else { + isDmg2 = 0; + } + if (!isDmg2) + continue; + + TEParams* dmg = getDmgMapCode(wall->mData); + *(f32*)((u8*)this + 0x484) = mPosition.x + + JMASSin((u16)mFaceAngle.y); + *(f32*)((u8*)this + 0x48C) = mPosition.z + + JMASCos((u16)mFaceAngle.y); + damageExec( + &mFloorHitActor, + dmg->mDamage.get(), dmg->mDownType.get(), + dmg->mWaterEmit.get(), dmg->mMinSpeed.get(), + dmg->mMotor.get(), dmg->mDirty.get(), + dmg->mInvincibleTime.get()); + } + } + + // Ground check + f32 f30 = mPosition.z; + f32 f31 = mPosition.x; + *(f32*)((u8*)this + 0xEC) + = gpMap->checkGround(f31, 25.0f + mPosition.y, f30, &mGroundPlane); + + if (mGroundPlane->isMarioThrough()) { + *(f32*)((u8*)this + 0xEC) = gpMap->checkGround( + f31, *(f32*)((u8*)this + 0xEC) - 1.0f, f30, &mGroundPlane); + } + + // Roof check + *(f32*)((u8*)this + 0xE8) + = gpMap->checkRoof(mPosition.x, 80.0f + mPosition.y, mPosition.z, + &mRoofPlane); + + // Ground damage check (third skip check) + { + u8 skip3; + if (unk14C > 0) { + skip3 = 1; + } else { + u8 mStatebit; + if (*(u32*)((u8*)this + 0x118) & 0x8) { + mStatebit = 1; + } else { + mStatebit = 0; + } + if (mStatebit) { + skip3 = 1; + } else { + u32 action3 = mAction; + if (action3 == 0x89C) { + skip3 = 1; + } else { + u8 dirState3 + = *(u8*)((u8*)gpMarDirector + 0x124); + if (dirState3 == 3 || dirState3 == 4) { + skip3 = 1; + } else { + u8 isDemo3; + if (dirState3 == 1 + || dirState3 == 2) { + isDemo3 = 1; + } else { + isDemo3 = 0; + } + if (isDemo3) { + skip3 = 1; + } else { + u8 bit19_3; + if (action3 & 0x1000) { + bit19_3 = 1; + } else { + bit19_3 = 0; + } + if (bit19_3) { + skip3 = 1; + } else { + skip3 = 0; + } + } + } + } + } + } + + if (!skip3) { + // Check ground damage + u8 nearGround; + if (mPosition.y <= 4.0f + *(f32*)((u8*)this + 0xEC)) { + nearGround = 1; + } else { + nearGround = 0; + } + if (nearGround) { + TBGCheckData* ground = mGroundPlane; + u16 gt = ground->mBGType; + u8 isDmgG; + if (gt == 0xB || gt == 0x800B || gt == 0x103 + || gt == 0x101) { + isDmgG = 1; + } else { + isDmgG = 0; + } + if (isDmgG) { + u8 bit15; + if (mAction & 0x10000) { + bit15 = 1; + } else { + bit15 = 0; + } + if (!bit15) { + TEParams* dmg + = getDmgMapCode(ground->mData); + *(f32*)((u8*)this + 0x484) + = mPosition.x + + JMASSin(*(u16*)((u8*)this + + 0x96)); + *(f32*)((u8*)this + 0x48C) + = mPosition.z + + JMASCos(*(u16*)((u8*)this + + 0x96)); + damageExec( + (THitActor*)*(u32*)((u8*)this + + 0x474), + dmg->mDamage.get(), + dmg->mDownType.get(), + dmg->mWaterEmit.get(), + dmg->mMinSpeed.get(), + dmg->mMotor.get(), + dmg->mDirty.get(), + dmg->mInvincibleTime.get()); + } + } + } + + // Check roof damage + if (160.0f + mPosition.y > *(f32*)((u8*)this + 0xE8)) { + TBGCheckData* roof = mRoofPlane; + u16 rt = roof->mBGType; + u8 isDmgR; + if (rt == 0xB || rt == 0x800B || rt == 0x103 + || rt == 0x101) { + isDmgR = 1; + } else { + isDmgR = 0; + } + if (isDmgR) { + TEParams* dmg = getDmgMapCode(roof->mData); + *(f32*)((u8*)this + 0x484) + = mPosition.x + + JMASSin( + (u16)mFaceAngle.y); + *(f32*)((u8*)this + 0x48C) + = mPosition.z + + JMASCos( + (u16)mFaceAngle.y); + damageExec( + &mFloorHitActor, + dmg->mDamage.get(), + dmg->mDownType.get(), + dmg->mWaterEmit.get(), + dmg->mMinSpeed.get(), + dmg->mMotor.get(), dmg->mDirty.get(), + dmg->mInvincibleTime.get()); + } + } + } + } + + // Slope detection + { + TBGCheckData* ground = mGroundPlane; + u8 isLegal; + if (ground->mFlags & 0x10) { + isLegal = 1; + } else { + isLegal = 0; + } + // invert: isLegal means data IS illegal, so !isLegal means legal + if (isLegal != 1) { + // compute slope angle + *(s16*)((u8*)this + 0xF4) = matan( + ground->mNormal.z, ground->mNormal.x); + + s32 slipResult; + if ((u8)isForceSlip()) { + slipResult = 1; + } else { + u16 bgType = ground->mBGType; + if (bgType == 0xC || bgType == 0x800C + || bgType == 0xA00C) { + slipResult = 1; + } else if (bgType == 0x2 || bgType == 0x8002) { + if (ground->mNormal.y < 0.8660254f) { + slipResult = 1; + } else { + slipResult = 0; + } + } else if (bgType == 0x3 || bgType == 0x8003) { + slipResult = 0; + } else { + if (ground->mNormal.y + < mDeParams.mSlipStart.value) { + slipResult = 1; + } else { + slipResult = 0; + } + } + } + + if (slipResult != 0 + || (*(u32*)((u8*)this + 0x118) & 0x800)) { + mInput |= 0x8; + } + + // High slope check + if (mPosition.y + > 100.0f + *(f32*)((u8*)this + 0xEC)) { + mInput |= 0x4; + } + } + } + + // Clear bit 20 of mState + *(u32*)((u8*)this + 0x118) = *(u32*)((u8*)this + 0x118) & ~0x800; +} +#pragma dont_inline on +TMario::TEParams* TMario::getDmgMapCode(int code) const +{ + switch (code) { + case 0: return (TEParams*)((u8*)this + 0x3BD4); + case 1: return (TEParams*)((u8*)this + 0x3C68); + case 2: return (TEParams*)((u8*)this + 0x3CFC); + case 3: return (TEParams*)((u8*)this + 0x3D90); + case 4: return (TEParams*)((u8*)this + 0x3E24); + case 5: return (TEParams*)((u8*)this + 0x3EB8); + case 6: return (TEParams*)((u8*)this + 0x3F4C); + case 7: return (TEParams*)((u8*)this + 0x3FE0); + case 8: return (TEParams*)((u8*)this + 0x4074); + case 9: return (TEParams*)((u8*)this + 0x4108); + default: return (TEParams*)((u8*)this + 0x3BD4); + } +} +#pragma dont_inline off + +void TMario::thinkParams() +{ + mRotation.y = + (f32)mFaceAngle.y * (360.0f / 65536.0f); + + { + s32 invTimer = unk14C; + if (invTimer > 0) { + unk14C = invTimer - 1; + } + } + + u8 waterFlag; + if (mState & 0x400) + waterFlag = 1; + else + waterFlag = 0; + if (waterFlag) return; + + u32 motionBits = mState & 0x30000; + { + u8 hasMotion; + if (motionBits) + hasMotion = 1; + else + hasMotion = 0; + if (hasMotion) { + u8 nonZero; + if (motionBits) + nonZero = 1; + else + nonZero = 0; + + u8 belowThreshold; + if (!nonZero) { + belowThreshold = 0; + } else if (*(f32*)((u8*)this + 0x170) + < *(f32*)((u8*)this + 0xF0) - mSwimParams.mCanBreathDepth.value) { + belowThreshold = 1; + } else { + belowThreshold = 0; + } + if (!belowThreshold) { + u16 bgType = *(u16*)((u8*)mWaterFloor); + u8 isWaterGround; + if (bgType == 0x0B || bgType == 0x800B || bgType == 0x103 + || bgType == 0x101) + isWaterGround = 1; + else + isWaterGround = 0; + + if (isWaterGround) { + u8 isWaterGround2; + if (bgType == 0x0B || bgType == 0x800B || bgType == 0x103 + || bgType == 0x101) + isWaterGround2 = 1; + else + isWaterGround2 = 0; + + if (isWaterGround2) { + u8 actionBit; + if (mAction & 0x10000) + actionBit = 1; + else + actionBit = 0; + + if (!actionBit) { + s16 data = mWaterFloor->mData; + TEParams* params; + switch (data) { + case 0: params = (TEParams*)((u8*)this + 0x3BD4); break; + case 1: params = (TEParams*)((u8*)this + 0x3C68); break; + case 2: params = (TEParams*)((u8*)this + 0x3CFC); break; + case 3: params = (TEParams*)((u8*)this + 0x3D90); break; + case 4: params = (TEParams*)((u8*)this + 0x3E24); break; + case 5: params = (TEParams*)((u8*)this + 0x3EB8); break; + case 6: params = (TEParams*)((u8*)this + 0x3F4C); break; + case 7: params = (TEParams*)((u8*)this + 0x3FE0); break; + case 8: params = (TEParams*)((u8*)this + 0x4074); break; + case 9: params = (TEParams*)((u8*)this + 0x4108); break; + default: params = (TEParams*)((u8*)this + 0x3BD4); break; + } + floorDamageExec(*params); + } + } + } + } + + // resetCounters + *(u16*)((u8*)this + 0x126) = 0; + *(s16*)((u8*)this + 0x128) = mDeParams.mHotTimer.value; + } else { + // capBranch + TMarioCap* cap = *(TMarioCap**)((u8*)this + 0x3E0); + if (cap != 0) { + u8 hatOn; + if (*(u16*)((u8*)cap + 4) & 1) + hatOn = 1; + else + hatOn = 0; + if (!hatOn) { + *(u16*)((u8*)this + 0x126) = *(u16*)((u8*)this + 0x126) + 1; + if ((u16)*(u16*)((u8*)this + 0x126) + > (s16)*(s16*)((u8*)this + 0x128)) { + decHP(1); + if (gpMSound->gateCheck(0x480C)) { + MSoundSESystem::MSoundSE::startSoundSystemSE( + 0x480C, 0, (JAISound**)0, 0); + } + + *(s16*)((u8*)this + 0x128) = mDeParams.mHotTimer.value; + *(u16*)((u8*)this + 0x126) = 0; + rumbleStart(20, mMotorParams.mMotorWall.value); + *(u16*)&unk14C = (int)*(f32*)((u8*)this + 0x55C); + } + } + } + unk122 = 0; + } + } + { + const TBGCheckData* ground = mGroundPlane; + if (ground != 0) { + const TLiveActor* actor = ground->mActor; + if (actor != 0) { + if (*(u32*)((u8*)actor + 0x4C) == 0x400002C7) { + emitFootPrintWithEffect(-1, 66); + } + } + } + } +} + +void TMario::thinkWaterSurface() +{ + // Early out if on water (bit 16 of mAction) + u8 onWater; + if (mAction & 0x10000) + onWater = 1; + else + onWater = 0; + if (onWater) + return; + + // Check if currently in water (bits 14-15 of mState) + u32 waterBits = mState & 0x30000; + u8 wasInWater; + if (waterBits) + wasInWater = 1; + else + wasInWater = 0; + + u8 r31 = wasInWater; + u8 r30 = 0; + + u8 wasOnSurface; + if (waterBits != 0) + wasOnSurface = 1; + else + wasOnSurface = 0; + + if ((u8)wasOnSurface == 1) { + r30 = 1; + } else { + *(f32*)((u8*)this + 0xF0) = mPosition.y; + } + + // Clear water surface flags + mState &= ~0x10000; + mState &= ~0x20000; + + // Check if ground plane is a pool type + u16 bgType = mGroundPlane->mBGType; + u8 isPool; + if (bgType == BG_TYPE_POOL || bgType == BG_TYPE_INDOOR_POOL + || bgType == BG_TYPE_SHADED_POOL) + isPool = 1; + else + isPool = 0; + + if (isPool) { + *(f32*)((u8*)this + 0xF0) = gpPoolManager->getWaterLevel(mGroundPlane); + if (*(f32*)((u8*)this + 0xF0) > mPosition.y) { + r30 = 1; + mState |= 0x10000; + } + } + + // Check height above ground with offset + { + f32 heightDiff = mPosition.y - *(f32*)((u8*)this + 0x2A0); + f32 clampedDiff = heightDiff; + if (heightDiff > 0.0f) + clampedDiff = 0.0f; + + f32 checkHeight + = *(f32*)((u8*)this + 0xF0) - clampedDiff + + mSwimParams.mWaterLevelCheckHeight.value; + f32 groundHeight = gpMap->checkGround( + mPosition.x, checkHeight, mPosition.z, &mWaterFloor); + + // Check if ground at water level is a water surface type + u16 groundType = mWaterFloor->mBGType; + u8 isWaterSurface; + if (groundType == BG_TYPE_WATER + || groundType == BG_TYPE_DAMAGING_WATER + || (u16)(groundType - BG_TYPE_SEA_WATER) <= 3 + || groundType == BG_TYPE_SHADED_POOL) + isWaterSurface = 1; + else + isWaterSurface = 0; + + if (isWaterSurface) { + *(f32*)((u8*)this + 0xF0) = groundHeight; + if (*(f32*)((u8*)this + 0xF0) >= mPosition.y) { + r30 = 1; + mState |= 0x20000; + } + } else { + // Check ground at current position + const TBGCheckData* groundCheck2; + gpMap->checkGround(mPosition.x, mPosition.y, mPosition.z, + &groundCheck2); + u8 isSpecialType; + if (groundCheck2->mBGType == 0x810B) + isSpecialType = 1; + else + isSpecialType = 0; + + if (isSpecialType) { + r30 = 1; + mState |= 0x20000; + } + } + } + + if (r30 != 0) { + // Water surface logic + f32 posY2 = mPosition.y; + f32 waterLvl = *(f32*)((u8*)this + 0xF0); + if (posY2 < waterLvl) { + + // Check deep water threshold + f32 deepThreshold = posY2 + mRunParams.mSwimDepth.value; + if (waterLvl <= deepThreshold) { + // Deep water - check yoshi + TYoshi* yoshi = (TYoshi*)mYoshi; + r30 = 0; + if (yoshi != NULL) { + if (yoshi->onYoshi()) + r30 = 1; + } + + if (r30) { + yoshi->disappear(); + if (mWaterGun != NULL) { + mWaterGun->changeNozzle(TWaterGun::Hover, true); + normalizeNozzle(); + } + } + + // Check ripple height + f32 rippleCheck = 160.0f + mPosition.y; + if (rippleCheck <= *(f32*)((u8*)this + 0xF0)) + rippleEffect(); + + swimmingBubbleEffect(); + + // Determine if should enter water + u8 shouldEnter = 1; + u32 action7C = mAction; + u8 isBit18; + if (action7C & 0x2000) + isBit18 = 1; + else + isBit18 = 0; + u32 statusLow = action7C & 0x1FF; + + if (isBit18) + shouldEnter = 0; + + // Swimming status range checks + u8 isSwimming; + if (statusLow >= 0x168 && statusLow <= 0x16C) + isSwimming = 1; + else + isSwimming = 0; + if (isSwimming) + shouldEnter = 0; + + if (statusLow >= 0x145 && statusLow <= 0x14A) + shouldEnter = 0; + + if (statusLow >= 0x140 && statusLow <= 0x143) + shouldEnter = 0; + + // Check held object + u8 holdingObj; + if (mHolder != 0) + holdingObj = 1; + else + holdingObj = 0; + if (holdingObj) + shouldEnter = 0; + + if ((u8)shouldEnter == 1) { + // Apply water drag + mForwardVel = mForwardVel * mSwimParams.mStartVMult.value; + mVel.y = mVel.y * mSwimParams.mStartVYMult.value; + + // Check if falling from air + u8 isFalling; + if (mAction & 0x20000) + isFalling = 1; + else + isFalling = 0; + + if (isFalling) { + // Entering water from air - dive + changePlayerStatus(0x24DA, 0, true); + } else { + // Check if running on ground + u8 isRunning; + if (mState & 0x4000) + isRunning = 1; + else + isRunning = 0; + + if (isRunning) { + // Wading + changePlayerStatus(0x24D5, 0, true); + mVel.y = 0.0f; + mPosition.y = *(f32*)((u8*)this + 0xF0); + startSoundActor(0x828); + } else { + // Shallow water entry + changePlayerStatus(0x22D1, 0, true); + } + } + } + } else { + // Shallow water - check frame-based effects + f32 shallowThreshold = posY2 + mWaterEffectParams.mRunningRippleDepth.value; + if (waterLvl >= shallowThreshold) { + // Check if in walking state 0x04000440 + u32 actionVal = mAction; + if ((u32)(actionVal - 0x04000000) == 0x440) { + J3DFrameCtrl& frameCtrl = getMotionFrameCtrl(); + if (frameCtrl.checkPass(38.0f) || getMotionFrameCtrl().checkPass(8.0f)) { + runningRippleEffect(); + } + } + } else { + rippleEffect(); + } + } + } + } + + + // Check wet ground type + { + u16 gndBGType = mGroundPlane->mBGType; + u8 isWetGround; + if (gndBGType == 0x4 || gndBGType == 0x4004 + || gndBGType == 0x8004 || gndBGType == 0xC004) + isWetGround = 1; + else + isWetGround = 0; + + if (isWetGround) { + u32 actionVal = mAction; + if ((u32)(actionVal - 0x04000000) == 0x440) { + J3DFrameCtrl& frameCtrl = getMotionFrameCtrl(); + if (frameCtrl.checkPass(38.0f) || getMotionFrameCtrl().checkPass(8.0f)) { + runningRippleEffect(); + } + } + } + } + + // Build water surface matrix + { + if (mState & 0x30000) + r30 = 1; + else + r30 = 0; + + s16 rotY = mModelFaceAngle; + J3DGetTranslateRotateMtx(0, rotY, 0, mPosition.x, *(f32*)((u8*)this + 0xF0), mPosition.z, + *(Mtx*)((u8*)this + 0x220)); + + // Store water position + *(f32*)((u8*)this + 0x190) = mPosition.x; + *(f32*)((u8*)this + 0x194) = *(f32*)((u8*)this + 0xF0); + *(f32*)((u8*)this + 0x198) = mPosition.z; + + // Copy joint matrix + u32 modelPtr = *(u32*)((u8*)this + 0x3A8); + u8 jointIdx = *(u8*)((u8*)this + 0x3CF); + u32 modelData = *(u32*)(modelPtr + 0x8); + u32 jointMtxArr = *(u32*)(modelData + 0x58); + MtxPtr anmMtx = (MtxPtr)(jointMtxArr + jointIdx * 0x30); + PSMTXCopy(anmMtx, *(Mtx*)((u8*)this + 0x1C0)); + + // Check if water state changed + if (r30 != r31) { + inOutWaterEffect(*(f32*)((u8*)this + 0xF0)); + f32 splashHeight = *(f32*)((u8*)this + 0xF0) - *(f32*)((u8*)this + 0xEC); + + if (r31 == 1 && r30 == 0) { + // Entering water + *(s16*)((u8*)this + 0x362) = 0x78; + + if (splashHeight < 32.0f) { + // Small splash + if (gpMSound->gateCheck(0x1939)) { + MSoundSESystem::MSoundSE::startSoundActor( + 0x1939, &mPosition, 0, (JAISound**)NULL, 0, 4); + } + } else if (splashHeight < 80.0f) { + // Medium splash + if (gpMSound->gateCheck(0x181D)) { + MSoundSESystem::MSoundSE::startSoundActor( + 0x181D, &mPosition, 0, (JAISound**)NULL, 0, 4); + } + } else { + // Large splash + if (gpMSound->gateCheck(0x181E)) { + MSoundSESystem::MSoundSE::startSoundActor( + 0x181E, &mPosition, 0, (JAISound**)NULL, 0, 4); + } + } + } else { + // Exiting water + if (splashHeight < 32.0f) { + if (gpMSound->gateCheck(0x1938)) { + MSoundSESystem::MSoundSE::startSoundActor( + 0x1938, &mPosition, 0, (JAISound**)NULL, 0, 4); + } + } else if (splashHeight < 80.0f) { + if (gpMSound->gateCheck(0x1805)) { + MSoundSESystem::MSoundSE::startSoundActor( + 0x1805, &mPosition, 0, (JAISound**)NULL, 0, 4); + } + } else { + if (gpMSound->gateCheck(0x1806)) { + MSoundSESystem::MSoundSE::startSoundActor( + 0x1806, &mPosition, 0, (JAISound**)NULL, 0, 4); + } + } + } + } + } + + // Drowning/air recovery logic + { + u8 shouldDrown = 0; + + u8 isInWater2; + if (mState & 0x30000) + isInWater2 = 1; + else + isInWater2 = 0; + + if (isInWater2) { + f32 airThreshold = *(f32*)((u8*)this + 0xF0) - mSwimParams.mCanBreathDepth.value; + if (*(f32*)((u8*)this + 0x170) < airThreshold) { + shouldDrown = 1; + } + } + if (!shouldDrown) { + u8 isDiving; + if (mState & 0x8000) + isDiving = 1; + else + isDiving = 0; + if (isDiving) + shouldDrown = 1; + } + + if (shouldDrown) { + f32 prevAir = *(f32*)((u8*)this + 0x12C); + u8 isHelm; + isHelm = isWearingHelm(); + if (isHelm) { + u32 actionVal = mAction; + if ((u32)(actionVal - 0x10020000) != 0x370) { + *(f32*)((u8*)this + 0x12C) -= mSwimParams.mAirDecDive.value; + } + } else { + *(f32*)((u8*)this + 0x12C) -= mSwimParams.mAirDec.value; + } + f32 currentAir = *(f32*)((u8*)this + 0x12C); + + // Compare truncated values to detect crossing + s32 prevInt; + s32 currInt; + { + f32 prev = prevAir; + f32 curr = currentAir; + // fctiwz + store/load pattern + s32 prevTrunc = (s32)prev; + s32 currTrunc = (s32)curr; + prevInt = prevTrunc; + currInt = currTrunc; + } + + if (prevInt != currInt) { + rumbleStart(0x14, mMotorParams.mMotorWall.value); + s32 truncHP = (s32)(*(f32*)((u8*)this + 0x55C)); + unk14C = (s16)truncHP; + } + + if (*(f32*)((u8*)this + 0x12C) < 1.0f) { + *(f32*)((u8*)this + 0x12C) = 0.0f; + loserExec(); + changePlayerStatus(0x000224E0, 0, false); + } + return; + } else { + *(f32*)((u8*)this + 0x12C) += mSwimParams.mAirInc.value; + if (*(f32*)((u8*)this + 0x12C) >= *(f32*)((u8*)this + 0x130)) { + *(f32*)((u8*)this + 0x12C) = *(f32*)((u8*)this + 0x130); + } + } + } +} + +void TMario::thinkSituation() +{ + // Save previous situation flags + mPrevState = mState; + + // Recovery timer + f32 recoveryVal = unkBC; + if (recoveryVal < 0.0f) { + unkBC = recoveryVal + mJumpParams.mTrampolineDec.value; + } else { + unkBC = 0.0f; + } + + // If being held, set position from holder's taking matrix + if (mHolder != NULL) { + if (mHolder->getTakingMtx() != NULL) { + mPosition.x = mHolder->getTakingMtx()[0][3]; + mPosition.y = mHolder->getTakingMtx()[1][3]; + mPosition.z = mHolder->getTakingMtx()[2][3]; + } + } + + // Clear slippery and shadow bits + mState &= ~0x2; + mState &= ~0x20; + + // Check slippery ground + { + u8 isSlippery; + u16 bgType = mGroundPlane->mBGType; + if (bgType == 0x106 || bgType == 0x108) + isSlippery = 1; + else + isSlippery = 0; + if (isSlippery) { + mState |= 0x2; + } + } + + // Ground damage check + { + u8 didDamage = 0; + if (isMario()) { + u8 isOnGround; + if (mPosition.y <= mFloorPosition.y + 4.0f) + isOnGround = 1; + else + isOnGround = 0; + + if (isOnGround) { + if (mAction != 0x133F) { + u8 hasDmgFlag; + if (mGroundPlane->mFlags & 0x10) + hasDmgFlag = 1; + else + hasDmgFlag = 0; + + u8 isDmgType; + if (mGroundPlane->mBGType == 0x600) + isDmgType = 1; + else + isDmgType = 0; + + if (hasDmgFlag || isDmgType) { + unk2BA += mDeParams.mIllegalPlaneCtInc.value; + if (unk2BA > mDeParams.mIllegalPlaneTime.value) { + decHP(mDeParams.mHpMax.value); + } + didDamage = 1; + } + } + } + } + + if (!didDamage) { + unk2BA -= 1; + if (unk2BA < 0) + unk2BA = 0; + } + } + + // BGM handling for slippery ground + if (isMario()) { + u8 curSlippery; + if (mState & 0x2) + curSlippery = 1; + else + curSlippery = 0; + + if (curSlippery == 1) { + u8 prevSlippery; + if (mPrevState & 0x2) + prevSlippery = 1; + else + prevSlippery = 0; + if (!prevSlippery) { + MSBgm::startBGM(0x8001001B); + } + } + + u8 curSlippery2; + if (mState & 0x2) + curSlippery2 = 1; + else + curSlippery2 = 0; + + if (!curSlippery2) { + u8 prevSlippery2; + if (mPrevState & 0x2) + prevSlippery2 = 1; + else + prevSlippery2 = 0; + if (prevSlippery2 == 1) { + MSBgm::stopBGM(0x8001001B, 10); + } + } + } + + // Death plane check + { + const TBGCheckData* checkPlane; + f32 groundHeight = gpMap->checkGround( + mPosition.x, mPosition.y - mVel.y, mPosition.z, &checkPlane); + + u8 isDeathPlane; + if (checkPlane->mBGType == 0x800) + isDeathPlane = 1; + else + isDeathPlane = 0; + + if (isDeathPlane) { + if (groundHeight > mPosition.y) { + mState |= 0x400; + mHealth = 0; + mAnmSound->stop(); + if (mYoshi != NULL) + mYoshi->kill(); + changePlayerStatus(0x208B9, 0, true); + if (mAnimationId != 0x120) { + startSoundActor(0x786B); + } + *(u16*)((u8*)gpCamera + 0x64) |= 0x800; + *(u16*)((u8*)gpMarDirector + 0x4E) |= 0x8; + return; + } + } + } + + // Ground collision matrix setup + J3DGetTranslateRotateMtx(0, mModelFaceAngle, 0, mPosition.x, mPosition.y, + mPosition.z, *(Mtx*)((u8*)this + 0x1F0)); + + // Light/shadow setup + mLightID = 0; + + { + u8 hasShadowBit = 0; + if (mGroundPlane->mBGType & 0x4000) + hasShadowBit = 1; + if (hasShadowBit) { + if (mFloorPosition.y + 200.0f > mPosition.y) { + mLightID = mGroundPlane->mData; + if (mLightID == 1) { + mState |= 0x20; + } + } + } + } + + // Cube shadow check + if (gpCubeShadow != NULL) { + if ((s32)gpCubeShadow->getInCubeNo(*(Vec*)&mPosition) != -1) { + mLightID = 1; + } + } + + // Water particle ground check + mState &= ~0x10; + if (isMario()) { + u8 isOnGround; + if (mPosition.y <= mFloorPosition.y + 4.0f) + isOnGround = 1; + else + isOnGround = 0; + if (isOnGround) { + if (gpModelWaterManager->askHitWaterParticleOnGround( + *(JGeometry::TVec3*)&mPosition)) { + mState |= 0x10; + } + } + } + + // Stage warp check (ground type 0x300) + { + u8 isWarpGround; + if (mGroundPlane->mBGType == 0x300) + isWarpGround = 1; + else + isWarpGround = 0; + + if (isWarpGround) { + u8 isOnYoshi = 0; + if (mYoshi != NULL) { + if (mYoshi->onYoshi()) + isOnYoshi = 1; + } + if (isOnYoshi) { + getOffYoshi(false); + } + + gpMarDirector->setNextStage(mGroundPlane->mData, + (JDrama::TActor*)NULL); + mSubState &= ~0x2; + mSubState &= ~0x400; + } + } + + // Option map position constraints + if (SMS_isOptionMap()) { + mPosition.z = mOptionParams.mZ.value; + if (mPosition.x < mOptionParams.mXMin.value) + mPosition.x = mOptionParams.mXMin.value; + if (mPosition.x > mOptionParams.mXMax.value) + mPosition.x = mOptionParams.mXMax.value; + } + + // Save ground Y when not airborne + { + u8 isAirborne; + if (mAction & 0x800) + isAirborne = 1; + else + isAirborne = 0; + if (!isAirborne) { + unk2BC = mPosition.y; + } + } + + calcGroundMtx(*(JGeometry::TVec3*)&mPosition); + + // Area type check for indoor flag + { + u8 areaID = *(u8*)((u8*)gpMarDirector + 0x124); + u8 isIndoor; + if (areaID == 1 || areaID == 2) + isIndoor = 1; + else + isIndoor = 0; + + u8 hasWaterBit; + if (mAction & 0x1000) + hasWaterBit = 1; + else + hasWaterBit = 0; + + if (areaID == 3 || areaID == 4 || isIndoor || hasWaterBit) { + mState |= 0x8; + } else { + mState &= ~0x8; + } + } +} + +void TMario::getOffYoshi(bool knockedOff) +{ + mInput &= ~0x8000; + if (knockedOff) { + changePlayerStatus(0x89C, 0, false); + mYoshi->getOff(true); + } else { + changePlayerStatus(0x883, 0, false); + mVel.y = mJumpParams.mGetOffYoshiY.value; + mYoshi->getOff(false); + } + setAnimation(0x4D, 1.0f); + unk78 &= ~0x100; + mPosition.y += 100.0f; + mForwardVel = -8.0f; + mWaterGun->changeNozzle(4, true); + normalizeNozzle(); + TWaterGun* gun = mWaterGun; + TNozzleBase* nozzle = gun->getCurrentNozzle(); + *(s32*)((u8*)gun + 0x1C80) = *(s32*)((u8*)nozzle + 0xCC); +} + +void TMario::thinkYoshiHeadCollision() +{ + u8 riding = 0; + if (mYoshi != NULL) { + if (mYoshi->onYoshi()) + riding = 1; + } + if (!riding) + return; + + JGeometry::TVec3 tempPos(mPosition); + int maxResults = 4; + int flags = 0; + + f32 headOffset = mYoshiParams.mHeadFront.value; + u32 angleIdx = static_cast(mFaceAngle.y) >> jmaSinShift; + tempPos.x += jmaSinTable[angleIdx] * headOffset; + tempPos.z += jmaCosTable[angleIdx] * headOffset; + + TBGWallCheckRecord wallCheck; + wallCheck.mCenter.x = tempPos.x; + wallCheck.mCenter.y = 100.0f + tempPos.y; + f32 savedZ = tempPos.z; + wallCheck.mCenter.z = savedZ; + wallCheck.mRadius = mYoshiParams.mHeadRadius.value; + wallCheck.mMaxResults = maxResults; + wallCheck.mFlags = flags; + + if (gpMap->isTouchedWallsAndMoveXZ(&wallCheck) != true) + return; + + f32 dz = wallCheck.mCenter.z - savedZ; + f32 dx = wallCheck.mCenter.x - tempPos.x; + f32 distSq = dz * dz + dx * dx; + f32 dist; + if (distSq > 0.0f) + dist = sqrtf(distSq); + else + dist = distSq; + + f32 pushDist = dist; + if (dist <= 0.0f) + return; + + f32 invDist = 1.0f / dist; + f32 maxPush = 50.0f; + dx *= invDist; + dz *= invDist; + if (maxPush < dist) + pushDist = maxPush; + + dx *= pushDist; + mPosition.x += dx; + dz *= pushDist; + mPosition.z += dz; +} + +void TMario::checkWet() +{ + if (!isMario()) + return; + + u8 onYoshiFlag = 0; + if (mYoshi != NULL) { + if (mYoshi->onYoshi()) + onYoshiFlag = 1; + } + if (onYoshiFlag) + return; + + s16 wetTimer = *(s16*)((u8*)this + 0x362); + if (wetTimer <= 0) + return; + *(s16*)((u8*)this + 0x362) = wetTimer - 1; + + const TBGCheckData* check; + f32 posZ = mPosition.z; + f32 posX = mPosition.x; + f32 groundY = + gpMap->checkGround(posX, 320.0f + mPosition.y, posZ, &check); + + if (check->isMarioThrough()) { + groundY = + gpMap->checkGround(posX, groundY - 1.0f, posZ, &check); + } + + u16 bgType = check->mBGType; + u8 isWater; + if (bgType == 0x100 || bgType == 0x101 + || (u16)(bgType - 0x102) <= 3 || bgType == 0x4104) + isWater = 1; + else + isWater = 0; + + if (isWater) + return; + + if (*(f32*)((u8*)this + 0xF0) > mPosition.y) + return; + + u8 actionCheck; + if (mAction & 0x200) + actionCheck = 1; + else + actionCheck = 0; + if (actionCheck) + return; + + if (*(s16*)((u8*)this + 0x362) & 7) + return; + + TWaterEmitInfo* emitInfo = (TWaterEmitInfo*)*(u32*)((u8*)this + 0x158); + *(JGeometry::TVec3*)((u8*)emitInfo + 0x70) = mPosition; + *(f32*)((u8*)emitInfo + 0x74) += 5.0f; + + JGeometry::TVec3 vel(*(JGeometry::TVec3*)&dummyMactorStringValue1); + vel.x = 0.3f * mVel.x; + vel.y = 0.3f * mVel.y; + vel.z = 0.3f * mVel.z; + *(JGeometry::TVec3*)((u8*)emitInfo + 0x8C) = vel; + + gpModelWaterManager->emitRequest(*emitInfo); +} + +void TMario::checkEnforceJump() +{ + u8 groundFlag; + if (mGroundPlane->mFlags & 0x10) + groundFlag = 1; + else + groundFlag = 0; + + u8 notWall; + if (groundFlag == 1) + notWall = 0; + else + notWall = 1; + + if (!notWall) + return; + + u8 isTrampoline; + if (mGroundPlane->mBGType == 0x7 || mGroundPlane->mBGType == 0x8007) + isTrampoline = 1; + else + isTrampoline = 0; + + if (!isTrampoline) + return; + + u8 yCheck; + if (mPosition.y <= 4.0f + *(f32*)((u8*)this + 0xEC)) + yCheck = 1; + else + yCheck = 0; + + if (!yCheck) + return; + + if (!(*(u32*)((u8*)this + 0x80) & 0x800)) + return; + + gpMSound->startForceJumpSound((Vec*)&mPosition, *(u32*)((u8*)this + 0x4E8), + 0.0f, (u32)*(s16*)((u8*)mGroundPlane + 2)); + startVoice(0x78B9); + changePlayerStatus(0x884, 0, false); + rumbleStart(0x15, mMotorParams.mMotorWall.value); + + TLiveActor* groundActor = (TLiveActor*)mGroundPlane->mActor; + if (groundActor != NULL) { + ((THitActor*)groundActor)->receiveMessage((THitActor*)this, 0); + } +} + +void TMario::checkReturn() +{ + u8 shouldReturn; + + if (unk14C > 0) { + shouldReturn = 1; + } else { + u8 hasFlag; + if (mState & 0x8) + hasFlag = 1; + else + hasFlag = 0; + + if (hasFlag) { + shouldReturn = 1; + } else if (mAction == 0x89C) { + shouldReturn = 1; + } else { + u8 areaID = *(u8*)((u8*)gpMarDirector + 0x124); + if (areaID == 3 || areaID == 4) { + shouldReturn = 1; + } else { + u8 isEvent = 1; + if (areaID != 1) { + if (areaID != 2) + isEvent = 0; + } + + if (isEvent) { + shouldReturn = 1; + } else { + u8 hasBit; + if (mAction & 0x1000) + hasBit = 1; + else + hasBit = 0; + + if (hasBit) + shouldReturn = 1; + else + shouldReturn = 0; + } + } + } + } + + if (shouldReturn) + return; + + u8 groundFlag; + if (*(u16*)((u8*)mGroundPlane + 4) & 0x10) + groundFlag = 1; + else + groundFlag = 0; + + u8 isSafe; + if (groundFlag == 1) + isSafe = 0; + else + isSafe = 1; + + if (!isSafe) + return; + + *(JGeometry::TVec3*)((u8*)this + 0x2A8) = mPosition; + *(u32*)((u8*)this + 0x2B4) = *(u32*)((u8*)this + 0x94); + *(u16*)((u8*)this + 0x2B8) = *(u16*)((u8*)this + 0x98); +} + +BOOL TMario::checkStickRotate(int* outDirection) +{ + int increasing = 0; + int decreasing = 0; + int count = unk534; + + int q[4]; + for (int i = 0; i < count - 1; i++) { + s16 angle = unk530[i]; + f32 val = (f32)angle; + + if (val < -24576.0f || val > 24576.0f) + q[0] = 1; + if (-24576.0f <= val && val <= -8192.0f) + q[1] = 1; + if (-8192.0f < val && val < 8192.0f) + q[2] = 1; + if (8192.0f <= val && val <= 24576.0f) + q[3] = 1; + + f32 next = (f32)unk530[i + 1]; + if (val < next) + increasing++; + else + decreasing++; + } + + int quadrants = 0; + if (q[0] == 1) + quadrants++; + if (q[1] == 1) + quadrants++; + if (q[2] == 1) + quadrants++; + if (q[3] == 1) + quadrants++; + + if (quadrants >= 4) { + if (increasing > decreasing) + *outDirection = 2; + else + *outDirection = 3; + return true; + } + + *outDirection = 4; + return false; +} + +f32 TMario::getSlideStopCatch() +{ + const TBGCheckData* plane = mGroundPlane; + u16 bgType = plane->mBGType; + + u8 isSand; + if (bgType == 0x1 || bgType == 0x4001 || bgType == 0x8001 || bgType == 0xC001) + isSand = 1; + else + isSand = 0; + + u8 shouldSlip; + if (isSand) { + shouldSlip = 1; + } else { + shouldSlip = 0; + if (unk350 == 2) { + u8 hasFlag; + if (mState & 0x40) + hasFlag = 1; + else + hasFlag = 0; + if (hasFlag) { + if (plane->getNormal().y < mDirtyParams.mSlopeAngle.value) + shouldSlip = 1; + } + } + if (!shouldSlip) { + if (plane->getNormal().y < mDeParams.mForceSlipAngle.value) + shouldSlip = 1; + else + shouldSlip = 0; + } + } + + if (shouldSlip) + return mSlipParamsAll.mSlideStopCatch.value; + + u8 isTypeC; + if (bgType == 0xC || bgType == 0x800C || bgType == 0xA00C) + isTypeC = 1; + else + isTypeC = 0; + if (isTypeC) + return mSlipParamsAllSlider.mSlideStopCatch.value; + + u8 isType2; + if (bgType == 0x2 || bgType == 0x8002) + isType2 = 1; + else + isType2 = 0; + if (isType2) { + if (plane->getNormal().y < 0.0f) + return mSlipParams45.mSlideStopCatch.value; + } + + u8 isType4; + if (bgType == 0x4 || bgType == 0x4004 || bgType == 0x8004 || bgType == 0xC004) + isType4 = 1; + else + isType4 = 0; + if (isType4) { + if (plane->getNormal().y > 0.0f) + return mSlipParamsWaterGround.mSlideStopCatch.value; + return mSlipParamsWaterSlope.mSlideStopCatch.value; + } + + return mSlipParamsNormal.mSlideStopCatch.value; +} + +f32 TMario::getSlideStopNormal() +{ + const TBGCheckData* plane = mGroundPlane; + u16 bgType = plane->mBGType; + + u8 isSand; + if (bgType == 0x1 || bgType == 0x4001 || bgType == 0x8001 || bgType == 0xC001) + isSand = 1; + else + isSand = 0; + + u8 shouldSlip; + if (isSand) { + shouldSlip = 1; + } else { + shouldSlip = 0; + if (unk350 == 2) { + u8 hasFlag; + if (mState & 0x40) + hasFlag = 1; + else + hasFlag = 0; + if (hasFlag) { + if (plane->getNormal().y < mDirtyParams.mSlopeAngle.value) + shouldSlip = 1; + } + } + if (!shouldSlip) { + if (plane->getNormal().y < mDeParams.mForceSlipAngle.value) + shouldSlip = 1; + else + shouldSlip = 0; + } + } + + if (shouldSlip) + return mSlipParamsAll.mSlideStopNormal.value; + + u8 isTypeC; + if (bgType == 0xC || bgType == 0x800C || bgType == 0xA00C) + isTypeC = 1; + else + isTypeC = 0; + if (isTypeC) + return mSlipParamsAllSlider.mSlideStopNormal.value; + + u8 isType2; + if (bgType == 0x2 || bgType == 0x8002) + isType2 = 1; + else + isType2 = 0; + if (isType2) { + if (plane->getNormal().y < 0.0f) + return mSlipParams45.mSlideStopNormal.value; + } + + u8 isType4; + if (bgType == 0x4 || bgType == 0x4004 || bgType == 0x8004 || bgType == 0xC004) + isType4 = 1; + else + isType4 = 0; + if (isType4) { + if (plane->getNormal().y > 0.0f) + return mSlipParamsWaterGround.mSlideStopNormal.value; + } + + return mSlipParamsWaterSlope.mSlideStopNormal.value; +} + +BOOL TMario::canSlipJump() +{ + const TBGCheckData* plane = mGroundPlane; + u16 bgType = plane->mBGType; + + // Sand types + u8 isSand; + if (bgType == 0x1 || bgType == 0x4001 || bgType == 0x8001 || bgType == 0xC001) + isSand = 1; + else + isSand = 0; + + u8 shouldSlip; + if (isSand) { + shouldSlip = 1; + } else { + shouldSlip = 0; + if (unk350 == 2) { + u8 hasFlag; + if (mState & 0x40) + hasFlag = 1; + else + hasFlag = 0; + if (hasFlag) { + if (plane->getNormal().y < mDirtyParams.mSlopeAngle.value) + shouldSlip = 1; + } + } + if (!shouldSlip) { + if (plane->getNormal().y < mDeParams.mForceSlipAngle.value) + shouldSlip = 1; + else + shouldSlip = 0; + } + } + + if (shouldSlip) + return *((u8*)this + 0x2BB8); + + // Type 0xC + u8 isTypeC; + if (bgType == 0xC || bgType == 0x800C || bgType == 0xA00C) + isTypeC = 1; + else + isTypeC = 0; + if (isTypeC) + return *((u8*)this + 0x2C9C); + + // Type 2 + u8 isType2; + if (bgType == 0x2 || bgType == 0x8002) + isType2 = 1; + else + isType2 = 0; + if (isType2) + return *((u8*)this + 0x2D80); + + // Type 4 with slope check + u8 isType4; + if (bgType == 0x4 || bgType == 0x4004 || bgType == 0x8004 || bgType == 0xC004) + isType4 = 1; + else + isType4 = 0; + if (isType4) { + if (plane->getNormal().y > 0.0f) + return *((u8*)this + 0x2F48); + return *((u8*)this + 0x2E64); + } + + // Type 3 + u8 isType3; + if (bgType == 0x3 || bgType == 0x8003) + isType3 = 1; + else + isType3 = 0; + if (isType3) + return true; + + return true; +} + +BOOL TMario::isSlipStart() +{ + const TBGCheckData* plane = mGroundPlane; + u16 bgType = plane->mBGType; + + // Sand types always slip + u8 isSand; + if (bgType == 0x1 || bgType == 0x4001 || bgType == 0x8001 || bgType == 0xC001) + isSand = 1; + else + isSand = 0; + + u8 shouldSlip; + if (isSand) { + shouldSlip = 1; + } else { + shouldSlip = 0; + if (unk350 == 2) { + u8 hasFlag; + if (mState & 0x40) + hasFlag = 1; + else + hasFlag = 0; + if (hasFlag) { + if (plane->getNormal().y < mDirtyParams.mSlopeAngle.value) + shouldSlip = 1; + } + } + if (!shouldSlip) { + if (plane->getNormal().y < mDeParams.mForceSlipAngle.value) + shouldSlip = 1; + else + shouldSlip = 0; + } + } + + if (shouldSlip) + return true; + + // Type 0xC (graffito?) types + u8 isTypeC; + if (bgType == 0xC || bgType == 0x800C || bgType == 0xA00C) + isTypeC = 1; + else + isTypeC = 0; + if (isTypeC) + return true; + + // Type 2 (wet surface) with slope check + u8 isType2; + if (bgType == 0x2 || bgType == 0x8002) + isType2 = 1; + else + isType2 = 0; + if (isType2) { + if (plane->getNormal().y < 0.0f) + return true; + } + + // Type 3 - explicitly NOT slippery + u8 isType3; + if (bgType == 0x3 || bgType == 0x8003) + isType3 = 1; + else + isType3 = 0; + if (isType3) + return false; + + // Default slope check + if (plane->getNormal().y < mDeParams.mSlipStart.value) + return true; + return false; +} + +const TBGCheckData* TMario::checkWallPlane(Vec* pos, f32 height, f32 radius) +{ + TBGCheckData* result = 0; + TBGWallCheckRecord record(pos->x, pos->y + height, pos->z, radius, 4, 0); + + u8 touched = gpMap->isTouchedWallsAndMoveXZ(&record); + if (touched == 1) { + int numWalls = record.mResultWallsNum; + for (int i = 0; i < numWalls; i++) { + TBGCheckData* wall = record.mResultWalls[i]; + if (wall->mActor == mRidingActor) { + result = wall; + break; + } + f32 dist = wall->getNormal().y * pos->y + wall->getNormal().x * pos->x + + wall->getNormal().z * pos->z + wall->getPlaneDistance(); + if (dist < 0.0f) + dist = -dist; + if (dist < radius) { + result = wall; + radius = dist; + } + } + } + + pos->x = record.mCenter.x; + pos->z = record.mCenter.z; + return result; +} + +void TMario::thinkHeight() +{ + u8 isAirborne; + if (mAction & 0x800) + isAirborne = 1; + else + isAirborne = 0; + + if (isAirborne) { + f32 heightAboveGround = mPosition.y - mFloorPosition.y; + if (unk36C < heightAboveGround) + unk36C = heightAboveGround; + } else { + unk36C = 0.0f; + } + + JGeometry::TVec3 forwardPos; + forwardPos.x = mPosition.x + unk15C * JMASSin(mFaceAngle.y); + forwardPos.y = mPosition.y; + forwardPos.z = mPosition.z + unk15C * JMASCos(mFaceAngle.y); + + if (checkWallPlane(&forwardPos, 80.0f, unk15C) == NULL) { + const TBGCheckData* groundPlane; + f32 sinV = JMASSin(mFaceAngle.y); + f32 cosV = JMASCos(mFaceAngle.y); + f32 dx = 100.0f * sinV; + f32 dz = 100.0f * cosV; + f32 groundHeight = gpMap->checkGround( + mPosition.x + dx, + 100.0f + mPosition.y, + mPosition.z + dz, + &groundPlane); + unk370 = mPosition.y - groundHeight; + } else { + unk370 = 0.0f; + } +} + +void TMario::checkSink() +{ + u8 shouldSkip; + if (unk14C > 0) { + shouldSkip = 1; + } else { + u8 flagCheck; + if (mState & 0x8) + flagCheck = 1; + else + flagCheck = 0; + if (flagCheck) { + shouldSkip = 1; + } else if (mAction == 0x89C) { + shouldSkip = 1; + } else { + u8 areaId = *(u8*)((u8*)gpMarDirector + 0x124); + if (areaId == 3 || areaId == 4) { + shouldSkip = 1; + } else { + u8 inArea = 1; + if (areaId != 1) { + if (areaId != 2) + inArea = 0; + } + if (inArea) { + shouldSkip = 1; + } else { + u8 actionBit; + if (mAction & 0x1000) + actionBit = 1; + else + actionBit = 0; + if (actionBit) + shouldSkip = 1; + else + shouldSkip = 0; + } + } + } + } + if (shouldSkip) return; + + u8 groundBit; + if (mGroundPlane->mFlags & 0x10) + groundBit = 1; + else + groundBit = 0; + if (groundBit) return; + + if (100.0f + mFloorPosition.y < mPosition.y) { + *(f32*)((u8*)this + 0x368) = 0.0f; + return; + } + + s32 sinkState = *(s32*)((u8*)this + 0x350); + u8 sinkHandled = 0; + + if (sinkState == 0) { + u8 bit6; + if (mState & 0x40) + bit6 = 1; + else + bit6 = 0; + if (bit6) { + *(f32*)((u8*)this + 0x368) += 1.0f; + *(s16*)((u8*)this + 0x360) = mDeParams.mFootPrintTimerMax.value; + + if (*(s16*)((u8*)this + 0x120) > 0) { + f32 limit = (f32)mGraffitoParams.mSinkTime.value + * mGraffitoParams.mSinkDmgDepth.value; + if (*(f32*)((u8*)this + 0x368) > limit) + *(f32*)((u8*)this + 0x368) = limit; + } + + s16 interval = mGraffitoParams.mSinkDmgTime.value; + if (gpMarDirector->unk58 % interval == 0) { + floorDamageExec(1, 3, 0, + mMotorParams.mMotorReturn.value); + } + + if (gpMSound->gateCheck(0x100B)) { + MSoundSESystem::MSoundSE::startSoundActor( + 0x100B, (Vec*)&mPosition, 0, (JAISound**)0, 0, 4); + } + + if (*(f32*)((u8*)this + 0x368) > (f32)mGraffitoParams.mSinkTime.value) { + loserExec(); + changePlayerStatus(0x10001123, 0, false); + } + + SMS_EmitSinkInPollutionEffect( + mPosition, + *(JGeometry::TVec3*)((u8*)mGroundPlane + 0x34), + true); + startVoice(0x7865); + sinkHandled = 1; + } + } + + if (!sinkHandled && sinkState == 5) { + u8 bit6; + if (mState & 0x40) + bit6 = 1; + else + bit6 = 0; + if (bit6) { + *(f32*)((u8*)this + 0x374) -= mJumpParams.mGravity.value; + *(f32*)((u8*)this + 0x378) += *(f32*)((u8*)this + 0x374); + mVel.x = 0.0f; + mVel.y = 0.0f; + mVel.z = 0.0f; + mForwardVel = 0.0f; + *(f32*)((u8*)this + 0xB4) = 0.0f; + *(f32*)((u8*)this + 0xB8) = 0.0f; + loserExec(); + changePlayerStatus(0x10001123, 0, false); + sinkHandled = 1; + } + } + + if (!sinkHandled) { + *(f32*)((u8*)this + 0x374) = 0.0f; + *(f32*)((u8*)this + 0x378) = 0.0f; + *(f32*)((u8*)this + 0x368) = 0.0f; + } +} + +void TMario::getRidingMtx(MtxPtr outMtx) +{ + if (mRidingActor->getRootJointMtx() == NULL) { + SMS_GetActorMtx(*mRidingActor, outMtx); + } else { + PSMTXCopy(*(mRidingActor->getRootJointMtx()), outMtx); + } +} + +void TMario::playerControl(JDrama::TGraphics* gfx) +{ + // Save angle and position history + *(s16*)((u8*)this + 0x9C) = mFaceAngle.y; + *(JGeometry::TVec3*)((u8*)this + 0x29C) = mPosition; + mSubState &= ~0x8; + + // Scene 1: force status change + u8 areaID = *(u8*)((u8*)gpMarDirector + 0x124); + if (areaID == 1) { + if ((mAction - 0x10000000) != 0x1308) { + changePlayerStatus(0x10001308, 0, false); + } + } + + // Camera angle adjustment for original Mario + if (gpMarioOriginal == this) { + if ((u8)gpCamera->isLButtonCameraSpecifyMode( + *(int*)((u8*)gpCamera + 0x50))) { + u32 actionLow = mAction & 0x1FF; + if (!(actionLow >= 0x14B && actionLow <= 0x14F)) { + if (*(u8*)((u8*)gpMarDirector + 0x124) != 1) { + s16 camAngle = *(s16*)((u8*)gpCamera + 0x258); + s16 offsetY = gpCamera->getOffsetAngleY(); + mFaceAngle.y = + (s16)((camAngle + 0x8000) - offsetY); + } + } + } + } + + // Inlined checkPlayerAction + mInput = 0; + checkController(gfx); + makeHistory(); + checkCurrentPlane(); + checkRideMovement(); + if (!(mInput & 3)) + mInput |= 0x20; + + checkCollision(); + considerTake(); + + // Yoshi check + u8 isOnYoshi = 0; + if (mYoshi != NULL) { + if (mYoshi->onYoshi()) + isOnYoshi = 1; + } + if (isOnYoshi) { + if (*(u32*)((u8*)mGamePad + 0xD4) & 0x200000) { + getOffYoshi(false); + } + } + + thinkYoshiHeadCollision(); + + // Coaster angle interpolation + s16 stickValue = *(s16*)unk108; + f32 rate = mDeParams.mToroccoRotSp.value; + mToroccoAngle = (s16)((f32)stickValue * rate + (f32)mToroccoAngle); + + stateMachine(); + stateMachineUpper(); + thinkSituation(); + thinkWaterSurface(); + + // Sand effect handling + u8 hasSandFlags; + if (mState & 0x30000) + hasSandFlags = 1; + else + hasSandFlags = 0; + + if (!hasSandFlags) { + u16 bgType = mGroundPlane->mBGType; + u8 isSandGround; + if (bgType == 0x0701 || bgType == 0x4701 || bgType == 0x8701 + || bgType == 0xC701) + isSandGround = 1; + else + isSandGround = 0; + if (isSandGround) { + mState |= 0x40000; + emitSandEffect(); + } else { + mState &= ~0x40000; + } + } else { + mState &= ~0x40000; + } + + // Inlined thinkHeight + { + u8 isAirborne; + if (mAction & 0x800) + isAirborne = 1; + else + isAirborne = 0; + + if (isAirborne) { + f32 heightAboveGround = mPosition.y - mFloorPosition.y; + if (unk36C < heightAboveGround) + unk36C = heightAboveGround; + } else { + unk36C = 0.0f; + } + + JGeometry::TVec3 forwardPos; + forwardPos.x = mPosition.x + unk15C * JMASSin(mFaceAngle.y); + forwardPos.y = mPosition.y; + forwardPos.z = mPosition.z + unk15C * JMASCos(mFaceAngle.y); + + if (checkWallPlane(&forwardPos, 80.0f, unk15C) == NULL) { + const TBGCheckData* groundPlane; + f32 sinV = JMASSin(mFaceAngle.y); + f32 cosV = JMASCos(mFaceAngle.y); + f32 dx = 100.0f * sinV; + f32 dz = 100.0f * cosV; + f32 groundHeight = gpMap->checkGround( + mPosition.x + dx, + 100.0f + mPosition.y, + mPosition.z + dz, + &groundPlane); + unk370 = mPosition.y - groundHeight; + } else { + unk370 = 0.0f; + } + } + + thinkParams(); + + // Inlined checkRideReCalc + if (mRidingActor != NULL) { + Mtx localMtx; + if (mRidingActor->getRootJointMtx() == NULL) { + SMS_GetActorMtx(*mRidingActor, localMtx); + } else { + PSMTXCopy(*(mRidingActor->getRootJointMtx()), localMtx); + } + PSMTXInverse(localMtx, localMtx); + + *(JGeometry::TVec3*)((u8*)this + 0x300) = + *(JGeometry::TVec3*)((u8*)this + 0x2F4); + + PSMTXMultVec(localMtx, (Vec*)&mPosition, + (Vec*)((u8*)this + 0x2F4)); + } + + checkWet(); + checkGraffito(); + thinkDirty(); + checkSink(); + gunExec(); + + // Stop sound if not in specific action + if (mAction != 0x208B8) { + if (mSound != NULL) { + mSound->stop(1); + } + } +} + +void TMario::gunExec() +{ + u8 isOnYoshi = 0; + u8 isYoshiActive = 0; + + if (mYoshi != NULL) { + if (((TYoshi*)mYoshi)->onYoshi()) + isOnYoshi = 1; + } + if (isOnYoshi) + isYoshiActive = 1; + + if (!isYoshiActive) + *(u8*)((u8*)gpModelWaterManager + 0x5D5F) = 0; + + // Guard: need gun flag or be on Yoshi + u8 hasGunFlag; + if (mState & 0x8000) + hasGunFlag = 1; + else + hasGunFlag = 0; + + if (!hasGunFlag) { + u8 onYoshi2 = 0; + if (mYoshi != NULL) { + if (((TYoshi*)mYoshi)->onYoshi()) + onYoshi2 = 1; + } + if (!onYoshi2) + return; + } + + // Main body + TWaterGun* waterGun = mWaterGun; + u8 hasTrigger = 0; + waterGun->mIsEmitWater = false; + + // Interpolation math + TNozzleBase* nozzle0 = waterGun->mNozzleList[0]; + TNozzleBase* curNozzle = waterGun->getCurrentNozzle(); + s16 decRate = curNozzle->mEmitParams.mDecRate.get(); + s32 amountMax = nozzle0->mEmitParams.mAmountMax.get(); + waterGun->unk1C88 += 10.0f * ((f32)decRate / (f32)amountMax); + + // Trigger pressure + waterGun->triggerPressureMovement( + *(TMarioControllerWork*)unk108); + + // Clear bit 0x80 + mState &= ~0x80; + + // Check trigger bits + if (mState & 0x30000) + hasTrigger = 1; + + if (hasTrigger) { + // Suck section + u8 onYoshi3 = 0; + if (mYoshi != NULL) { + if (((TYoshi*)mYoshi)->onYoshi()) + onYoshi3 = 1; + } + + if (!onYoshi3) { + if (mWaterGun->suck() == 1) { + mState |= 0x80; + + u8 hasWaterFlag; + if (mState & 0x10000) + hasWaterFlag = 1; + else + hasWaterFlag = 0; + + if (hasWaterFlag) { + u16 bgType = mGroundPlane->mBGType; + u8 isPoolType; + if (bgType == 0x104 || bgType == 0x105 + || bgType == 0x4104) + isPoolType = 1; + else + isPoolType = 0; + + if (isPoolType) + gpPoolManager->subWaterLevel(mGroundPlane); + } + } + } + + // Water level check + curNozzle = waterGun->getCurrentNozzle(); + if (waterGun->mCurrentWater + == curNozzle->mEmitParams.mAmountMax.get()) { + if (mPumpState == 0) { + waterGun->emit(); + waterGun->mCurrentWater + = waterGun->getCurrentNozzle() + ->mEmitParams.mAmountMax.get(); + } + } + } else { + // No trigger + if (mPumpState == 0) { + mWaterGun->emit(); + } + } + + // Yoshi analog R check + u8 onYoshi4 = 0; + if (mYoshi != NULL) { + if (((TYoshi*)mYoshi)->onYoshi()) + onYoshi4 = 1; + } + + if (onYoshi4) { + if (((TMarioControllerWork*)unk108)->mAnalogR > 0.0f) { + mWaterGun->emit(); + } + } + + // mSubState bit 7 -> refill water + u8 hasBit0; + if (mSubState & 0x80) + hasBit0 = 1; + else + hasBit0 = 0; + + if (hasBit0) { + TWaterGun* wg = mWaterGun; + wg->mCurrentWater + = wg->getCurrentNozzle()->mEmitParams.mAmountMax.get(); + } + + // Action-based nozzle backup + if (mAction != 0x883) { + if (mAction != 0x208B8) { + if (mGamePad->mEnabledFrameMeaning & 0x200000) { + u8 onYoshi5 = 0; + if (mYoshi != NULL) { + if (((TYoshi*)mYoshi)->onYoshi()) + onYoshi5 = 1; + } + + if (!onYoshi5) { + if (mAction != 0x800447) { + mWaterGun->changeBackup(); + } + } + } + } + } + + // Pollution cleaning (spray nozzle only, while emitting) + if (mWaterGun->mCurrentNozzle != 0) + return; + + if (!mWaterGun->mIsEmitWater) + return; + + s32 sinIdx = (s32)(u16)mFaceAngle.y >> jmaSinShift; + JGeometry::TVec3 dir; + dir.x = jmaSinTable[sinIdx]; + dir.y = 0.0f; + dir.z = jmaCosTable[sinIdx]; + + for (s32 i = 0; i < mGraffitoParams.mFootEraseTimes.value; i++) { + f32 dist = mGraffitoParams.mFootEraseFront.value; + f32 radius = mGraffitoParams.mFootEraseSize.value; + + JGeometry::TVec3 tempDir(dir); + PSVECScale((Vec*)&tempDir, (Vec*)&tempDir, dist); + + JGeometry::TVec3 pos(mPosition); + PSVECAdd((Vec*)&pos, (Vec*)&tempDir, (Vec*)&pos); + + gpPollution->clean(pos.x, pos.y, pos.z, radius); + } +} diff --git a/src/Player/MarioPhysics.cpp b/src/Player/MarioPhysics.cpp index 8b137891..9481e9bd 100644 --- a/src/Player/MarioPhysics.cpp +++ b/src/Player/MarioPhysics.cpp @@ -1 +1,863 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma dont_inline on +// playerRefrection: reflect facing angle off wall, optionally negate velocity +void TMario::playerRefrection(int param) +{ + if (mWallPlane != NULL) { + const JGeometry::TVec3& normal = mWallPlane->getNormal(); + s16 wallAngle = matan(normal.z, normal.x); + s16 diff = (s16)(mFaceAngle.y - wallAngle); + mFaceAngle.y = (s16)(wallAngle - diff); + } + + if (param != 0) { + setPlayerVelocity(-mForwardVel); + } else { + mFaceAngle.y = mFaceAngle.y + 0x8000; + } +} +#pragma dont_inline off + +// keepDistance(TVec3, f32, f32): push Mario away from a reference point +void TMario::keepDistance(const JGeometry::TVec3& refPos, f32 radius, + f32 param) +{ + f32 dz = mPosition.z - refPos.z; + f32 dx = mPosition.x - refPos.x; + f32 totalDist = param + (radius + unk15C); + + f32 sqDist = dz * dz + dx * dx; + f32 dist; + if (sqDist > 0.0f) { + double g = __frsqrte((double)sqDist); + g = .5 * g * (3.0 - g * g * sqDist); + dist = (f32)(sqDist * g); + } else { + dist = sqDist; + } + + if (dist == 0.0f) { + dx = 1.0f; + dist = dx; + } + + if (dist >= totalDist) + return; + + if (onYoshi() == 0) { + f32* unk8E8 = (f32*)((u8*)this + 0x8E8); + u8 flag = 0; + if (mForwardVel > *unk8E8) + flag = 1; + + f32 vx = mVel.x; + f32 vz = mVel.z; + f32 speed = sqrtf(vx * vx + vz * vz); + + if (speed > *unk8E8) + flag = 1; + + if ((u8)flag == 1) { + emitParticle(12); + changePlayerDropping(0x208B0, 0); + return; + } + } + + s16 angle; + if (0.0f == dist) { + angle = mFaceAngle.y; + } else { + angle = matan(dz, dx); + } + + Vec newPos; + newPos.x = refPos.x + totalDist * JMASSin(angle); + newPos.y = mPosition.y; + newPos.z = refPos.z + totalDist * JMASCos(angle); + + checkWallPlane(&newPos, 60.0f, unk15C); + + f32 groundY; + const TBGCheckData* groundPlane; + checkGroundPlane(newPos.x, newPos.y, newPos.z, &groundY, &groundPlane); + + u8 isIllegal; + if (groundPlane->mFlags & 0x10) + isIllegal = 1; + else + isIllegal = 0; + + u8 isValid; + if (isIllegal == 1) + isValid = 0; + else + isValid = 1; + if (!isValid) + return; + + JGeometry::TVec3 diff(newPos); + diff.sub(mPosition); + + JGeometry::TVec3 push(diff); + f32 sq = push.x * push.x + push.y * push.y + push.z * push.z; + f32 len = JGeometry::TUtil::sqrt(sq); + + f32 pushDist = len; + if (len <= 0.0f) + return; + + if (50.0f < len) + pushDist = 50.0f; + + if (sq <= JGeometry::TUtil::epsilon()) { + push.x = 0.0f; + push.y = 0.0f; + push.z = 0.0f; + } else { + f32 invLen = JGeometry::TUtil::inv_sqrt(sq); + f32 scale = 1.0f * invLen; + push.x *= scale; + push.y *= scale; + push.z *= scale; + } + + push.scale(pushDist); + + JGeometry::TVec3 offset(push); + mPosition.x += offset.x; + mPosition.y += offset.y; + mPosition.z += offset.z; +} + +// keepDistance(THitActor, f32): thin wrapper that extracts position and radius +void TMario::keepDistance(const THitActor& actor, f32 param) +{ + keepDistance(*(const JGeometry::TVec3*)&actor.mPosition, + *(f32*)((u8*)&actor + 0x58), param); +} + +// checkDescent: check if Mario should start descending a slope +void TMario::checkDescent() +{ + f32 descentSp = mHangingParams.mDescentSp.value; + + u8 canDescent = 0; + if (mHeldObject == NULL) { + if (!onYoshi()) + canDescent = 1; + } + + if (canDescent != 1) + return; + + if (!(mForwardVel < descentSp)) + return; + + u8 isOnIllegal = 1; + u8 zero = 0; + + f32 posY = mPosition.y; + TBGWallCheckRecord wallRecord; + wallRecord.mCenter.x = mPosition.x; + wallRecord.mCenter.y = posY - 10.0f; + wallRecord.mCenter.z = mPosition.z; + wallRecord.mRadius = descentSp; + wallRecord.mMaxResults = isOnIllegal; + wallRecord.mFlags = zero; + + if (!gpMap->isTouchedWallsAndMoveXZ(&wallRecord)) + return; + + f32 groundY; + const TBGCheckData* groundPlane; + checkGroundPlane(wallRecord.mCenter.x, 30.0f + mPosition.y, wallRecord.mCenter.z, &groundY, &groundPlane); + + if (!(groundPlane->mFlags & 0x10)) { + isOnIllegal = zero; + } + + if (isOnIllegal) + return; + + if (!(mPosition.y - groundY > 160.0f)) + return; + + const TBGCheckData* wallData = wallRecord.mResultWalls[0]; + s16 wallAngle = matan(wallData->mNormal.z, wallData->mNormal.x); + s16 diff = (s16)(wallAngle - mFaceAngle.y); + + if (diff <= -16384) + return; + if (diff >= 16384) + return; + + f32 push = 20.0f + descentSp; + mPosition.x = wallRecord.mCenter.x - push * wallData->mNormal.x; + mPosition.z = wallRecord.mCenter.z - push * wallData->mNormal.z; + mFaceAngle.y = (s16)(wallAngle + 0x8000); + changePlayerStatus(0x3000054E, 0, false); + setAnimation(28, 1.0f); +} + +// checkGroundAtWalking: check ground collision during walk movement +int TMario::checkGroundAtWalking(Vec* pos) +{ + const TBGCheckData* roofPlane; + const TBGCheckData* groundPlane; + f32 groundY; + + checkWallPlane(pos, 30.0f, 0.5f * unk15C); + const TBGCheckData* wallPlane = checkWallPlane(pos, 60.0f, unk15C); + + u8 isOnWater; + if (mAction & 0x10000) + isOnWater = 1; + else + isOnWater = 0; + + if (isOnWater) { + groundY = gpMap->checkGround(pos->x, 30.0f + pos->y, pos->z, &groundPlane); + } else { + checkGroundPlane(pos->x, 30.0f + pos->y, pos->z, &groundY, &groundPlane); + } + + f32 roofY = gpMap->checkRoof(pos->x, 80.0f + mPosition.y, pos->z, &roofPlane); + + mWallPlane = wallPlane; + + u8 isIllegal; + if (groundPlane->mFlags & 0x10) + isIllegal = 1; + else + isIllegal = 0; + if (isIllegal) + return 2; + + if (160.0f + pos->y >= roofY) + return 2; + + if (pos->y > 100.0f + groundY) { + mPosition.x = pos->x; + mPosition.y = pos->y; + mPosition.z = pos->z; + mGroundPlane = groundPlane; + *(f32*)((u8*)this + 0xEC) = groundY; + return 0; + } + + if (fabsf(mPosition.y - groundY) > 100000.0f) { + mPosition = unk29C; + } else { + mPosition.x = pos->x; + mPosition.y = groundY; + mPosition.z = pos->z; + mGroundPlane = groundPlane; + *(f32*)((u8*)this + 0xEC) = groundY; + } + + if (wallPlane != NULL) { + u16 bgType = wallPlane->mBGType; + u8 isThrough; + if (bgType == 0x400 + || bgType == 0x8400 + || (u16)(bgType - 0x100) <= 3 + || (u16)(bgType - 0x800) <= 1 + || bgType == 0x201 + || bgType == 0x203) + isThrough = 1; + else + isThrough = 0; + if (!isThrough) { + s16 wallAngle = matan(wallPlane->mNormal.z, wallPlane->mNormal.x); + s16 diff = wallAngle - mFaceAngle.y; + + if (diff >= 0x2AAA && diff <= 0x5555) + return 1; + + if (diff <= -0x2AAA && diff >= -0x5555) + return 1; + + return 3; + } + } + + return 1; +} + +// stopProcess: stop all movement, snap to floor +void TMario::stopProcess() +{ + setPlayerVelocity(0.0f); + mVel.y = 0.0f; + mPosition.y = mFloorPosition.y; + mFaceAngle.x = 0; + mModelFaceAngle = mFaceAngle.y; +} + +// waitProcess: idle standing physics +int TMario::waitProcess() +{ + setPlayerVelocity(0.0f); + + f32 posY = mPosition.y; + f32 floorY = mFloorPosition.y; + if (fabsf(posY - floorY) > 4.0f) { + mPosition = unk29C; + changePlayerStatus(0x088D, 0, false); + } else { + mPosition.y = floorY; + } + + mFaceAngle.x = 0; + mModelFaceAngle = mFaceAngle.y; + + const TBGCheckData* ground = mGroundPlane; + u8 isIllegal; + if (ground->mFlags & 0x10) + isIllegal = 1; + else + isIllegal = 0; + if (isIllegal) + return 2; + + f32 normalY = 0.0f; + if (ground != NULL) + normalY = ground->mNormal.y; + + Vec nextPos; + nextPos.x = mPosition.x + mVel.x * 1.0f * normalY; + nextPos.z = mPosition.z + mVel.z * 1.0f * normalY; + nextPos.y = mPosition.y; + + int result = checkGroundAtWalking(&nextPos); + mFaceAngle.x = 0; + if (result == 3) + return 2; + mModelFaceAngle = mFaceAngle.y; + return result; +} + +// walkProcess: walking physics +int TMario::walkProcess() +{ + const TBGCheckData* ground = mGroundPlane; + u8 isIllegal; + if (ground->mFlags & 0x10) + isIllegal = 1; + else + isIllegal = 0; + if (isIllegal) + return 2; + + f32 normalY = 0.0f; + if (ground != NULL) + normalY = ground->mNormal.y; + + Vec nextPos; + nextPos.x = mPosition.x + mVel.x * 1.0f * normalY; + nextPos.z = mPosition.z + mVel.z * 1.0f * normalY; + nextPos.y = mPosition.y; + + int result = checkGroundAtWalking(&nextPos); + mFaceAngle.x = 0; + if (result == 3) + return 2; + mModelFaceAngle = mFaceAngle.y; + return result; +} + +// barProcess: pole/bar climbing physics +int TMario::barProcess() +{ + int result = 0; + const TBGCheckData* groundPlane; + const TBGCheckData* roofPlane; + f32 groundY; + + Vec pos; + pos.x = mHolder->mPosition.x; + pos.y = mPosition.y; + pos.z = mHolder->mPosition.z; + + const TBGCheckData* wall1 = checkWallPlane(&pos, 60.0f, 43.0f); + const TBGCheckData* wall2 = checkWallPlane(&pos, 30.0f, 24.0f); + + int isThrough1 = 0; + int isThrough2 = 0; + + if (wall1 == NULL) { + isThrough1 = 1; + } else { + u16 bgType = wall1->mBGType; + u8 isMarioThru; + if (bgType == 0x400 + || bgType == 0x8400 + || (u16)(bgType - 0x100) <= 3 + || (u16)(bgType - 0x800) <= 1 + || bgType == 0x201 + || bgType == 0x203) + isMarioThru = 1; + else + isMarioThru = 0; + if (isMarioThru) + isThrough1 = 1; + } + + if (wall2 == NULL) { + isThrough2 = 1; + } else { + u16 bgType = wall2->mBGType; + u8 isMarioThru; + if (bgType == 0x400 + || bgType == 0x8400 + || (u16)(bgType - 0x100) <= 3 + || (u16)(bgType - 0x800) <= 1 + || bgType == 0x201 + || bgType == 0x203) + isMarioThru = 1; + else + isMarioThru = 0; + if (isMarioThru) + isThrough2 = 1; + } + + if (isThrough1 == 1 && isThrough2 == 1) { + mPosition.x = mHolder->mPosition.x; + mPosition.y = mHolder->mPosition.y + mHolderHeightDiff; + mPosition.z = mHolder->mPosition.z; + } else { + *(Vec*)&mPosition = pos; + } + + f32 roofY = gpMap->checkRoof(mPosition.x, 80.0f + mPosition.y, mPosition.z, &roofPlane); + if (mPosition.y > roofY - 160.0f) { + mPosition.y = roofY - 160.0f; + } + + checkGroundPlane(mPosition.x, mPosition.y, mPosition.z, &groundY, &groundPlane); + + if (mPosition.y < groundY) { + mPosition.y = groundY; + changePlayerStatus(0x0C400201, 0, false); + result = 1; + } + + setPlayerVelocity(0.0f); + mFaceAngle.x = 0; + mModelFaceAngle = mFaceAngle.y; + + return result; +} + +// hangonCheck: check if Mario can hang onto a surface +int TMario::hangonCheck(const TBGCheckData* checkData, const Vec& pos1, + const Vec& pos2) +{ + if (mVel.y > 0.0f) + return 0; + + if ((pos2.x - pos1.x) * mVel.x + (pos2.z - pos1.z) * mVel.z > 0.0f) + return 0; + + Vec newPos; + const TBGCheckData* groundPlane; + newPos.x = pos2.x - 60.0f * checkData->mNormal.x; + newPos.z = pos2.z - 60.0f * checkData->mNormal.z; + checkGroundPlane(newPos.x, 160.0f + pos2.y, newPos.z, &newPos.y, + &groundPlane); + + if (newPos.y - pos2.y <= 100.0f) + return 0; + + if (mFloorPosition.x < 160.0f + newPos.y) + return 0; + + u16 bgType = groundPlane->mBGType; + + u8 isWater; + if (bgType == 0x0001 || bgType == 0x4001 || bgType == 0x8001 + || bgType == 0xC001) + isWater = 1; + else + isWater = 0; + if (isWater) + return 0; + + u8 isType6; + if (bgType == 0x0006 || bgType == 0x4006 || bgType == 0x8006 + || bgType == 0xC006) + isType6 = 1; + else + isType6 = 0; + if (isType6) + return 0; + + mPosition.x = newPos.x; + mPosition.y = newPos.y; + mPosition.z = newPos.z; + mGroundPlane = groundPlane; + *(f32*)((u8*)this + 0xEC) = newPos.y; + + const JGeometry::TVec3& normal = groundPlane->getNormal(); + mSlopeAngle = matan(normal.z, normal.x); + + s16 angle = matan(checkData->mNormal.z, checkData->mNormal.x); + mFaceAngle.y = (s16)(angle + 0x8000); + + return 1; +} + +// checkGroundAtJumping: ground collision during airborne movement +int TMario::checkGroundAtJumping(const Vec& pos, int flags) +{ + Vec stackPos; + ((u32*)&stackPos)[0] = ((const u32*)&pos)[0]; + ((u32*)&stackPos)[1] = ((const u32*)&pos)[1]; + ((u32*)&stackPos)[2] = ((const u32*)&pos)[2]; + + const TBGCheckData* wall1 = checkWallPlane(&stackPos, 30.0f, unk15C); + const TBGCheckData* wall2 = checkWallPlane(&stackPos, 60.0f, unk15C); + + u8 isOnWater; + if (mAction & 0x10000) + isOnWater = 1; + else + isOnWater = 0; + + if (isOnWater) { + *(f32*)((u8*)this + 0xEC) = gpMap->checkGround(stackPos.x, 30.0f + stackPos.y, stackPos.z, &mGroundPlane); + } else { + checkGroundPlane(stackPos.x, 30.0f + stackPos.y, stackPos.z, (f32*)((u8*)this + 0xEC), &mGroundPlane); + } + + mFloorPosition.x = checkRoofPlane(stackPos, 80.0f + mPosition.y, &mRoofPlane); + + int groundResult = 7; + int hangResult = 7; + int slopeResult = 7; + + mPosition.x = stackPos.x; + mPosition.y = stackPos.y; + mPosition.z = stackPos.z; + + u8 isIllegal; + if (mGroundPlane->mFlags & 0x10) + isIllegal = 1; + else + isIllegal = 0; + + if (isIllegal) { + mPosition = unk29C; + groundResult = 2; + } else { + u16 bgType = mGroundPlane->mBGType; + int isWater = 0; + + u8 isMarioThrough; + if (bgType == 0x400 + || bgType == 0x8400 + || (u16)(bgType - 0x100) <= 3 + || (u16)(bgType - 0x800) <= 1 + || bgType == 0x201 + || bgType == 0x203) + isMarioThrough = 1; + else + isMarioThrough = 0; + if (isMarioThrough) + isWater = 1; + + if ((mAction - 0x800000) == 0x08A9) { + u8 isType107; + if (bgType == 0x107) + isType107 = 1; + else + isType107 = 0; + if (isType107) + isWater = 1; + } + + u8 isActionWater; + if (mAction & 0x10000) + isActionWater = 1; + else + isActionWater = 0; + + if (isActionWater) { + u8 isWaterSurface; + if (bgType == 0x100 + || bgType == 0x101 + || (u16)(bgType - 258) <= 3 + || bgType == 0x4104) + isWaterSurface = 1; + else + isWaterSurface = 0; + if (isWaterSurface) + isWater = 0; + } + + if (isWater == 0) { + if (stackPos.y <= *(f32*)((u8*)this + 0xEC)) { + mPosition.y = *(f32*)((u8*)this + 0xEC); + groundResult = 1; + } + } + } + + if (groundResult == 1) { + unkBC = mVel.y; + } + + if (mRoofPlane != NULL) { + u16 roofType = mRoofPlane->mBGType; + u8 isRoofThrough; + if (roofType == 0x400 + || roofType == 0x8400 + || (u16)(roofType - 0x100) <= 3 + || (u16)(roofType - 0x800) <= 1 + || roofType == 0x201 + || roofType == 0x203) + isRoofThrough = 1; + else + isRoofThrough = 0; + + if (!isRoofThrough) { + if (160.0f + stackPos.y > mFloorPosition.x) { + mPosition.y = mFloorPosition.x - 160.0f; + + if (mRoofPlane->mActor != NULL) { + ((TLiveActor*)mRoofPlane->mActor)->receiveMessage((THitActor*)this, 2); + } + + setPlayerVelocity(0.0f); + + if (mVel.y >= 0.0f) { + mVel.y = 0.0f; + + u8 hasFlag2; + if (mState & 2) + hasFlag2 = 1; + else + hasFlag2 = 0; + if (hasFlag2) { + mState |= 0x200; + } + + if (flags & 2) { + u8 canClimb = 0; + if (mHeldObject == NULL) { + if (!onYoshi()) + canClimb = 1; + } + + if (canClimb) { + u8 isFence; + if (mRoofPlane->mBGType == 0x10A) + isFence = 1; + else + isFence = 0; + if (isFence) { + slopeResult = 4; + } else { + slopeResult = 0; + } + } else { + slopeResult = 0; + } + } else { + slopeResult = 0; + } + } + } + } + } + + mWallPlane = (const TBGCheckData*)NULL; + int isWall1Water = 0; + int isWall2Water = 0; + + if (wall1 == NULL) { + isWall1Water = 1; + } else { + u16 bgType = wall1->mBGType; + u8 isMarioThru; + if (bgType == 0x400 + || bgType == 0x8400 + || (u16)(bgType - 0x100) <= 3 + || (u16)(bgType - 0x800) <= 1 + || bgType == 0x201 + || bgType == 0x203) + isMarioThru = 1; + else + isMarioThru = 0; + if (isMarioThru) + isWall1Water = 1; + } + + if (wall2 == NULL) { + isWall2Water = 1; + } else { + u16 bgType = wall2->mBGType; + u8 isMarioThru; + if (bgType == 0x400 + || bgType == 0x8400 + || (u16)(bgType - 0x100) <= 3 + || (u16)(bgType - 0x800) <= 1 + || bgType == 0x201 + || bgType == 0x203) + isMarioThru = 1; + else + isMarioThru = 0; + if (isMarioThru) + isWall2Water = 1; + } + + if ((flags & 1) && isWall1Water == 1 && isWall2Water == 0) { + u8 canHang = 0; + if (mHeldObject == NULL) { + if (!onYoshi()) + canHang = 1; + } + + if ((u8)canHang == 1) { + mWallPlane = wall2; + if (hangonCheck(wall2, pos, stackPos)) { + hangResult = 3; + } else { + hangResult = 0; + } + } + } else { + if (isWall1Water != 0 && isWall2Water != 0) { + // both water, skip + } else { + const TBGCheckData* wall; + if (wall1 != NULL) + wall = wall1; + else + wall = wall2; + mWallPlane = wall; + + const JGeometry::TVec3& normal = mWallPlane->getNormal(); + int wallAngle = matan(normal.z, normal.x); + s16 limit = mJumpParams.mClashAngle.value; + s16 diff = (s16)(wallAngle - (mFaceAngle.y + 0x8000)); + + if (-limit < diff && diff < limit) { + hangResult = 2; + } + } + } + + if (groundResult != 7) + return groundResult; + if (slopeResult != 7) + return slopeResult; + if (hangResult != 7) + return hangResult; + return 0; +} + +// fallProcess: apply gravity and air physics +void TMario::fallProcess() +{ + u32 action = mAction; + + if (action == 0x0891) { + mVel.y -= mDivingParams.mGravity.value; + if (mVel.y < -75.0f) { + mVel.y = -75.0f; + } + return; + } + + u8 inAir; + if (unk78 & 0x100) + inAir = 1; + else + inAir = 0; + + u8 shouldBrake; + if (!inAir) { + shouldBrake = 0; + } else if (action & 0x00021000) { + shouldBrake = 0; + } else { + if (!(mInput & 0x80) && mVel.y > 20.0f) + shouldBrake = 1; + else + shouldBrake = 0; + } + + if (shouldBrake) { + mVel.y *= 0.75f; + } else { + u32 diff = action - 0x0895; + if (diff <= 1 && mVel.y < 0.0f) { + mVel.y -= mJumpParams.mJumpAccelControl.value; + } else { + mVel.y -= mJumpParams.mJumpSpeedBrake.value; + } + } + + if (onYoshi()) { + mYoshi->thinkHoldOut(); + } + + if (mVel.y < -75.0f) { + mVel.y = -75.0f; + } +} + +// jumpProcess: process jump movement each frame +int TMario::jumpProcess(int param) +{ + int result = 0; + + f32 velX = mVel.x; + f32 velZ = mVel.z; + f32 speed = sqrtf(velX * velX + velZ * velZ); + + f32 maxSpeed = mJumpParams.mJumpSlideControl.value; + if (speed > maxSpeed) { + mVel.x = mVel.x * (maxSpeed / speed); + mVel.z = mVel.z * (mJumpParams.mJumpSlideControl.value / speed); + } + + f32 factor = 0.25f; + Vec nextPos; + nextPos.x = mPosition.x + factor * mVel.x; + nextPos.y = mPosition.y + factor * mVel.y; + nextPos.z = mPosition.z + factor * mVel.z; + + int groundResult = checkGroundAtJumping(nextPos, param); + if (groundResult != 0) { + result = groundResult; + } + + if (mVel.y < 0.0f) { + *(f32*)((u8*)this + 0x104) = mPosition.y; + } + + fallProcess(); + + if (mAction != 0x80088A) { + mFaceAngle.x = 0; + } + + mModelFaceAngle = mFaceAngle.y; + + return result; +} diff --git a/src/Player/MarioRun.cpp b/src/Player/MarioRun.cpp index 8b137891..58ec7772 100644 --- a/src/Player/MarioRun.cpp +++ b/src/Player/MarioRun.cpp @@ -1 +1,2636 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// NOTE: -inline deferred means functions must be in REVERSE address order. + +// considerRotateStart - 0x8013C118 +BOOL TMario::considerRotateStart() +{ + int stickDir; + if (checkStickRotate(&stickDir) != 1) + return 0; + + TWaterGun* gun = mWaterGun; + if (gun == nullptr) + return 0; + + u8 canSpray; + if (gun->mCurrentWater == 0) { + canSpray = 0; + } else { + s32 kind = gun->getCurrentNozzle()->getNozzleKind(); + if (kind == 1) { + TNozzleTrigger* trigger = (TNozzleTrigger*)gun->getCurrentNozzle(); + if (trigger->unk385 == 1) { + canSpray = 1; + } else { + canSpray = 0; + } + } else { + if (gun->getCurrentNozzle()->unk378 > 0.0f) { + canSpray = 1; + } else { + canSpray = 0; + } + } + } + + if (canSpray) { + if (stickDir > 0) { + changePlayerStatus(0x0441, 0, false); + } else { + changePlayerStatus(0x0442, 0, false); + } + } else { + return 0; + } +} + +// doRunningAnimation - 0x8013BFA4 +void TMario::doRunningAnimation() +{ + f32 speed = mIntendedMag; + f32 fwdVel = mForwardVel; + if (speed <= fwdVel) + speed = fwdVel; + + f32 clampedSpeed = speed; + if (speed < 0.0f) + clampedSpeed = 0.0f; + + f32 anmSpd1 = 1.0f; + f32 softWalk = 0.5f; + f32 walk2Soft = 0.0f; + + s32 redo = 1; + while (redo) { + u16 anmId = mAnimationId; + if (anmId == 0x92) { + // softStep animation + if (clampedSpeed > mRunParams.mWalk2Soft.get()) { + setAnimation(0x72, anmSpd1); + redo = 1; + continue; + } + f32 mult = clampedSpeed; + if (mult < softWalk) + mult = softWalk; + setAnimation(0x92, mult * mRunParams.mSoftStepAnmMult.get()); + redo = 0; + continue; + } else if (anmId == 0xF5) { + // pumping slide animation + if (mForwardVel < mRunParams.mPumpingSlideSp.get() - anmSpd1) { + setAnimation(0x72, anmSpd1); + redo = 1; + continue; + } + f32 anmSpeed = mRunParams.mRunAnmSpeedMult.get() * clampedSpeed + mRunParams.mRunAnmSpeedBase.get(); + setAnimation(0xF5, anmSpeed); + redo = 0; + continue; + } else { + // default (0x72 run animation) + if (mForwardVel >= mRunParams.mPumpingSlideSp.get() - anmSpd1) { + setAnimation(0xF5, anmSpd1); + redo = 1; + continue; + } + + if (clampedSpeed < mRunParams.mSoft2Walk.get()) { + setAnimation(0x92, anmSpd1); + redo = 1; + continue; + } + + u8 isShallow; + if (mState & 0x30000) + isShallow = 1; + else + isShallow = 0; + + f32 anmSpeed = clampedSpeed * mRunParams.mRunAnmSpeedMult.get() + mRunParams.mRunAnmSpeedBase.get(); + + if (isShallow) { + u8 isSwimDepth; + if (mFloorPosition.y < mPosition.y + mRunParams.mSwimDepth.get()) + isSwimDepth = 1; + else + isSwimDepth = 0; + if (isSwimDepth) { + f32 ratio = (mFloorPosition.y - mPosition.y) / mRunParams.mSwimDepth.get(); + f32 factor = anmSpd1 - mRunParams.mInWaterAnmBrake.get(); + anmSpeed = anmSpeed * (anmSpd1 - ratio * factor) * mRunParams.mInWaterBrake.get(); + } + } + + setAnimation(0x72, anmSpeed); + + f32 walkSp = mRunParams.mMotBlendWalkSp.get(); + f32 runSp = mRunParams.mMotBlendRunSp.get(); + f32 blend = 0.0f; + if (anmSpeed < walkSp) + blend = 1.0f; + if (runSp < anmSpeed) + blend = 1.0f; + if (walkSp <= anmSpeed && anmSpeed <= runSp) { + blend = (anmSpeed - walkSp) / (runSp - walkSp); + } + + *(f32*)((u8*)this + 0x41C) = anmSpd1 - blend; + + if (isRunningInWater()) { + mModel->unk20->unk18->unk50 = walk2Soft; + } else { + mModel->unk20->unk18->unk50 = *(f32*)((u8*)this + 0x41C); + } + + redo = 0; + continue; + } + } +} + +// isRunningInWater - 0x8013C0D0 +BOOL TMario::isRunningInWater() +{ + u8 flag; + if (mState & 0x30000) { + flag = 1; + } else { + flag = 0; + } + if (flag) { + if (mFloorPosition.z < mPosition.y + mRunParams.mSwimDepth.get()) { + return 1; + } + } + return 0; +} + +void TMario::getSlopeNormalAccele(f32* accelUp, f32* accelDown) +{ + if (isForceSlip()) { + *accelUp = mSlipParamsAll.mSlopeAcceleUp.value; + *accelDown = mSlipParamsAll.mSlopeAcceleDown.value; + return; + } + + const TBGCheckData* ground = mGroundPlane; + u16 type = ground->mBGType; + + u8 isOil; + if (type == 0x0c || type == 0x800c || type == 0xa00c) + isOil = 1; + else + isOil = 0; + if (isOil) { + *accelUp = mSlipParamsAllSlider.mSlopeAcceleUp.value; + *accelDown = mSlipParamsAllSlider.mSlopeAcceleDown.value; + return; + } + + u8 isAll; + if (type == 0x02 || type == 0x8002) + isAll = 1; + else + isAll = 0; + if (isAll) { + *accelUp = mSlipParams45.mSlopeAcceleUp.value; + *accelDown = mSlipParams45.mSlopeAcceleDown.value; + return; + } + + u8 isWater; + if (type == 0x04 || type == 0x4004 || type == 0x8004 || type == 0xc004) + isWater = 1; + else + isWater = 0; + if (isWater) { + if (ground->mNormal.y > 0.99f) { + *accelUp = mSlipParamsWaterGround.mSlopeAcceleUp.value; + *accelDown = mSlipParamsWaterGround.mSlopeAcceleDown.value; + } else { + *accelUp = mSlipParamsWaterSlope.mSlopeAcceleUp.value; + *accelDown = mSlipParamsWaterSlope.mSlopeAcceleDown.value; + } + return; + } + + *accelUp = mSlipParamsNormal.mSlopeAcceleUp.value; + *accelDown = mSlipParamsNormal.mSlopeAcceleDown.value; +} + +void TMario::getSlopeSlideAccele(f32* accelUp, f32* accelDown) +{ + if (isForceSlip()) { + *accelUp = mSlipParamsAll.mSlideAcceleUp.value; + *accelDown = mSlipParamsAll.mSlideAcceleDown.value; + return; + } + + const TBGCheckData* ground = mGroundPlane; + u16 type = ground->mBGType; + + u8 isOil; + if (type == 0x0c || type == 0x800c || type == 0xa00c) + isOil = 1; + else + isOil = 0; + if (isOil) { + *accelUp = mSlipParamsAllSlider.mSlideAcceleUp.value; + *accelDown = mSlipParamsAllSlider.mSlideAcceleDown.value; + return; + } + + u8 isAll; + if (type == 0x02 || type == 0x8002) + isAll = 1; + else + isAll = 0; + if (isAll) { + *accelUp = mSlipParams45.mSlideAcceleUp.value; + *accelDown = mSlipParams45.mSlideAcceleDown.value; + return; + } + + u8 isWater; + if (type == 0x04 || type == 0x4004 || type == 0x8004 || type == 0xc004) + isWater = 1; + else + isWater = 0; + if (isWater) { + if (ground->mNormal.y > 0.99f) { + *accelUp = mSlipParamsWaterGround.mSlideAcceleUp.value; + *accelDown = mSlipParamsWaterGround.mSlideAcceleDown.value; + } else { + *accelUp = mSlipParamsWaterSlope.mSlideAcceleUp.value; + *accelDown = mSlipParamsWaterSlope.mSlideAcceleDown.value; + } + return; + } + + *accelUp = mSlipParamsNormal.mSlideAcceleUp.value; + *accelDown = mSlipParamsNormal.mSlideAcceleDown.value; +} + +// getChangeAngleSpeed - returns angle change speed (s16 param -> f32 -> * forwardVel * 1/32) +// Despite being declared void, the asm returns a value in f1 +f32 TMario::getChangeAngleSpeed() +{ + f32 angleSpeed; + + if (isForceSlip()) { + angleSpeed = (f32) mSlipParamsAll.mSlideAngleYSp.value; + } else { + const TBGCheckData* ground = mGroundPlane; + u16 type = ground->mBGType; + + u8 isOil; + if (type == 0x0c || type == 0x800c || type == 0xa00c) + isOil = 1; + else + isOil = 0; + if (isOil) { + angleSpeed = (f32) mSlipParamsAllSlider.mSlideAngleYSp.value; + } else { + u8 isAll; + if (type == 0x02 || type == 0x8002) + isAll = 1; + else + isAll = 0; + if (isAll) { + angleSpeed = (f32) mSlipParams45.mSlideAngleYSp.value; + } else { + u8 isWater; + if (type == 0x04 || type == 0x4004 + || type == 0x8004 || type == 0xc004) + isWater = 1; + else + isWater = 0; + if (isWater) { + if (ground->mNormal.y > 0.99f) { + angleSpeed = (f32) *(s16*)((u8*)this + + 0x2F70); + } else { + angleSpeed = (f32) *(s16*)((u8*)this + + 0x2E8C); + } + } else { + angleSpeed + = (f32) mSlipParamsNormal.mSlideAngleYSp.value; + } + } + } + } + + return 0.03125f * angleSpeed * mForwardVel; +} + +// getSlideStickMult - 0x8013B8E8 +f32 TMario::getSlideStickMult() +{ + if (isForceSlip()) { + return mSlipParamsAll.mStickSlideMult.get(); + } + + u16 groundType = mGroundPlane->mBGType; + + u8 isIce; + if (groundType == 0x0C || groundType == 0x800C || groundType == 0xA00C) { + isIce = 1; + } else { + isIce = 0; + } + if (isIce) { + return mSlipParamsAllSlider.mStickSlideMult.get(); + } + + u8 isSand; + if (groundType == 0x02 || groundType == 0x8002) { + isSand = 1; + } else { + isSand = 0; + } + if (isSand) { + return mSlipParams45.mStickSlideMult.get(); + } + + return mSlipParamsNormal.mStickSlideMult.get(); +} + +// slideProcess - 0x80139E28 +void TMario::slideProcess(f32 accelUp, f32 accelDown) +{ + const TBGCheckData* ground = mGroundPlane; + s16 slopeAngle = matan(ground->mNormal.z, ground->mNormal.x); + + f32 nx = ground->mNormal.x; + f32 nz = ground->mNormal.z; + f32 slopeMag = nx * nx + nz * nz; + f32 sqrtMag; + if (slopeMag > 0.0f) { + f32 root = __frsqrte(slopeMag); + f32 refined = 0.5f * root * (3.0f - slopeMag * (root * root)); + sqrtMag = slopeMag * refined; + sqrtMag = (f32)sqrtMag; + } else { + sqrtMag = slopeMag; + } + + s16 faceY = mFaceAngle.y; + f32 accelUpLocal; + f32 accelDownLocal; + s32 angleDiff = (s16)(mSlopeAngle - faceY); + getSlopeSlideAccele(&accelUpLocal, &accelDownLocal); + + s16 diffExt = (s16)angleDiff; + if (diffExt > -16384 && diffExt < 16384) { + accelUp = accelUp + accelUpLocal * sqrtMag; + } else { + accelUp = accelUp + accelDownLocal * sqrtMag; + } + + u16 uAngle = (u16)slopeAngle; + mSlideVelX = mSlideVelX + accelUp * JMASSin(uAngle); + mSlideVelZ = mSlideVelZ + accelUp * JMASCos(uAngle); + + mSlideVelX = mSlideVelX * accelDown; + mSlideVelZ = mSlideVelZ * accelDown; + + mSlideAngle = matan(mSlideVelZ, mSlideVelX); + + f32 negThresh = -1.0f; + s32 angleChange = 0; + if (negThresh < mSlideVelX && mSlideVelX < 0.0f + && negThresh < mSlideVelZ && mSlideVelZ < 0.0f) { + // velocity very small, skip angle processing + } else { + s16 slideAngle = mSlideAngle; + s16 faceDiff = (s16)(mFaceAngle.y - slideAngle); + s32 faceDiffExt = (s16)faceDiff; + angleChange = faceDiffExt; + + if (faceDiffExt > 0 && faceDiffExt <= 16384) { + getChangeAngleSpeed(); + f32 changeAngleSpd = 0.03125f * (f32)mSlipParamsNormal.mSlideAngleYSp.value * mForwardVel; + angleChange = (s32)((f32)faceDiffExt - changeAngleSpd); + if (angleChange < 0) + angleChange = 0; + } else if (faceDiffExt > -16384 && faceDiffExt < 0) { + getChangeAngleSpeed(); + f32 changeAngleSpd = 0.03125f * (f32)mSlipParamsNormal.mSlideAngleYSp.value * mForwardVel; + angleChange = (s32)((f32)faceDiffExt + changeAngleSpd); + if (angleChange > 0) + angleChange = 0; + } else if (faceDiffExt > 16384 && faceDiffExt < 0x18000) { + getChangeAngleSpeed(); + f32 changeAngleSpd = 0.03125f * (f32)mSlipParamsNormal.mSlideAngleYSp.value * mForwardVel; + angleChange = (s32)((f32)faceDiffExt + changeAngleSpd); + if (angleChange > 0x18000) + angleChange = 0x18000; + } else if (faceDiffExt > -32768 && faceDiffExt < -16384) { + getChangeAngleSpeed(); + f32 changeAngleSpd = 0.03125f * (f32)mSlipParamsNormal.mSlideAngleYSp.value * mForwardVel; + angleChange = (s32)((f32)faceDiffExt - changeAngleSpd); + if (angleChange < -32768) + angleChange = -32768; + } + + mFaceAngle.y = (s16)(mSlideAngle + angleChange); + } + + mVel.x = mSlideVelX; + f32 zero = 0.0f; + mVel.y = zero; + mVel.z = mSlideVelZ; + + f32 sx = mSlideVelX; + f32 sz = mSlideVelZ; + f32 velSq = sx * sx + sz * sz; + f32 velMag; + if (velSq > zero) { + f32 root = __frsqrte(velSq); + f32 refined = 0.5f * root * (3.0f - velSq * (root * root)); + velMag = velSq * refined; + velMag = (f32)velMag; + } else { + velMag = velSq; + } + mForwardVel = velMag; + + if (mForwardVel > zero) { + mSlideVelX = zero * mSlideVelX / mForwardVel; + mSlideVelZ = zero * mSlideVelZ / mForwardVel; + } + + if (angleChange < -16384 || angleChange > 16384) { + mForwardVel = mForwardVel * 0.5f; + } +} + +// doSliding - 0x80139A3C +int TMario::doSliding(f32 stopThreshold) +{ + s32 result = 0; + + // Compute sine/cosine of face-slide direction difference + s16 slideDir = mSlideAngle; + s16 faceDir = mIntendedYaw; + u16 angleDiff = (u16)(faceDir - slideDir); + f32 sinVal = JMASSin(angleDiff); + f32 cosVal = JMASCos(angleDiff); + + // Adjust acceleration based on forward velocity sign + if (sinVal < 0.0f) { + f32 fwdVel = mForwardVel; + if (fwdVel >= 0.0f) { + f32 mult1 = 2.0f; + f32 mult2 = 1.5f; + f32 factor = mult1 * fwdVel * (mult2 * sinVal + mult1); + sinVal = sinVal * factor; + } + } + + // Check for specific sliding action + u32 action = mAction; + if ((action - 0x007C0000) == 0x045D) { + f32 slideStop = mSlipParamsOil.mSlipFriction.value; + } else { + u8 forceSlip; + forceSlip = isForceSlip(); + if (forceSlip) { + f32 slideStop = mSlipParamsAll.mSlipFriction.value; + } else { + const TBGCheckData* ground = mGroundPlane; + u16 type = ground->mBGType; + + u8 isOil; + if (type == 0x0c || type == 0x800c || type == 0xa00c) + isOil = 1; + else + isOil = 0; + if (isOil) { + f32 slideStop = mSlipParamsAllSlider.mSlipFriction.value; + } else { + u8 isAll; + if (type == 0x02 || type == 0x8002) + isAll = 1; + else + isAll = 0; + if (isAll) { + f32 slideStop = mSlipParams45.mSlipFriction.value; + } else { + u8 isWater; + if (type == 0x04 || type == 0x4004 || type == 0x8004 || type == 0xc004) + isWater = 1; + else + isWater = 0; + if (isWater) { + if (ground->mNormal.y > 0.99f) { + f32 slideStop = mSlipParamsWaterGround.mSlipFriction.value; + } else { + f32 slideStop = mSlipParamsWaterSlope.mSlipFriction.value; + } + } else { + u8 isYoshi; + isYoshi = onYoshi(); + if (isYoshi) { + f32 slideStop = mSlipParamsYoshi.mSlipFriction.value; + } else { + f32 slideStop2 = mSlipParamsNormal.mSlipFriction.value; + u32 action2 = mAction; + if ((action2 - 0x00800000) == 0x0456) { + if (mActionState == 1) + slideStop2 = mDeParams.mWasOnWaterSlip.value; + u8 isInShallow; + if (mState & 0x30000) + isInShallow = 1; + else + isInShallow = 0; + if (isInShallow) { + slideStop2 = mDeParams.mInWaterSlip.value; + } + } + } + } + } + } + } + } + + // This is getting too complex - the real logic involves computing slideStop + // and using it in slideProcess. Let me use a simpler approach. + + f32 slideStop; + if ((mAction - 0x007C0000) == 0x045D) { + slideStop = mSlipParamsOil.mSlipFriction.value; + } else if (isForceSlip()) { + slideStop = mSlipParamsAll.mSlipFriction.value; + } else { + const TBGCheckData* ground2 = mGroundPlane; + u16 type2 = ground2->mBGType; + u8 isOil2; + if (type2 == 0x0c || type2 == 0x800c || type2 == 0xa00c) + isOil2 = 1; + else + isOil2 = 0; + if (isOil2) { + slideStop = mSlipParamsAllSlider.mSlipFriction.value; + } else { + u8 isAll2; + if (type2 == 0x02 || type2 == 0x8002) + isAll2 = 1; + else + isAll2 = 0; + if (isAll2) { + slideStop = mSlipParams45.mSlipFriction.value; + } else { + u8 isWater2; + if (type2 == 0x04 || type2 == 0x4004 || type2 == 0x8004 || type2 == 0xc004) + isWater2 = 1; + else + isWater2 = 0; + if (isWater2) { + if (ground2->mNormal.y > 0.99f) + slideStop = mSlipParamsWaterGround.mSlipFriction.value; + else + slideStop = mSlipParamsWaterSlope.mSlipFriction.value; + } else if (onYoshi()) { + slideStop = mSlipParamsYoshi.mSlipFriction.value; + } else { + slideStop = mSlipParamsNormal.mSlipFriction.value; + if ((mAction - 0x00800000) == 0x0456) { + if (mActionState == 1) + slideStop = mDeParams.mWasOnWaterSlip.value; + u8 isInShallow; + if (mState & 0x30000) + isInShallow = 1; + else + isInShallow = 0; + if (isInShallow) + slideStop = mDeParams.mInWaterSlip.value; + } + } + } + } + } + + // Compute slide deceleration + f32 mag = mSlideVelX * mSlideVelX + mSlideVelZ * mSlideVelZ; + f32 oldSpeed; + if (mag > 0.0f) { + f32 root = __frsqrte(mag); + f32 refined = 0.5f * root * (3.0f - mag * (root * root)); + oldSpeed = mag * refined; + oldSpeed = (f32)oldSpeed; + } else { + oldSpeed = mag; + } + + f32 stickMult = getSlideStickMult(); + f32 stickEffect = mIntendedMag * 0.03125f; + f32 accelX = mSlideVelZ * stickEffect * cosVal; + mSlideVelX = mSlideVelX + accelX * sinVal; + + stickMult = getSlideStickMult(); + stickEffect = mIntendedMag * 0.03125f; + f32 accelZ = mSlideVelX * stickEffect * cosVal; + mSlideVelZ = mSlideVelZ - accelZ * sinVal; + + // Compute new speed + f32 newSx = mSlideVelX; + f32 newSz = mSlideVelZ; + f32 newMag = newSx * newSx + newSz * newSz; + f32 newSpeed; + if (newMag > 0.0f) { + f32 root = __frsqrte(newMag); + f32 refined = 0.5f * root * (3.0f - newMag * (root * root)); + newSpeed = newMag * refined; + newSpeed = (f32)newSpeed; + } else { + newSpeed = newMag; + } + + // Keep speed from increasing + if (oldSpeed > 0.0f && newSpeed > 0.0f) { + mSlideVelX = mSlideVelX * oldSpeed / newSpeed; + mSlideVelZ = mSlideVelZ * oldSpeed / newSpeed; + } + + slideProcess(sinVal, slideStop); + + // Check ground type for stop conditions + const TBGCheckData* ground3 = mGroundPlane; + u16 type3 = ground3->mBGType; + u8 isSlippery; + if (type3 == 0x01 || type3 == 0x4001 || type3 == 0x8001 || type3 == 0xC001) + isSlippery = 1; + else + isSlippery = 0; + if (!isSlippery) { + u8 isOil3; + if (type3 == 0x0c || type3 == 0x800c || type3 == 0xa00c) + isOil3 = 1; + else + isOil3 = 0; + if (!isOil3) { + if (mForwardVel * mForwardVel < stopThreshold * stopThreshold) { + setPlayerVelocity(0.0f); + mInput = mInput & ~0x4; + result = 1; + } + } + } + + return result; +} + +void TMario::slopeProcess() +{ + const TBGCheckData* ground = mGroundPlane; + f32 nx = ground->mNormal.x; + f32 nz = ground->mNormal.z; + + f32 slopeMag = nx * nx + nz * nz; + f32 sqrtMag = sqrtf(slopeMag); + + s16 faceY = mFaceAngle.y; + s16 slopeAngle = mSlopeAngle; + s16 angleDiff = slopeAngle - faceY; + + f32 accelUp; + f32 accelDown; + getSlopeNormalAccele(&accelUp, &accelDown); + + s16 diffExt = (s16)angleDiff; + if (diffExt > -16384 && diffExt < 16384) { + mForwardVel = mForwardVel + accelUp * sqrtMag; + } else { + mForwardVel = mForwardVel - accelDown * sqrtMag; + } + + if (mForwardVel > mDeParams.mRunningMax.value) { + mForwardVel = mDeParams.mRunningMax.value; + } + + s16 modelAngle = mFaceAngle.y; + mModelFaceAngle = modelAngle; + + u16 angle = mFaceAngle.y; + f32 sinVal = JMASSin(angle); + mSlideVelX = mForwardVel * sinVal; + + angle = mFaceAngle.y; + f32 cosVal = JMASCos(angle); + mSlideVelZ = mForwardVel * cosVal; + + mVel.x = mSlideVelX; + mVel.y = 0.0f; + mVel.z = mSlideVelZ; +} + +// doRunning - 0x8013B5DC +void TMario::doRunning() +{ + f32 maxSpeed = mIntendedMag; + if (maxSpeed >= mRunParams.mMaxSpeed.value) + maxSpeed = mRunParams.mMaxSpeed.value; + + f32 runMult = maxSpeed; + + if (isRunningInWater()) { + runMult = runMult * mYoshiParams.mRunYoshiMult.value; + } + + f32 fwdVel = mForwardVel; + if (fwdVel <= 0.0f) { + // Accelerate from zero + mForwardVel = fwdVel + mRunParams.mVelMinusBrake.value; + } else if (fwdVel <= runMult) { + // Accelerate towards target + f32 addVelDiv = mRunParams.mAddVelDiv.value; + f32 addBase = mRunParams.mAddBase.value; + mForwardVel = fwdVel + (addBase - fwdVel * addVelDiv); + } else { + // Decelerate from above target + const TBGCheckData* ground = mGroundPlane; + if (ground->mNormal.y >= mRunParams.mDecStartNrmY.value) { + // Normal ground - no extra deceleration + } else { + f32 decBrake = mRunParams.mDecBrake.value; + mForwardVel = fwdVel - decBrake; + mForwardVel = mForwardVel - mYoshiParams.mDecBrake.value; + } + } + + if (mForwardVel < 0.0f) + mForwardVel = 0.0f; + + // Compute angle change speed based on mPumpState + s16 angleChange; + u8 isPumpState; + if (mPumpState == 0 || mPumpState == 1) + isPumpState = 1; + else + isPumpState = 0; + if (isPumpState) { + s16 minRot = mDeParams.mPumpingRotSpMin.value; + s16 maxRot = mDeParams.mPumpingRotSpMax.value; + f32 fwdSpd = mForwardVel; + f32 scale = 0.03125f; + angleChange = (s16)((f32)minRot + scale * fwdSpd * (f32)(maxRot - minRot)); + } else { + s16 minRot = mDeParams.mRunningRotSpMin.value; + s16 maxRot = mDeParams.mRunningRotSpMax.value; + f32 fwdSpd = mForwardVel; + f32 scale = 0.03125f; + angleChange = (s16)((f32)minRot + scale * fwdSpd * (f32)(maxRot - minRot)); + } + + if (isRunningInWater()) { + angleChange = (s16)((f32)angleChange * mYoshiParams.mRotYoshiMult.value); + } + + u8 isInWater; + if (mState & 0x4000) + isInWater = 1; + else + isInWater = 0; + if (isInWater) { + angleChange = mRunParams.mDashRotSp.value; + } + + u8 isInShallow; + if (mState & 0x30000) + isInShallow = 1; + else + isInShallow = 0; + if (isInShallow) { + if (mFloorPosition.y < mPosition.y + mRunParams.mSwimDepth.value) { + isInShallow = 1; + } else { + isInShallow = 0; + } + } + if (isInShallow) { + f32 depth = mFloorPosition.y - mPosition.y; + f32 swimDepth = mRunParams.mSwimDepth.value; + f32 brake = 1.0f - mRunParams.mInWaterBrake.value; + f32 ratio = depth / swimDepth; + mForwardVel = mForwardVel * (1.0f - ratio * brake); + } + + s16 yawDiff = (s16)(mIntendedYaw - mFaceAngle.y); + s16 converged = IConverge((s16)yawDiff, 0, (s16)angleChange, (s16)angleChange); + mFaceAngle.y = mIntendedYaw - converged; + + slopeProcess(); +} + +// getSurfingParamsWater - 0x8013ACA0 +TMario::TSurfingParams& TMario::getSurfingParamsWater() +{ + switch (unk389) { + case 1: + return mSurfingParamsWaterYellow; + case 2: + return mSurfingParamsWaterGreen; + default: + return mSurfingParamsWaterRed; + } +} + +// doSurfing - 0x8013B198 +void TMario::doSurfing() +{ + f32 waterHeight = mPosition.y - mVel.y; + const TBGCheckData* waterPlane = nullptr; + checkGroundPlane(mPosition.x, waterHeight, mPosition.z, nullptr, + &waterPlane); + + u16 groundType = waterPlane ? *(u16*)waterPlane : 0; + + u8 isSurfType; + if (groundType == 0x100 || groundType == 0x101 + || (u16)(groundType - 0x102) <= 3 + || groundType == 0x4104) + isSurfType = 1; + else + isSurfType = 0; + + f32 rotMin, rotMax, powMin, powMax; + u8 color = unk389; + + if (isSurfType) { + TSurfingParams& params = getSurfingParamsWater(); + rotMin = params.mRotMin.get(); + + TSurfingParams& params2 = getSurfingParamsWater(); + rotMax = params2.mRotMax.get(); + + TSurfingParams& params3 = getSurfingParamsWater(); + powMin = params3.mPowMin.get(); + + TSurfingParams& params4 = getSurfingParamsWater(); + powMax = params4.mPowMax.get(); + } else { + switch (color) { + case 1: rotMin = *(f32*)((u8*)this + 0x1BC4 + 0x18); break; + case 2: rotMin = *(f32*)((u8*)this + 0x1F6C + 0x18); break; + default: rotMin = *(f32*)((u8*)this + 0x181C + 0x18); break; + } + switch (color) { + case 1: rotMax = *(f32*)((u8*)this + 0x1BC4 + 0x2C); break; + case 2: rotMax = *(f32*)((u8*)this + 0x1F6C + 0x2C); break; + default: rotMax = *(f32*)((u8*)this + 0x181C + 0x2C); break; + } + switch (color) { + case 1: powMin = *(f32*)((u8*)this + 0x1BC4 + 0x40); break; + case 2: powMin = *(f32*)((u8*)this + 0x1F6C + 0x40); break; + default: powMin = *(f32*)((u8*)this + 0x181C + 0x40); break; + } + switch (color) { + case 1: powMax = *(f32*)((u8*)this + 0x1BC4 + 0x54); break; + case 2: powMax = *(f32*)((u8*)this + 0x1F6C + 0x54); break; + default: powMax = *(f32*)((u8*)this + 0x181C + 0x54); break; + } + } + + // Clamp intended magnitude to speed range + f32 clampedMag = 0.03125f * mIntendedMag; + f32 speedInput = clampedMag; + if (speedInput > powMax) + speedInput = powMax; + if (speedInput < powMin) + speedInput = powMin; + + // Accelerate or decelerate forward velocity + f32 fwdVel = mForwardVel; + if (fwdVel <= 0.0f) { + // Accelerate forward + mForwardVel = fwdVel + rotMin; + } else if (fwdVel <= speedInput) { + // Under target speed - check ground and accelerate + u16 gt2 = mGroundPlane ? *(u16*)mGroundPlane : 0; + u8 isSurf2; + if (gt2 == 0x100 || gt2 == 0x101 + || (u16)(gt2 - 0x102) <= 3 + || gt2 == 0x4104) + isSurf2 = 1; + else + isSurf2 = 0; + + f32 accel; + if (isSurf2) { + TSurfingParams& p = getSurfingParamsWater(); + accel = p.mAccel.get(); + } else { + switch (color) { + case 1: accel = *(f32*)((u8*)this + 0x1BC4 + 0x68); break; + case 2: accel = *(f32*)((u8*)this + 0x1F6C + 0x68); break; + default: accel = *(f32*)((u8*)this + 0x181C + 0x68); break; + } + } + + mForwardVel = fwdVel + (1.0f - fwdVel / accel); + } else { + // Over target speed - check ground slope + if (mGroundPlane->mNormal.y >= 0.0f) { + mForwardVel = fwdVel - 0.3f; + } + } + + if (mForwardVel > powMax) + mForwardVel = powMax; + + // Interpolate rotation speed + f32 rotRange = speedInput - powMin; + f32 maxRange = powMax - powMin; + f32 powRange = rotMax - rotMin; + f32 rotSpeed; + if (maxRange > 0.0f) + rotSpeed = rotMin + (rotRange / maxRange) * powRange; + else + rotSpeed = rotMin; + + s16 yawDiff = (s16)(mIntendedYaw - mFaceAngle.y); + s16 rotSpeedS16 = (s16)rotSpeed; + s16 converged = IConverge((s16)yawDiff, 0, rotSpeedS16, rotSpeedS16); + mFaceAngle.y = mIntendedYaw - converged; + + slopeProcess(); + + // Check if on surfing ground for special handling + u16 gt3 = waterPlane ? *(u16*)waterPlane : 0; + u8 isSurf3; + if (gt3 == 0x100 || gt3 == 0x101 + || (u16)(gt3 - 0x102) <= 3 + || gt3 == 0x4104) + isSurf3 = 1; + else + isSurf3 = 0; + if (isSurf3) { + postureControl(); + } +} + +void TMario::doPushingAnimation(const Vec& target) +{ + f32 dx = mPosition.x - target.x; + f32 dz = mPosition.z - target.z; + + if (mForwardVel > 6.0f) { + setPlayerVelocity(0.0f); + } + + s16 wallAngle; + s16 angleDiff; + if (mWallPlane != nullptr) { + wallAngle = getWallAngle(); + angleDiff = wallAngle - mFaceAngle.y; + } + + if (mWallPlane == nullptr) { + setAnimation(0x6c, 1.0f); + startVoice(0x7094); + return; + } + + s16 extDiff = (s16)angleDiff; + if (extDiff < -29127 || extDiff > 29127) { + setAnimation(0x6c, 1.0f); + startVoice(0x7094); + return; + } + + f32 distSq = dz * dz + dx * dx; + f32 dist; + if (distSq > 0.0f) { + f32 root = __frsqrte(distSq); + f32 refined = 0.5f * root * (3.0f - distSq * (root * root)); + dist = distSq * refined; + dist = (f32)dist; + } else { + dist = distSq; + } + + f32 speed = 2.0f * dist; + + if ((s16)angleDiff < 0) { + setAnimation(0x80, speed); + } else { + setAnimation(0x7f, speed); + } + + mFaceAngle.x = 0; + mModelFaceAngle = (s16)(wallAngle + 0x18000); +} + +// running - 0x801398B0 +void TMario::running() +{ + u16 timer = mActionTimer; + mActionTimer = timer + 1; + + // Check held object throw + TTakeActor* held = mHeldObject; + BOOL throwResult; + if (held == nullptr) { + throwResult = 0; + } else { + u8 isJumpHeld; + if (mInput & 0x2000) + isJumpHeld = 1; + else + isJumpHeld = 0; + if (!isJumpHeld) { + throwResult = 0; + } else { + u32 heldType = held->mActorType; + u8 isBitSet; + if (heldType & 0x10000000) + isBitSet = 1; + else + isBitSet = 0; + if (isBitSet) { + throwResult = changePlayerStatus(0x80000588, 0, false); + } else if (heldType == (u32)0x80000001) { + throwResult = changePlayerStatus(0x80000588, 0, false); + } else if (mForwardVel > 0.0f) { + throwResult = changePlayerStatus(0x80000588, 0, false); + } else if (canPut()) { + throwResult = changePlayerStatus(0x80000387, 0, false); + } else { + throwResult = 0; + } + } + } + + if (throwResult != 0) + return; + + // Check jump inputs + u32 input = mInput; + if (input & 0x08) { + if (mForwardVel <= 0.0f || isFrontSlip(0)) { + changePlayerStatus(0x50, 0, false); + return; + } + } + + if (input & 0x02) { + changePlayerJumping(0x0887, 0); + return; + } + + // Check stick rotate + int stickDir; + BOOL rotated = checkStickRotate(&stickDir); + if (rotated == 1 && mWaterGun != nullptr) { + if (mWaterGun->isEmitting()) { + if (stickDir > 0) { + changePlayerStatus(0x441, 0, false); + } else { + changePlayerStatus(0x442, 0, false); + } + } else { + rotated = 0; + } + } else { + rotated = 0; + } + + if (rotated != 0) + return; + + // Check crouch/slide + if (mInput & 0x20) { + changePlayerStatus(0x04000445, 0, false); + return; + } + + // Check turn + u8 isTurnable; + u32 pumpState = mPumpState; + if (pumpState == 0 || pumpState == 1) + isTurnable = 1; + else + isTurnable = 0; + u8 shouldTurn; + if (!isTurnable) { + if (isRunningTurnning()) { + shouldTurn = 0; + } else { + s16 yawDiff = (s16)(mIntendedYaw - mFaceAngle.y); + shouldTurn = 1; + if (yawDiff >= -18204 && yawDiff <= 18204) + shouldTurn = 0; + } + } else { + shouldTurn = 0; + } + + if (shouldTurn) { + if (mForwardVel >= mRunParams.mTurnNeedSp.value) { + // Set up turn wall checks + s16 turnAngle = mFaceAngle.y; + checkPlayerAround(21, (f32)(turnAngle + 0x18000)); + checkPlayerAround(23, (f32)(turnAngle + 0x18000)); + checkPlayerAround(22, (f32)(turnAngle + 0x18000)); + changePlayerStatus(0x0443, 0, false); + return; + } + } + + // Check if on dirt + u8 isDirty; + isDirty = isRunningSlipStart(); + if (isDirty) { + setPlayerVelocity(0.0f); + changePlayerStatus(0x0C018220, 0, false); + return; + } + + // Check slip start + if (isSlipStart()) { + TWaterGun* gun = mWaterGun; + f32 gunHeight = *(f32*)((u8*)gun + 0x1D40); + *(f32*)((u8*)this + 0x314) = mFloorPosition.y + gunHeight; + changePlayerStatus(0x088B, 0, false); + return; + } + + // Main running logic + s32 pushWall = 0; + mActionState = 0; + Vec prevPos; + prevPos.x = mPosition.x; + prevPos.y = mPosition.y; + prevPos.z = mPosition.z; + + doRunning(); + doRunningAnimation(); + + int walkResult = walkProcess(); + switch (walkResult) { + case 0: + changePlayerStatus(0x088C, 0, false); + setAnimation(0x56, 1.0f); + break; + case 1: + slopeProcess(); + break; + case 2: + case 3: + if (isThrowStart()) { + setPlayerVelocity(0.0f); + } + + u8 isWallBit; + if (mState & 0x4) + isWallBit = 1; + else + isWallBit = 0; + if (isWallBit) + pushWall = 1; + break; + } + + if (!pushWall) { + if (mForwardVel > mDeParams.mClashSpeed.value) { + checkPlayerAround(12, 0.0f); + changePlayerDropping(0x000208B0, 0); + return; + } + + if (mInput & 0x2) { + if (mPosition.y + mDeParams.mJumpWallHeight.value > mFloorPosition.y) { + if (mIntendedMag > 0.0f && mForwardVel > mDeParams.mDashMax.value - 1.0f) { + mVel.y = 0.0f; + changePlayerStatus(0x0080088A, 1, false); + return; + } + } + changePlayerStatus(0x384, 0, false); + return; + } + + if (mInput & 0x4) { + if (mInput & 0x4000) { + if (mForwardVel > mRunParams.mDoJumpCatchSp.value) { + changePlayerJumping(0x0888, 0); + return; + } + } + + if (mInput & 0x2) { + changePlayerTriJump(); + return; + } + + if (considerRotateStart()) + return; + + if (mInput & 0x10000) { + if (mForwardVel > mDeParams.mDashMax.value - 1.0f) { + if (mIntendedMag > 0.0f) { + mVel.y = 0.0f; + s16 turnAngle = mFaceAngle.y; + mFaceAngle.y = (s16)(turnAngle + 0x18000); + setPlayerVelocity(-1.0f); + changePlayerStatus(0x02000886, 0, false); + return; + } + } + } + + const TBGCheckData* wall = mWallPlane; + if (wall != nullptr) { + u8 isWallType; + if (*(u16*)wall == 0x10A) + isWallType = 1; + else + isWallType = 0; + if (isWallType) { + s16 wallAngle = matan(wall->mNormal.z + 52, wall->mNormal.x); + s16 faceAngle = mFaceAngle.y; + mFaceAngle.y = (s16)(wallAngle + 0x18000); + mModelFaceAngle = mFaceAngle.y; + changePlayerStatus(0x3000036B, 0, false); + return; + } + } + + doPushingAnimation(prevPos); + mFaceAngle.x = 0; + mState = mState & ~0x4000; + } + } + + doRunningAnimation(); + + u8 isInWater; + if (mState & 0x4000) + isInWater = 1; + else + isInWater = 0; + if (isInWater) { + setPlayerVelocity(mDeParams.mDashMax.value); + checkPlayerAround(25, 0.0f); + } +} + +// rotating - 0x80139E80 +void TMario::rotating() +{ + if (mInput & 0x2) { + if (mAction == 0x0441) { + changePlayerStatus(0x0896, 0, false); + return; + } else { + changePlayerStatus(0x0895, 0, false); + return; + } + } + + setAnimation(244, 1.0f); + emitRotateShootEffect(); + emitBlurSpinJump(); + + mActionTimer++; + if (mActionTimer > 120) { + changePlayerStatus(0x0C400201, 0, false); + return; + } + + doRunning(); + if (walkProcess() == 0) { + changePlayerStatus(0x088C, 0, false); + } + + if (mAction == 0x0441) { + mModelFaceAngle = (s16)(mActionTimer << 12); + } else { + mModelFaceAngle = (s16)(-(mActionTimer << 12)); + } + return; +} + +void TMario::fireDashing() +{ + if (mInput & 0x02) { + changePlayerStatus(0x000208b4, 0, false); + return; + } + + u16 timer = mActionTimer; + mActionTimer = timer + 1; + if (timer > 160) { + changePlayerStatus(0x04000440, 0, false); + return; + } + + u8 isWallHit; + if (mState & 0x00030000) + isWallHit = 1; + else + isWallHit = 0; + if (isWallHit) { + changePlayerStatus(0x04000440, 0, false); + return; + } + + if (mForwardVel < 0.0f) + mForwardVel = 0.0f; + + if (mForwardVel > 8.0f) + mForwardVel = 8.0f; + + mForwardVel = FConverge(mForwardVel, 48.0f, 32.0f, 4.0f); + + if (mInput & 0x01) { + s16 yawDiff = mIntendedYaw - mFaceAngle.y; + s16 result = IConverge((s16)yawDiff, 0, 1536, 1536); + mFaceAngle.y = mIntendedYaw - result; + } + + slopeProcess(); + int walkResult = walkProcess(); + if (walkResult == 0) { + changePlayerStatus(0x000208b5, 0, false); + } + + f32 anmSpeed = 0.5f * mForwardVel * 0.1f; + setAnimation(0x29, anmSpeed); +} + +void TMario::walkEnd() +{ + u32 input = mInput; + if (!(input & 0x10)) { + bool doJump; + if (input & 0x08) { + if (mForwardVel <= 0.1f || isFrontSlip(0)) { + doJump = true; + } else { + doJump = false; + } + } else { + doJump = false; + } + + if (doJump) { + changePlayerStatus(0x50, 0, false); + return; + } + + input = mInput; + if (input & 0x02) { + changePlayerTriJump(); + return; + } + + if (input & 0x01) { + changePlayerStatus(0x04000440, 0, false); + return; + } + + if (input & 0x8000) { + changePlayerStatus(0x384, 0, false); + return; + } + } + + int stickDir; + BOOL rotated = checkStickRotate(&stickDir); + if (rotated == 1 && mWaterGun != nullptr) { + if (mWaterGun->isEmitting()) { + if (stickDir > 0) { + changePlayerStatus(0x441, 0, false); + } else { + changePlayerStatus(0x442, 0, false); + } + } else { + rotated = 0; + } + } else { + rotated = 0; + } + + if (rotated != 0) { + // return 1 + return; + } + + s32 stopped = 0; + mForwardVel = FConverge(mForwardVel, 0.0f, 4.0f, 0.0f); + if (0.0f == mForwardVel) + stopped = 1; + + setPlayerVelocity(mForwardVel); + + if (stopped) { + changePlayerStatus(0x0C400201, 0, false); + return; + } + + int walkResult = walkProcess(); + switch (walkResult) { + case 0: + changePlayerStatus(0x088c, 0, false); + break; + case 2: + setPlayerVelocity(0.0f); + break; + default: + break; + } + + f32 anmSpeed = mForwardVel * 0.25f; + if (anmSpeed < 0.1f) + anmSpeed = 0.1f; + setAnimation(0x48, anmSpeed); +} + +void TMario::surfing() +{ + setAnimation(0x6d, 1.0f); + + if (mActionTimer > 0) { + mActionTimer = mActionTimer - 1; + return; + } + + if (mInput & 0x02) { + mPosition.y = mPosition.y + 1.0f; + changePlayerStatus(0x0281089a, 0, false); + return; + } + + doSurfing(); + walkProcess(); + int walkResult = walkProcess(); + + switch (walkResult) { + case 1: + return; + case 0: + changePlayerStatus(0x0081089b, 0, false); + return; + case 2: { + const TBGCheckData* wall = mWallPlane; + if (wall == nullptr) { + setPlayerVelocity(0.0f); + loserExec(); + return; + } + + f32 nz = wall->mNormal.z; + f32 nx = wall->mNormal.x; + s16 wallAngle = matan(nz, nx); + + s16 faceDiff = wallAngle - mFaceAngle.y; + + const TBGCheckData* ground = mGroundPlane; + u16 groundType = ground->mBGType; + + u8 isSurfType; + if (groundType == 0x100 || groundType == 0x101 + || (u16)(groundType - 0x102) <= 3 + || groundType == 0x4104) + isSurfType = 1; + else + isSurfType = 0; + + s16 clashAngle; + f32 clashSpeed; + if (isSurfType) { + u8 color = *(u8*)((u8*)this + 0x389); + switch (color) { + case 1: + clashAngle + = *(s16*)((u8*)this + 0x19F0 + 0x1D0); + break; + case 2: + clashAngle + = *(s16*)((u8*)this + 0x1D98 + 0x1D0); + break; + default: + clashAngle + = *(s16*)((u8*)this + 0x1648 + 0x1D0); + break; + } + + switch (color) { + case 1: + clashSpeed + = *(f32*)((u8*)this + 0x19F0 + 0x1BC); + break; + case 2: + clashSpeed + = *(f32*)((u8*)this + 0x1D98 + 0x1BC); + break; + default: + clashSpeed + = *(f32*)((u8*)this + 0x1648 + 0x1BC); + break; + } + } else { + u8 color = *(u8*)((u8*)this + 0x389); + switch (color) { + case 1: + clashAngle + = *(s16*)((u8*)this + 0x1BC4 + 0x1D0); + break; + case 2: + clashAngle + = *(s16*)((u8*)this + 0x1F6C + 0x1D0); + break; + default: + clashAngle + = *(s16*)((u8*)this + 0x181C + 0x1D0); + break; + } + + switch (color) { + case 1: + clashSpeed + = *(f32*)((u8*)this + 0x1BC4 + 0x1BC); + break; + case 2: + clashSpeed + = *(f32*)((u8*)this + 0x1F6C + 0x1BC); + break; + default: + clashSpeed + = *(f32*)((u8*)this + 0x181C + 0x1BC); + break; + } + } + + s16 negClash = -clashAngle; + if (faceDiff < negClash || clashAngle < faceDiff) { + if (mForwardVel > clashSpeed) { + decHP(mDeParams.mHpMax.value); + changePlayerStatus(0x000208b3, 0, true); + mForwardVel = -(0.8f * mForwardVel); + mVel.y = 0.0f; + return; + } + } + + setPlayerVelocity(0.0f); + return; + } + default: + break; + } +} + +// turnning - 0x8013A39C +void TMario::turnning() +{ + // Check held object throw + TTakeActor* held = mHeldObject; + BOOL throwResult; + if (held == nullptr) { + throwResult = 0; + } else { + u8 isJumpHeld; + if (mInput & 0x2000) + isJumpHeld = 1; + else + isJumpHeld = 0; + if (!isJumpHeld) { + throwResult = 0; + } else { + u32 heldType = held->mActorType; + u8 isBitSet; + if (heldType & 0x10000000) + isBitSet = 1; + else + isBitSet = 0; + if (isBitSet) { + throwResult = changePlayerStatus(0x80000588, 0, false); + } else if (heldType == (u32)0x80000001) { + throwResult = changePlayerStatus(0x80000588, 0, false); + } else if (mForwardVel > 0.0f) { + throwResult = changePlayerStatus(0x80000588, 0, false); + } else if (canPut()) { + throwResult = changePlayerStatus(0x80000387, 0, false); + } else { + throwResult = 0; + } + } + } + + if (throwResult != 0) + return; + + // Check jump + if (mInput & 0x08) { + changePlayerStatus(0x50, 0, false); + return; + } + + // Check B press + if (mInput & 0x02) { + changePlayerJumping(0x0887, 0); + return; + } + + // Check stick rotate + int stickDir; + BOOL rotated = checkStickRotate(&stickDir); + if (rotated == 1 && mWaterGun != nullptr) { + if (mWaterGun->isEmitting()) { + if (stickDir > 0) { + changePlayerStatus(0x441, 0, false); + } else { + changePlayerStatus(0x442, 0, false); + } + } else { + rotated = 0; + } + } else { + rotated = 0; + } + + if (rotated != 0) + return; + + // Check crouch + if (mInput & 0x20) { + changePlayerStatus(0x04000445, 0, false); + return; + } + + // Check turn state + u8 shouldStop; + u32 pumpState = mPumpState; + if (pumpState == 0 || pumpState == 1) + shouldStop = 1; + else + shouldStop = 0; + if (!shouldStop) { + if (isRunningTurnning()) { + shouldStop = 0; + } else { + s16 yawDiff = (s16)(mIntendedYaw - mFaceAngle.y); + shouldStop = 1; + if (yawDiff >= -18204 && yawDiff <= 18204) + shouldStop = 0; + } + } + + if (!shouldStop) { + changePlayerStatus(0x04000440, 0, false); + return; + } + + // Decelerate + s32 stopped = 0; + mForwardVel = FConverge(mForwardVel, 0.0f, 0.0f, 0.0f); + if (0.0f == mForwardVel) + stopped = 1; + + doRunning(); + + if (stopped) { + mFaceAngle.y = mIntendedYaw; + setPlayerVelocity(0.0f); + changePlayerStatus(0x0444, 0, false); + return; + } + + if (walkProcess() != 0) { + // walk ok + } else { + changePlayerStatus(0x088C, 0, false); + } + + if (mForwardVel >= 0.0f) { + setAnimation(0xBC, 1.0f); + } else { + setAnimation(0xBD, 1.0f); + + if (isLast1AnimeFrame()) { + if (mForwardVel > 0.0f) { + mFaceAngle.y = mIntendedYaw; + setPlayerVelocity(-mForwardVel); + changePlayerStatus(0x04000440, 0, false); + } else { + mFaceAngle.y = mIntendedYaw; + setPlayerVelocity(0.0f); + changePlayerStatus(0x04000440, 0, false); + } + } + } +} + +void TMario::turnEnd() +{ + // Part 1: check held object for throw/put + BOOL result; + TTakeActor* held = mHeldObject; + if (held == nullptr) { + result = 0; + } else { + u8 isJumpHeld; + if (mInput & 0x2000) + isJumpHeld = 1; + else + isJumpHeld = 0; + + if (!isJumpHeld) { + result = 0; + } else { + u32 heldType = held->mActorType; + + u8 isBitSet; + if (heldType & 0x10000000) + isBitSet = 1; + else + isBitSet = 0; + if (isBitSet) { + result = changePlayerStatus(0x80000588, 0, false); + } else if (heldType == (u32)0x80000001) { + result = changePlayerStatus(0x80000588, 0, false); + } else if (mForwardVel > 16.0f) { + result = changePlayerStatus(0x80000588, 0, false); + } else if (canPut()) { + result = changePlayerStatus(0x80000387, 0, false); + } else { + result = 0; + } + } + } + + if (result != 0) { + // return 1 + return; + } + + // Part 2: check input flags + u32 input = mInput; + if (input & 0x08) { + changePlayerStatus(0x50, 0, false); + return; + } + + if (input & 0x02) { + changePlayerJumping(0x887, 0); + return; + } + + // Part 3: check stick rotate + int stickDir; + BOOL rotated = checkStickRotate(&stickDir); + if (rotated == 1 && mWaterGun != nullptr) { + if (mWaterGun->isEmitting()) { + if (stickDir > 0) { + changePlayerStatus(0x441, 0, false); + } else { + changePlayerStatus(0x442, 0, false); + } + } else { + rotated = 0; + } + } else { + rotated = 0; + } + + if (rotated != 0) { + // return 1 + return; + } + + // Part 4: do running and animation + doRunning(); + setAnimation(0xbd, 1.0f); + + if (walkProcess() == 0) { + changePlayerStatus(0x088c, 0, false); + } + + if (isLast1AnimeFrame()) { + changePlayerStatus(0x04000440, 0, false); + } + + mFaceAngle.x = 0; + s16 modelAngle = mModelFaceAngle; + mModelFaceAngle = (s16)(modelAngle + 0x18000); +} + +// slippingBasic - 0x80138F00 +void TMario::slippingBasic(int statusOnStop, int slipStatus, int slipArg) +{ + slopeProcess(); + + // Check for jump/crouch to enter fire slide + if (mInput & 0x2) { + if (walkProcess() == 1) { + changePlayerStatus(0x02000880, 0, false); + return; + } + } + + // Check for L trigger water landing + if (mInput & 0x10000) { + mVel.y = 0.0f; + changePlayerStatus(0x0080088A, 1, false); + return; + } + + int walkResult = walkProcess(); + switch (walkResult) { + case 0: + changePlayerStatus(slipStatus, 0, false); + return; + case 1: + setAnimation(0x6C, 1.0f); + *(u16*)((u8*)this + 0x114) = *(u16*)((u8*)this + 0x114) | 0x8; + loserExec(); + return; + case 2: + case 3: + if (isThrowStart()) { + setPlayerVelocity(0.0f); + } + break; + } + + // Check speed for down state + f32 fwdVel = mForwardVel; + if (fwdVel < 0.0f) + fwdVel = -fwdVel; + if (fwdVel > mDeParams.mClashSpeed.value) { + checkPlayerAround(12, 0.0f); + } + + if (isSlipStart()) { + const TBGCheckData* wall = mWallPlane; + if (wall != nullptr) { + s16 wallAngle = matan(wall->mNormal.z + 52, wall->mNormal.x); + + f32 svx = mSlideVelX; + f32 svz = mSlideVelZ; + f32 velSq = svx * svx + svz * svz; + f32 speed; + if (velSq > 0.0f) { + f32 root = __frsqrte(velSq); + f32 refined = 0.5f * root * (3.0f - velSq * (root * root)); + speed = velSq * refined; + speed = (f32)speed; + } else { + speed = velSq; + } + + f32 speedHalf = speed * 0.5f; + if (speedHalf < 0.0f) + speedHalf = 0.0f; + + s16 slideDirAngle = mSlideAngle; + s16 wallDiff = (s16)(slideDirAngle - wallAngle); + s16 wallDiffExt = (s16)wallDiff; + s16 newAngle = (s16)(wallAngle - wallDiffExt + 0x18000); + mSlideAngle = newAngle; + + u16 uAngle = mSlideAngle; + mSlideVelX = speedHalf * JMASSin(uAngle); + mVel.x = mSlideVelX; + + uAngle = mSlideAngle; + mSlideVelZ = speedHalf * JMASCos(uAngle); + mVel.z = mSlideVelZ; + + // Play slip sound + u8 groundAttr = mWallPlane ? ((u8*)mWallPlane)[6] : 0; + s32 soundId; + soundId = *(s32*)((u8*)gpMSound + 0); + if (gpMSound->gateCheck(soundId)) { + MSoundSESystem::MSoundSE::startSoundActor(soundId, (const Vec*)&mPosition, + 0, nullptr, 0, 4); + } + } + } else { + if (mForwardVel > 0.0f) { + checkPlayerAround(1, 0.0f); + changePlayerDropping(0x00020466, 0); + } else { + setPlayerVelocity(0.0f); + changePlayerStatus(statusOnStop, 0, false); + } + } + + *(u16*)((u8*)this + 0x114) = *(u16*)((u8*)this + 0x114) | 0x8; +} + +// slipForeCommon - 0x80138D30 +void TMario::slipForeCommon(int statusOnStop, int jumpStatus, int slipStatus, int slipArg) +{ + if (mActionTimer > 20 && canSlipJump()) { + if (mInput & 0x2) { + changePlayerJumping(jumpStatus, 0); + return; + } + } else { + mActionTimer++; + } + + f32 slideStop = getSlideStopNormal(); + if (doSliding(slideStop)) { + changePlayerStatus(statusOnStop, 0, false); + } else { + slippingBasic(statusOnStop, slipStatus, slipArg); + } + return; +} + +// slipBackCommon - 0x80138C50 +void TMario::slipBackCommon(int statusOnStop, int slipStatus, int slipArg) +{ + u16 timer = mActionTimer; + if (timer > 20) { + u32 input = mInput; + if (!(input & 0x8)) { + if (input & 0x2) { + if (canSlipJump()) { + changePlayerDropping(0x08A6, 0); + return; + } + } + } + } else { + mActionTimer = timer + 1; + } + + f32 slideStop = getSlideStopNormal(); + if (doSliding(slideStop)) { + changePlayerStatus(statusOnStop, 0, false); + } else { + slippingBasic(statusOnStop, slipStatus, slipArg); + } + return; +} + +// catching - 0x80138AFC +void TMario::catching() +{ + u32 input = mInput; + if (!(input & 0x8)) { + if (input & 0x2) { + if (mForwardVel > mJumpParams.mRotBroadEnableV.get()) { + changePlayerStatus(0x02000889, 0, false); + return; + } else { + changePlayerStatus(0x08A6, 0, false); + return; + } + } + } + + u8 onWater; + if (mState & 0x10) { + onWater = 1; + } else { + onWater = 0; + } + if (onWater) { + mActionState = 1; + } + + f32 slideStop = getSlideStopCatch(); + if (doSliding(slideStop)) { + setPlayerVelocity(0.0f); + changePlayerStatus(902, 0, false); + return; + } + + slippingBasic(902, 0x088C, 136); + + if (gpMSound->gateCheck(0x1009)) { + MSoundSESystem::MSoundSE::startSoundActor(0x1009, (const Vec*)&mPosition, 0, + nullptr, 0, 4); + } + + J3DFrameCtrl& frameCtrl = getMotionFrameCtrl(); + if (frameCtrl.getFrame() > 45.0f) { + getMotionFrameCtrl().setFrame(45.0f); + } + return; +} + +// oilRun - 0x80138530 +void TMario::oilRun() +{ + u32 input = mInput; + + // Check B button for jump + if (input & 0x2) { + setPlayerVelocity(0.0f); + changePlayerJumping(0x02000880, 0); + return; + } + + // Check L trigger for water landing + if (input & 0x10000) { + mVel.y = 0.0f; + changePlayerStatus(0x0080088A, 1, false); + return; + } + + // Check if velocity is very small + f32 negThresh = -1.0f; + f32 velX = mVel.x; + if (negThresh < velX && velX < 0.0f) { + f32 velZ = mVel.z; + if (negThresh < velZ && velZ < 0.0f) { + setPlayerVelocity(0.0f); + changePlayerStatus(0x0C400201, 0, false); + return; + } + } + + // Stamp pollution + gpPollution->stamp(mDirtyParams.mPolSizeRun.value, mPosition.x, mPosition.y, + mPosition.z, 1); + + // Compute angle rotation + s16 rotSpeed = mDirtyParams.mSlipRotate.value; + s16 yawDiff = (s16)(mIntendedYaw - mFaceAngle.y); + s16 converged = IConverge((s16)yawDiff, 0, (s16)rotSpeed, (s16)rotSpeed); + mFaceAngle.y = mIntendedYaw - converged; + + // Apply stick input to velocity + u16 stickAngle = mFaceAngle.y; + f32 sinVal = JMASSin(stickAngle); + f32 stickMult = mDirtyParams.mSlipRunSp.value; + mVel.x = mVel.x + mIntendedMag * sinVal * stickMult; + + u16 stickAngle2 = mFaceAngle.y; + f32 cosVal = JMASCos(stickAngle2); + mVel.z = mVel.z + mIntendedMag * cosVal * stickMult; + + // Decrement slip timer + s16 slipTimer = *(s16*)((u8*)this + 0x13C); + *(s16*)((u8*)this + 0x13C) = slipTimer - 1; + if (*(s16*)((u8*)this + 0x13C) <= 0) { + *(s16*)((u8*)this + 0x13C) = 0; + *(f32*)((u8*)this + 0x138) = 0.0f; + } + + // Apply friction + mVel.x = mVel.x * *(f32*)((u8*)this + 0x138); + mVel.z = mVel.z * *(f32*)((u8*)this + 0x138); + + // Reset forward velocity and slide components + mForwardVel = 0.0f; + mSlideVelX = 0.0f; + mSlideVelZ = 0.0f; + + // Check if stationary + f32 mag = mIntendedMag; + if (0.0f == mag) { + u8 isInWater; + if (mState & 0x4000) + isInWater = 1; + else + isInWater = 0; + if (isInWater) { + setAnimation(0x98, 1.0f); + } else { + setAnimation(0xC3, 1.0f); + } + } else { + f32 anmSpeed = 0.5f * mag * mDirtyParams.mSlipAnmSpeed.value; + setAnimation(0x72, anmSpeed); + startVoice(30931); + + if (gpMSound->gateCheck(0x1001)) { + MSoundSESystem::MSoundSE::startSoundActor( + 0x1001, (Vec*)&mPosition, 0, nullptr, 0, 4); + } + } + + int walkResult = walkProcess(); + if (walkResult == 0) { + changePlayerStatus(0x088C, 0, false); + return; + } +} + +void TMario::oilSlip() +{ + if (mInput & 0x02) { + setPlayerVelocity(0.0f); + changePlayerJumping(0x02000880, 0); + return; + } + + // Convert s16 rotation speed param to float + s16 rotSpeed = mDirtyParams.mSlipCatchRotate.value; + s16 yawDiff = mIntendedYaw - mFaceAngle.y; + s16 targetRot = (s16)((f32)rotSpeed); + s16 converged = IConverge((s16)yawDiff, 0, (s16)targetRot, (s16)targetRot); + mFaceAngle.y = mIntendedYaw - converged; + + // Decrement slip timer + s16 slipTimer = *(s16*)((u8*)this + 0x13C); + *(s16*)((u8*)this + 0x13C) = slipTimer - 1; + + if (*(s16*)((u8*)this + 0x13C) <= 0) { + *(s16*)((u8*)this + 0x13C) = 0; + *(f32*)((u8*)this + 0x138) = 0.0f; + changePlayerStatus(0x00800456, 0, false); + } + + // Pollution stamp + gpPollution->stamp(mDirtyParams.mPolSizeSlip.value, mPosition.x, mPosition.y, + mPosition.z, 1); + + // Sound effect + if (gpMSound->gateCheck(0x1141)) { + MSoundSESystem::MSoundSE::startSoundActor( + 0x1141, (Vec*)&mPosition, 0, nullptr, 0, 4); + } + + // Compute velocity from stick input and cos table + // angle = (u16)(faceAngle.y - intendedYaw), then cos lookup + u16 stickAngle = (u16)(mFaceAngle.y - mIntendedYaw); + f32 cosVal = JMASCos(stickAngle); + f32 stickMult = mDirtyParams.mSlipCatchSp.value; + mForwardVel = mForwardVel + mIntendedMag * cosVal * stickMult; + + // Apply friction + mForwardVel = mForwardVel * *(f32*)((u8*)this + 0x138); + + setPlayerVelocity(mForwardVel); + + // Check if speed is near zero (between -1 and 1) + if (-1.0f < mForwardVel && mForwardVel < 1.0f) { + setPlayerVelocity(0.0f); + changePlayerStatus(0x386, 0, false); + return; + } + + int walkResult = walkProcess(); + if (walkResult == 0) { + changePlayerStatus(0x088c, 0, false); + return; + } + + // Clamp animation frame to 50 + J3DFrameCtrl& frameCtrl = getMotionFrameCtrl(); + if (frameCtrl.getFrame() > 50.0f) { + J3DFrameCtrl& frameCtrl2 = getMotionFrameCtrl(); + frameCtrl2.setFrame(50.0f); + } +} + +// downingCommon - 0x801384A8 +f32 TMario::downingCommon(int anmId, f32 threshold, int nextState) +{ + f32 prevFrame = setAnimation(anmId, 1.0f); + if (prevFrame < threshold) { + slopeProcess(); + mForwardVel *= 0.96f; + if (mForwardVel * mForwardVel < 1.0f) { + setPlayerVelocity(0.0f); + } + } else if (mForwardVel >= 0.0f) { + setPlayerVelocity(3.0f); + } else { + setPlayerVelocity(-3.0f); + } + + if (walkProcess() == 0) { + if (mForwardVel >= 0.0f) { + changePlayerStatus(0x000208B0, nextState, false); + } else { + changePlayerStatus(0x000208B1, nextState, false); + } + } else { + if (isLast1AnimeFrame()) { + changePlayerStatus(0x0C400201, 0, false); + } + } + return prevFrame; +} + +// loserDown - 0x80138384 +void TMario::loserDown() +{ + slopeProcess(); + mForwardVel *= 0.9f; + if (mForwardVel * mForwardVel < 1.0f) { + setPlayerVelocity(0.0f); + } + + setAnimation(275, 1.0f); + + switch (mActionState) { + case 0: + startVoice(30813); + mActionState++; + break; + case 1: + if (gpMSound->checkMarioVoicePlaying(0) != 0) + break; + mActionTimer = 0; + mActionState++; + break; + case 2: + if (mActionTimer++ > 60) { + mActionState++; + } + break; + case 3: + startVoice(30817); + mActionState++; + break; + case 4: + break; + } + return; +} + +// jumpSlipCommon - 0x8013824C +void TMario::jumpSlipCommon(short anmId, u32 status) +{ + if (mInput & 0x1) { + slopeProcess(); + mForwardVel *= 0.98f; + if (mForwardVel * mForwardVel < 1.0f) { + setPlayerVelocity(0.0f); + } + } else if (mForwardVel >= 0.0f) { + mForwardVel = FConverge(mForwardVel, 0.0f, 0.0f, 0.0f); + slopeProcess(); + } else { + mVel.x = 0.0f; + } + + int result = walkProcess(); + switch (result) { + case 0: + changePlayerStatus(status, 0, false); + break; + case 1: + case 2: + setAnimation(108, 1.0f); + break; + } + + setAnimation(anmId, 1.0f); + return; +} + +// jumpSlipEvents - 0x80138114 +BOOL TMario::jumpSlipEvents(TMario::JumpSlipRecord* record) +{ + if (mInput & 0x10) { + changePlayerStatus(record->mStatus, 0, false); + return 1; + } + + mActionTimer++; + if (mActionTimer >= record->mTimer) { + changePlayerStatus(record->mStatus, 0, false); + return 1; + } + + u32 input = mInput; + if (input & 0x2) { + u32 jumpStatus = record->mJumpStatus; + if ((jumpStatus - 0x02000000) == 0x0881) { + if (mForwardVel >= mJumpParams.mSecJumpEnableSp.get()) { + changePlayerJumping(0x02000881, 0); + return 1; + } + } + if (jumpStatus == 0x0882) { + if (mForwardVel >= mJumpParams.mTriJumpEnableSp.get()) { + changePlayerJumping(0x0882, 0); + return 1; + } + } + changePlayerJumping(0x02000880, 0); + return 1; + } else if (input & 0x4000) { + mVel.y = 0.0f; + changePlayerStatus(0x0080088A, 1, false); + return 1; + } else if (input & 0x4) { + changePlayerStatus(record->mFallbackStatus, 0, false); + return 1; + } + return 0; +} + +// moveMain - 0x80138000 +BOOL TMario::moveMain() +{ + static JumpSlipRecord sRecords[] = { + {16, 0, 0x0C000230, 0x02000881, 0x0000088C, 0x50}, + {16, 0, 0x0C000232, 0x02000881, 0x0000088C, 0x50}, + {16, 0, 0x0C000231, 0x00000882, 0x0000088C, 0x50}, + {16, 0, 0x0C000233, 0x02000880, 0x0000088C, 0x50}, + {4, 0, 0x0800023A, 0x02000880, 0x0000088C, 0x50}, + {24, 0, 0x0800023B, 0x00000888, 0x0000088C, 0x50}, + }; + + BOOL result = 0; + checkEnforceJump(); + checkController(nullptr); + + u32 action = mAction; + u8 flag; + if (action & 0x40000) + flag = 1; + else + flag = 0; + if (!flag) { + if (!(action & 0x4045C)) { + if (!(action & 0x84045D)) { + if (gpMSound->gateCheck(0x1009)) { + MSoundSESystem::MSoundSE::startSoundActor( + 0x1009, (Vec*)&mPosition, 0, nullptr, 0, 4); + } + } + } + } + + switch (mAction) { + case 0x04000440: + running(); + result = 0; + break; + case 0x0441: + case 0x0442: + rotating(); + result = 0; + break; + case 0x0443: + turnning(); + result = 0; + break; + case 0x0444: + turnEnd(); + result = 0; + break; + case 0x04000445: { + if (!(mInput & 0x10) && (mInput & 0xF)) { + result = checkAllMotions(); + break; + } + int stopped = 0; + mForwardVel = FConverge(mForwardVel, 0.0f, 4.0f, 4.0f); + if (mForwardVel == 0.0f) + stopped = 1; + slopeProcess(); + if (stopped) { + changePlayerStatus(0x0C00023D, 0, false); + } else { + int wp = walkProcess(); + switch (wp) { + case 0: + changePlayerStatus(0x088C, 0, false); + break; + case 2: + if (mForwardVel > 16.0f) { + playerRefrection(1); + changePlayerDropping(0x00020462, 0); + } else { + setPlayerVelocity(0.0f); + changePlayerStatus(0x0C00023D, 0, false); + } + break; + } + } + setAnimation(15, 1.0f); + result = 0; + break; + } + case 0x00810446: + surfing(); + result = 0; + break; + case 0x00800447: + soundTorocco(); + toroccoEffect(); + result = 0; + break; + case 0x0400044A: + walkEnd(); + result = 0; + break; + case 0x044C: { + s16 savedAngle = mFaceAngle.y; + f32 stopNormal = getSlideStopNormal(); + if (doSliding(stopNormal)) { + changePlayerStatus(0x0C00023E, 0, false); + } else { + slippingBasic(0x0C00023E, 0x0200088E, 15); + mFaceAngle.y = savedAngle; + result = 0; + } + break; + } + case 0x00020449: + fireDashing(); + result = 0; + break; + case 0x00840452: + slipForeCommon(0x0C00023E, 0x02000880, 0x0200088E, 145); + result = 0; + break; + case 0x00840453: + slipBackCommon(902, 0x088C, 137); + result = 0; + break; + case 0x00800456: + catching(); + result = 0; + break; + case 0x04808459: { + setNormalAttackArea(); + if (mInput & 0x8) { + changePlayerStatus(0x00840452, 0, false); + } else if (mInput & 0x2) { + changePlayerJumping(0x02000880, 0); + } else if (mInput & 0x10) { + changePlayerStatus(0x04000445, 0, false); + } else { + slipForeCommon(0x0C008220, 0x02000880, 0x088C, 151); + } + result = 0; + break; + } + case 0x0004045C: + oilRun(); + result = 0; + break; + case 0x0084045D: + oilSlip(); + result = 0; + break; + case 0x0004045E: { + s16 timer = *(s16*)((u8*)this + 0x13C); + *(s16*)((u8*)this + 0x13C) = timer - 1; + if (*(s16*)((u8*)this + 0x13C) <= 0) { + *(s16*)((u8*)this + 0x13C) = 0; + *(f32*)((u8*)this + 0x138) = 0.0f; + changePlayerStatus(0x00800456, 0, false); + } + gpPollution->stamp(1, mPosition.x, mPosition.y, mPosition.z, + mDirtyParams.mPolSizeSlip.value); + slipBackCommon(902, 0x088C, 137); + result = 0; + break; + } + case 0x00020460: + if (mActionTimer == 0) { + mActionTimer++; + emitParticle(12); + rumbleStart(21, mMotorParams.mMotorWall.value); + } + downingCommon(1, 86.0f, mActionArg); + result = 0; + break; + case 0x00020461: + if (mActionTimer == 0) { + mActionTimer++; + emitParticle(12); + rumbleStart(21, mMotorParams.mMotorWall.value); + } + downingCommon(44, 42.0f, mActionArg); + result = 0; + break; + case 0x00020462: + if (mActionTimer == 0) { + mActionTimer++; + emitParticle(12); + rumbleStart(21, mMotorParams.mMotorWall.value); + } + downingCommon(123, 88.0f, mActionArg); + result = 0; + break; + case 0x00020463: + if (mActionTimer == 0) { + mActionTimer++; + emitParticle(12); + rumbleStart(21, mMotorParams.mMotorWall.value); + } + downingCommon(124, 80.0f, mActionArg); + result = 0; + break; + case 0x00020464: + if (mActionTimer == 0) { + mActionTimer++; + emitParticle(12); + rumbleStart(21, mMotorParams.mMotorWall.value); + } + downingCommon(116, 200.0f, mActionArg); + result = 0; + break; + case 0x00020465: + if (mActionTimer == 0) { + mActionTimer++; + emitParticle(12); + rumbleStart(21, mMotorParams.mMotorWall.value); + } + downingCommon(117, 100.0f, mActionArg); + result = 0; + break; + case 0x00020466: + if (mActionTimer == 0) { + mActionTimer++; + emitParticle(12); + rumbleStart(21, mMotorParams.mMotorWall.value); + } + downingCommon(138, 128.0f, mActionArg); + result = 0; + break; + case 0x00020467: + loserDown(); + result = 0; + break; + case 0x04000470: + if (jumpSlipEvents(&sRecords[0])) { + result = 1; + } else { + jumpSlipCommon(78, 0x088C); + result = 0; + } + break; + case 0x04000471: + if (jumpSlipEvents(&sRecords[1])) { + result = 1; + } else { + jumpSlipCommon(87, 0x088C); + result = 0; + } + break; + case 0x04000472: + if (jumpSlipEvents(&sRecords[2])) { + result = 1; + } else { + jumpSlipCommon(75, 0x088C); + result = 0; + } + break; + case 0x04000473: + if (jumpSlipEvents(&sRecords[3])) { + result = 1; + } else { + jumpSlipCommon(190, 0x088C); + if (result != 2) { + mFaceAngle.x = 0; + s16 angle = mModelFaceAngle; + mModelFaceAngle = angle + 0x8000; + } + result = 0; + } + break; + case 0x04000478: + if (jumpSlipEvents(&sRecords[4])) { + result = 1; + } else { + jumpSlipCommon(192, 0x088C); + result = 0; + } + break; + case 0x0479: { + if (!(mInput & 0x4000)) { + mInput &= ~0x2; + } + if (jumpSlipEvents(&sRecords[5])) { + result = 1; + } else { + jumpSlipCommon(152, 0x088C); + result = 0; + } + break; + } + default: + break; + } + + return result; +} diff --git a/src/Player/MarioSpecial.cpp b/src/Player/MarioSpecial.cpp index 8b137891..5bc6af57 100644 --- a/src/Player/MarioSpecial.cpp +++ b/src/Player/MarioSpecial.cpp @@ -1 +1,2329 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +BOOL TMario::specMain() +{ + mWireBounceVelPrev = mWireBounceVel; + + f32 bounceVel = mWireBounceVel; + f32 wireSag = mWireSag; + f32 factor1 = mWireParams.mSwingRate.value; + mWireBounceVel = bounceVel - wireSag * factor1; + + f32 sag = mWireSag; + f32 vel = mWireBounceVel; + mWireSag = sag + vel; + + f32 sag2 = mWireSag; + f32 damping = mWireParams.mWireSwingBrake.value; + mWireSag = sag2 * damping; + + f32 maxBounce = mWireParams.mWireSwingMax.value; + f32 curSag = mWireSag; + f32 negMax = -maxBounce; + if (curSag < negMax) + mWireSag = negMax; + + f32 posMax = mWireParams.mWireSwingMax.value; + if (posMax < mWireSag) + mWireSag = posMax; + + switch (mAction) { + case 0x18100340: + barWait(); + break; + case 0x10100341: + // barClimb entry variant - rumble + bar process + if (mActionTimer == 0) { + rumbleStart(0x15, mMotorParams.mMotorWall.value); + mActionTimer++; + } + if ((u32)mHeldObject == 0) { + changePlayerStatus(0x208b6, 0, false); + break; + } + mForwardVel = 0.0f; + barProcess(); + { + setAnimation(6, 1.0f); + if (isLast1AnimeFrame()) { + changePlayerStatus(0x18100340, 0, false); + } + } + mFaceAngle.x = 0; + mModelFaceAngle = mFaceAngle.y; + break; + case 0x10100344: + barClimb(); + break; + case 0x00200345: + // kick roof + setAnimation(0x102, 1.0f); + kickRoofEffect(); + { + u16 timer = mActionTimer; + mActionTimer = timer + 1; + if (timer == 8) { + startVoice(0x7890); + } + } + if (isLast1AnimeFrame()) { + mPosition.y = mPosition.y + 10.0f; + mFloorPosition.y = mPosition.y; + mInput &= ~0x4; + changePlayerStatus(0x0C400201, 0, false); + break; + } + return 0; + case 0x00200346: + // fence slide anim + setAnimation(0x103, 1.0f); + if (isLast1AnimeFrame()) { + changePlayerStatus(0x00200349, 0, false); + break; + } + return 0; + case 0x00200347: + // fence kick + setAnimation(0x101, 1.0f); + { + f32 entryR1 = mAttackParamsKickRoof.mRadius.value; + mAttackRadius = entryR1; + calcEntryRadius(); + f32 entryR2 = mAttackParamsKickRoof.mHeight.value; + mAttackHeight = entryR2; + calcEntryRadius(); + } + kickRoofEffect(); + if (isLast1AnimeFrame()) { + mInput &= ~0x2; + changePlayerStatus(0x00200349, 0, false); + break; + } + return 0; + case 0x08200348: + // roof check + { + f32 roofHeight = gpMap->checkRoof(mPosition.x, mPosition.y + 30.0f, mPosition.z, &mRoofPlane); + mFloorPosition.y = roofHeight; + mActionTimer++; + if ((mInput & 1) && mActionTimer > 0x78) { + changePlayerStatus(0x00200349, 0, false); + break; + } + } + if (roofCommonEvents()) + return 1; + { + mModelFaceAngle = mFaceAngle.y; + setAnimation(0x35, 1.0f); + f32 zero = 0.0f; + mForwardVel = zero; + mSlideVelX = zero; + mSlideVelZ = zero; + f32 roofY = mFloorPosition.y; + mPosition.y = roofY - 10.0f; + mVel.z = zero; + mForwardVel = zero; + mVel.x = zero; + if (isLast1AnimeFrame()) { + changePlayerStatus(0x00200349, 0, false); + } + } + return 0; + case 0x00200349: + // roof common + if (roofCommonEvents()) + return 1; + { + u32 input = mInput; + if (input & 1) { + changePlayerStatus(0x0020054a, mActionArg, false); + break; + } + if (mActionArg & 1) { + setAnimation(0xc6, 1.0f); + } else { + setAnimation(0xc7, 1.0f); + } + f32 zero = 0.0f; + mForwardVel = zero; + mSlideVelX = zero; + mSlideVelZ = zero; + f32 roofY = mFloorPosition.y; + mPosition.y = roofY - 10.0f; + mVel.z = zero; + mForwardVel = zero; + mVel.x = zero; + } + break; + case 0x0020054a: + moveRoof(); + break; + case 0x3800034b: + hanging(); + break; + case 0x3000054c: + // hang landing wait + if (mInput & 0x4) { + startHangLanding(0x208b6); + break; + } + waitProcess(); + setAnimation(0, 1.0f); + if (isLast1AnimeFrame()) { + changePlayerStatus(0x0C400201, 0, false); + } + return 0; + case 0x3000054e: + // hang landing wait 2 + if (mInput & 0x4) { + startHangLanding(0x208b6); + break; + } + waitProcess(); + setAnimation(0x1c, 1.0f); + if (isLast1AnimeFrame()) { + changePlayerStatus(0x3800034b, 0, false); + } + return 0; + case 0x30000550: + // hang landing wait 3 + if (mInput & 0x4) { + startHangLanding(0x208b6); + break; + } + waitProcess(); + setAnimation(0x34, 1.0f); + if (isLast1AnimeFrame()) { + changePlayerStatus(0x0C400201, 0, false); + } + return 0; + case 0x10020370: + // rope position + { + MtxPtr mtx = mHeldObject->getTakingMtx(); + mPosition.x = mtx[0][3]; + mPosition.y = mtx[1][3]; + mPosition.z = mtx[2][3]; + mFaceAngle.x = 0; + mModelFaceAngle = mFaceAngle.y; + setAnimation(0x120, 1.0f); + } + return 0; + case 0x350: + wireWait(); + break; + case 0x351: + wireSWait(); + break; + case 0x352: + // wireWaitToHang R + getOnWirePosAngle(&mPosition, &mModelFaceAngle); + { + s16 angle = mModelFaceAngle; + mFaceAngle.y = angle + 0x4000; + setAnimation(0xde, 1.0f); + } + if (isLast1AnimeFrame()) { + changePlayerStatus(0x351, 0, false); + } + return 0; + case 0x353: + // wireWaitToHang L + getOnWirePosAngle(&mPosition, &mModelFaceAngle); + { + s16 angle = mModelFaceAngle; + mFaceAngle.y = angle - 0x4000; + setAnimation(0xdf, 1.0f); + } + if (isLast1AnimeFrame()) { + changePlayerStatus(0x351, 0, false); + // swap wire start/end + JGeometry::TVec3 temp; + temp = mWireStartPos; + mWireStartPos = mWireEndPos; + mWireEndPos = temp; + mWirePosRatio = 1.0f - mWirePosRatio; + } + return 0; + case 0x10000554: + // wireSWaitToHang + getOnWirePosAngle(&mPosition, &mModelFaceAngle); + { + s16 angle = mModelFaceAngle; + mFaceAngle.y = angle + 0x4000; + setAnimation(0xe1, 1.0f); + } + if (isLast1AnimeFrame()) { + u8 canHang = 0; + if ((u32)mHeldObject == 0) { + if (!onYoshi()) + canHang = 1; + } + if (canHang) { + changePlayerStatus(0x10000357, 0, false); + break; + } + changePlayerStatus(0x88c, 0, false); + } + return 0; + case 0x10000555: + // wireSWaitToHang L variant + getOnWirePosAngle(&mPosition, &mFaceAngle.y); + { + mModelFaceAngle = mFaceAngle.y; + setAnimation(0xe2, 1.0f); + } + if (isLast1AnimeFrame()) { + u8 canHang = 0; + if ((u32)mHeldObject == 0) { + if (!onYoshi()) + canHang = 1; + } + if (canHang) { + changePlayerStatus(0x10000357, 0, false); + break; + } + changePlayerStatus(0x88c, 0, false); + } + return 0; + case 0x10000556: + // wire swing enter + getOnWirePosAngle(&mPosition, &mFaceAngle.y); + { + mModelFaceAngle = mFaceAngle.y; + setAnimation(0xe4, 1.0f); + } + if (isLast1AnimeFrame()) { + changePlayerStatus(0x350, 0, false); + } + return 0; + case 0x10000357: + wireHanging(); + break; + case 0x10000359: + // wire rolling variant + wireRolling(); + break; + case 0x35b: + // wire turn L + getOnWirePosAngle(&mPosition, &mModelFaceAngle); + { + s16 angle = mModelFaceAngle; + mFaceAngle.y = angle - 0x4000; + setReverseAnimation(0xdf, 1.0f); + } + if (isAnimeLoopOrStop()) { + changePlayerStatus(0x350, 0, false); + } + return 0; + case 0x35d: + // wire turn R + getOnWirePosAngle(&mPosition, &mModelFaceAngle); + { + s16 angle = mModelFaceAngle; + mFaceAngle.y = angle + 0x4000; + setReverseAnimation(0xde, 1.0f); + } + if (isAnimeLoopOrStop()) { + changePlayerStatus(0x350, 0, false); + } + return 0; + case 0x560: + case 0x40561: + pulling(); + break; + case 0x3000036a: + fencePunch(); + break; + case 0x3000036c: + // fence walk anim + { + setAnimation(0xf9, 1.0f); + JGeometry::TVec3 nextPos; + nextPos.x = mPosition.x; + nextPos.y = mPosition.y; + nextPos.z = mPosition.z; + f32 sinVal = JMASSin(mFaceAngle.y); + f32 cosVal = JMASCos(mFaceAngle.y); + nextPos.x += 50.0f * sinVal * 1.0f; + nextPos.z += 50.0f * cosVal * 1.0f; + const TBGCheckData* wall = checkWallPlane(&nextPos, 0.0f, 0.0f); + if (wall == NULL) { + s16 angle = mFaceAngle.y; + mFaceAngle.y = angle + 0x8000; + setPlayerVelocity(0.0f); + mForwardVel = 0.0f; + changePlayerStatus(0x208b6, 0, false); + } + if (isLast1AnimeFrame()) { + f32 margin = 50.0f; + if (mFloorPosition.y + margin > mPosition.y) { + mPosition.y += margin; + } + if (mFloorPosition.y - margin < mPosition.y) { + mPosition.y -= margin; + } + changePlayerStatus(0x38000368, 0, false); + break; + } + } + return 0; + case 0x3000036b: + // fence walk anim 2 + { + setAnimation(0xfa, 1.0f); + JGeometry::TVec3 nextPos; + nextPos.x = mPosition.x; + nextPos.y = mPosition.y; + nextPos.z = mPosition.z; + f32 sinVal = JMASSin(mFaceAngle.y); + f32 cosVal = JMASCos(mFaceAngle.y); + nextPos.x += 50.0f * sinVal * 1.0f; + nextPos.z += 50.0f * cosVal * 1.0f; + const TBGCheckData* wall = checkWallPlane(&nextPos, 0.0f, 0.0f); + if (wall == NULL) { + s16 angle = mFaceAngle.y; + mFaceAngle.y = angle + 0x8000; + setPlayerVelocity(0.0f); + mForwardVel = 0.0f; + changePlayerStatus(0x208b6, 0, false); + } + if (isLast1AnimeFrame()) { + changePlayerStatus(0x38000368, 0, false); + break; + } + } + return 0; + case 0x30000569: + case 0x38000368: + fenceMove(); + break; + case 0x10100342: + break; + default: + break; + } + + return 0; +} + +BOOL TMario::fencePunch() +{ + JGeometry::TVec3 nextPos; + nextPos.x = mPosition.x; + nextPos.y = mPosition.y; + nextPos.z = mPosition.z; + f32 sinVal = JMASSin(mFaceAngle.y); + f32 cosVal = JMASCos(mFaceAngle.y); + nextPos.x += 0.5f * (50.0f * sinVal); + nextPos.z += 0.5f * (50.0f * cosVal); + const TBGCheckData* wall = checkWallPlane((Vec*)&nextPos, 80.0f, 50.0f); + + if (mInput & 0x2) { + if (gpMSound->gateCheck(0x193a)) { + MSoundSESystem::MSoundSE::startSoundActor( + 0x193a, (Vec*)&mPosition, 0, (JAISound**)0, 0, 4); + } + rumbleStart(0x15, mMotorParams.mMotorWall.value); + startJumpWall(); + return; + } + + if (wall != 0) { + mPosition = nextPos; + } else { + s16 angle = mFaceAngle.y; + mFaceAngle.y = angle + 0x8000; + mModelFaceAngle = mFaceAngle.y; + setPlayerVelocity(0.0f); + changePlayerStatus(0x88c, 0, false); + return; + } + + setAnimation(0x100, 1.0f); + f32 entryR1 = mAttackParamsFencePunch.mRadius.value; + mAttackRadius = entryR1; + calcEntryRadius(); + f32 entryR2 = mAttackParamsFencePunch.mHeight.value; + mAttackHeight = entryR2; + calcEntryRadius(); + + *(u32*)((u8*)this + 0x64) &= ~0x2; + + mModelFaceAngle = mFaceAngle.y; + + if (getMotionFrameCtrl().checkPass(5.0f)) { + emitParticle(0x39, (JGeometry::TVec3*)((u8*)this + 0x184)); + rumbleStart(0x15, mMotorParams.mMotorWall.value); + + TLiveActor* actor = *(TLiveActor**)((u8*)this + 0x2C0); + if (actor != 0) { + actor->receiveMessage(this, 3); + startVoice(0x7890); + + if (actor->mActorType - 0x40000000 == 0x6a) { + f32 val1 = *(f32*)((u8*)this + 0x2F4); + f32 val2 = *(f32*)((u8*)this + 0x2F8); + if (val1 < -120.0f) + val1 = -120.0f; + if (120.0f < val1) + val1 = 120.0f; + if (val2 < -190.0f) + val2 = -190.0f; + if (60.0f < val2) + val2 = 60.0f; + *(f32*)((u8*)this + 0x2F4) = val1; + *(f32*)((u8*)this + 0x2F8) = val2; + + Mtx ridingMtx; + getRidingMtx(ridingMtx); + PSMTXMultVec(ridingMtx, (Vec*)((u8*)this + 0x2F4), + (Vec*)&mPosition); + } + } + } + + if (isLast1AnimeFrame()) { + changePlayerStatus(0x38000368, 0, false); + return TRUE; + } + return FALSE; +} + +void TMario::fenceMove() +{ + JGeometry::TVec3 nextPos(mPosition); + nextPos.x += 0.5f * (50.0f * JMASSin(mFaceAngle.y)); + nextPos.z += 0.5f * (50.0f * JMASCos(mFaceAngle.y)); + const TBGCheckData* wall = checkWallPlane((Vec*)&nextPos, 80.0f, 50.0f); + + if (wall != NULL) { + if (mInput & 0x1) { + JGeometry::TVec3 movePos(mPosition); + + TMarioControllerWork* ctrl = (TMarioControllerWork*)unk108; + f32 fenceSpeed = mJumpParams.mFenceSpeed.value; + movePos.y += (0.015625f * ctrl->mStickV) * fenceSpeed; + + s16 camAngle = *(s16*)((u8*)gpCamera + 0x258); + s16 angleDiff = (s16)(mFaceAngle.y - camAngle); + + f32 normalZ, normalX; + if (angleDiff > -16384 && angleDiff < 16384) { + normalZ = -wall->mNormal.z; + normalX = wall->mNormal.x; + } else { + normalZ = wall->mNormal.z; + normalX = -wall->mNormal.x; + } + + f32 moveScale = 0.015625f * ctrl->mStickH; + f32 moveX = normalZ * moveScale; + f32 moveZ = normalX * moveScale; + movePos.x += moveX * fenceSpeed; + movePos.z += moveZ * fenceSpeed; + + JGeometry::TVec3 checkPos(movePos); + checkPos.x += 0.5f * (50.0f * JMASSin(mFaceAngle.y)); + checkPos.z += 0.5f * (50.0f * JMASCos(mFaceAngle.y)); + + JGeometry::TVec3 checkPos2(checkPos); + const TBGCheckData* wall2 = checkWallPlane((Vec*)&checkPos, 20.0f, 50.0f); + const TBGCheckData* wall3 = checkWallPlane((Vec*)&checkPos2, 140.0f, 50.0f); + + if (wall2 != NULL) { + u8 isFence = (wall2->mBGType == 0x10a) ? 1 : 0; + if (isFence) { + if (wall3 != NULL) { + u8 isFence2 = (wall3->mBGType == 0x10a) ? 1 : 0; + if (isFence2) { + if (mAction == 0x38000368) { + changePlayerStatus(0x30000569, 0, false); + } + mPosition = movePos; + } + } + } + } + } else { + setAnimation(0xfb, 1.0f); + changePlayerStatus(0x38000368, 0, false); + } + + s16 newAngle = (s16)(matan(wall->mNormal.z, wall->mNormal.x) + 0x8000); + mFaceAngle.y = newAngle; + } else { + s16 angle = mFaceAngle.y; + mFaceAngle.y = angle + 0x8000; + mModelFaceAngle = mFaceAngle.y; + setPlayerVelocity(0.0f); + changePlayerStatus(0x88c, 0, false); + return; + } + + mModelFaceAngle = mFaceAngle.y; + + mPosition.x += 0.5f * (50.0f * JMASSin(mFaceAngle.y)); + mPosition.z += 0.5f * (50.0f * JMASCos(mFaceAngle.y)); + + mVel.x = 0.0f; + mVel.y = 0.0f; + mVel.z = 0.0f; + + int jumpResult = jumpProcess(1); + + switch (jumpResult) { + case 1: { + s16 angle2 = mFaceAngle.y; + mFaceAngle.y = angle2 + 0x8000; + changePlayerStatus(0x88c, 0, false); + return; + } + case 3: { + mPosition.x += 50.0f * JMASSin(mFaceAngle.y); + mPosition.y = 1.0f + mFloorPosition.y; + mPosition.z += 50.0f * JMASCos(mFaceAngle.y); + setAnimation(0, 1.0f); + mInput &= ~0x4; + changePlayerDropping(0x3000054c, 0); + return; + } + } + + if (mRoofPlane != NULL) { + f32 margin = 160.0f; + f32 roofY = mFloorPosition.x; + if (roofY < margin + mPosition.y) { + mPosition.y = roofY - margin; + changePlayerStatus(0x38000368, 0, false); + } + } + + if (mInput & 0x2) { + if (gpMSound->gateCheck(0x193a)) { + MSoundSESystem::MSoundSE::startSoundActor( + 0x193a, (Vec*)&mPosition, 0, (JAISound**)0, 0, 4); + } + rumbleStart(0x15, mMotorParams.mMotorWall.value); + startJumpWall(); + return; + } + + if (mInput & 0x8000) { + mInput &= ~0x8000; + changePlayerStatus(0x3000036a, 0, false); + return; + } + + if (mIntendedMag <= 0.0f) + return; + + f32 f31, f30; + if (mRidingActor == NULL) { + f32 fenceSin = JMASSin(mFaceAngle.y); + f32 fenceCos = JMASCos(mFaceAngle.y); + f30 = mPosition.y - unk29C.y; + + JGeometry::TVec3 posOffset(mPosition); + posOffset.sub(unk29C); + + JGeometry::TVec3 posOffset2(posOffset); + + f32 c0 = 0.0f * fenceSin; + f32 cross1 = 0.0f * fenceCos - c0; + f32 cross2 = 1.0f * fenceSin - 0.0f; + f32 cross3 = c0 - 0.0f; + f32 cross4 = 0.0f - 1.0f * fenceCos; + + f32 dotY = posOffset2.y * posOffset2.y; + f32 d1 = cross1 * posOffset2.y; + f32 dotXY = posOffset2.x * posOffset2.x + dotY; + f32 d2 = cross2 * posOffset2.x + d1; + f32 lenSq = posOffset2.z * posOffset2.z + dotXY; + f31 = cross4 * posOffset2.z + d2; + + f32 dist; + if (lenSq <= 0.0f) { + dist = lenSq; + } else { + f32 rsqrt = __frsqrte(lenSq); + rsqrt = (f32)rsqrt; + f32 est = rsqrt * rsqrt; + f32 halfRsqrt = 0.5f * rsqrt; + f32 correction = -(lenSq * est - 3.0f); + f32 refined = halfRsqrt * correction; + dist = lenSq * refined; + } + (void)dist; + } else { + s16 faceAngle = mFaceAngle.y; + f32 angleFloat = (f32)faceAngle; + f32 rotAngle = *(f32*)((u8*)this + 0x30C); + + f32 yTop = *(f32*)((u8*)this + 0x2F8); + f32 yBot = *(f32*)((u8*)this + 0x304); + f32 xTop = *(f32*)((u8*)this + 0x2F4); + f32 xBot = *(f32*)((u8*)this + 0x300); + f32 zBot = *(f32*)((u8*)this + 0x308); + + f30 = yTop - yBot; + f31 = xTop - xBot; + + s16 relAngle = (s16)((65536.0f / 360.0f) * rotAngle - angleFloat); + if (relAngle != 0) + f31 = -f31; + + JGeometry::TVec3 fencePos(*(JGeometry::TVec3*)((u8*)this + 0x2F4)); + fencePos.x -= xBot; + fencePos.y -= yBot; + fencePos.z -= zBot; + + JGeometry::TVec3 distVec(fencePos); + f32 dist = JGeometry::TUtil::sqrt( + distVec.x * distVec.x + distVec.y * distVec.y + + distVec.z * distVec.z); + } + + f32 absF31 = f31; + if (f31 < 0.0f) + absF31 = -f31; + f32 absF30 = f30; + if (f30 < 0.0f) + absF30 = -f30; + + if (absF30 > 5.0f * absF31) { + if (f30 > 0.0f) { + setAnimation(254, 0.0f); + } else { + setAnimation(255, 0.0f); + } + } else { + if (f31 > 0.0f) { + setAnimation(252, 0.0f); + } else { + setAnimation(253, 0.0f); + } + } +} + +void TMario::pulling() +{ + u32 input = mInput; + + // Check if B button pressed (bit 29 = 0x4) + if (input & 0x4) { + mHeldObject->receiveMessage(this, 8); + mHeldObject = 0; + startVoice(0x78e0); + changePlayerStatus(0x88c, 0, false); + return; + } + + // Check if held object is still valid (flag check at 0x108 offset field) + u32* ptr108 = *(u32**)((u8*)this + 0x108); + if (!(*(u32*)((u8*)ptr108 + 0x4) & 0x200)) { + mHeldObject->receiveMessage(this, 8); + mHeldObject = 0; + startVoice(0x78e0); + changePlayerStatus(0x0C00022F, 0, false); + return; + } + + // Check A button (bit 30 = 0x2) + if (input & 0x2) { + setAnimation(0xf0, 1.0f); + changePlayerJumping(0x894, 0); + return; + } + + // Check if action is 0x40561 and specific flag + if (mAction == 0x40561) { + u32 flags118 = mState; + u8 isGrounded; + if (flags118 & 0x40) { + isGrounded = 1; + } else { + isGrounded = 0; + } + if (!isGrounded) { + changePlayerStatus(0x560, 0, false); + } + } + + // Get matrix from held object and compute facing angle + MtxPtr heldMtx = mHeldObject->getTakingMtx(); + f32 negCol2z = -heldMtx[2][2]; + f32 negCol0z = -heldMtx[0][2]; + s16 angle = matan(negCol2z, negCol0z); + mFaceAngle.y = angle; + mModelFaceAngle = mFaceAngle.y; + + // Build next position + JGeometry::TVec3 nextPos; + nextPos.x = mPosition.x; + nextPos.y = mPosition.y; + nextPos.z = mPosition.z; + + // Compute pull direction + s16 pullAngle = mFaceAngle.y + 0x8000; + s16 pullDiff = pullAngle - mIntendedYaw; + + f32 cosDirection = JMASCos(pullDiff); + if (cosDirection < 0.0f) + cosDirection = 0.0f; + f32 sinDirection = JMASSin(pullDiff); + cosDirection = cosDirection * mIntendedMag; + sinDirection = sinDirection * mIntendedMag; + + f32 outSpeed; + f32 outAccel; + getCurrentPullParams(&outSpeed, &outAccel); + + // Compute movement from trig tables + s16 pullAngleU = (u16)pullAngle; + f32 cosAngle = JMASCos(pullAngleU); + f32 sinAngle = JMASSin(pullAngleU); + + nextPos.x = nextPos.x + (outAccel * (cosDirection * sinAngle) - outSpeed * (sinDirection * cosAngle)); + nextPos.z = nextPos.z + (outAccel * (cosDirection * cosAngle) + outSpeed * (sinDirection * sinAngle)); + + // Check if held object accepts the position + u32 moveResult = mHeldObject->receiveMessage(this, 0); + // vtable call at offset 0xAC + if (moveResult == 1) { + mPosition = nextPos; + } + + stopProcess(); + + f32 animSpeed = 1.0f; + if (mAction == 0x40561) { + animSpeed = 5.0f; + } + + u16 currentAnim = *(u16*)((u8*)this + 0xFA); + if (currentAnim == 0xf2 || currentAnim == 0xea) { + if (isLast1AnimeFrame()) { + setAnimation(0xeb, 1.0f); + } + return; + } + + // Check actor type for pull direction + TTakeActor* heldObj = mHeldObject; + u8 isTree; + if (heldObj->mActorType - 0x08000000 == 0x6) { + isTree = 1; + } else { + isTree = 0; + } + if (!isTree) { + if (heldObj->mActorType - 0x08000000 == 0x8) { + isTree = 1; + } else { + isTree = 0; + } + } + + JGeometry::TVec3 diff; + if (isTree) { + diff = nextPos; + diff.sub(unk29C); + } else { + diff.x = mPosition.x; + diff.y = mPosition.y; + diff.z = mPosition.z; + diff.sub(unk29C); + } + + // Compute distance using Newton-Raphson sqrt + JGeometry::TVec3 dir = diff; + f32 distSq = dir.y * dir.y + dir.x * dir.x + dir.z * dir.z; + f32 dist; + if (distSq <= 0.0f) { + dist = distSq; + } else { + f32 guess = __frsqrte(distSq); + guess = (f32)guess; + dist = distSq * (0.5f * guess * (3.0f - distSq * guess * guess)); + } + + if (dist < 1.0f) { + setAnimation(0xeb, animSpeed); + return; + } + + // Determine pull animation based on angle difference + f32 pullZ = diff.z; + f32 pullX = diff.x; + s16 pullDir = matan(pullZ, pullX); + s16 faceDiff = pullDir - mFaceAngle.y; + + if (faceDiff >= -0x2000 && faceDiff <= 0x2000) { + setAnimation(0xef, animSpeed); + } + if (faceDiff <= -0x6000 || faceDiff >= 0x6000) { + setAnimation(0xec, animSpeed); + } + if (faceDiff > -0x6000 && faceDiff < -0x2000) { + setAnimation(0xed, animSpeed); + } + if (faceDiff > 0x2000 && faceDiff < 0x6000) { + setAnimation(0xee, animSpeed); + } +} + +void TMario::getCurrentPullParams(f32* outSpeed, f32* outAccel) +{ + u32 actorType = mHeldObject->mActorType; + f32* params = 0; + + switch (actorType) { + case 0x08000008: + params = (f32*)((u8*)this + 0x147c); + break; + case 0x08000007: + break; + case 0x08000006: + params = (f32*)((u8*)this + 0x14d4); + break; + case 0x0800000d: + params = (f32*)((u8*)this + 0x152c); + break; + case 0x10000028: + params = (f32*)((u8*)this + 0x1584); + break; + } + + if (params == NULL) + return; + + if (mAction == 0x560) { + *outSpeed = *(f32*)((u8*)params + 0x18); + *outAccel = *(f32*)((u8*)params + 0x2c); + } else { + *outSpeed = *(f32*)((u8*)params + 0x40); + *outAccel = *(f32*)((u8*)params + 0x54); + } +} + +void TMario::wireRolling() +{ + s16 savedAngle = mFaceAngle.x; + + JGeometry::TVec3 startPos = mWireStartPos; + JGeometry::TVec3 diff = mWireEndPos; + diff.sub(startPos); + + JGeometry::TVec3 dir = diff; + f32 ratio = mWirePosRatio; + + JGeometry::TVec3 scaled = dir; + scaled.scale(ratio); + + JGeometry::TVec3 wirePos = startPos; + wirePos.add(scaled); + + mPosition.x = wirePos.x; + mPosition.y = wirePos.y; + mPosition.z = wirePos.z; + mPosition.y = mPosition.y - 160.0f; + + Mtx mtxX; + J3DGetTranslateRotateMtx(mFaceAngle.x, 0, 0, 0.0f, 0.0f, 0.0f, mtxX); + Mtx mtxY; + J3DGetTranslateRotateMtx(0, mFaceAngle.y, 0, 0.0f, 0.0f, 0.0f, mtxY); + Mtx mtxResult; + PSMTXConcat(mtxY, mtxX, mtxResult); + + Vec sagOffset; + sagOffset.x = 0.0f; + f32 negSag = -mWireSag; + sagOffset.y = negSag * 1.0f; + sagOffset.z = 0.0f; + Vec sagResult; + PSMTXMultVec(mtxResult, &sagOffset, &sagResult); + + mPosition.x = mPosition.x + sagResult.x; + mPosition.y = mPosition.y + sagResult.y; + mPosition.z = mPosition.z + sagResult.z; + + s16 wireAngle = matan(dir.z, dir.x); + + if (mInput & 0x2) { + mActionState |= 1; + } + + u16 actionState = mActionState; + s16 maxRotSpeed = mWireParams.mRotSpeedMax.get(); + u8 bit0 = actionState & 1; + + if (bit0 && (s16)unkF6 < 0 && mFaceAngle.x > -8192 + && mFaceAngle.x <= maxRotSpeed - 8192) { + changePlayerStatus(0x893, 0, false); + return; + } + + if (bit0 && (s16)unkF6 > 0 && (24576 - maxRotSpeed) <= mFaceAngle.x + && mFaceAngle.x < 24576) { + changePlayerStatus(0x893, 1, false); + return; + } + + if (mInput & 0x1) { + s16 angleDiff = (s16)wireAngle - mIntendedYaw; + s16 diff16 = angleDiff; + if (diff16 > -8192 && diff16 < 13653) { + wireMove(3.0f); + } + if (diff16 < -24576 || diff16 > 19114) { + wireMove(-3.0f); + } + } + + if (mPumpState == 0) { + TWaterGun* waterGun = mWaterGun; + if (waterGun != NULL) { + u8 emitting; + if (waterGun->mCurrentWater == 0) { + emitting = 0; + } else { + s32 kind = waterGun->getCurrentNozzle()->getNozzleKind(); + if (kind == 1) { + TNozzleTrigger* triggerNozzle + = (TNozzleTrigger*)waterGun->getCurrentNozzle(); + if (triggerNozzle->unk385 == 1) { + emitting = 1; + } else { + emitting = 0; + } + } else if (waterGun->getCurrentNozzle()->unk378 > 0.0f) { + emitting = 1; + } else { + emitting = 0; + } + } + if (emitting) { + s16 rotAccel; + if (mWaterGun == NULL) { + rotAccel = 0; + } else { + switch (mWaterGun->mCurrentNozzle) { + case 1: + rotAccel = mWireParams.mRotSpeedTrgRocket.get(); + break; + case 4: + rotAccel = mWireParams.mRotSpeedTrgHover.get(); + break; + case 5: + rotAccel = mWireParams.mRotSpeedTrgTurbo.get(); + break; + default: + rotAccel = mWireParams.mRotSpeed.get(); + break; + } + } + unkF6 = unkF6 + rotAccel; + } + } + } + + s16 gravity = mWireParams.mRotGravity.get(); + unkF6 = unkF6 - (s16)(JMASSin(mFaceAngle.x) * (f32)gravity); + + u8 hasBrake; + if (mInput & 0x4000) { + hasBrake = 1; + } else { + hasBrake = 0; + } + if (hasBrake) { + unkF6 = (s16)((f32)(s16)unkF6 * mWireParams.mRotBrake.get()); + } + + if ((s16)unkF6 < -maxRotSpeed) { + unkF6 = -maxRotSpeed; + } + if ((s16)unkF6 > maxRotSpeed) { + unkF6 = maxRotSpeed; + } + + mFaceAngle.x = mFaceAngle.x + (s16)unkF6; + mWireSag = 0.1f * __fabsf((f32)(s16)unkF6); + mWireBounceVel = 0.0f; + + if (savedAngle < -16384 && (s16)mFaceAngle.x > 16384) { + mActionState |= 2; + } else if (savedAngle >= (s16)mFaceAngle.x) { + // do nothing + } else { + mActionState &= ~2; + } + + u16 state = mActionState; + s16 soundId = 0; + u8 shouldPlay = 0; + if (state & 2) { + if ((s16)mFaceAngle.x < mWireSwingPosAngle + && mWireSwingPosAngle <= savedAngle) { + mWireQueuedSfxID = 0x381E; + soundId = 0x1817; + shouldPlay = 1; + mWireSfxTimer = mWireSfxDelay; + if (!(mActionState & 0xC)) { + if (MsRandF() < 0.5f) { + mActionState |= 4; + } else { + mActionState |= 8; + } + } + u16 sfxDir = mActionState & 0xC; + if (sfxDir == 4) { + startVoice(0x78C1); + } + if (sfxDir == 8) { + startVoice(0x78C4); + } + } + + if ((s16)mFaceAngle.x < mWireSwingNegAngle + && mWireSwingNegAngle <= savedAngle) { + soundId = 0x180F; + shouldPlay = 1; + } + + if ((s16)savedAngle > 0 && (s16)mFaceAngle.x <= 0) { + mWireQueuedSfxID = 0x381E; + mWireSfxTimer = mWireSfxDelay; + } + } else { + if ((s16)mFaceAngle.x < mWireRollAngle + && mWireRollAngle <= savedAngle) { + mWireQueuedSfxID = 0x381F; + soundId = 0x1815; + shouldPlay = 1; + mWireSfxTimer = mWireSfxDelay; + } + + s16 rollAngle = mWireRollAngle; + if (savedAngle < rollAngle + && rollAngle <= (s16)mFaceAngle.x) { + mWireQueuedSfxID = 0x3820; + soundId = 0x1816; + shouldPlay = 1; + mWireSfxTimer = mWireSfxDelay; + } + } + + if (shouldPlay == 1) { + f32 sag = mWireSag; + if (gpMSound->gateCheck(soundId)) { + MSoundSESystem::MSoundSE::startSoundActorWithInfo( + soundId, (Vec*)&mPosition, 0, sag, 0, 0, 0, 0, 4); + } + } + + blurEffect(); + + s16 rotStop = mWireParams.mRotStop.get(); + if ((s16)mFaceAngle.x > -512 && (s16)mFaceAngle.x < 512 + && (s16)unkF6 > -rotStop && (s16)unkF6 < rotStop) { + u8 canHang = 0; + mFaceAngle.x = 0; + unkF6 = 0; + mWireBounceVel = 0.0f; + mWireSag = 0.0f; + if ((u32)mHeldObject == 0) { + if (!onYoshi()) + canHang = 1; + } + if (canHang) { + changePlayerStatus(0x10000357, 0, false); + return; + } + changePlayerStatus(0x88c, 0, false); + return; + } + + mFaceAngle.y = wireAngle + 0x4000; + mModelFaceAngle = mFaceAngle.y; + setAnimation(0x33, 1.0f); +} + +void TMario::wireHanging() +{ + JGeometry::TVec3 startPos = mWireStartPos; + JGeometry::TVec3 diff = mWireEndPos; + diff.sub(startPos); + + JGeometry::TVec3 dir = diff; + JGeometry::TVec3 scaled = dir; + scaled.scale(mWirePosRatio); + + JGeometry::TVec3 wirePos = startPos; + wirePos.add(scaled); + + mPosition = wirePos; + mPosition.y = mPosition.y - 160.0f; + + Mtx mtxX; + J3DGetTranslateRotateMtx(mFaceAngle.x, 0, 0, 0.0f, 0.0f, 0.0f, mtxX); + Mtx mtxY; + J3DGetTranslateRotateMtx(0, mFaceAngle.y, 0, 0.0f, 0.0f, 0.0f, mtxY); + Mtx mtxResult; + PSMTXConcat(mtxY, mtxX, mtxResult); + + Vec sagOffset; + sagOffset.x = 0.0f; + sagOffset.y = -mWireSag * 1.0f; + sagOffset.z = 0.0f; + Vec sagResult; + PSMTXMultVec(mtxResult, &sagOffset, &sagResult); + + mPosition.x = mPosition.x + sagResult.x; + mPosition.y = mPosition.y + sagResult.y; + mPosition.z = mPosition.z + sagResult.z; + + s16 angle; + angle = matan(dir.z, dir.x); + + if ((mInput & 0x2) && (mFloorPosition.x - mFloorPosition.y >= 160.0f)) { + mWireBounceVel = 5.0f; + changePlayerStatus(0x10000556, 0, false); + return; + } + + if (mInput & 0x8000) { + mHolder->receiveMessage(this, 8); + mHolder = 0; + + const TBGCheckData* plane; + f32 zero = 0.0f; + mVel.y = zero; + mForwardVel = zero; + + mPosition.x -= 60.0f * JMASSin(mFaceAngle.y); + mPosition.z -= 60.0f * JMASCos(mFaceAngle.y); + + f32 groundY = gpMap->checkGround(mPosition.x, mPosition.y, mPosition.z, &plane); + + f32 limit = mPosition.y - 100.0f; + if (groundY < limit) { + mPosition.y = limit; + } else { + mPosition.y = groundY; + } + + changePlayerStatus(0x208ba, 0, false); + return; + } + + u8 canSpray = 0; + unkF6 = 0; + + s16 wireAngle = (s16)angle; + mFaceAngle.x = 0; + mFaceAngle.y = wireAngle + 0x4000; + mModelFaceAngle = angle; + + if (mInput & 0x1) { + s16 angleDiff = (s16)(wireAngle - mIntendedYaw); + + if (angleDiff > -0x2000 && angleDiff < 0x3555) { + int moveResult = wireMove(3.0f); + if (moveResult != 0) { + setAnimation(0xe5, 1.0f); + } else { + setAnimation(0xe3, 1.0f); + } + } + + if (angleDiff < -0x6000 || angleDiff > 0x4AAA) { + int moveResult = wireMove(-3.0f); + if (moveResult != 0) { + setAnimation(0xe6, 1.0f); + } else { + setAnimation(0xe3, 1.0f); + } + } + + if (angleDiff >= -0x6000 && angleDiff <= -0x2000) { + mWireBounceVel = 5.0f; + changePlayerStatus(0x10000556, 0, false); + return; + } + + if (angleDiff >= 0x3555 && angleDiff <= 0x4AAA) { + const TBGCheckData* plane; + f32 zero = 0.0f; + mVel.y = zero; + mForwardVel = zero; + + mPosition.x -= 60.0f * JMASSin(mFaceAngle.y); + mPosition.z -= 60.0f * JMASCos(mFaceAngle.y); + + f32 groundY = gpMap->checkGround(mPosition.x, mPosition.y, mPosition.z, &plane); + + f32 limit = mPosition.y - 100.0f; + if (groundY < limit) { + mPosition.y = limit; + } else { + mPosition.y = groundY; + } + + changePlayerStatus(0x208ba, 0, false); + } + } else { + if (mPumpState == 0) { + TWaterGun* waterGun = mWaterGun; + if (waterGun != 0) { + if (waterGun->mCurrentWater != 0) { + s32 kind = waterGun->getCurrentNozzle()->getNozzleKind(); + if (kind == 1) { + if (((TNozzleTrigger*)waterGun->getCurrentNozzle())->unk385 == 1) { + canSpray = 1; + } + } else { + if (waterGun->getCurrentNozzle()->unk378 > 0.0f) { + canSpray = 1; + } + } + } + + if (canSpray) { + s16 rotSpeed; + if (mWaterGun == 0) { + rotSpeed = 0; + } else { + switch (mWaterGun->mCurrentNozzle) { + case TWaterGun::Rocket: + rotSpeed = mWireParams.mRotSpeedTrgRocket.get(); + break; + case TWaterGun::Hover: + rotSpeed = mWireParams.mRotSpeedTrgHover.get(); + break; + case TWaterGun::Turbo: + rotSpeed = mWireParams.mRotSpeedTrgTurbo.get(); + break; + default: + rotSpeed = mWireParams.mRotSpeed.get(); + break; + } + } + + if (rotSpeed > 0) { + unkF6 = unkF6 + mWireParams.mRotStop.get() * 2; + } else { + unkF6 = unkF6 - mWireParams.mRotStop.get() * 2; + } + + s16 rotSpeed2; + if (mWaterGun == 0) { + rotSpeed2 = 0; + } else { + switch (mWaterGun->mCurrentNozzle) { + case TWaterGun::Rocket: + rotSpeed2 = mWireParams.mRotSpeedTrgRocket.get(); + break; + case TWaterGun::Hover: + rotSpeed2 = mWireParams.mRotSpeedTrgHover.get(); + break; + case TWaterGun::Turbo: + rotSpeed2 = mWireParams.mRotSpeedTrgTurbo.get(); + break; + default: + rotSpeed2 = mWireParams.mRotSpeed.get(); + break; + } + } + + unkF6 = unkF6 + rotSpeed2; + changePlayerStatus(0x10000358, 0, false); + return; + } + } + } + + setAnimation(0xe3, 1.0f); + } +} + +void TMario::changeWireHanging() +{ + u8 canHang = 0; + if (mHeldObject == 0) { + if (!onYoshi()) + canHang = 1; + } + if (canHang) { + changePlayerStatus(0x10000357, 0, false); + } else { + changePlayerStatus(0x88c, 0, false); + } +} + +void TMario::wireWait() +{ + s16 angle; + // getOnWirePosAngle inlined + { + JGeometry::TVec3 startPos = mWireStartPos; + JGeometry::TVec3 diff = mWireEndPos; + diff.sub(startPos); + + JGeometry::TVec3 dir = diff; + JGeometry::TVec3 scaled = dir; + scaled.scale(mWirePosRatio); + + JGeometry::TVec3 wirePos = startPos; + wirePos.add(scaled); + + mPosition = wirePos; + mPosition.y = mPosition.y - 160.0f; + + Mtx mtxX; + J3DGetTranslateRotateMtx(mFaceAngle.x, 0, 0, 0.0f, 0.0f, 0.0f, mtxX); + Mtx mtxY; + J3DGetTranslateRotateMtx(0, mFaceAngle.y, 0, 0.0f, 0.0f, 0.0f, mtxY); + Mtx mtxResult; + PSMTXConcat(mtxY, mtxX, mtxResult); + + Vec sagOffset; + sagOffset.x = 0.0f; + sagOffset.y = -mWireSag * 1.0f; + sagOffset.z = 0.0f; + Vec sagResult; + PSMTXMultVec(mtxResult, &sagOffset, &sagResult); + + mPosition.x = mPosition.x + sagResult.x; + mPosition.y = mPosition.y + sagResult.y; + mPosition.z = mPosition.z + sagResult.z; + + angle = matan(dir.z, dir.x); + } + + if (mInput & 0x2) { + mState |= 0x100; + } + + u8 onWire; + if (mState & 0x100) { + onWire = 1; + } else { + onWire = 0; + } + + if (onWire && mWireSag <= 0.0f) { + mHolder->receiveMessage(this, 8); + mHolder = 0; + changePlayerStatus(0x892, 0, false); + setPlayerVelocity(0.0f); + if (mWireBounceVel < 0.0f) { + f32 factor = mWireParams.mWireJumpMult.value; + mVel.y = -(mWireBounceVel * factor - mVel.y); + } + mVel.y = mVel.y + mWireParams.mWireJumpBase.value; + + u32 soundId; + if (mWireBounceVel < *(f32*)((u8*)this + 0x544)) { + soundId = 0x1812; + } else if (mWireBounceVel < *(f32*)((u8*)this + 0x540)) { + soundId = 0x1811; + } else { + soundId = 0x1810; + } + + f32 wireSag = mWireSag; + if (gpMSound->gateCheck(soundId)) { + MSoundSESystem::MSoundSE::startSoundActorWithInfo( + soundId, (Vec*)&mPosition, 0, wireSag, 0, 0, 0, 0, 4); + } + + unk78 &= ~0x100; + return; + } + + if (mInput & 0x8000) { + mWireBounceVel = 5.0f; + changePlayerStatus(0x10000554, 0, false); + return; + } + + s16 angleDiff; + if (mInput & 0x1) { + angleDiff = (s16)angle - mIntendedYaw; + if (angleDiff > -0x2000 && angleDiff < 0x2000) { + f32 animSpeed = 0.05f * mIntendedMag; + int moveResult = wireMove(0.1f * mIntendedMag); + if (moveResult != 0) { + if (mIntendedMag > 16.0f) { + setAnimation(0xdc, animSpeed); + } else { + setAnimation(0xdb, animSpeed); + } + } else { + setAnimation(0xdd, 1.0f); + } + } + + if (angleDiff < -0x2000) { + startVoice(0x78e0); + changePlayerStatus(0x352, 0, false); + return; + } + + if (angleDiff > 0x2000) { + startVoice(0x78e0); + changePlayerStatus(0x353, 0, false); + return; + } + } else { + setAnimation(0xdd, 1.0f); + } + + mFaceAngle.y = angle; + mModelFaceAngle = mFaceAngle.y; +} + +void TMario::wireSWait() +{ + s16 angle; + // getOnWirePosAngle inlined + { + JGeometry::TVec3 startPos = mWireStartPos; + JGeometry::TVec3 diff = mWireEndPos; + diff.sub(startPos); + + JGeometry::TVec3 dir = diff; + JGeometry::TVec3 scaled = dir; + scaled.scale(mWirePosRatio); + + JGeometry::TVec3 wirePos = startPos; + wirePos.add(scaled); + + mPosition = wirePos; + mPosition.y = mPosition.y - 160.0f; + + Mtx mtxX; + J3DGetTranslateRotateMtx(mFaceAngle.x, 0, 0, 0.0f, 0.0f, 0.0f, mtxX); + Mtx mtxY; + J3DGetTranslateRotateMtx(0, mFaceAngle.y, 0, 0.0f, 0.0f, 0.0f, mtxY); + Mtx mtxResult; + PSMTXConcat(mtxY, mtxX, mtxResult); + + Vec sagOffset; + sagOffset.x = 0.0f; + sagOffset.y = -mWireSag * 1.0f; + sagOffset.z = 0.0f; + Vec sagResult; + PSMTXMultVec(mtxResult, &sagOffset, &sagResult); + + mPosition.x = mPosition.x + sagResult.x; + mPosition.y = mPosition.y + sagResult.y; + mPosition.z = mPosition.z + sagResult.z; + + angle = matan(dir.z, dir.x); + } + + if (mInput & 0x2) { + mState |= 0x100; + } + + u8 onWire; + if (mState & 0x100) { + onWire = 1; + } else { + onWire = 0; + } + + if (onWire && mWireSag < 0.0f) { + mHolder->receiveMessage(this, 8); + mHolder = 0; + changePlayerStatus(0x892, 0, false); + setPlayerVelocity(0.0f); + if (mWireBounceVel < 0.0f) { + mVel.y = -(5.0f * mWireBounceVel - mVel.y); + } + return; + } + + if (mInput & 0x8000) { + mWireBounceVel = 5.0f; + startVoice(0x78e0); + changePlayerStatus(0x10000554, 0, false); + return; + } + + if (mInput & 0x1) { + s16 diff = (s16)angle - mIntendedYaw; + if (diff > -0x4000 && diff < 0x4000) { + changePlayerStatus(0x35c, 0, false); + return; + } + + JGeometry::TVec3 temp = mWireStartPos; + mWireStartPos = mWireEndPos; + mWireEndPos = temp; + mWirePosRatio = 1.0f - mWirePosRatio; + changePlayerStatus(0x35b, 0, false); + return; + } + + setAnimation(0xe0, 1.0f); + mModelFaceAngle = angle; + mFaceAngle.y = mModelFaceAngle + 0x4000; +} + +int TMario::wireMove(f32 rate) +{ + JGeometry::TVec3 startPos = mWireStartPos; + JGeometry::TVec3 diff = mWireEndPos; + diff.sub(startPos); + + JGeometry::TVec3 dir = diff; + + f32 lenSq = dir.squared(); + if (lenSq > 0.0f) { + lenSq = JGeometry::TUtil::sqrt(lenSq); + } + + f32 step = rate / lenSq; + f32 ratio = mWirePosRatio; + s32 ok = 1; + f32 upperLimit = 1.0f - 100.0f / lenSq; + if (ratio + step > upperLimit) { + mWirePosRatio = upperLimit; + ok = 0; + } + f32 newRatio = mWirePosRatio; + if (newRatio + step < 100.0f / lenSq) { + mWirePosRatio = 100.0f / lenSq; + ok = 0; + } + if (ok == 1) { + mWirePosRatio = mWirePosRatio + step; + } + return ok; +} + +void TMario::getOnWirePosAngle(JGeometry::TVec3* outPos, short* outAngle) +{ + JGeometry::TVec3 startPos = mWireStartPos; + JGeometry::TVec3 diff = mWireEndPos; + diff.sub(startPos); + + JGeometry::TVec3 dir = diff; + JGeometry::TVec3 scaled = dir; + scaled.scale(mWirePosRatio); + + JGeometry::TVec3 wirePos = startPos; + wirePos.add(scaled); + + *outPos = wirePos; + outPos->y = outPos->y - 160.0f; + + Mtx mtxX; + J3DGetTranslateRotateMtx(mFaceAngle.x, 0, 0, 0.0f, 0.0f, 0.0f, mtxX); + Mtx mtxY; + J3DGetTranslateRotateMtx(0, mFaceAngle.y, 0, 0.0f, 0.0f, 0.0f, mtxY); + Mtx mtxResult; + PSMTXConcat(mtxY, mtxX, mtxResult); + + Vec sagOffset; + sagOffset.x = 0.0f; + sagOffset.y = -mWireSag * 1.0f; + sagOffset.z = 0.0f; + Vec sagResult; + PSMTXMultVec(mtxResult, &sagOffset, &sagResult); + + outPos->x = outPos->x + sagResult.x; + outPos->y = outPos->y + sagResult.y; + outPos->z = outPos->z + sagResult.z; + + *outAngle = matan(dir.z, dir.x); +} + +void TMario::hanging() +{ + s32 movedToWall = 0; + const TBGCheckData* plane; + + f32 heightDiff = mFloorPosition.x - mFloorPosition.y; + u16 timer = mActionTimer; + s16 faceY = mFaceAngle.y; + s16 intYaw = mIntendedYaw; + s16 stickAngleDiff = intYaw - faceY; + u8 highEnough = (heightDiff >= 160.0f); + + if (timer == 0) { + unkF6 = 0; + } + + s16* hangTimerParam = (s16*)((u8*)this + 0x12EC); + if (mActionTimer < *hangTimerParam) { + mActionTimer = mActionTimer + 1; + } + + u32 input = mInput; + s32 shouldDrop = 0; + + if (input & 0x8004) { + shouldDrop = 1; + } else { + u16 groundType = mGroundPlane->mBGType; + u8 isType1; + if (groundType == 0x0001 || groundType == 0x4001 || groundType == 0x8001 || groundType == 0xC001) { + isType1 = 1; + } else { + isType1 = 0; + } + if (isType1) { + shouldDrop = 1; + } else { + u8 isType6; + if (groundType == 0x0006 || groundType == 0x4006 || groundType == 0x8006 || groundType == 0xC006) { + isType6 = 1; + } else { + isType6 = 0; + } + if (isType6) { + shouldDrop = 1; + } + } + } + + if (shouldDrop) { + mInput = input & ~0x4; + mInput = mInput & ~0x8000; + mVel.y = 0.0f; + mForwardVel = 0.0f; + mPosition.x -= 60.0f * JMASSin(mFaceAngle.y); + mPosition.z -= 60.0f * JMASCos(mFaceAngle.y); + f32 groundY = gpMap->checkGround(mPosition.x, mPosition.y, mPosition.z, &plane); + f32 limit = mPosition.y - 100.0f; + if (groundY < limit) { + mPosition.y = limit; + } else { + mPosition.y = groundY; + } + changePlayerStatus(0x8A7, 0, false); + return; + } + + if ((input & 0x2) && highEnough) { + startVoice(0x788F); + changePlayerStatus(0x3000054F, 0, false); + return; + } + + TBGWallCheckRecord wallCheck1; + wallCheck1.mCenter.x = mPosition.x; + wallCheck1.mCenter.y = mPosition.y - 10.0f; + wallCheck1.mCenter.z = mPosition.z; + wallCheck1.mRadius = 30.0f; + wallCheck1.mMaxResults = 4; + wallCheck1.mFlags = 0; + gpMap->isTouchedWallsAndMoveXZ(&wallCheck1); + + const TBGCheckData* bestWall = (const TBGCheckData*)0; + f32 bestDist = 50.0f; + s32 loopIdx = 0; + s32 arrayOff = 0; + for (; loopIdx < wallCheck1.mResultWallsNum; loopIdx++, arrayOff += 4) { + TBGCheckData* wall = wallCheck1.mResultWalls[loopIdx]; + s32 wallAngle = matan(wall->mNormal.z, wall->mNormal.x); + s16 diff = wallAngle - (mFaceAngle.y + 0x8000); + if (diff > -0x2000 && diff < 0x2000) { + JGeometry::TVec3 pos; + pos.x = mPosition.x; + pos.y = mPosition.y; + pos.z = mPosition.z; + f32 planeDist = wall->mNormal.y * pos.y + wall->mNormal.x * pos.x + wall->mNormal.z * pos.z + wall->mPlaneDistance; + if (planeDist < bestDist) { + bestWall = wall; + } + } + } + + if (bestWall == (const TBGCheckData*)0) { + changePlayerStatus(0x88C, 0, false); + } + + if (bestWall != (const TBGCheckData*)0) { + u8 isFence; + if (bestWall->mBGType == 0x10A) { + isFence = 1; + } else { + isFence = 0; + } + if (isFence) { + mPosition.x = mPosition.x - 40.0f * JMASSin(mFaceAngle.y); + mPosition.y = mPosition.y - 160.0f; + mPosition.z = mPosition.z - 40.0f * JMASCos(mFaceAngle.y); + changePlayerStatus(0x3000036B, 0, false); + return; + } + } + + TBGWallCheckRecord wallCheck2; + wallCheck2.mCenter.x = mPosition.x; + wallCheck2.mCenter.y = 10.0f + mPosition.y; + wallCheck2.mCenter.z = mPosition.z; + wallCheck2.mRadius = 50.0f; + wallCheck2.mMaxResults = 1; + wallCheck2.mFlags = 0; + gpMap->isTouchedWallsAndMoveXZ(&wallCheck2); + + mPosition.x = wallCheck2.mCenter.x; + mPosition.y = wallCheck2.mCenter.y; + mPosition.z = wallCheck2.mCenter.z; + + if (mActionTimer >= 40 && (mInput & 0x1)) { + s16 stickDiffExt = (s16)stickAngleDiff; + if (stickDiffExt >= -1024 && stickDiffExt <= 1024) { + if (highEnough) { + setAnimation(0, 1.0f); + changePlayerStatus(0x3000054C, 0, false); + return; + } + } + + s16 stickDiffExt2 = (s16)stickAngleDiff; + if (stickDiffExt2 > -29127 && stickDiffExt2 < 29127) { + if (mActionTimer >= mHangingParams.mRapidTime.value && wallCheck1.mResultWallsNum > 0) { + JGeometry::TVec3 targetPos; + targetPos.x = mPosition.x; + targetPos.y = mPosition.y; + targetPos.z = mPosition.z; + + f32 moveSpeed = mHangingParams.mMoveSp.value; + if (stickDiffExt2 > 1024 && stickDiffExt2 < 29127) { + f32 mag = mIntendedMag; + targetPos.x = mPosition.x - moveSpeed * (mag * bestWall->mNormal.z); + targetPos.y = mPosition.y; + targetPos.z = mPosition.z + moveSpeed * (mag * bestWall->mNormal.x); + } + if ((s16)stickAngleDiff > -29127 && (s16)stickAngleDiff < -1024) { + f32 mag = mIntendedMag; + targetPos.x = mPosition.x + moveSpeed * (mag * bestWall->mNormal.z); + targetPos.y = mPosition.y; + targetPos.z = mPosition.z - moveSpeed * (mag * bestWall->mNormal.x); + } + + const TBGCheckData* bestWall3 = (const TBGCheckData*)0; + TBGWallCheckRecord wallCheck3; + wallCheck3.mCenter.x = targetPos.x; + wallCheck3.mCenter.y = 10.0f + targetPos.y; + wallCheck3.mCenter.z = targetPos.z; + wallCheck3.mRadius = 50.0f; + wallCheck3.mMaxResults = 1; + wallCheck3.mFlags = 0; + gpMap->isTouchedWallsAndMoveXZ(&wallCheck3); + + targetPos.x = wallCheck3.mCenter.x; + targetPos.y = wallCheck3.mCenter.y; + targetPos.z = wallCheck3.mCenter.z; + + f32 targetGroundY = gpMap->checkGround(targetPos.x, 10.0f + targetPos.y, targetPos.z, &plane); + f32 f31Save = targetPos.z; + + f32 limit1 = mPosition.y - 100.0f; + if (limit1 < targetGroundY && targetGroundY < 50.0f + mPosition.y) { + TBGWallCheckRecord wallCheck4; + wallCheck4.mCenter.x = targetPos.x - 20.0f * JMASSin(mFaceAngle.y); + wallCheck4.mCenter.y = targetGroundY - 20.0f; + wallCheck4.mCenter.z = f31Save - 20.0f * JMASCos(mFaceAngle.y); + wallCheck4.mRadius = 30.0f; + wallCheck4.mMaxResults = 4; + wallCheck4.mFlags = 0; + gpMap->isTouchedWallsAndMoveXZ(&wallCheck4); + + bestDist = 50.0f; + s32 loopIdx2 = 0; + for (; loopIdx2 < wallCheck4.mResultWallsNum; loopIdx2++) { + TBGCheckData* wall = wallCheck4.mResultWalls[loopIdx2]; + s32 wallAngle = matan(wall->mNormal.z, wall->mNormal.x); + s16 diff = wallAngle - (mFaceAngle.y + 0x8000); + if (diff > -0x2000 && diff < 0x2000) { + JGeometry::TVec3 pos; + pos.x = mPosition.x; + pos.y = mPosition.y; + pos.z = mPosition.z; + f32 planeDist = wall->mNormal.y * pos.y + wall->mNormal.x * pos.x + wall->mNormal.z * pos.z + wall->mPlaneDistance; + if (planeDist < bestDist) { + bestWall3 = wall; + } + } + } + + if (bestWall3 != (const TBGCheckData*)0) { + f32 dot = bestWall->mNormal.y * bestWall3->mNormal.y + + bestWall->mNormal.x * bestWall3->mNormal.x + + bestWall->mNormal.z * bestWall3->mNormal.z; + + if (mDeParams.mHangWallMovableAngle.value < dot) { + s32 newAngle = matan(bestWall3->mNormal.z, bestWall3->mNormal.x); + mFaceAngle.y = (s16)(newAngle + 0x8000); + + mPosition.x = wallCheck3.mCenter.x - 40.0f * bestWall3->mNormal.x; + mPosition.z = wallCheck3.mCenter.z - 40.0f * bestWall3->mNormal.z; + + f32 groundY = gpMap->checkGround(mPosition.x, 160.0f + mPosition.y, mPosition.z, &plane); + mPosition.y = groundY; + movedToWall = 1; + } + } + } + } + } else { + mVel.y = 0.0f; + mForwardVel = 0.0f; + mPosition.x -= 60.0f * JMASSin(mFaceAngle.y); + mPosition.z -= 60.0f * JMASCos(mFaceAngle.y); + f32 groundY = gpMap->checkGround(mPosition.x, mPosition.y, mPosition.z, &plane); + f32 limit = mPosition.y - 100.0f; + if (groundY < limit) { + mPosition.y = limit; + } else { + mPosition.y = groundY; + } + changePlayerStatus(0x208B6, 0, false); + return; + } + } + + if (mActionTimer >= *hangTimerParam) { + mVel.y = 0.0f; + mForwardVel = 0.0f; + mPosition.x -= 60.0f * JMASSin(mFaceAngle.y); + mPosition.z -= 60.0f * JMASCos(mFaceAngle.y); + f32 groundY = gpMap->checkGround(mPosition.x, mPosition.y, mPosition.z, &plane); + f32 limit = mPosition.y - 100.0f; + if (groundY < limit) { + mPosition.y = limit; + } else { + mPosition.y = groundY; + } + changePlayerStatus(0x208B6, 0, false); + return; + } + + checkPlayerAround(-0x8000, 30.0f); + f32 heightAboveGround = mPosition.y - mPosition.y; + + if (highEnough && heightAboveGround < 100.0f) { + startVoice(0x788F); + changePlayerStatus(0x3000054F, 0, false); + return; + } + + setPlayerVelocity(0.0f); + f32 zero = 0.0f; + mVel.y = zero; + mPosition.y = mFloorPosition.y; + mModelFaceAngle = mFaceAngle.y; + + if (movedToWall != 1) { + f32 dz = mPosition.z - unk29C.z; + f32 dx = mPosition.x - unk29C.x; + f32 distSq = dz * dz + dx * dx; + f32 dist; + if (distSq <= zero) { + dist = distSq; + } else { + f32 guess = __frsqrte(distSq); + dist = distSq * (0.5f * guess * (3.0f - distSq * guess * guess)); + } + f32 animSpeed = dist * mHangingParams.mAnmRate.value; + if ((s16)stickAngleDiff < 0) { + setAnimation(0xD7, animSpeed); + } else { + setAnimation(0xD8, animSpeed); + } + } else { + if (mActionTimer < mHangingParams.mRapidTime.value) { + setAnimation(0x33, 1.0f); + } else { + setAnimation(0x33, mHangingParams.mAnmRapid.value); + } + } +} + + +void TMario::startHangLanding(u32 status) +{ + const TBGCheckData* plane; + mVel.y = 0.0f; + mForwardVel = 0.0f; + mPosition.x = mPosition.x - 60.0f * JMASSin(mFaceAngle.y); + mPosition.z = mPosition.z - 60.0f * JMASCos(mFaceAngle.y); + f32 groundY = gpMap->checkGround(mPosition.x, mPosition.y, mPosition.z, &plane); + f32 limit = mPosition.y - 100.0f; + if (groundY < limit) { + mPosition.y = limit; + } else { + mPosition.y = groundY; + } + changePlayerStatus(status, 0, false); +} + +void TMario::moveRoof() +{ + // roofCommonEvents inlined + { + u32 input = mInput; + if (input & 0x8000) { + mInput = input & ~0x8000; + changePlayerStatus(0x88c, 0, false); + return; + } else if (input & 0x2) { + TLiveActor* actor = (TLiveActor*)mRoofPlane->mActor; + if (actor != 0) { + actor->receiveMessage(this, 3); + if (actor->mActorType == 0x4000006a) { + emitParticle(0x39, &unk160[1]); + rumbleStart(0x15, mMotorParams.mMotorWall.value); + changePlayerStatus(0x00200345, 0, false); + return; + } + } + changePlayerStatus(0x00200347, 0, false); + return; + } + } + + if (mInput & 1) { + int result = doRoofMovingProcess(); + if (result == 2) { + changePlayerStatus(0x88c, 0, false); + return; + } else if (result == 3) { + changePlayerStatus(0x3000036b, 0, false); + } + } + + if (mInput & 0x20) { + changePlayerStatus(0x00200349, mActionArg, false); + return; + } + + JGeometry::TVec3 diff; + diff = mPosition; + diff.x -= unk29C.x; + diff.y -= unk29C.y; + diff.z -= unk29C.z; + + JGeometry::TVec3 dir = diff; + f32 dist = JGeometry::TUtil::sqrt(dir.x * dir.x + dir.y * dir.y + dir.z * dir.z); + + f32 speed = dist * mHangRoofParams.mAnmMult.value; + if (mActionArg & 1) { + setAnimation(0x5c, speed); + } else { + setAnimation(0x5d, speed); + } + + if (isLast1AnimeFrame()) { + mActionArg = 1 - mActionArg; + } + + return; +} + +BOOL TMario::roofCommonEvents() +{ + u32 input = mInput; + if (input & 0x8000) { + mInput = input & ~0x8000; + changePlayerStatus(0x88c, 0, false); + } else if (input & 0x2) { + TLiveActor* actor = (TLiveActor*)mRoofPlane->mActor; + if (actor != 0) { + actor->receiveMessage(this, 3); + if (actor->mActorType == 0x4000006a) { + emitParticle(0x39, &unk160[1]); + rumbleStart(0x15, mMotorParams.mMotorWall.value); + return changePlayerStatus(0x00200345, 0, false); + } + } + changePlayerStatus(0x00200347, 0, false); + } else { + return FALSE; + } +} + +int TMario::doRoofMovingProcess() +{ + f32 speed = mForwardVel + 1.0f; + mForwardVel = speed; + if (speed > 4.0f) { + mForwardVel = 4.0f; + } + + if (mIntendedMag > 0.0f) { + s16 diff = mIntendedYaw - mFaceAngle.y; + s16 turn = IConverge(diff, 0, 0x800, 0x800); + mFaceAngle.y = mIntendedYaw - turn; + } + + mSlideAngle = mFaceAngle.y; + + mSlideVelX = mForwardVel * JMASSin(mFaceAngle.y); + mSlideVelZ = mForwardVel * JMASCos(mFaceAngle.y); + mVel.x = mSlideVelX; + mVel.y = 0.0f; + mVel.z = mSlideVelZ; + + f32 normalX = *(f32*)((u8*)mRoofPlane + 0x38); + JGeometry::TVec3 nextPos; + nextPos.x = mPosition.x - mSlideVelX * normalX; + nextPos.z = mPosition.z - mSlideVelZ * normalX; + nextPos.y = mPosition.y; + + int result = hangingCheckRoof(&nextPos); + if (result == 2) { + return 0; + } + + mPosition = nextPos; + mModelFaceAngle = mFaceAngle.y; + return result; +} + +int TMario::hangingCheckRoof(JGeometry::TVec3* pos) +{ + const TBGCheckData* wall = checkWallPlane((Vec*)pos, 50.0f, 50.0f); + if (wall != 0) { + u8 isFence = (wall->mBGType == 0x10a) ? 1 : 0; + if (isFence) { + s16 angle = matan(wall->getNormal().z, wall->getNormal().x); + angle = angle + 0x18000; + mFaceAngle.y = angle; + mModelFaceAngle = mFaceAngle.y; + return 3; + } + } + + const TBGCheckData* groundPlane; + const TBGCheckData* roofPlane; + f32 groundY = gpMap->checkGround(pos->x, pos->y, pos->z, &groundPlane); + f32 roofY = gpMap->checkRoof(pos->x, 80.0f + groundY, pos->z, &roofPlane); + + wall = checkWallPlane((Vec*)pos, 50.0f, 50.0f); + if (wall == 0) { + return 2; + } + + u8 isFence2 = (wall->mBGType == 0x10a) ? 1 : 0; + if (!isFence2) { + return 2; + } + + f32 gap = roofY - groundY; + if (gap <= 10.0f) { + return 1; + } + + f32 headroom = roofY - (10.0f + pos->y); + if (headroom < -30.0f) { + return 1; + } + + if (headroom > 30.0f) { + return 2; + } + + pos->y = mFloorPosition.x - 10.0f; + mGroundPlane = groundPlane; + mFloorPosition.y = roofY; + mRoofPlane = roofPlane; + mFloorPosition.x = groundY; + return 0; +} + +void TMario::barClimb() +{ + if ((u32)mHolder == 0) { + changePlayerStatus(0x208b6, 0, false); + return; + } + + if (mInput & 0x2) { + mPosition.x -= 200.0f * JMASSin(mFaceAngle.y); + mPosition.z -= 200.0f * JMASCos(mFaceAngle.y); + mFaceAngle.y = mFaceAngle.y + 0x8000; + changePlayerStatus(0x02000886, 0, false); + return; + } + + mPosition.x = mHolder->mPosition.x; + mPosition.y = mHolder->mPosition.y + mHolderHeightDiff; + mPosition.z = mHolder->mPosition.z; + + f32 climbParam = *(f32*)((u8*)unk108 + 0x14); + if (climbParam < 8.0f) { + changePlayerStatus(0x18100340, 0, false); + return; + } + + mVel.y = 0.0f; + f32 climbRate = mBarParams.mClimbSp.value; + mPosition.y = mPosition.y + climbParam * climbRate; + mHolderHeightDiff = mPosition.y - mHolder->mPosition.y; + + if (mPosition.y > mHolder->mPosition.y + *(f32*)((u8*)mHolder + 0x5c)) { + setPlayerVelocity(0.0f); + changePlayerStatus(0x02000880, 0, false); + unk78 &= ~0x100; + } + + int barResult = barProcess(); + if (barResult == 0) { + f32 animSpeed = *(f32*)((u8*)unk108 + 0x14) * mBarParams.mClimbAnmRate.value + 1.0f; + setAnimation(5, animSpeed); + } + + if (mHolder->mActorType - 0x40000000 == 0x39) { + if (mHolderHeightDiff > 500.0f) { + mHolderHeightDiff = 500.0f; + mPosition.y = mHolder->mPosition.y + mHolderHeightDiff; + } + } + + if (mHolder->mActorType - 0x40000000 == 0x246) { + u8 mapId = gpMarDirector->mMap; + if (mapId == 8) { + if (mHolderHeightDiff > 750.0f) { + mHolderHeightDiff = 750.0f; + mPosition.y = mHolder->mPosition.y + mHolderHeightDiff; + } + } else if (mapId == 9) { + if (mHolderHeightDiff > 750.0f) { + mHolderHeightDiff = 750.0f; + mPosition.y = mHolder->mPosition.y + mHolderHeightDiff; + } + } else if (mapId == 4) { + if (mHolderHeightDiff > 2500.0f) { + mHolderHeightDiff = 2500.0f; + mPosition.y = mHolder->mPosition.y + mHolderHeightDiff; + } + } + } +} + +void TMario::barWait() +{ + if ((u32)mHolder == 0) { + changePlayerStatus(0x208b6, 0, false); + return; + } + + if (mInput & 0x2) { + mPosition.x -= 200.0f * JMASSin(mFaceAngle.y); + mPosition.z -= 200.0f * JMASCos(mFaceAngle.y); + mFaceAngle.y = mFaceAngle.y + 0x8000; + changePlayerStatus(0x02000886, 0, false); + return; + } + + mPosition.x = mHolder->mPosition.x; + mPosition.y = mHolder->mPosition.y + mHolderHeightDiff; + mPosition.z = mHolder->mPosition.z; + + if ((mInput & 0x8000) || mHolderHeightDiff <= 100.0f) { + setPlayerVelocity(-2.0f); + mPosition.x -= 200.0f * JMASSin(mFaceAngle.y); + mPosition.z -= 200.0f * JMASCos(mFaceAngle.y); + changePlayerStatus(0x208b6, 0, false); + return; + } + + f32 climbParam = *(f32*)((u8*)unk108 + 0x14); + if (climbParam > 16.0f) { + changePlayerStatus(0x10100343, 0, false); + return; + } + + if (climbParam < -16.0f) { + mVel.y = (1.0f / 512.0f) * climbParam + mVel.y; + mPosition.y = mPosition.y + mVel.y; + mHolderHeightDiff = mPosition.y - mHolder->mPosition.y; + treeSlipEffect(); + if (gpMSound->gateCheck(0x1140)) { + MSoundSESystem::MSoundSE::startSoundActor( + 0x1140, (Vec*)&mPosition, 0, (JAISound**)0, 0, 4); + } + } + + if (mHolder->mActorType - 0x40000000 == 0xBB) { + if (*(f32*)((u8*)unk108 + 0x14) <= 0.0f) { + mPosition.y -= 2.0f; + mHolderHeightDiff = mPosition.y - mHolder->mPosition.y; + } + if (mPosition.y < mHolder->mPosition.y + 200.0f) { + mPosition.y = mHolder->mPosition.y + 200.0f; + mHolderHeightDiff = mPosition.y - mHolder->mPosition.y; + } + } + + if (mHolder->mActorType - 0x40000000 == 0x39) { + if (mHolderHeightDiff > 500.0f) { + mHolderHeightDiff = 500.0f; + mPosition.y = mHolder->mPosition.y + mHolderHeightDiff; + } + } + + if (mHolder->mActorType - 0x40000000 == 0x246) { + u8 mapId = gpMarDirector->mMap; + if (mapId == 8) { + if (mHolderHeightDiff > 750.0f) { + mHolderHeightDiff = 750.0f; + mPosition.y = mHolder->mPosition.y + mHolderHeightDiff; + } + } else if (mapId == 9) { + if (mHolderHeightDiff > 750.0f) { + mHolderHeightDiff = 750.0f; + mPosition.y = mHolder->mPosition.y + mHolderHeightDiff; + } + } else if (mapId == 4) { + if (mHolderHeightDiff > 2500.0f) { + mHolderHeightDiff = 2500.0f; + mPosition.y = mHolder->mPosition.y + mHolderHeightDiff; + } + } + } + + mFaceAngle.y += (int)(*(f32*)((u8*)unk108 + 0x10) * mBarParams.mRotateSp.value); + mFaceAngle.x = 0; + mModelFaceAngle = mFaceAngle.y; + + int barResult = barProcess(); + if (barResult == 0) { + if (*(f32*)((u8*)unk108 + 0x10) == 0.0f) { + setAnimation(0xd, 1.0f); + } + if (*(f32*)((u8*)unk108 + 0x10) > 0.0f) { + setAnimation(0x112, 1.0f); + } + if (*(f32*)((u8*)unk108 + 0x10) < 0.0f) { + setAnimation(0x111, 1.0f); + } + } +} diff --git a/src/Player/MarioSwim.cpp b/src/Player/MarioSwim.cpp index 8b137891..385d3f47 100644 --- a/src/Player/MarioSwim.cpp +++ b/src/Player/MarioSwim.cpp @@ -1 +1,419 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// MarioSwim: -inline deferred, functions in REVERSE address order. + +// doSwimming: 0x801522B4, size 0x384 +void TMario::doSwimming() +{ + // Check shallow water flag + if (mInput & (1 << 16)) { + changePlayerStatus(0x24D9, 0, false); + return; + } + + // Check above swim end depth + f32 floorY = mFloorPosition.y; + f32 endDepth = mSwimParams.mEndDepth.value; + f32 posY = mPosition.y; + if (floorY + endDepth > posY) { + changePlayerStatus(0x0C400201, 0, false); + return; + } + + // Apply swim movement + f32 stickMag = mIntendedMag; + f32 swimMoveSp = mSwimParams.mMoveSp.value; + f32 fwdVel = mForwardVel; + f32 startMult = *(f32*)((u8*)0 + 0); + fwdVel = startMult * stickMag * swimMoveSp + fwdVel; + mForwardVel = fwdVel; + + // Brake + f32 curVel = mForwardVel; + f32 brake = mSwimParams.mMoveBrake.value; + mForwardVel = curVel * brake; + + // Rotation + u32 pumpState = mPumpState; + if (pumpState == 0 || pumpState == 1) { + s16 rotMin = mSwimParams.mPumpingRotSpMin.value; + s16 rotMax = mSwimParams.mPumpingRotSpMax.value; + // int-to-float conversion and interpolation + } else { + s16 rotMin = mSwimParams.mSwimmingRotSpMin.value; + s16 rotMax = mSwimParams.mSwimmingRotSpMax.value; + } + + considerJumpRotate(); + + s16 yawDiff = mFaceAngle.y; + mModelFaceAngle = mIntendedYaw - yawDiff; + + setPlayerVelocity(mForwardVel); + + // Gravity + f32 velY = mVel.y; + f32 gravity = mSwimParams.mGravity.value; + mVel.y = velY - gravity; + + // Depth ratio + f32 waterLevel = mPosition.y; + f32 curPosY = mPosition.y; + f32 floatHeight = mSwimParams.mFloatHeight.value; + f32 depthRatio = (waterLevel - curPosY) / floatHeight; + if (depthRatio < 0.0f) depthRatio = 0.0f; + if (depthRatio > 1.0f) depthRatio = 1.0f; + + u16 animId = mAnimationId; + if (animId == 0x107 || animId == 0x106 || mAction == 0x22D2) { + f32 mult = mSwimParams.mWaitBouyancy.value; + depthRatio *= mult; + } else { + f32 mult = mSwimParams.mMoveBouyancy.value; + depthRatio *= mult; + } + + f32 curVelY = mVel.y; + mVel.y = curVelY + depthRatio; + + f32 brakeVelY = mVel.y; + f32 upDownBrake = mSwimParams.mUpDownBrake.value; + mVel.y = brakeVelY * upDownBrake; + + // Swim paddle result + int result = jumpProcess(1); + if (result == 2) { + if (checkFlag(MARIO_FLAG_FLUDD_EMITTING)) { + if (checkSwimToHangFence()) { + f32 vel = mForwardVel; + f32 negMult = *(f32*)((u8*)0 + 0); + mForwardVel = -vel * negMult; + changePlayerStatus(0x24DA, 0, true); + } else { + changePlayerStatus(0x208B3, 0, true); + f32 vel = mForwardVel; + f32 negMult = *(f32*)((u8*)0 + 0); + mForwardVel = -vel * negMult; + mVel.y = 0.0f; + } + } else { + setPlayerVelocity(0.0f); + mVel.y = 0.0f; + } + } + + // Check above water surface + f32 checkConst = *(f32*)((u8*)0 + 0); + f32 flrY = mFloorPosition.y; + f32 pY = mPosition.y; + if (pY > checkConst + flrY) { + if (mPosition.y < *(f32*)((u8*)0 + 0) + flrY) { + if (mAction != 0x22D2) { + *(u32*)((u8*)this + 0x1A8) = *(u32*)((u8*)this + 0x10); + *(u32*)((u8*)this + 0x1AC) = *(u32*)((u8*)this + 0x14); + *(u32*)((u8*)this + 0x1B0) = *(u32*)((u8*)this + 0x18); + *(f32*)((u8*)this + 0x1AC) = mFloorPosition.y; + u32 director = *(u32*)((u8*)0 + 0); + inOutWaterEffect(0x11E); + } + } + } + + // Clamp Y + f32 clampConst = *(f32*)((u8*)0 + 0); + f32 fY = mFloorPosition.y; + f32 myY = mPosition.y; + f32 minY = clampConst + fY; + if (myY < minY) + mPosition.y = minY; +} + +// checkSwimJump: 0x80152144, size 0x170 +BOOL TMario::checkSwimJump() +{ + if (!(mInput & 0x02)) + return FALSE; + + // FLUDD emitting + hang fence + if (checkFlag(MARIO_FLAG_FLUDD_EMITTING)) { + if (!checkSwimToHangFence()) { + f32 jumpY = *(f32*)((u8*)0 + 0); + mPosition.y = jumpY + mFloorPosition.y; + changePlayerStatus(0x828, 0, false); + changePlayerStatus(0x888, 0, false); + return TRUE; + } + } + + // Depth check + f32 floorY = mFloorPosition.y; + f32 canJumpDepth = mSwimParams.mCanJumpDepth.value; + f32 curY = mPosition.y; + f32 depth = floorY - canJumpDepth; + if (depth < curY) + return FALSE; + + // Dive conditions + u8 shouldDive = 0; + f32 stickMag = mIntendedMag; + if (stickMag == 0.0f) + shouldDive = 1; + if (mWallPlane != NULL) + shouldDive = 1; + s16 faceY = mFaceAngle.y; + s16 modelY = mModelFaceAngle; + s16 diff = modelY - faceY; + if (diff < -21845 || diff > 21845) + shouldDive = 1; + + if ((u8)shouldDive == 1) { + setPlayerVelocity(0.0f); + changePlayerStatus(0x20880, 0, false); + return TRUE; + } + + // Surface jump + if (stickMag == 0.0f) { + changePlayerStatus(0x24D8, 0, false); + } else { + changePlayerStatus(0x24D4, 0, false); + } + return FALSE; +} + +// swimPaddle: 0x80152014, size 0x130 +void TMario::swimPaddle() +{ + f32 animSpeed = *(f32*)((u8*)0 + 0); + if (checkFlag(MARIO_FLAG_FLUDD_EMITTING)) { + animSpeed = *(f32*)((u8*)0 + 0); + } + setAnimation(0x119, animSpeed); + + if (checkFlag(MARIO_FLAG_FLUDD_EMITTING)) { + f32 paddleUp = mDeParams.mDashMax.value; + addVelocity(paddleUp); + setAnimation(0x19, 0); + startSoundActor(0x117D); + } + + f32 stickMag = mIntendedMag; + if (stickMag == 0.0f) { + changePlayerStatus(0x24D6, 0, false); + } + + checkSwimToHangFence(); + + if (!checkActionFlag(0x2000)) { + return; + } + + if (checkFlag(MARIO_FLAG_FLUDD_EMITTING)) { + if (!checkSwimToHangFence()) { + setPlayerVelocity(0.0f); + } + } +} + +// swimMain: 0x8015191C, size 0x6F8 +BOOL TMario::swimMain() +{ + if (checkFlag(MARIO_FLAG_IS_PERFORMING)) { + changePlayerStatus(0x224E1, 0, false); + } + + if (checkSwimJump()) + return FALSE; + + // Wall collision check + f32 stickMag = mIntendedMag; + if (stickMag > 0.0f) { + const TBGCheckData* wall = mWallPlane; + if (wall != NULL) { + u16 wallType = *(u16*)((u8*)wall + 0); + if (wallType == 0x10A) { + f32 wallZ = *(f32*)((u8*)wall + 0x34 + 8); + f32 wallX = *(f32*)((u8*)wall + 0x34); + s16 angle = (s16)atan2f(wallZ, wallX); + s16 yawDiff = mModelFaceAngle - angle; + if (yawDiff < -21845 || yawDiff > 21845) { + s16 newAngle = angle + 0x8000; + mModelFaceAngle = (s16)newAngle; + s16 storedAngle = mModelFaceAngle; + mFaceAngle.z = storedAngle; + f32 posY = mPosition.y; + f32 addY = *(f32*)((u8*)0 + 0); + mPosition.y = posY + addY; + changePlayerStatus(0x3000036B, 0, false); + } + } + } + } + + // Clamp Y + f32 waterSurface = mPosition.y; + f32 floatHeight = mSwimParams.mFloatHeight.value; + f32 curY = mPosition.y; + f32 minY = waterSurface - floatHeight; + if (curY <= minY) + mPosition.y = minY; + + *(f32*)((u8*)this + 0x2AC) = mPosition.y; + + // Check FLUDD + if (checkFlag(MARIO_FLAG_FLUDD_EMITTING)) { + if (mAction != 0x24D4 && mAction != 0x24D5) { + changePlayerStatus(0x24D4, 0, false); + return FALSE; + } + } + + switch (mAction) { + case 0x22D1: + doSwimming(); + if (checkActionFlag(0x2000)) + return 1; + setAnimation(0x115, 0.0f); + if (checkSwimJump()) + changePlayerStatus(0x22D2, 0, false); + return FALSE; + + case 0x22D2: + setAnimation(0x116, 0.0f); + if (mInput & 0x01) + changePlayerStatus(0x24D3, 0, false); + doSwimming(); + if (checkActionFlag(0x2000)) + return 1; + setAnimation(0x116, 0.0f); + if (gpMSound->gateCheck(0x1950)) + startSoundActor(0x1950); + return FALSE; + + case 0x24D3: + setAnimation(0x117, 0.0f); + doSwimming(); + if (checkSwimJump()) + changePlayerStatus(0x24D4, 0, false); + doSwimming(); + if (checkActionFlag(0x2000)) + return 1; + return FALSE; + + case 0x24D4: { + setAnimation(0x118, 0.0f); + f32 fwdVel = mForwardVel; + f32 accel = mSwimParams.mPaddleSpeedUp.value; + mForwardVel = fwdVel + accel; + f32 velY = mVel.y; + f32 accelY = mSwimParams.mPaddleJumpUp.value; + mVel.y = velY + accelY; + doSwimming(); + if (checkSwimJump()) + changePlayerStatus(0x24D5, 0, false); + doSwimming(); + if (checkActionFlag(0x2000)) + return 1; + return FALSE; + } + + case 0x24D5: + swimPaddle(); + return FALSE; + + case 0x24D6: + setAnimation(0x11A, 0.0f); + doSwimming(); + if (checkSwimJump()) + changePlayerStatus(0x24D7, 0, false); + doSwimming(); + if (checkActionFlag(0x2000)) + return 1; + return FALSE; + + case 0x24D7: + setAnimation(0x11B, 0.0f); + doSwimming(); + if (checkSwimJump()) + changePlayerStatus(0x22D2, 0, false); + doSwimming(); + if (checkActionFlag(0x2000)) + return 1; + return FALSE; + + case 0x24D8: { + setAnimation(0x11C, 0.0f); + f32 velY = mVel.y; + f32 accelUp = mSwimParams.mFloatUp.value; + mVel.y = velY + accelUp; + doSwimming(); + if (checkSwimJump()) { + setAnimation(0x116, 0.0f); + changePlayerStatus(0x22D2, 0, false); + } + doSwimming(); + if (checkActionFlag(0x2000)) + return 1; + return FALSE; + } + + case 0x24D9: + doSwimming(); + setAnimation(0x128, 296); + doSwimming(); + if (checkSwimJump()) { + mSwimParams.mWaitSinkTime.value = *(s16*)((u8*)this + 0x0366); + } + { + s16 timer = *(s16*)((u8*)this + 0x0366); + if (timer > 0) { + *(s16*)((u8*)this + 0x0366) = timer - 1; + f32 velY = mVel.y; + f32 sinkSpeed = mSwimParams.mWaitSinkSpeed.value; + mVel.y = velY - sinkSpeed; + } + } + doSwimming(); + if (checkSwimJump()) { + setAnimation(0x116, 0.0f); + changePlayerStatus(0x24D6, 0, false); + } + doSwimming(); + if (checkActionFlag(0x2000)) + return 1; + return FALSE; + + case 0x24DA: + doSwimming(); + setAnimation(0x24DA, 298); + doSwimming(); + if (checkSwimJump()) + changePlayerStatus(0x22D2, 0, false); + return FALSE; + + case 0x224E0: + doSwimming(); + setAnimation(0x24E0, 268); + return FALSE; + + case 0x224E1: + doSwimming(); + setAnimation(0x24E1, 299); + return FALSE; + } + + return FALSE; +} diff --git a/src/Player/MarioUpper.cpp b/src/Player/MarioUpper.cpp index 8b137891..4616e378 100644 --- a/src/Player/MarioUpper.cpp +++ b/src/Player/MarioUpper.cpp @@ -1 +1,273 @@ +#include +#include +#include +#include +#include + +// MarioUpper: -inline deferred, functions in REVERSE address order. + +// checkPumping: 0x80141C98, size 0x118 +void TMario::checkPumping() +{ + // Check pump trigger active (controller work offset 0x1C) + f32 pumpFrame = *(f32*)((u8*)unk108 + 0x1C); + if (pumpFrame > 0.0f) { + if (mPumpState != 0) { + mPumpState = 0; + unk37E = 0; + } + return; + } + + // Check if gpMarioOriginal == this and pad button pressed + if (gpMarioOriginal == this) { + TMario* mario = gpMarioOriginal; + u32 prevAction = mario->mPrevAction; + if (isMario()) { + if (checkPumpEnable()) { + mPumpState = 1; + unk37E = 0; + } + return; + } + } + + // Check action in range 0x800447 + u32 action = mAction; + if ((action - 0x800000) == 0x447) { + mPumpState = 1; + unk37E = 0; + return; + } + + // Check action 0xC408220 + if ((action - 0xC000000) == 0x408220) { + if (mPumpState == 5) { + mPumpState = 1; + unk37E = 0; + } + return; + } + + // Check FLUDD emitting flag (bit 14) + if (checkFlag(MARIO_FLAG_FLUDD_EMITTING)) { + mPumpState = 0; + unk37E = 0; + } +} + +// checkPumpEnable: 0x80141ACC, size 0x1CC +BOOL TMario::checkPumpEnable() +{ + // Must have watergun + if (mWaterGun == NULL) { + mPumpState = 5; + unk37E = 0; + return FALSE; + } + + // Must have HAS_FLUDD flag + if (!checkFlag(MARIO_FLAG_HAS_FLUDD)) { + mPumpState = 5; + unk37E = 0; + return FALSE; + } + + // Check animation lookup table + u16 animId = mAnimationId; + u32 tblBase = *(u32*)((u8*)0 + 0); // addr from sdata + if (!*(u32*)(tblBase + (u32)(animId << 3))) { + mPumpState = 5; + unk37E = 0; + return FALSE; + } + + // Must not already be pumping + if (isUpperPumpingStyle()) { + mPumpState = 5; + unk37E = 0; + return FALSE; + } + + // Check dirty amount and ratio + f32 dirty = *(f32*)((u8*)this + 0x368); + if (dirty > 0.0f) { + s16 val = mGraffitoParams.mSinkTime.value; + f32 limit = mGraffitoParams.mSinkPumpLimit.value; + f32 ratio = dirty / (f32)val; + if (ratio > limit) { + mPumpState = 5; + unk37E = 0; + return FALSE; + } + } + + // Pump state checks + if (mPumpState == 4) { + mPumpState = 5; + unk37E = 0; + return FALSE; + } + if (mPumpState == 3) { + mPumpState = 5; + unk37E = 0; + return FALSE; + } + if (mPumpState == 2) { + mPumpState = 5; + unk37E = 0; + return FALSE; + } + + // Check status 0x88D + if (mAction == 0x88D) { + TWaterGun* wg = mWaterGun; + TNozzleBase* nozzle = wg->getCurrentNozzle(); + if (*(u8*)((u8*)nozzle + 0x18) == 1) { + mPumpState = 5; + unk37E = 0; + return FALSE; + } + } + + // Check nozzle kind == 1 (trigger type) + TWaterGun* wg2 = mWaterGun; + TNozzleBase* nozzle2 = wg2->getCurrentNozzle(); + s32 kind = (*(s32(**)(TNozzleBase*))((u32*)(*(u32*)nozzle2) + 3))(nozzle2); + if (kind == 1) { + TWaterGun* wg3 = mWaterGun; + TNozzleBase* nozzle3 = wg3->getCurrentNozzle(); + if (*(u8*)((u8*)nozzle3 + 0x385) == 2) { + mPumpState = 5; + unk37E = 0; + return FALSE; + } + } + + // Check watergun unk1D00 + TWaterGun* wg4 = mWaterGun; + f32 wgVal = wg4->unk1D00; + if (wgVal < 0.0f) { + mPumpState = 5; + unk37E = 0; + return FALSE; + } + if (wgVal > 0.0f) { + mPumpState = 5; + unk37E = 0; + return FALSE; + } + + // Check action flag bit 12 + if (checkActionFlag(0x1000)) { + mPumpState = 5; + unk37E = 0; + return FALSE; + } + + return TRUE; +} + +// stateMachineUpper: 0x80141854, size 0x278 +void TMario::stateMachineUpper() +{ + switch (mPumpState) { + case 0: { + if (!checkPumpEnable()) { + M3UModelMario* model = mModel; + J3DModel* j3dModel = *(J3DModel**)((u8*)model + 0x0C); + *(f32*)((u8*)j3dModel + 0x24) = 0.0f; + mPumpState = 5; + } + + f32 pumpFrame = *(f32*)((u8*)unk108 + 0x1C); + if (pumpFrame == 0.0f) { + mPumpState = 1; + unk37E = mUpperBodyParams.mPumpWaitTime.value; + } + + if (!checkFlag(MARIO_FLAG_FLUDD_EMITTING)) + break; + + TWaterGun* wg = mWaterGun; + if (wg == NULL) + break; + + if (wg->mCurrentWater == 0) { + break; + } + + TNozzleBase* nozzle = wg->getCurrentNozzle(); + s32 kind = (*(s32(**)(TNozzleBase*))((u32*)(*(u32*)nozzle) + 3))(nozzle); + if (kind == 1) { + TNozzleBase* nozzle2 = wg->getCurrentNozzle(); + if (*(u8*)((u8*)nozzle2 + 0x385) == 1) { + break; + } + break; + } + + TNozzleBase* nozzle3 = wg->getCurrentNozzle(); + if (*(f32*)((u8*)nozzle3 + 0x378) > 0.0f) { + checkPumping(); + } + break; + } + + case 1: { + if (!checkPumpEnable()) { + M3UModelMario* model = mModel; + J3DModel* j3dModel = *(J3DModel**)((u8*)model + 0x0C); + *(f32*)((u8*)j3dModel + 0x24) = 0.0f; + mPumpState = 5; + } + + u16 timer = unk37E; + if (timer != 0) { + unk37E = timer - 1; + } else { + M3UModelMario* model = mModel; + J3DModel* j3dModel = *(J3DModel**)((u8*)model + 0x0C); + *(f32*)((u8*)j3dModel + 0x24) = 0.0f; + mPumpState = 5; + } + + checkPumping(); + break; + } + + case 2: { + u32 action = mAction; + if ((action - 0x80000000) == 0x387) + mPumpState = 5; + + if (*(u32*)((u8*)this + 0x6C) == 0) + mPumpState = 5; + + if ((action - 0xFC000000) == 0x440) { + if (mForwardVel > 0.0f) + checkPumping(); + } + break; + } + + case 4: { + M3UModelMario* model = mModel; + J3DModel* j3dModel = *(J3DModel**)((u8*)model + 0x0C); + u8 flags = *(u8*)((u8*)j3dModel + 0x19); + if (flags & 0x3) { + mPumpState = 5; + } + break; + } + + case 3: + case 5: + default: + if (checkPumpEnable()) { + checkPumping(); + } + break; + } +} diff --git a/src/Player/MarioWait.cpp b/src/Player/MarioWait.cpp index 8b137891..e2f262ad 100644 --- a/src/Player/MarioWait.cpp +++ b/src/Player/MarioWait.cpp @@ -1 +1,937 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// NOTE: -inline deferred means functions must be in REVERSE address order. + +// startTalking - 0x80145D44 +BOOL TMario::startTalking() +{ + const TBGCheckData* ground = mGroundPlane; + u8 isTalking; + if (ground->mFlags & 0x10) { + isTalking = 1; + } else { + isTalking = 0; + } + + u8 notTalking; + if (isTalking == 1) { + notTalking = 0; + } else { + notTalking = 1; + } + + if (notTalking) { + f32 height = 1.0f + mFloorPosition.y; + mPosition.y = height; + changePlayerStatus(0x10001308, 0, true); + return 1; + } + return 0; +} + +// canSleep - 0x80145B88 +BOOL TMario::canSleep() +{ + u8 hasFlags; + if (mState & 0x00030000) { + hasFlags = 1; + } else { + hasFlags = 0; + } + if (hasFlags) + return 0; + + f32 sleepCheckDist = mDeParams.mSleepingCheckDist.value; + f32 sleepCheckTol = mDeParams.mSleepingCheckHeight.value; + const TBGCheckData* bgData; + + f32 groundY = gpMap->checkGround( + mPosition.x - sleepCheckDist, 10.0f + mPosition.y, mPosition.z, &bgData); + f32 floorY = mFloorPosition.y; + if (groundY < floorY - sleepCheckTol || floorY + sleepCheckTol < groundY) + return 0; + + groundY = gpMap->checkGround( + mPosition.x + sleepCheckDist, 10.0f + mPosition.y, mPosition.z, &bgData); + floorY = mFloorPosition.y; + if (groundY < floorY - sleepCheckTol || floorY + sleepCheckTol < groundY) + return 0; + + groundY = gpMap->checkGround( + mPosition.x, 10.0f + mPosition.y, mPosition.z - sleepCheckDist, &bgData); + floorY = mFloorPosition.y; + if (groundY < floorY - sleepCheckTol || floorY + sleepCheckTol < groundY) + return 0; + + groundY = gpMap->checkGround( + mPosition.x, 10.0f + mPosition.y, mPosition.z + sleepCheckDist, &bgData); + floorY = mFloorPosition.y; + if (groundY < floorY - sleepCheckTol || floorY + sleepCheckTol < groundY) + return 0; + + if (gpMap->isTouchedOneWall(mPosition.x, 10.0f + mPosition.y, mPosition.z, 80.0f)) + return 0; + + return 1; +} + +// canPut - 0x80145AC8 +BOOL TMario::canPut() +{ + u16 faceAngle = mFaceAngle.y; + TTakeActor* heldObj = mHeldObject; + s32 idx = (s32)faceAngle >> jmaSinShift; + f32 sinVal = jmaSinTable[idx]; + f32 cosVal = jmaCosTable[idx]; + f32 x = 100.0f * sinVal + mPosition.x; + f32 z = 100.0f * cosVal + mPosition.z; + f32 y = 10.0f + mPosition.y; + f32 radius = heldObj->mDamageRadius; + + if (gpMap->isTouchedOneWall(x, y, z, radius)) + return 0; + + f32 y2 = 10.0f + mPosition.y; + f32 radius2 = mHeldObject->mDamageRadius; + + if (gpMap->isTouchedOneWall(mPosition.x, y2, mPosition.z, radius2)) + return 0; + + return 1; +} + +// waitingCommonEvents - 0x801458A8 +BOOL TMario::waitingCommonEvents() +{ + u32 input = mInput; + + if (input & 0x02) { + if (considerRotateJumpStart()) + return 1; + changePlayerJumping(0x02000880, 0); + return 1; + } + + if (input & 0x04) { + changePlayerStatus(0x088C, 0, false); + return 1; + } + + if (input & 0x08) { + changePlayerStatus(0x50, 0, false); + return 1; + } + + if (input & 0x10) { + changePlayerStatus(0x0C000227, 0, false); + return 1; + } + + if (input & 0x01) { + s16 faceY = mFaceAngle.y; + s16 intendedYaw = mIntendedYaw; + s16 turnSpeed = mDeParams.mWaitingRotSp.value; + s16 diff = (s16)(intendedYaw - faceY); + s16 converged = IConverge((int)diff, 0, (int)turnSpeed, (int)turnSpeed); + mFaceAngle.y = (s16)(intendedYaw - converged); + + if (mIntendedMag > mControllerParams.mStartToWalkLevel.value) { + emitSmoke(mFaceAngle.y); + changePlayerStatus(0x04000440, 0, false); + return 1; + } + } + + u8 hasJumpInput; + if (mState & 0x00004000) { + hasJumpInput = 1; + } else { + hasJumpInput = 0; + } + if (hasJumpInput) { + changePlayerStatus(0x04000440, 0, false); + return 1; + } + + if (canSquat()) { + mForwardVel = 0.0f; + changePlayerStatus(0x0C018220, 0, false); + return 1; + } + + if (mInput & 0x00008000) { + changePlayerStatus(0x384, 0, false); + return 1; + } + + if (rocketCheck()) { + TWaterGun* gun = mWaterGun; + f32 rocketHeight = *(f32*)((u8*)gun + 0x1D40); + mHolderHeightDiff = mFloorPosition.y + rocketHeight; + changePlayerStatus(0x088B, 0, false); + return 1; + } + + if (considerRotateStart()) + return 1; + + return 0; +} + +// stopCommon - 0x801457EC +void TMario::stopCommon(int animId, int nextState) +{ + waitProcess(); + setAnimation(animId, 1.0f); + + if (onYoshi()) { + MActor* actor = *(MActor**)((u8*)mYoshi + 0x34); + if (actor->curAnmEndsNext(0, 0)) { + changePlayerStatus(nextState, 0, false); + return; + } + } else { + if (isLast1AnimeFrame()) { + changePlayerStatus(nextState, 0, false); + } + } +} + +// changeMontemanWaitingAnim - 0x801457CC +void TMario::changeMontemanWaitingAnim() +{ + if (mAction != 0x0C400201) + return; + mActionState |= 0x02; +} + +// waiting - 0x8014552C +BOOL TMario::waiting() +{ + if (waitingCommonEvents()) + return 1; + + if (isMario()) { + u8 isPumpFive; + if (mPumpState == 5) { + isPumpFive = 1; + } else { + isPumpFive = 0; + } + if (isPumpFive) { + if (canSleep()) { + if (mAnimationId == 0xC3) { + if (isAnimeLoopOrStop()) { + if (mGroundPlane != nullptr) { + if (mGroundPlane->mNormal.y > 0.99f) { + mActionTimer++; + if (mActionTimer >= 10) { + changePlayerStatus(0x0C400202, 0, false); + return 1; + } + } + } + } + } + } + } + } + + u16 actionState = mActionState; + + if (actionState & 0x02) { + setAnimation(0x114, 1.0f); + } else if (gpMarDirector->unk124 == 3) { + setAnimation(0xD9, 1.0f); + } else { + f32 val368 = *(f32*)((u8*)this + 0x368); + int isPositive; + if (val368 > 0.0f) { + isPositive = 1; + } else { + isPositive = 0; + } + + if (isPositive != 0) { + setAnimation(0xE7, 1.0f); + } else { + bool doMontemanWait = false; + if (mPumpState == 5) { + if (mPrevAction - 0x0C00023D == 0) { + doMontemanWait = true; + } else { + u8 hasFlag; + if (mState & 0x00000040) { + hasFlag = 1; + } else { + hasFlag = 0; + } + if (hasFlag) { + doMontemanWait = true; + } + } + } + + if (doMontemanWait) { + // montemanWait + if (!(actionState & 0x01)) { + setAnimation(0xDA, 1.0f); + + J3DFrameCtrl* frameCtrl = mModel->unkC; + if (frameCtrl->checkPass(138.0f)) { + emitSweat((s16)(mFaceAngle.y - 0x4000)); + } + + if (isLast1AnimeFrame()) { + mActionState |= 0x01; + } + + waitProcess(); + return 0; + } + } + + // regularWait + if (mHealth <= 3) { + if (mAnimationId != 0x11D && mAnimationId != 0x127) { + setAnimation(0x127, 1.0f); + } else if (mAnimationId == 0x127) { + if (isLast1AnimeFrame()) { + setAnimation(0x11D, 1.0f); + } + } + } else { + if (0.0f == mIntendedMag) { + setAnimation(0xC3, 1.0f); + } else { + setAnimation(0x12C, 1.0f); + } + } + } + } + + waitProcess(); + return 0; +} + +// sleepily - 0x8014540C +BOOL TMario::sleepily() +{ + if (waitingCommonEvents()) + return 1; + + { + u32* ctrlWork = (u32*)unk108; + f32 stickX = *(f32*)((u8*)ctrlWork + 0x1c); + f32 stickY = *(f32*)((u8*)ctrlWork + 0x20); + if (stickX > 0.0f || stickY > 0.0f) { + changePlayerStatus(0x0C400201, 0, false); + } + } + + if (mActionState == 3) { + changePlayerStatus(0x0C000203, 0, false); + return 1; + } + + switch (mActionState) { + case 0: + setAnimation(0x12F, 1.0f); + break; + case 1: + setAnimation(0x130, 1.0f); + break; + case 2: + setAnimation(0x131, 1.0f); + break; + } + + if (isLast1AnimeFrame()) { + mActionState++; + } + + waitProcess(); + return 0; +} + +// sleeping - 0x80145278 +BOOL TMario::sleeping() +{ + u32 input = mInput; + + bool shouldWake = true; + if (!(input & 0xA41F)) { + u32* ctrlWork = (u32*)unk108; + f32 stickX = *(f32*)((u8*)ctrlWork + 0x1c); + f32 stickY = *(f32*)((u8*)ctrlWork + 0x20); + if (stickX <= 0.0f && stickY <= 0.0f) { + shouldWake = false; + } + } + + if (shouldWake) { + // wakeUp + if (mActionState == 0) { + startSoundActor(0x7883); + } else { + startSoundActor(0x789A); + } + changePlayerStatus(0x0C000204, mActionState, false); + return 1; + } + + waitProcess(); + + { + u8 hasFlag; + if (mSubState & 0x02) { + hasFlag = 1; + } else { + hasFlag = 0; + } + if (hasFlag) { + *(u32*)((u8*)this + 0x1B4) = *(u32*)&mPosition.x; + *(u32*)((u8*)this + 0x1B8) = *(u32*)&mPosition.y; + *(u32*)((u8*)this + 0x1BC) = *(u32*)&mPosition.z; + sleepingEffect(); + } + } + + switch (mActionState) { + case 0: + setAnimation(0x132, 1.0f); + if (isLast1AnimeFrame()) { + u16 timer = mActionTimer; + timer++; + mActionTimer = timer; + if ((u16)timer > 40) { + mActionState++; + } + } + break; + case 1: + setAnimation(0x134, 1.0f); + if (isLast1AnimeFrame()) { + mActionState++; + } + break; + case 2: + setAnimation(0x135, 1.0f); + break; + } + + return 0; +} + +// getSideWalkValues - 0x801451A8 +void TMario::getSideWalkValues(E_SIDEWALK_TYPE* outType, f32* outSpeed, f32* outStickMag) +{ + s16 faceY = mFaceAngle.y; + s16 intendedYaw = mIntendedYaw; + s16 diff = (s16)(intendedYaw - faceY); + s16 diffExt = diff; + u16 diffU = (u16)diffExt; + s32 idx = (s32)diffU >> jmaSinShift; + f32 sinVal = jmaSinTable[idx]; + f32 sideComponent = mIntendedMag * sinVal; + f32 ff8 = mRunParams.mPumpingSlideSp.value; + f32 sidewalkVel = sideComponent * ff8; + + if (0.0f == sidewalkVel) { + *outType = (E_SIDEWALK_TYPE)0; + *outSpeed = getMotionFrameCtrl().getRate(); + } else { + f32 f100c = mRunParams.mPumpingSlideAnmSp.value; + f32 speed = sidewalkVel * f100c; + if (speed < 0.0f) + speed = -speed; + *outSpeed = speed; + + if (diffExt > 0) { + *outType = (E_SIDEWALK_TYPE)1; + } else { + *outType = (E_SIDEWALK_TYPE)2; + } + } + + *outStickMag = sidewalkVel; +} + +// squating - 0x80144DEC +BOOL TMario::squating() +{ + u32 input = mInput; + + if (input & 0x04) { + changePlayerStatus(0x088C, 0, false); + return 1; + } + + if (input & 0x08) { + changePlayerStatus(0x50, 0, false); + return 1; + } + + if (input & 0x10) { + changePlayerStatus(0x0C018222, 0, false); + return 1; + } + + if (!(input & 0x4000) && !(input & 0x200)) { + changePlayerStatus(0x0C018222, 0, false); + return 1; + } + + TWaterGun* gun = mWaterGun; + if (gun == nullptr) { + changePlayerStatus(0x0C018222, 0, false); + return 1; + } + + { + u8 hasFlag; + if (mState & 0x00008000) { + hasFlag = 1; + } else { + hasFlag = 0; + } + if (!hasFlag) { + changePlayerStatus(0x0C018222, 0, false); + return 1; + } + } + + if (input & 0x02) { + TMarioGamePad* pad = mGamePad; + if (pad->mMeaning & 0x2000) { + if (gun != nullptr) { + if (*(u8*)((u8*)gun + 0x1C84) == 0) { + rumbleStart(21, mMotorParams.mMotorHipDrop.value); + changePlayerStatus(0x0883, 0, false); + return 1; + } + } + } + } + + { + TNozzleBase* nozzle = gun->getCurrentNozzle(); + u8 nozzleKind = *(u8*)((u8*)nozzle + 0x18); + if (nozzleKind == 1) { + TWaterGun* gun2 = mWaterGun; + u8 canSpray; + if (gun2->mCurrentWater == 0) { + canSpray = 0; + } else { + TNozzleBase* nozzle2 = gun2->getCurrentNozzle(); + s32 kind = nozzle2->getNozzleKind(); + if (kind == 1) { + TNozzleTrigger* trigger = (TNozzleTrigger*)gun2->getCurrentNozzle(); + if (trigger->unk385 == 1) { + canSpray = 1; + } else { + canSpray = 0; + } + } else { + if (gun2->getCurrentNozzle()->unk378 > 0.0f) { + canSpray = 1; + } else { + canSpray = 0; + } + } + } + + if (canSpray) { + TWaterGun* gun3 = mWaterGun; + f32 rocketHeight = *(f32*)((u8*)gun3 + 0x1D40); + mHolderHeightDiff = mFloorPosition.y + rocketHeight; + changePlayerStatus(0x088B, 0, false); + return 1; + } + } + } + + { + TMarioGamePad* pad = mGamePad; + u32 meaning = pad->mMeaning; + + if (meaning & 0x2000) { + E_SIDEWALK_TYPE sideType; + f32 sideSpeed; + f32 stickMag; + getSideWalkValues(&sideType, &sideSpeed, &stickMag); + + switch (sideType) { + case 0: + setAnimation(0x98, 1.0f); + break; + case 1: + setAnimation(0x7F, sideSpeed); + break; + case 2: + setAnimation(0x80, sideSpeed); + break; + } + + u16 faceAngle = mFaceAngle.y; + s32 cosIdx = (s32)faceAngle >> jmaSinShift; + f32 cosVal = jmaCosTable[cosIdx << 2 >> 2]; + mPosition.x = stickMag * cosVal + mPosition.x; + + faceAngle = mFaceAngle.y; + s32 sinIdx = (s32)faceAngle >> jmaSinShift; + f32 sinVal = jmaSinTable[sinIdx << 2 >> 2]; + mPosition.z = -(stickMag * sinVal) + mPosition.z; + } else if (meaning & 0x0800) { + f32 analogStick = *(f32*)((u8*)pad + 0xA8); + int isPositive = 1; + f32 absVal = __fabsf(analogStick); + if (analogStick < 0.0f) + isPositive = 0; + + f32 threshold = mControllerParams.mSquatRotMidAnalog.value; + f32 maxSpeed = mControllerParams.mSquatRotMidValue.value; + f32 turnSpeed; + + if (absVal < threshold) { + turnSpeed = maxSpeed * (absVal / threshold); + } else { + f32 range = 1.0f - threshold; + f32 excess = absVal - threshold; + f32 speedRange = 1.0f - maxSpeed; + turnSpeed = speedRange * (excess / range) + maxSpeed; + } + + if (!isPositive) + turnSpeed = -turnSpeed; + + s16 maxTurnRate = mDeParams.mWaitingRotSp.value; + s32 negRate = -maxTurnRate; + s16 faceY = mFaceAngle.y; + s16 angleDelta = (s16)(s32)(turnSpeed * (f64)negRate); + mFaceAngle.y = (s16)(faceY + angleDelta); + + setAnimation(0x98, 1.0f); + } + } + + waitProcess(); + return 0; +} + +// squatStandup - 0x80144CD0 +BOOL TMario::squatStandup() +{ + u32 input = mInput; + + if (input & 0x04) { + changePlayerStatus(0x088C, 0, false); + return 1; + } + + if (input & 0x08) { + changePlayerStatus(0x50, 0, false); + return 1; + } + + if (input & 0x02) { + changePlayerStatus(0x02000880, 0, false); + return 1; + } + + if (input & 0x01) { + changePlayerStatus(0x04000440, 0, false); + return 1; + } + + waitProcess(); + + if (mAction - 0x0C000223 == 0) { + setAnimation(0x121, 1.0f); + } else { + setAnimation(0x96, 1.0f); + } + + if (isLast1AnimeFrame()) { + changePlayerStatus(0x0C400201, 0, false); + } + + return 0; +} + +// jumpEndCommon - 0x80144C50 +BOOL TMario::jumpEndCommon(int animId, int nextState) +{ + waitProcess(); + setAnimation(animId, 1.0f); + + if (isLast1AnimeFrame()) { + return changePlayerStatus(nextState, 0, false); + } + return 0; +} + +// jumpEndEvents - 0x80144BD8 +BOOL TMario::jumpEndEvents(u32 nextState) +{ + u32 input = mInput; + + if (input & 0x10) { + changePlayerStatus(0x0C400201, 0, false); + return 1; + } + + if (input & 0x02) { + if (nextState == 0) { + changePlayerTriJump(); + } else { + changePlayerJumping(nextState, 0); + } + return 1; + } + + if (input & 0x0F) { + checkAllMotions(); + return 1; + } + + return 0; +} + +// waitMain - 0x80144300 +BOOL TMario::waitMain() +{ + BOOL result = 0; + + checkEnforceJump(); + + checkController(nullptr); + + setNormalAttackArea(); + + // Check held object for wall collision + TTakeActor* heldObj = mHeldObject; + if (heldObj != nullptr) { + u8 hasInput; + if (mInput & 0x2000) { + hasInput = 1; + } else { + hasInput = 0; + } + if (hasInput) { + if (heldObj->mActorType == (u32)0x80000001) { + changePlayerStatus(0x80000588, 0, false); + } else { + int putResult = canPut(); + if (putResult) { + changePlayerStatus(0x80000387, 0, false); + } + } + } + } + + u32 action = mAction; + + if (action == 0x0C400201) { + result = waiting(); + } else if (action == 0x0C400203) { + result = sleepily(); + } else if (action == 0x0C000203) { + result = sleeping(); + } else if (action == 0x0C000204) { + // Wakeup + u32 input = mInput; + if (input & 0x04) { + sleepingEffectKill(); + changePlayerStatus(0x088C, 0, false); + } else if (input & 0x08) { + sleepingEffectKill(); + changePlayerStatus(0x50, 0, false); + } else if (waitingCommonEvents()) { + sleepingEffectKill(); + return 1; + } else { + waitProcess(); + int animId; + if (mActionArg == 0) { + animId = 0x133; + } else { + animId = 0x136; + } + setAnimation(animId, 1.0f); + if (isLast1AnimeFrame()) { + sleepingEffectKill(); + changePlayerStatus(0x0C400201, 0, false); + } else { + result = 0; + } + } + result = result; + } else if (action == 0x0C018220) { + result = squating(); + } else if (action == 0x0C018222 || action == 0x0C000223) { + result = squatStandup(); + } else if (action >= 0x0C00022F && action <= 0x0C00022F) { + // action == 0x0C00022F: squat landing + u32 input = mInput; + if (input & 0x04) { + sleepingEffectKill(); + changePlayerStatus(0x088C, 0, false); + } else if (input & 0x08) { + sleepingEffectKill(); + changePlayerStatus(0x50, 0, false); + } else { + waitProcess(); + setAnimation(0xF3, 1.0f); + if (isLast1AnimeFrame()) { + changePlayerStatus(0x0C400201, 0, false); + } + result = 0; + } + result = result; + } else if (action == 0x0C000230) { + // jumpEnd - landing type 1 + if (jumpEndEvents(0)) { + result = 1; + } else { + waitProcess(); + setAnimation(0x4E, 1.0f); + if (isLast1AnimeFrame()) { + changePlayerStatus(0x0C400201, 0, false); + } + result = 0; + } + } else if (action == 0x0C000232) { + // jumpEnd - landing type 2 + if (jumpEndEvents(0)) { + result = 1; + } else { + waitProcess(); + setAnimation(0x4B, 1.0f); + if (isLast1AnimeFrame()) { + changePlayerStatus(0x0C400201, 0, false); + } + result = 0; + } + } else if (action == 0x0C000233) { + // jumpEnd - landing type 3 (broadjump/fire) + if (jumpEndEvents(0)) { + result = 1; + } else { + waitProcess(); + setAnimation(0x57, 1.0f); + if (isLast1AnimeFrame()) { + changePlayerStatus(0x0C400201, 0, false); + } + result = 0; + } + } else if (action == 0x0C000233) { + // Duplicate - skip + } else if (action == 0x0C400202) { + // sleepily transition? + result = 0; + } else if (action == 0x0C000233 + 0) { + // fire jump end + if (jumpEndEvents(0)) { + result = 1; + } else { + waitProcess(); + setAnimation(0xBE, 1.0f); + if (isLast1AnimeFrame()) { + changePlayerStatus(0x0C400201, 0, false); + } + // Reset stickLate and update modelFaceAngle + result = 0; + } + } else if (action == 0x80000A36) { + // throw end + checkThrowObject(); + jumpEndCommon(0x65, 0x0C400201); + result = 0; + } else if (action == 0x08000239) { + // pullEnd + mInput &= ~0x2010; + if (jumpEndEvents(0)) { + result = 1; + } else { + waitProcess(); + setAnimation(0x28, 1.0f); + if (isLast1AnimeFrame()) { + changePlayerStatus(0x0C400201, 0, false); + } + result = 0; + } + } else if (action == 0x0800023B) { + // uTurnJumpEnd + mInput &= ~0x2000; + if (jumpEndEvents(0x02000880)) { + if (mAction - 0x04000440 == 0) { + changePlayerStatus(0x0C018222, 0, false); + } else { + result = 1; + } + } else { + waitProcess(); + setAnimation(0x98, 1.0f); + if (isLast1AnimeFrame()) { + changePlayerStatus(0x0C018222, 0, false); + } + result = 0; + } + } else if (action == 0x0080023C) { + // broadJumpEnd + mActionState = 1; + u32 input = mInput; + if (input & 0x04) { + changePlayerStatus(0x088C, 0, false); + } else if (input & 0x08) { + changePlayerStatus(0x00840452, 0, false); + } else { + waitProcess(); + setAnimation(0x3A, 1.0f); + if (isLast1AnimeFrame()) { + changePlayerStatus(0x0C00023E, 0, false); + } + result = 0; + } + result = result; + } else if (action == 0x0C00023D) { + // hipAttackEnd + u32 input = mInput; + if (!(input & 0x10) && (input & 0x0F)) { + checkAllMotions(); + } else { + stopCommon(0x10, 0x0C400201); + result = 0; + } + result = result; + } else if (action == 0x0C00023F) { + // slipEnd + u32 input = mInput; + if (input & 0x0F) { + checkAllMotions(); + } else { + stopCommon(0x8F, 0x0C400201); + result = 0; + } + result = result; + } else { + result = 0; + } + + return result; +} diff --git a/src/Player/WaterGun.cpp b/src/Player/WaterGun.cpp index e70e1303..60d54ff2 100644 --- a/src/Player/WaterGun.cpp +++ b/src/Player/WaterGun.cpp @@ -345,7 +345,7 @@ void TWaterGun::calcAnimation(JDrama::TGraphics* graphics) return; } - s32 var380 = mMario->unk380; + s32 var380 = mMario->mPumpState; if ((var380 & 0x8000) != 0) { var380 = 0; } @@ -439,7 +439,6 @@ MtxPtr TWaterGun::getNozzleMtx() void TWaterGun::initInLoadAfter() { } -bool TWaterGun::isEmitting() { return false; } void TWaterGun::changeNozzle(TNozzleType nozzleType, bool animate) { @@ -634,40 +633,38 @@ void TNozzleBase::emit(int param_1) f32 temp = emittedWaterF * decRateF; f32 temp2 = temp / unk1C88OldF; *unk1C88Ptr = 10.0f * temp2 + unk1C88; - if (emittedWaterU32 == 0) { - goto skip_velocity; - } - mFludd->mCurrentWater - -= emittedWaterU32 * mEmitParams.mDecRate.get(); - if (mFludd->mCurrentWater < 0) { - mFludd->mCurrentWater = 0; - } + if (emittedWaterU32 != 0) { + mFludd->mCurrentWater + -= emittedWaterU32 * mEmitParams.mDecRate.get(); + if (mFludd->mCurrentWater < 0) { + mFludd->mCurrentWater = 0; + } - f32* powPtr = &emitInfo->mPow.value; - JGeometry::TVec3* dirPtr = &emitInfo->mDir.value; - f32 powVal = *powPtr; - s16 faceAngleY = mFludd->mMario->mFaceAngle.y; - f32 cosAngle = JMASCos(faceAngleY); - f32 sinAngle = JMASSin(faceAngleY); - f32 dirX = -dirPtr->x; - f32 dirZ = dirPtr->z; - f32 dirY = dirPtr->y; - f32 reactionPow = powVal * mEmitParams.mReactionPow.get(); - f32 reactionY = mEmitParams.mReactionY.get(); - f32 unkE0 = mEmitParams.mReactionPow.value; - f32 unkF4 = mEmitParams.mReactionY.value; - f32 f31 = powVal * unkE0; - - mFludd->mMario->addVelocity((dirX * sinAngle - dirZ * cosAngle) - * reactionPow); - - f32* velX = &mFludd->mMario->mVel.x; - *velX = -dirPtr->x * reactionPow - *velX; - f32* velZ = &mFludd->mMario->mVel.z; - *velZ = -dirPtr->z * reactionPow - *velZ; - f32* velY = &mFludd->mMario->mVel.y; - *velY = *velY - dirY * powVal * unkF4 * reactionY; - skip_velocity:; + f32* powPtr = &emitInfo->mPow.value; + JGeometry::TVec3* dirPtr = &emitInfo->mDir.value; + f32 powVal = *powPtr; + s16 faceAngleY = mFludd->mMario->mFaceAngle.y; + f32 cosAngle = JMASCos(faceAngleY); + f32 sinAngle = JMASSin(faceAngleY); + f32 dirX = -dirPtr->x; + f32 dirZ = dirPtr->z; + f32 dirY = dirPtr->y; + f32 reactionPow = powVal * mEmitParams.mReactionPow.get(); + f32 reactionY = mEmitParams.mReactionY.get(); + f32 unkE0 = mEmitParams.mReactionPow.value; + f32 unkF4 = mEmitParams.mReactionY.value; + f32 f31 = powVal * unkE0; + + mFludd->mMario->addVelocity((dirX * sinAngle - dirZ * cosAngle) + * reactionPow); + + f32* velX = &mFludd->mMario->mVel.x; + *velX = -dirPtr->x * reactionPow - *velX; + f32* velZ = &mFludd->mMario->mVel.z; + *velZ = -dirPtr->z * reactionPow - *velZ; + f32* velY = &mFludd->mMario->mVel.y; + *velY = *velY - dirY * powVal * unkF4 * reactionY; + } } } } @@ -929,7 +926,7 @@ void TNozzleTrigger::movement(const TMarioControllerWork& controllerWork) // Very likely an inline bool check; - if (mFludd->mMario->unk380 == 0) { + if (mFludd->mMario->mPumpState == 0) { check = true; } else { check = false; @@ -967,12 +964,12 @@ void TNozzleTrigger::movement(const TMarioControllerWork& controllerWork) // Most likely some inlined stuff, not matching bool canSpray; bool other = true; - if (mario->unk380 == 0) { + if (mario->mPumpState == 0) { canSpray = true; } else { canSpray = false; } - if ((mario->unk118 & 0x30000) == 0 + if ((mario->mState & 0x30000) == 0 && mFludd->mCurrentWater < mEmitParams.mAmountMax.get()) { canSpray = false; } diff --git a/src/System/MarDirectorDirect.cpp b/src/System/MarDirectorDirect.cpp index 52c83a14..7af2a531 100644 --- a/src/System/MarDirectorDirect.cpp +++ b/src/System/MarDirectorDirect.cpp @@ -596,7 +596,7 @@ void TMarDirector::setMario() u32 uVar6 = SMS_getShineIDofExStage(gpApplication.mCurrArea.getStage()); if (uVar6 != 0xff && TFlagManager::getInstance()->getShineFlag(uVar6) == 0) - gpMarioOriginal->unk118 &= ~0x8000; + gpMarioOriginal->mState &= ~0x8000; } void TMarDirector::nextStateInitialize(u8 next_state) diff --git a/tools/blue_coin_tracker.txt b/tools/blue_coin_tracker.txt new file mode 100644 index 00000000..b040d955 --- /dev/null +++ b/tools/blue_coin_tracker.txt @@ -0,0 +1,150 @@ +=============================================================================== + Blue Coin Pause Counter for Super Mario Sunshine (GMSJ01) + Gecko Code v1.0 +=============================================================================== + +Displays the total blue coin count on screen while the game is paused. +Uses JUTDirectPrint to render directly to the embedded framebuffer, +bypassing J2D/GX setup entirely. + +Text appears as white 8x8 pixel debug text near the bottom-left: + "Blue: XX/240" + +Key addresses: + gpMarDirector = 0x8040A2A8 (game director singleton) + gpMarDirector+0x124 = game state (10 = paused) + sDirectPrint = 0x80409870 (JUTDirectPrint singleton) + drawString_f = 0x800115D8 (printf-style EFB text draw) + smInstance = 0x8040A290 (TFlagManager singleton) + smInstance+0xD4 = mGameInts[1] (total blue coin count) + +TFlagManager layout (offsets to mGameInts[1]): + mCardBools[119] @ +0x00 (119 bytes) + <1 byte padding> + mCardInts[21] @ +0x78 (84 bytes) + mGameBools[4] @ +0xCC (4 bytes) + mGameInts[5] @ +0xD0 (20 bytes) ← [1] = blue coin count @ +0xD4 + +=============================================================================== + GECKO CODE +=============================================================================== + +$Blue Coin Pause Counter [GMSJ01] +C0000000 00000014 +9421FFA0 7C0802A6 +90010064 3D608041 +806BA2A8 28030000 +41820074 88030124 +2C00000A 40820068 +806B9870 28030000 +4182005C 808BA290 +28040000 41820050 +80E400D4 3C00426C +60007565 90010030 +3C003A20 60002564 +90010034 3C002F32 +60003430 90010038 +38000000 9001003C +38800010 38A001A4 +38C10030 3D808001 +618C15D8 7D8903A6 +4E800421 80010064 +7C0803A6 38210060 +4E800020 00000000 + +=============================================================================== + DISASSEMBLY +=============================================================================== + +// C0 code: runs every frame via Gecko handler + +// --- Prologue --- +stwu r1, -0x60(r1) // allocate stack (0x60 for param save + locals) +mflr r0 // save link register +stw r0, 0x64(r1) + +// --- Load common base for globals (all in 0x8040xxxx) --- +lis r11, 0x8041 // r11 = 0x80410000 + +// --- Check: is game paused? --- +lwz r3, 0xA2A8(r11) // r3 = gpMarDirector (0x8040A2A8) +cmplwi r3, 0 +beq cleanup // director not initialized +lbz r0, 0x124(r3) // r0 = game state byte +cmpwi r0, 10 // 10 = STATE_PAUSE +bne cleanup // not paused, skip drawing + +// --- Get JUTDirectPrint singleton --- +lwz r3, 0x9870(r11) // r3 = sDirectPrint (0x80409870) +cmplwi r3, 0 +beq cleanup // not initialized + +// --- Get TFlagManager + blue coin count --- +lwz r4, 0xA290(r11) // r4 = TFlagManager::smInstance (0x8040A290) +cmplwi r4, 0 +beq cleanup // not initialized +lwz r7, 0xD4(r4) // r7 = mGameInts[1] = total blue coins + +// --- Build format string "Blue: %d/240" on stack --- +lis r0, 0x426C // +ori r0, r0, 0x7565 // "Blue" +stw r0, 0x30(r1) +lis r0, 0x3A20 // +ori r0, r0, 0x2564 // ": %d" +stw r0, 0x34(r1) +lis r0, 0x2F32 // +ori r0, r0, 0x3430 // "/240" +stw r0, 0x38(r1) +li r0, 0 // +stw r0, 0x3C(r1) // "\0\0\0\0" (null terminator) + +// --- Call drawString_f(this, x, y, fmt, count) --- +// r3 = sDirectPrint (this) [already set] +li r4, 16 // x = 16 pixels from left +li r5, 420 // y = 420 pixels from top +addi r6, r1, 0x30 // fmt = stack string address +// r7 = blue coin count [already set] +lis r12, 0x8001 +ori r12, r12, 0x15D8 // r12 = drawString_f (0x800115D8) +mtctr r12 +bctrl // JUTDirectPrint::drawString_f(16, 420, "Blue: %d/240", count) + +// --- Cleanup --- +cleanup: +lwz r0, 0x64(r1) // restore link register +mtlr r0 +addi r1, r1, 0x60 // deallocate stack +blr // return to Gecko handler + +=============================================================================== + NOTES +=============================================================================== + +- Uses C0 (execute) Gecko code type: runs once per frame regardless of + game state. All pause-state checks are done in the code itself. + +- JUTDirectPrint::drawString_f uses GXPokeARGB to write 8x8 pixel white + characters directly to the embedded framebuffer. No J2D/GX context + setup is required. + +- The format string is built on the stack to avoid needing a separate + memory write code (06 type) for string storage. + +- Stack frame is 0x60 bytes: 0x08 backchain/padding + 0x28 parameter + save area (for drawString_f callee) + 0x10 format string + 0x10 + saved registers/LR. + +- The blue coin count is read from TFlagManager::mGameInts[1] (flag + 0x40001), which the game maintains as a running total. It is + recomputed from individual blue coin flags on save load via + TFlagManager::correctFlag(). + +- Blue coin flags are stored as card-persistent bools in the range + 0x10078 - 0x10365 (9 stages x 50 slots, though only ~30 per stage + are used). Per-stage counts could be added by iterating these flags, + but that would roughly double the code size. + +- Text position (16, 420) places it in the bottom-left corner of + the screen. Adjust r4/r5 (instructions 29/30 in the code) to move it. + +=============================================================================== diff --git a/tools/claude/check_match.py b/tools/claude/check_match.py new file mode 100644 index 00000000..badc4de7 --- /dev/null +++ b/tools/claude/check_match.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +""" +Check match status for a specific source file. +Usage: python check_match.py +Example: python check_match.py System/J3DSysFlag +""" + +import json +import sys +from pathlib import Path + +def main(): + if len(sys.argv) < 2: + print("Usage: python check_match.py ") + print("Example: python check_match.py System/J3DSysFlag") + sys.exit(1) + + search = sys.argv[1].replace("\\", "/") + + # Find project root + script_dir = Path(__file__).parent + project_root = script_dir.parent.parent + report_path = project_root / "build" / "GMSJ01" / "report.json" + + if not report_path.exists(): + print(f"Error: {report_path} not found. Run 'ninja' first.") + sys.exit(1) + + with open(report_path) as f: + data = json.load(f) + + found = False + for unit in data.get("units", []): + name = unit.get("name", "") + if search.lower() in name.lower(): + found = True + print(f"\n=== {name} ===") + + total_code = unit.get("total_code", 0) + complete_code = unit.get("complete_code", 0) + if total_code > 0: + pct = complete_code / total_code * 100 + print(f"Overall: {complete_code}/{total_code} bytes ({pct:.1f}%)") + + functions = unit.get("functions", []) + matching = 0 + print(f"\nFunctions ({len(functions)} total):") + + for fn in functions: + fn_name = fn.get("name", "?") + fuzzy = fn.get("fuzzy_match_percent", 0) + size = fn.get("size", 0) + + if fuzzy == 100: + status = "MATCH" + matching += 1 + else: + status = f"{fuzzy:.1f}%" + + # Demangle name for readability + short_name = fn_name + if "__" in fn_name: + parts = fn_name.split("__") + short_name = parts[0] + + print(f" {short_name}: {status} ({size} bytes)") + + print(f"\nSummary: {matching}/{len(functions)} functions at 100%") + + if not found: + print(f"No units found matching '{search}'") + print("\nAvailable units containing your search:") + for unit in data.get("units", []): + name = unit.get("name", "") + if any(part.lower() in name.lower() for part in search.split("/")): + print(f" {name}") + +if __name__ == "__main__": + main() diff --git a/tools/claude/compare_asm.py b/tools/claude/compare_asm.py new file mode 100644 index 00000000..cfb45f4e --- /dev/null +++ b/tools/claude/compare_asm.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +""" +Compare assembly between original and compiled object files. +Usage: python compare_asm.py [function_name] +Example: python compare_asm.py System/J3DSysFlag perform__11TJ3DSysFlagFUlPQ26JDrama9TGraphics +""" + +import subprocess +import sys +from pathlib import Path + +def disassemble(objdump, obj_path, function=None): + """Disassemble an object file, optionally filtering to a specific function.""" + cmd = [str(objdump), "-d", str(obj_path)] + result = subprocess.run(cmd, capture_output=True, text=True) + + if result.returncode != 0: + return f"Error: {result.stderr}" + + output = result.stdout + + if function: + # Extract just the specified function + lines = output.split("\n") + in_function = False + func_lines = [] + + for line in lines: + if f"<{function}>:" in line: + in_function = True + elif in_function: + if line.strip() == "" or (line[0] != " " and ":" in line and "<" in line): + break + func_lines.append(line) + + if func_lines: + return "\n".join(func_lines) + else: + return f"Function '{function}' not found" + + return output + +def main(): + if len(sys.argv) < 2: + print("Usage: python compare_asm.py [function_name]") + print("Example: python compare_asm.py System/J3DSysFlag") + print("Example: python compare_asm.py System/J3DSysFlag perform__11TJ3DSysFlagFUlPQ26JDrama9TGraphics") + sys.exit(1) + + file_path = sys.argv[1].replace("\\", "/") + function = sys.argv[2] if len(sys.argv) > 2 else None + + # Find project root + script_dir = Path(__file__).parent + project_root = script_dir.parent.parent + + objdump = project_root / "build" / "binutils" / "powerpc-eabi-objdump.exe" + if not objdump.exists(): + print(f"Error: objdump not found at {objdump}") + print("Run 'ninja' first to download tools.") + sys.exit(1) + + # Find object files + orig_obj = project_root / "build" / "GMSJ01" / "obj" / f"{file_path}.o" + comp_obj = project_root / "build" / "GMSJ01" / "src" / f"{file_path}.o" + + if not orig_obj.exists(): + print(f"Error: Original object not found: {orig_obj}") + sys.exit(1) + + if not comp_obj.exists(): + print(f"Error: Compiled object not found: {comp_obj}") + print("Make sure the source file exists and run 'ninja'.") + sys.exit(1) + + # Disassemble both + print("=" * 60) + print("ORIGINAL") + print("=" * 60) + orig_asm = disassemble(objdump, orig_obj, function) + print(orig_asm) + + print("\n" + "=" * 60) + print("COMPILED") + print("=" * 60) + comp_asm = disassemble(objdump, comp_obj, function) + print(comp_asm) + + # Quick diff summary + if function: + orig_lines = [l.strip() for l in orig_asm.split("\n") if l.strip()] + comp_lines = [l.strip() for l in comp_asm.split("\n") if l.strip()] + + print("\n" + "=" * 60) + print("SUMMARY") + print("=" * 60) + print(f"Original: {len(orig_lines)} instructions") + print(f"Compiled: {len(comp_lines)} instructions") + + if len(orig_lines) == len(comp_lines): + matches = sum(1 for o, c in zip(orig_lines, comp_lines) + if o.split("\t")[-1] == c.split("\t")[-1]) + print(f"Matching instructions: {matches}/{len(orig_lines)}") + +if __name__ == "__main__": + main() diff --git a/tools/claude/diff_analyze.py b/tools/claude/diff_analyze.py new file mode 100644 index 00000000..163fd805 --- /dev/null +++ b/tools/claude/diff_analyze.py @@ -0,0 +1,49 @@ +import re + +with open('C:/Users/ryana/Documents/sms/diff_out.txt') as f: + lines = f.readlines() + +compiled_start = None +for i, line in enumerate(lines): + if 'COMPILED' in line: + compiled_start = i + break + +orig = lines[3:compiled_start] +comp = lines[compiled_start+3:] + +def get_hex(line): + m = re.match(r'\s+([0-9a-f]+):\t([0-9a-f ]+)\t', line) + if m: + return m.group(2).strip() + return None + +orig_hex = [] +comp_hex = [] +for line in orig: + h = get_hex(line) + if h: + orig_hex.append(h) +for line in comp: + h = get_hex(line) + if h: + comp_hex.append(h) + +print(f'Orig instructions: {len(orig_hex)}, Compiled: {len(comp_hex)}') + +for i in range(min(len(orig_hex), len(comp_hex))): + if orig_hex[i] != comp_hex[i]: + print(f'\nFirst diff at instruction {i}:') + for j in range(max(0,i-3), min(i+8, len(orig_hex), len(comp_hex))): + marker = '>>>' if j >= i and orig_hex[j] != comp_hex[j] else ' ' + print(f'{marker} {j}: O={orig_hex[j]:24s} C={comp_hex[j]}') + break +else: + if len(orig_hex) != len(comp_hex): + print(f'Instructions match up to {min(len(orig_hex), len(comp_hex))}, but lengths differ') + extra_in = 'orig' if len(orig_hex) > len(comp_hex) else 'comp' + longer = orig_hex if len(orig_hex) > len(comp_hex) else comp_hex + start = min(len(orig_hex), len(comp_hex)) + print(f'Extra in {extra_in}:') + for j in range(start, min(start+10, len(longer))): + print(f' {j}: {longer[j]}') diff --git a/tools/claude/find_bool_mat.py b/tools/claude/find_bool_mat.py new file mode 100644 index 00000000..696ab3fc --- /dev/null +++ b/tools/claude/find_bool_mat.py @@ -0,0 +1,132 @@ +import json +import subprocess +import re +from pathlib import Path + +# Parse report.json +report_path = Path(r'C:\users\ryana\documents\sms\build\GMSJ01\report.json') +with open(report_path) as f: + data = json.load(f) + +# Find non-matching functions in src/ with 80-99% match +candidates = [] +for unit in data.get('units', []): + metadata = unit.get('metadata', {}) + source_path = metadata.get('source_path', '') + + # Only look at game code (src/), not SDK + if not source_path.startswith('src/'): + continue + + # Skip if it's SDK code based on progress categories + categories = metadata.get('progress_categories', []) + if 'sdk' in categories: + continue + + for func in unit.get('functions', []): + match_pct = func.get('fuzzy_match_percent', 0) + if 80 <= match_pct < 100: + candidates.append({ + 'unit': unit.get('name', ''), + 'source_path': source_path, + 'function': func['name'], + 'match_pct': match_pct + }) + +# Sort by match percentage (highest first) +candidates.sort(key=lambda x: -x['match_pct']) + +print(f"Found {len(candidates)} candidates between 80-99% match") +print("\nTop 30 candidates:") +for i, c in enumerate(candidates[:30], 1): + print(f"{i}. {c['match_pct']:.1f}% {c['source_path']} :: {c['function']}") + +# Now check for boolean materialization pattern in top candidates +objdump = Path(r'C:\users\ryana\documents\sms\build\binutils\powerpc-eabi-objdump.exe') +bool_mat_pattern = b'\x54\x00\x06\x3f' # clrlwi. r0, r0, 24 + +print("\n\nChecking for boolean materialization pattern...") +print("=" * 80) + +results = [] +for c in candidates[:50]: # Check top 50 + # Convert src/path.cpp to path.o + source_path = c['source_path'] + unit_path = source_path.replace('src/', '').replace('.cpp', '').replace('.c', '') + orig_obj = Path(f"C:/users/ryana/documents/sms/build/GMSJ01/obj/{unit_path}.o") + comp_obj = Path(f"C:/users/ryana/documents/sms/build/GMSJ01/src/{unit_path}.o") + + if not orig_obj.exists() or not comp_obj.exists(): + continue + + try: + # Disassemble original + orig_asm = subprocess.run( + [str(objdump), '-d', str(orig_obj)], + capture_output=True, + text=True, + timeout=5 + ) + + # Disassemble compiled + comp_asm = subprocess.run( + [str(objdump), '-d', str(comp_obj)], + capture_output=True, + text=True, + timeout=5 + ) + + # Extract function assembly + func_name = c['function'] + orig_func = extract_function(orig_asm.stdout, func_name) + comp_func = extract_function(comp_asm.stdout, func_name) + + if not orig_func or not comp_func: + continue + + # Count clrlwi. r0, r0, 24 pattern (boolean materialization) + orig_count = orig_func.count('clrlwi.') + comp_count = comp_func.count('clrlwi.') + + # Also check for li r0, 1 / li r0, 0 pattern + orig_li_pattern = len(re.findall(r'li\s+r0,\s*[01]', orig_func)) + comp_li_pattern = len(re.findall(r'li\s+r0,\s*[01]', comp_func)) + + if orig_count > comp_count or (orig_count > 0 and orig_li_pattern > comp_li_pattern): + results.append({ + 'source_path': c['source_path'], + 'function': func_name, + 'match_pct': c['match_pct'], + 'orig_clrlwi': orig_count, + 'comp_clrlwi': comp_count, + 'orig_li': orig_li_pattern, + 'comp_li': comp_li_pattern + }) + except Exception as e: + pass + +def extract_function(asm_text, func_name): + """Extract assembly for a specific function""" + lines = asm_text.split('\n') + in_func = False + func_lines = [] + + for line in lines: + if f'<{func_name}>' in line or f'<{func_name}+' in line: + in_func = True + elif in_func and re.match(r'^[0-9a-f]+\s+<\w+>', line): + # Next function started + break + + if in_func: + func_lines.append(line) + + return '\n'.join(func_lines) + +print(f"\nFound {len(results)} functions with boolean materialization opportunities:") +print("=" * 80) +for r in results: + print(f"{r['match_pct']:.1f}% {r['source_path']} :: {r['function']}") + print(f" clrlwi.: orig={r['orig_clrlwi']}, comp={r['comp_clrlwi']}") + print(f" li r0,0/1: orig={r['orig_li']}, comp={r['comp_li']}") + print() diff --git a/tools/claude/find_easy_targets.py b/tools/claude/find_easy_targets.py new file mode 100644 index 00000000..e1014a8e --- /dev/null +++ b/tools/claude/find_easy_targets.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +""" +Find non-matching files sorted by number of functions (easier targets first). +Usage: python find_easy_targets.py [--category game|jsystem|sdk] [--max-functions N] +""" + +import json +import sys +import argparse +from pathlib import Path + +def main(): + parser = argparse.ArgumentParser(description="Find easy decompilation targets") + parser.add_argument("--category", choices=["game", "jsystem", "sdk"], + help="Filter by category") + parser.add_argument("--max-functions", type=int, default=20, + help="Maximum number of functions (default: 20)") + parser.add_argument("--min-match", type=float, default=0, + help="Minimum match percentage to show") + parser.add_argument("--show-functions", action="store_true", + help="Show individual function details") + args = parser.parse_args() + + # Find project root + script_dir = Path(__file__).parent + project_root = script_dir.parent.parent + report_path = project_root / "build" / "GMSJ01" / "report.json" + + if not report_path.exists(): + print(f"Error: {report_path} not found. Run 'ninja' first.") + sys.exit(1) + + with open(report_path) as f: + data = json.load(f) + + # Collect non-matching units + targets = [] + for unit in data.get("units", []): + name = unit.get("name", "") + functions = unit.get("functions", []) + total_code = unit.get("total_code", 0) + complete_code = unit.get("complete_code", 0) + + # Skip fully matching + if complete_code == total_code and total_code > 0: + continue + + # Calculate match percentage + match_pct = (complete_code / total_code * 100) if total_code > 0 else 0 + + # Filter by minimum match + if match_pct < args.min_match: + continue + + # Determine category + category = "game" + if "JSystem" in name or "jsystem" in name.lower(): + category = "jsystem" + elif "dolphin" in name.lower() or "sdk" in name.lower() or "PowerPC" in name or "TRK" in name or "MSL" in name: + category = "sdk" + + # Filter by category + if args.category and category != args.category: + continue + + # Filter by function count + num_functions = len(functions) + if num_functions > args.max_functions: + continue + + # Count matching functions + matching_fns = sum(1 for f in functions if f.get("fuzzy_match_percent", 0) == 100) + + targets.append({ + "name": name, + "category": category, + "num_functions": num_functions, + "matching_functions": matching_fns, + "total_bytes": total_code, + "match_pct": match_pct, + "functions": functions + }) + + # Sort by number of functions (ascending), then by match percentage (descending) + targets.sort(key=lambda x: (x["num_functions"], -x["match_pct"])) + + # Display results + print(f"Found {len(targets)} potential targets (max {args.max_functions} functions)") + print() + + for t in targets[:50]: # Show top 50 + name = t["name"] + num_fns = t["num_functions"] + matching = t["matching_functions"] + pct = t["match_pct"] + category = t["category"] + + status = f"{matching}/{num_fns} fns match" + print(f"[{category:7}] {name}") + print(f" {status}, {pct:.1f}% bytes, {t['total_bytes']} total bytes") + + if args.show_functions: + for fn in t["functions"]: + fn_name = fn.get("name", "?") + fn_pct = fn.get("fuzzy_match_percent", 0) + fn_size = fn.get("size", 0) + fn_status = "MATCH" if fn_pct == 100 else f"{fn_pct:.1f}%" + print(f" - {fn_name[:50]}: {fn_status} ({fn_size}b)") + + print() + +if __name__ == "__main__": + main() diff --git a/tools/claude/find_game_targets.py b/tools/claude/find_game_targets.py new file mode 100644 index 00000000..ceafc8d3 --- /dev/null +++ b/tools/claude/find_game_targets.py @@ -0,0 +1,77 @@ +"""Find game category units with 1-5 unmatched functions, sorted by easiest targets.""" +import json +import sys + +with open('build/GMSJ01/report.json', 'r') as f: + report = json.load(f) + +units = report.get('units', []) + +# Collect candidates from game category +candidates = [] +for unit in units: + # Check if this is a game unit + cats = unit.get('metadata', {}).get('progress_categories', []) + if 'game' not in cats: + continue + + name = unit.get('name', '') + measures = unit.get('measures', {}) + functions = unit.get('functions', []) + + total_code = int(measures.get('total_code', '0') or '0') + matched_code = int(measures.get('matched_code', '0') or '0') + matched_code_pct = float(measures.get('matched_code_percent', 0) or 0) + + total_funcs = len(functions) + if total_funcs == 0: + continue + + # Count matched vs unmatched functions + matched_funcs = 0 + unmatched_funcs = 0 + unmatched_details = [] + for func in functions: + fuzz = float(func.get('fuzzy_match_percent', 0) or 0) + func_size = int(func.get('size', '0') or '0') + # A function is matched if fuzzy_match_percent == 100 + if fuzz == 100.0: + matched_funcs += 1 + else: + unmatched_funcs += 1 + demangled = func.get('metadata', {}).get('demangled_name', '') + unmatched_details.append({ + 'name': func.get('name', '?'), + 'demangled': demangled, + 'size': func_size, + 'fuzzy_pct': fuzz, + }) + + # Filter: has 1-5 unmatched functions, total code > 0 + if 1 <= unmatched_funcs <= 5 and total_code > 0: + unmatched_code = total_code - matched_code + candidates.append({ + 'name': name, + 'total_funcs': total_funcs, + 'matched_funcs': matched_funcs, + 'unmatched_funcs': unmatched_funcs, + 'total_code': total_code, + 'matched_code': matched_code, + 'unmatched_code': unmatched_code, + 'matched_pct': matched_code_pct, + 'unmatched_details': unmatched_details, + }) + +# Sort by: unmatched code size (smallest first), then fewest unmatched functions +candidates.sort(key=lambda x: (x['unmatched_code'], x['unmatched_funcs'])) + +# Print top 20 +print(f'Found {len(candidates)} game units with 1-5 unmatched functions\n') +print(f'{"#":<3} {"Unit Name":<55} {"Funcs":>5} {"Match":>5} {"Unm":>4} {"TotalCode":>9} {"UnmCode":>8} {"MatchPct":>8}') +print('-' * 100) +for i, c in enumerate(candidates[:20]): + print(f'{i+1:<3} {c["name"]:<55} {c["total_funcs"]:>5} {c["matched_funcs"]:>5} {c["unmatched_funcs"]:>4} {c["total_code"]:>9} {c["unmatched_code"]:>8} {c["matched_pct"]:>7.1f}%') + for ud in c['unmatched_details']: + dn = ud['demangled'] if ud['demangled'] else ud['name'] + print(f' -> {dn} ({ud["size"]} bytes, fuzzy={ud["fuzzy_pct"]:.1f}%)') + print() diff --git a/tools/claude/get_symbols.py b/tools/claude/get_symbols.py new file mode 100644 index 00000000..83ec6ce0 --- /dev/null +++ b/tools/claude/get_symbols.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +""" +Get symbols for a specific source file from symbols.txt. +Usage: python get_symbols.py +Example: python get_symbols.py J3DSysFlag +Example: python get_symbols.py TButterfly +""" + +import sys +import re +from pathlib import Path + +def demangle_simple(name): + """Simple demangling for common patterns.""" + # Constructor + if name.startswith("__ct__"): + class_name = name[6:].split("F")[0] + return f"{class_name}::{class_name.split('Q')[-1].lstrip('0123456789')}()" + + # Destructor + if name.startswith("__dt__"): + class_name = name[6:].split("F")[0] + return f"{class_name}::~{class_name.split('Q')[-1].lstrip('0123456789')}()" + + # Virtual table + if name.startswith("__vt__"): + class_name = name[6:] + return f"{class_name}::__vtable" + + # Regular function + if "__" in name: + parts = name.split("__") + fn_name = parts[0] + if len(parts) > 1: + class_info = parts[1] + # Extract class name (before F which starts parameters) + class_match = re.match(r"(\d*)([A-Za-z_]\w*)", class_info) + if class_match: + class_name = class_match.group(2) + return f"{class_name}::{fn_name}()" + + return name + +def main(): + if len(sys.argv) < 2: + print("Usage: python get_symbols.py ") + print("Example: python get_symbols.py J3DSysFlag") + print("Example: python get_symbols.py TButterfly") + sys.exit(1) + + search = sys.argv[1] + + # Find project root + script_dir = Path(__file__).parent + project_root = script_dir.parent.parent + symbols_path = project_root / "config" / "GMSJ01" / "symbols.txt" + + if not symbols_path.exists(): + print(f"Error: {symbols_path} not found") + sys.exit(1) + + # Read and filter symbols + functions = [] + data_symbols = [] + other = [] + + with open(symbols_path, encoding="utf-8") as f: + for line in f: + line = line.strip() + if not line or line.startswith("#"): + continue + + if search.lower() in line.lower(): + # Parse symbol line + # Format: name = section:address; // type:X size:Y scope:Z + try: + name_part = line.split("=")[0].strip() + rest = line.split("//")[1].strip() if "//" in line else "" + + # Extract metadata + sym_type = "unknown" + size = 0 + scope = "global" + + for part in rest.split(): + if part.startswith("type:"): + sym_type = part.split(":")[1] + elif part.startswith("size:"): + size_str = part.split(":")[1] + size = int(size_str, 16) if size_str.startswith("0x") else int(size_str) + elif part.startswith("scope:"): + scope = part.split(":")[1] + + entry = { + "name": name_part, + "demangled": demangle_simple(name_part), + "type": sym_type, + "size": size, + "scope": scope, + "line": line + } + + if sym_type == "function": + functions.append(entry) + elif sym_type == "object": + data_symbols.append(entry) + else: + other.append(entry) + + except Exception: + other.append({"line": line}) + + # Display results + if functions: + print(f"=== Functions ({len(functions)}) ===") + # Sort by size descending + functions.sort(key=lambda x: x["size"], reverse=True) + for f in functions: + scope_marker = "[weak]" if f["scope"] == "weak" else "" + print(f" {f['demangled']}") + print(f" Size: 0x{f['size']:X} ({f['size']} bytes) {scope_marker}") + print(f" Symbol: {f['name']}") + print() + + if data_symbols: + print(f"=== Data ({len(data_symbols)}) ===") + for d in data_symbols: + print(f" {d['name']}: {d['size']} bytes") + + if other: + print(f"=== Other ({len(other)}) ===") + for o in other: + if "line" in o: + print(f" {o.get('line', '')[:80]}") + + if not functions and not data_symbols and not other: + print(f"No symbols found matching '{search}'") + + # Summary + total_code = sum(f["size"] for f in functions) + print(f"\nTotal: {len(functions)} functions, {total_code} bytes of code") + +if __name__ == "__main__": + main() diff --git a/tools/claude/inspect_report.py b/tools/claude/inspect_report.py new file mode 100644 index 00000000..c4b03673 --- /dev/null +++ b/tools/claude/inspect_report.py @@ -0,0 +1,67 @@ +"""Inspect report.json structure to understand matching fields.""" +import json + +with open('build/GMSJ01/report.json', 'r') as f: + report = json.load(f) + +print("Top-level keys:", list(report.keys())) +print() + +cats = report.get('categories', []) +print(f"Number of categories: {len(cats)}") +for cat in cats: + print(f" Category: id={cat.get('id')}, name={cat.get('name')}, units={len(cat.get('units', []))}") + +# Find game category +game_cat = None +for cat in cats: + if cat.get('id') == 'game': + game_cat = cat + break + +if not game_cat: + # Try other approaches + print("\nNo 'game' category. Checking all category ids:") + for cat in cats: + print(f" '{cat.get('id')}'") + exit() + +units = game_cat.get('units', []) +print(f"\nGame category has {len(units)} units") + +# Show a few example units with functions +shown = 0 +for unit in units: + functions = unit.get('functions', []) + measures = unit.get('measures', {}) + if len(functions) > 0 and measures.get('total_code', 0) > 0: + print(f"\n--- Unit: {unit.get('name')} ---") + print(f" Unit keys: {list(unit.keys())}") + print(f" Measures: {measures}") + print(f" Functions count: {len(functions)}") + if functions: + f0 = functions[0] + print(f" First function keys: {list(f0.keys())}") + print(f" First function: {json.dumps(f0, indent=2)[:500]}") + shown += 1 + if shown >= 3: + break + +# Also show units that have some non-100% match +print("\n\n=== Units with partial matches ===") +shown2 = 0 +for unit in units: + measures = unit.get('measures', {}) + mcp = measures.get('matched_code_percent', 0) + tc = measures.get('total_code', 0) + if tc > 0 and mcp > 0 and mcp < 100: + functions = unit.get('functions', []) + print(f"\nUnit: {unit.get('name')}") + print(f" Measures: matched_code_percent={mcp}, total_code={tc}, matched_code={measures.get('matched_code', 0)}") + print(f" Functions: {len(functions)}") + for func in functions[:3]: + fm = func.get('measures', {}) + print(f" Func: {func.get('name')}, measures={fm}, fuzzy={func.get('fuzzy_match_percent', 'N/A')}") + shown2 += 1 + if shown2 >= 5: + break diff --git a/tools/claude/inspect_report2.py b/tools/claude/inspect_report2.py new file mode 100644 index 00000000..1f342b49 --- /dev/null +++ b/tools/claude/inspect_report2.py @@ -0,0 +1,58 @@ +"""Inspect report.json structure - top-level units.""" +import json + +with open('build/GMSJ01/report.json', 'r') as f: + report = json.load(f) + +print("Top-level keys:", list(report.keys())) + +units = report.get('units', []) +print(f"Top-level units count: {len(units)}") + +# Check category structure more carefully +cats = report.get('categories', []) +for cat in cats: + print(f"\nCategory '{cat.get('id')}' keys: {list(cat.keys())}") + +# Show first few units +for i, unit in enumerate(units[:3]): + print(f"\n--- Unit {i} ---") + print(f" Keys: {list(unit.keys())}") + # Don't print functions, too long + unit_copy = {k: v for k, v in unit.items() if k != 'functions' and k != 'sections'} + print(f" Data: {json.dumps(unit_copy, indent=2)[:600]}") + funcs = unit.get('functions', []) + print(f" Functions: {len(funcs)}") + if funcs: + f0 = {k: v for k, v in funcs[0].items()} + print(f" First func: {json.dumps(f0, indent=2)[:400]}") + +# Find game units +print("\n\n=== Finding game units ===") +game_units = [] +for unit in units: + # Check if unit has category field + cat = unit.get('category', unit.get('metadata', {}).get('category', '')) + if not cat: + # Maybe categories are mapped by some other mechanism + name = unit.get('name', '') + # Just count by prefix patterns + game_units.append(unit) + +# Show all unique category values +cat_values = set() +for unit in units: + for key in unit.keys(): + if 'cat' in key.lower(): + cat_values.add(f"{key}={unit[key]}") + # also check metadata + if 'metadata' in unit: + for key in unit['metadata'].keys(): + if 'cat' in key.lower(): + cat_values.add(f"metadata.{key}={unit['metadata'][key]}") + +print(f"Category-related fields found: {cat_values}") + +# Check how categories reference units +for cat in cats: + print(f"\nCategory '{cat.get('id')}': {json.dumps(cat, indent=2)[:300]}") diff --git a/tools/claude/view_asm.py b/tools/claude/view_asm.py new file mode 100644 index 00000000..3bc0434c --- /dev/null +++ b/tools/claude/view_asm.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +""" +View original assembly for a source file. +Usage: python view_asm.py [function_name] +Example: python view_asm.py System/J3DSysFlag +Example: python view_asm.py System/J3DSysFlag TJ3DSysFlag::perform +""" + +import sys +import re +from pathlib import Path + +def main(): + if len(sys.argv) < 2: + print("Usage: python view_asm.py [function_search]") + print("Example: python view_asm.py System/J3DSysFlag") + print("Example: python view_asm.py System/J3DSysFlag TJ3DSysFlag::perform") + sys.exit(1) + + file_path = sys.argv[1].replace("\\", "/") + search = sys.argv[2] if len(sys.argv) > 2 else None + + # Find project root + script_dir = Path(__file__).parent + project_root = script_dir.parent.parent + asm_path = project_root / "build" / "GMSJ01" / "asm" / f"{file_path}.s" + + if not asm_path.exists(): + print(f"Error: Assembly file not found: {asm_path}") + print("Make sure the file path is correct and run 'ninja' first.") + sys.exit(1) + + with open(asm_path, encoding="utf-8") as f: + content = f.read() + + if search: + # Find functions matching search + lines = content.split("\n") + in_function = False + function_lines = [] + current_fn = "" + + for line in lines: + # Check for function start + fn_match = re.match(r"^\.fn (\S+),", line) + if fn_match: + current_fn = fn_match.group(1) + if search.lower() in current_fn.lower(): + in_function = True + function_lines.append(f"\n{'='*60}") + function_lines.append(f"# {current_fn}") + function_lines.append(f"{'='*60}") + + if in_function: + function_lines.append(line) + + # Check for function end + if line.startswith(".endfn") and in_function: + in_function = False + function_lines.append("") + + if function_lines: + print("\n".join(function_lines)) + else: + print(f"No functions found matching '{search}'") + print("\nAvailable functions in this file:") + for line in lines: + fn_match = re.match(r"^\.fn (\S+),", line) + if fn_match: + print(f" {fn_match.group(1)}") + else: + # Print entire file + print(content) + +if __name__ == "__main__": + main() diff --git a/tools/claude/wireHanging_impl.txt b/tools/claude/wireHanging_impl.txt new file mode 100644 index 00000000..0eea7141 --- /dev/null +++ b/tools/claude/wireHanging_impl.txt @@ -0,0 +1,173 @@ +void TMario::wireHanging() +{ + s16 angle; + getOnWirePosAngle(&mPosition, &angle); + + if ((mInput & 0x2) && (mFloorPosition.x - mFloorPosition.y >= 160.0f)) { + mWireBounceVel = 5.0f; + changePlayerStatus(0x10000556, 0, false); + return; + } + + if (mInput & 0x8000) { + mHolder->receiveMessage(this, 8); + mHolder = 0; + + f32 zero = 0.0f; + mVel.y = zero; + mForwardVel = zero; + + mPosition.x -= 60.0f * JMASSin(mFaceAngle.y); + mPosition.z -= 60.0f * JMASCos(mFaceAngle.y); + + f32 groundY = gpMap->checkGround(mPosition.x, mPosition.y, mPosition.z, (const TBGCheckData**)0); + + f32 limit = mPosition.y - 100.0f; + if (groundY < limit) { + mPosition.y = limit; + } else { + mPosition.y = groundY; + } + + changePlayerStatus(0x208ba, 0, false); + return; + } + + u8 canSpray = 0; + unkF6 = 0; + + s16 wireAngle = (s16)angle; + mFaceAngle.x = 0; + mFaceAngle.y = wireAngle + 0x4000; + mModelFaceAngle = angle; + + if (mInput & 0x1) { + s16 angleDiff = (s16)(wireAngle - mIntendedYaw); + + if (angleDiff > -0x2000 && angleDiff < 0x3555) { + int moveResult = wireMove(3.0f); + if (moveResult != 0) { + setAnimation(0xe5, 1.0f); + } else { + setAnimation(0xe3, 1.0f); + } + } + + if (angleDiff < -0x6000 || angleDiff > 0x4AAA) { + int moveResult = wireMove(-3.0f); + if (moveResult != 0) { + setAnimation(0xe6, 1.0f); + } else { + setAnimation(0xe3, 1.0f); + } + } + + if (angleDiff >= -0x6000 && angleDiff <= -0x2000) { + mWireBounceVel = 5.0f; + changePlayerStatus(0x10000556, 0, false); + return; + } + + if (angleDiff >= 0x3555 && angleDiff <= 0x4AAA) { + f32 zero = 0.0f; + mVel.y = zero; + mForwardVel = zero; + + mPosition.x -= 60.0f * JMASSin(mFaceAngle.y); + mPosition.z -= 60.0f * JMASCos(mFaceAngle.y); + + f32 groundY = gpMap->checkGround(mPosition.x, mPosition.y, mPosition.z, (const TBGCheckData**)0); + + f32 limit = mPosition.y - 100.0f; + if (groundY < limit) { + mPosition.y = limit; + } else { + mPosition.y = groundY; + } + + changePlayerStatus(0x208ba, 0, false); + } + } else { + if (unk380 != 0) { + setAnimation(0xe3, 1.0f); + return; + } + + TWaterGun* waterGun = mWaterGun; + if (waterGun == 0) { + setAnimation(0xe3, 1.0f); + return; + } + + if (waterGun->mCurrentWater == 0) { + } else { + s32 kind = waterGun->getCurrentNozzle()->getNozzleKind(); + if (kind == 1) { + if (waterGun->getCurrentNozzle()->unk385 == 1) { + canSpray = 1; + } + } else { + if (waterGun->getCurrentNozzle()->unk378 > 0.0f) { + canSpray = 1; + } + } + } + + if (!canSpray) { + setAnimation(0xe3, 1.0f); + return; + } + + s16 rotSpeed; + if (mWaterGun == 0) { + rotSpeed = 0; + } else { + switch (mWaterGun->mCurrentNozzle) { + case TWaterGun::Rocket: + rotSpeed = mWireParams.mRotSpeedTrgRocket.get(); + break; + case TWaterGun::Hover: + rotSpeed = mWireParams.mRotSpeedTrgHover.get(); + break; + case TWaterGun::Turbo: + rotSpeed = mWireParams.mRotSpeedTrgTurbo.get(); + break; + default: + rotSpeed = mWireParams.mRotSpeed.get(); + break; + } + } + + if (rotSpeed > 0) { + unkF6 = unkF6 + mWireParams.mRotStop.get() * 2; + } else { + unkF6 = unkF6 - mWireParams.mRotStop.get() * 2; + } + + s16 rotSpeed2; + if (mWaterGun == 0) { + rotSpeed2 = 0; + } else { + switch (mWaterGun->mCurrentNozzle) { + case TWaterGun::Rocket: + rotSpeed2 = mWireParams.mRotSpeedTrgRocket.get(); + break; + case TWaterGun::Hover: + rotSpeed2 = mWireParams.mRotSpeedTrgHover.get(); + break; + case TWaterGun::Turbo: + rotSpeed2 = mWireParams.mRotSpeedTrgTurbo.get(); + break; + default: + rotSpeed2 = mWireParams.mRotSpeed.get(); + break; + } + } + + unkF6 = unkF6 + rotSpeed2; + changePlayerStatus(0x10000358, 0, false); + return; + } + + setAnimation(0xe3, 1.0f); +} \ No newline at end of file diff --git a/tools/dive_slope_speed_fix.txt b/tools/dive_slope_speed_fix.txt new file mode 100644 index 00000000..8c28afdb --- /dev/null +++ b/tools/dive_slope_speed_fix.txt @@ -0,0 +1,96 @@ +=============================================================================== + Dive Slope Speed Fix for Super Mario Sunshine (GMSJ01) + Gecko Code v1.0 +=============================================================================== + +Fixes a bug where Mario walks faster than normal after diving (belly slide) +on sloped surfaces. The extra speed persists until the player stops moving +or leaves the slope. + +Root cause: When Mario transitions from an airborne action (dive) to a +ground movement action, changePlayerStatus does not clamp mForwardVel. +The running handler (action 0x04000440) never decelerates mForwardVel, +so the dive's high speed is preserved indefinitely on slopes. + +Fix: Hook changePlayerStatus at the point where mAction is stored. When +transitioning from an airborne action (mAction & 0x1C0 == 0x80) to a +ground movement action (new status & 0x1C0 == 0x40), cap mForwardVel +at 32.0 (normal maximum running speed). + +TMario field offsets used: + +0x7C = mAction (u32) + +0x80 = mPrevAction (u32) + +0xB0 = mForwardVel (f32) + +Hook address: 0x801335C4 (inside changePlayerStatus, where mAction is written) +Original instruction: stw r29, 0x7c(r30) + +Register state at hook point: + r30 = this (TMario*) + r29 = new action (status) + r4 = old mAction (already saved to mPrevAction) + r3 = 1 (return value, must be restored) + r0 = 0 (used for subsequent stores, must not be clobbered) + +=============================================================================== + GECKO CODE +=============================================================================== + +$Dive Slope Speed Fix [GMSJ01] +C21335C4 00000008 +93BE007C 548305F2 +2C030080 4082002C +57A305F2 2C030040 +40820020 C01E00B0 +3C604200 9061FFFC +C021FFFC FC000840 +40810008 D03E00B0 +38600001 00000000 + +=============================================================================== + DISASSEMBLY +=============================================================================== + +// --- Hook at changePlayerStatus+0x1A0 (0x801335C4) --- +// Original: stw r29, 0x7c(r30) [store new mAction] + +stw r29, 0x7C(r30) // execute original instruction + +// Check: was the old action airborne? (mAction & 0x1C0 == 0x80) +rlwinm r3, r4, 0, 23, 25 // r3 = oldAction & 0x1C0 +cmpwi r3, 0x80 // airborne type? +bne done // skip if not airborne + +// Check: is the new action ground movement? (status & 0x1C0 == 0x40) +rlwinm r3, r29, 0, 23, 25 // r3 = newAction & 0x1C0 +cmpwi r3, 0x40 // ground movement type? +bne done // skip if not ground + +// Clamp mForwardVel to 32.0 (normal max running speed) +lfs f0, 0xB0(r30) // f0 = mForwardVel +lis r3, 0x4200 // r3 = 0x42000000 (32.0 as IEEE754) +stw r3, -0x4(r1) // store float bits to stack temp +lfs f1, -0x4(r1) // f1 = 32.0 +fcmpo cr0, f0, f1 // mForwardVel vs 32.0 +ble done // skip if already <= 32.0 +stfs f1, 0xB0(r30) // mForwardVel = 32.0 + +done: +li r3, 1 // restore r3 (return value) + +=============================================================================== + NOTES +=============================================================================== + +- The cap value of 32.0 matches Mario's normal maximum ground running speed. + This does not affect normal gameplay since regular jumps don't land with + speeds above 32.0 in typical play. + +- The fix is targeted: it only activates on airborne-to-ground transitions + (action type 0x80 -> 0x40), so swimming, climbing, and other state + transitions are unaffected. + +- Compatible with all stages. The hook is in the core changePlayerStatus + function which handles all action transitions. + +=============================================================================== diff --git a/tools/hello_debug.txt b/tools/hello_debug.txt new file mode 100644 index 00000000..3ee90f71 --- /dev/null +++ b/tools/hello_debug.txt @@ -0,0 +1,94 @@ +=============================================================================== + Hello Debug Text - Super Mario Sunshine (GMSJ01) + v3: Hook in TApplication::gameLoop (runs every frame unconditionally) +=============================================================================== + +Hook: 0x800F9D60 in TApplication::gameLoop() + - Right after changeFrameBuffer + flushMessage (framebuffer is valid) + - Reached on every loop iteration (unconditional path) + - Normal function context (safe to call game functions) + +Original instruction: cmplwi r29, 0x1 [281D0001] + +=============================================================================== + GECKO CODE +=============================================================================== + +$Hello from Claude [GMSJ01] +C20F9D60 00000011 +3C608041 806B9870 +28030000 41820074 +80030014 28000000 +41820068 9421FFC0 +3C004865 60006C6C +90010020 3C006F20 +60006672 90010024 +3C006F6D 60002043 +90010028 3C006C61 +60007564 9001002C +3C006521 90010030 +38000000 90010034 +38800010 38A00020 +38C10020 3D808001 +618C15AC 7D8903A6 +4E800421 38210040 +281D0001 00000000 + +=============================================================================== + DISASSEMBLY +=============================================================================== + +// C2 hook at TApplication::gameLoop()+0x40C (0x800F9D60) +// This address is reached every frame, after changeFrameBuffer +// updates the JUTDirectPrint framebuffer pointer. +// Original instruction: cmplwi r29, 0x1 + +// --- Get sDirectPrint singleton --- +lis r3, 0x8041 +lwz r3, 0x9870(r3) // sDirectPrint (0x80409870) +cmplwi r3, 0 +beq done + +// --- Check framebuffer valid --- +lwz r0, 0x14(r3) // mFrameBuffer +cmplwi r0, 0 +beq done + +// --- Allocate temp stack --- +stwu r1, -0x40(r1) + +// --- Build "Hello from Claude!" --- +lis r0, 0x4865 +ori r0, r0, 0x6C6C // "Hell" +stw r0, 0x20(r1) +lis r0, 0x6F20 +ori r0, r0, 0x6672 // "o fr" +stw r0, 0x24(r1) +lis r0, 0x6F6D +ori r0, r0, 0x2043 // "om C" +stw r0, 0x28(r1) +lis r0, 0x6C61 +ori r0, r0, 0x7564 // "laud" +stw r0, 0x2C(r1) +lis r0, 0x6521 // "e!\0\0" +stw r0, 0x30(r1) +li r0, 0 +stw r0, 0x34(r1) + +// --- Call drawString --- +li r4, 16 // x +li r5, 32 // y +addi r6, r1, 0x20 // str +lis r12, 0x8001 +ori r12, r12, 0x15AC // drawString @ 0x800115AC +mtctr r12 +bctrl + +// --- Restore temp stack --- +addi r1, r1, 0x40 + +done: +// --- Original instruction --- +cmplwi r29, 0x1 // [281D0001] - sets CR0 for the ble that follows + +=============================================================================== diff --git a/tools/map_offsets.py b/tools/map_offsets.py new file mode 100644 index 00000000..8d3b9188 --- /dev/null +++ b/tools/map_offsets.py @@ -0,0 +1,372 @@ +#!/usr/bin/env python3 +"""Map raw field offsets to named TParams fields across Mario source files.""" +import re +import os + +def params_size(num_fields): + return 0x08 + num_fields * 0x14 + +structs = {} + +structs['TDeParams'] = [ + ('mHpMax', '8'), ('mRunningMax', '45.0f'), ('mDashMax', '60.0f'), + ('mDashAcc', '0.5f'), ('mDashBrake', '0.8f'), ('mDashStartTime', '0x78'), + ('mWaitingRotSp', '0x100'), ('mRunningRotSpMin', '0x200'), ('mRunningRotSpMax', '0x400'), + ('mRocketRotSp', '0x100'), ('mPumpingRotSpMin', '0x100'), ('mPumpingRotSpMax', '0x200'), + ('mInvincibleTime', '0x14'), ('mFootPrintTimerMax', '0x208'), + ('mWaterTriggerRate', '1'), ('mGraffitoNoDmgTime', '10'), ('mRestMax', '0xff'), + ('mShadowSize', '200.0f'), ('mShadowErase', '0.2f'), ('mHoldRadius', '100.0f'), + ('mDamageRadius', '42.0f'), ('mDamageHeight', '110.0f'), ('mAttackHeight', '50.0f'), + ('mTrampleRadius', '100.0f'), ('mPushupRadius', '100.0f'), ('mPushupHeight', '200.0f'), + ('mHipdropRadius', '250.0f'), ('mQuakeRadius', '500.0f'), ('mQuakeHeight', '500.0f'), + ('mTramplePowStep1', '30.0f'), ('mTramplePowStep2', '40.0f'), ('mTramplePowStep3', '50.0f'), + ('mJumpWallHeight', '1000.0f'), ('mThrowPower', '2.0f'), ('mSlipStart', '0.342f'), + ('mWasOnWaterSlip', '0.97f'), ('mInWaterSlip', '0.9f'), ('mToroccoRotSp', '1.0f'), + ('mRecoverTimer', '0xf0'), ('mHotTimer', '0x4b0'), ('mFeelDeep', '500.0f'), + ('mDamageFallHeight', '1000.0f'), ('mForceSlipAngle', '0.5f'), ('mClashSpeed', '50.0f'), + ('mHangWallMovableAngle', '0.3f'), ('mColMvMax', '10.0f'), + ('mNoFreezeTime', '0x78'), ('mKickFreezeTime', '5'), ('mSurfStartFreezeTime', '0x78'), + ('mSleepingCheckDist', '50.0f'), ('mSleepingCheckHeight', '30.0f'), + ('mIllegalPlaneCtInc', '4'), ('mIllegalPlaneTime', '0x1e0'), +] +structs['TBodyAngleParams'] = [ + ('mHeadRot', '0.0f'), ('mWaistRoll', '0.0f'), ('mWaistPitch', '80.0f'), + ('mWaistRollMax', '0'), ('mWaistPitchMax', '1000'), ('mWaistAngleChangeRate', '0.07f'), +] +structs['TAttackParams'] = [('mRadius', '100.0f'), ('mHeight', '50.0f')] +structs['TJumpParams'] = [ + ('mGravity', '0.8f'), ('mSpinJumpGravity', '0.6f'), ('mJumpingMax', '80.0f'), + ('mJumpSpeedBrake', '0.999f'), ('mJumpAccelControl', '0.5f'), ('mJumpSlideControl', '0.5f'), + ('mTurnJumpForce', '62.0f'), ('mFenceSpeed', '2.0f'), ('mFireDownForce', '80.0f'), + ('mFireDownControl', '1.0f'), ('mFireBackVelocity', '1.0f'), ('mBroadJumpForce', '80.0f'), + ('mBroadJumpForceY', '30.0f'), ('mRotateJumpForceY', '62.0f'), + ('mPopUpSpeedY', '5.0f'), ('mPopUpForceYMult', '10.0f'), + ('mBackJumpForce', '-16.0f'), ('mBackJumpForceY', '62.0f'), + ('mHipAttackSpeedY', '-50.0f'), ('mSuperHipAttackSpeedY', '-80.0f'), + ('mJumpCatchRotXSp', '0x100'), ('mJumpCatchRotXMax', '0x2aaa'), + ('mRotBroadEnableV', '30.0f'), ('mRotBroadJumpForce', '60.0f'), + ('mRotBroadJumpForceY', '20.0f'), ('mTrampolineDec', '1.0f'), + ('mSecJumpEnableSp', '20.0f'), ('mSecJumpForce', '52.0f'), + ('mSecJumpSpeedMult', '0.0f'), ('mSecJumpXZMult', '0.8f'), + ('mTriJumpEnableSp', '20.0f'), ('mUltraJumpForce', '70.0f'), + ('mUltraJumpSpeedMult', '0.25f'), ('mUltraJumpXZMult', '0.8f'), + ('mValleyDepth', '500.0f'), ('mThrownAccel', '0.5f'), ('mThrownSlide', '0.5f'), + ('mThrownBrake', '0.98f'), ('mTremblePower', '5.0f'), ('mTrembleAccele', '2.0f'), + ('mTrembleBrake', '0.99f'), ('mTrembleTime', '600'), ('mClashAngle', '0x3555'), + ('mJumpJumpCatchSp', '50.0f'), ('mGetOffYoshiY', '30.0f'), ('mSuperHipAttackCt', '0x32'), +] +structs['TRunParams'] = [ + ('mMaxSpeed', '32.0f'), ('mVelMinusBrake', '1.1f'), ('mAddBase', '1.1f'), + ('mAddVelDiv', '0.0233f'), ('mDecStartNrmY', '0.95f'), ('mDecBrake', '1.0f'), + ('mSoft2Walk', '8.0f'), ('mWalk2Soft', '5.0f'), ('mSoftStepAnmMult', '0.125f'), + ('mRunAnmSpeedBase', '1.0f'), ('mRunAnmSpeedMult', '0.06f'), + ('mMotBlendWalkSp', '1.0f'), ('mMotBlendRunSp', '3.0f'), + ('mSwimDepth', '120.0f'), ('mInWaterBrake', '0.9f'), ('mInWaterAnmBrake', '0.6f'), + ('mPumpingSlideSp', '0.1f'), ('mPumpingSlideAnmSp', '0.5f'), + ('mDoJumpCatchSp', '15.0f'), ('mTurnNeedSp', '10.0f'), ('mDashRotSp', '100'), +] +structs['TSwimParams'] = [ + ('mStartSp', '1.0f'), ('mMoveSp', '0.8f'), ('mMoveBrake', '1.0f'), + ('mSwimmingRotSpMin', '0x200'), ('mSwimmingRotSpMax', '0x400'), + ('mPumpingRotSpMin', '0x100'), ('mPumpingRotSpMax', '0x200'), + ('mGravity', '0.1f'), ('mWaitBouyancy', '1.0f'), ('mMoveBouyancy', '1.0f'), + ('mUpDownBrake', '0.95f'), ('mCanJumpDepth', '1.0f'), ('mEndDepth', '80.0f'), + ('mFloatHeight', '120.0f'), ('mStartVMult', '0.1f'), ('mStartVYMult', '0.1f'), + ('mRush', '3.0f'), ('mAnmBrake', '0.02f'), ('mPaddleSpeedUp', '0.3f'), + ('mPaddleJumpUp', '1.0f'), ('mFloatUp', '2.0f'), ('mWaterLevelCheckHeight', '10.0f'), + ('mPaddleDown', '1.0f'), ('mWaitSinkTime', '0x32'), ('mCanBreathDepth', '50.0f'), + ('mWaitSinkSpeed', '5.0f'), ('mAirDec', '0.001f'), ('mAirDecDive', '0.001f'), + ('mAirInc', '0.03f'), +] +structs['THangingParams'] = [ + ('mMoveSp', '0.1f'), ('mAnmRate', '0.5f'), ('mRapidTime', '2000'), + ('mLimitTime', '0x960'), ('mAnmRapid', '8.0f'), ('mDescentSp', '10.0f'), +] +structs['THangRoofParams'] = [('mAnmMult', '0.3f')] +structs['TWireParams'] = [ + ('mRotSpeed', '-8'), ('mRotSpeedTrgHover', '8'), ('mRotSpeedTrgTurbo', '1000'), + ('mRotSpeedTrgRocket', '1000'), ('mRotSpeedMax', '0x578'), ('mRotStop', '100'), + ('mRotGravity', '0x14'), ('mRotBrake', '0.98f'), ('mJumpRate', '0.09f'), + ('mSwingRate', '0.005f'), ('mWireJumpAccelControl', '0.01f'), + ('mWireJumpSlideControl', '0.3f'), ('mWireJumpMult', '5.0f'), + ('mWireJumpBase', '20.0f'), ('mWireSwingBrake', '0.99f'), ('mWireSwingMax', '100.0f'), +] +structs['TPullParams'] = [ + ('mPullRateV', '0.3f'), ('mPullRateH', '0.05f'), + ('mOilPullRateV', '0.1f'), ('mOilPullRateH', '0.01f'), +] +structs['TBarParams'] = [ + ('mClimbSp', '0.035f'), ('mRotateSp', '3.0f'), ('mClimbAnmRate', '0.0039f'), + ('mCatchRadius', '100.0f'), ('mCatchAngle', '0.8f'), +] +structs['TSurfingParams'] = [ + ('mRotMin', '2048.0f'), ('mRotMax', '1024.0f'), ('mPowMin', '24.0f'), + ('mPowMax', '64.0f'), ('mAccel', '58.0f'), ('mWaistRoll', '0.25f'), + ('mWaistPitch', '170.0f'), ('mWaistRollMax', '0x400'), ('mWaistPitchMax', '0x1555'), + ('mRoll', '-0.45f'), ('mPitch', '-170.0f'), ('mRollMax', '0x4000'), + ('mPitchMax', '0x1555'), ('mAngleChangeRate', '0.01f'), + ('mWaistAngleChangeRate', '0.01f'), ('mScaleMin', '0.5f'), ('mScaleMax', '1.0f'), + ('mScaleMinSpeed', '24.0f'), ('mScaleMaxSpeed', '60.0f'), + ('mJumpPow', '42.0f'), ('mJumpXZRatio', '0.25f'), + ('mClashSpeed', '40.0f'), ('mClashAngle', '0x5555'), +] +structs['THHoverParams'] = [('mRotSp', '0x80'), ('mAccelRate', '0.03f'), ('mBrake', '0.95f')] +structs['TDivingParams'] = [ + ('mRotSp', '0x80'), ('mGravity', '0.5f'), ('mAccelControl', '0.02f'), + ('mSeaBrake', '0.999f'), ('mSeaBrakeY', '0.98f'), +] +structs['TYoshiParams'] = [ + ('mRunYoshiMult', '1.2f'), ('mJumpYoshiMult', '1.0f'), ('mRotYoshiMult', '1.5f'), + ('mHeadFront', '80.0f'), ('mHeadRadius', '50.0f'), + ('mHoldOutAccCtrlF', '0.01f'), ('mHoldOutAccCtrlB', '0.023f'), + ('mHoldOutSldCtrl', '0.3f'), ('mDecBrake', '1.0f'), +] +structs['TWaterEffectParams'] = [ + ('mJumpIntoMdlEffectSpY', '10.0f'), ('mJumpIntoMinY', '20.0f'), + ('mJumpIntoMaxY', '50.0f'), ('mJumpIntoScaleMin', '0.75f'), + ('mJumpIntoScaleWidth', '1.0f'), ('mRunningRippleSpeed', '30.0f'), + ('mRunningRippleDepth', '30.0f'), +] +structs['TControllerParams'] = [ + ('mAnalogLRToZeroVal', '0x1e'), ('mAnalogLRToMiddleVal', '0x5a'), + ('mAnalogLRToMaxVal', '0x96'), ('mAnalogLRMiddleLevel', '0.1f'), + ('mStartToWalkLevel', '15.0f'), ('mStickRotateTime', '0x18'), + ('mLengthMultTimes', '10'), ('mLengthMult', '0.935f'), + ('mSquatRotMidAnalog', '0.7f'), ('mSquatRotMidValue', '0.05f'), +] +structs['TGraffitoParams'] = [ + ('mSinkTime', '0xf0'), ('mSinkDmgTime', '0xf0'), ('mSinkHeight', '150.0f'), + ('mSinkMoveMin', '0.3f'), ('mSinkMoveMax', '0.5f'), ('mSinkRecover', '0.05f'), + ('mSinkJumpRateMin', '0.1f'), ('mSinkJumpRateMax', '0.3f'), + ('mSinkPumpLimit', '0.25f'), ('mSinkDmgDepth', '0.25f'), + ('mFireHeight', '1000.0f'), ('mDizzySlipCtMax', '1000'), + ('mDizzyWalkCtMax', '1000'), ('mDizzyAngleY', '0x7fff'), + ('mDizzyAngleRate', '400.0f'), ('mDizzyPowerRate', '120.0f'), + ('mDizzyPower', '20.0f'), ('mFireInvincibleTime', '0x96'), + ('mFootEraseTimes', '4'), ('mFootEraseSize', '400.0f'), ('mFootEraseFront', '200.0f'), +] +structs['TDirtyParams'] = [ + ('mIncRunning', '0.1f'), ('mIncCatching', '0.3f'), ('mIncSlipping', '0.2f'), + ('mDecSwimming', '0.5f'), ('mDecWaterHit', '0.2f'), ('mDecRotJump', '0.1f'), + ('mBrakeStartValSlip', '0.99f'), ('mBrakeStartValRun', '0.98f'), + ('mDirtyTimeSlip', '600'), ('mDirtyTimeRun', '600'), + ('mPolSizeSlip', '200.0f'), ('mPolSizeRun', '80.0f'), + ('mPolSizeFootPrint', '200.0f'), ('mPolSizeJump', '200.0f'), + ('mSlopeAngle', '0.99f'), ('mDirtyMax', '200.0f'), + ('mSlipAnmSpeed', '3.0f'), ('mSlipRunSp', '0.01f'), ('mSlipCatchSp', '0.01f'), + ('mSlipRotate', '0x100'), ('mSlipCatchRotate', '0x100'), + ('mBrakeSlipNoPollute', '0.98f'), ('mFogTimeYellow', '0xf0'), ('mFogTimeRed', '600'), +] +structs['TMotorParams'] = [ + ('mMotorReturn', '0x19'), ('mMotorTrample', '8'), + ('mMotorHipDrop', '0xf'), ('mMotorWall', '6'), +] +structs['TParticleParams'] = [ + ('mMeltInWaterMax', '0.5f'), ('mWaveEmitSpeed', '5.0f'), ('mWaveAlphaDec', '5'), + ('mBubbleDepth', '10.0f'), ('mBodyBubbleSpMin', '0.0f'), ('mBodyBubbleSpMax', '40.0f'), + ('mBodyBubbleEmitMin', '0.0f'), ('mBodyBubbleEmitMax', '0.5f'), + ('mBubbleToRipple', '0.3f'), ('mToroccoWind', '0.001f'), ('mToroccoSpark', '0.001f'), +] +structs['TEffectParams'] = [ + ('mDashInc', '0.033f'), ('mDashDec', '0.017f'), + ('mDashMaxBlendInBlur', '0xb4'), ('mDashMaxBlendInIris', '0xb4'), + ('mDashBlendScale', '0.2f'), +] +structs['TSlipParams'] = [ + ('mSlipFriction', '0.9f'), ('mSlopeAcceleUp', '0.0f'), ('mSlopeAcceleDown', '0.0f'), + ('mSlideAcceleUp', '0.0f'), ('mSlideAcceleDown', '0.0f'), + ('mSlideStopNormal', '15.0f'), ('mSlideStopCatch', '15.0f'), + ('mJumpEnable', '1'), ('mMissJump', '1'), + ('mSlideAngleYSp', '0x200'), ('mStickSlideMult', '0.05f'), +] +structs['TUpperParams'] = [ + ('mPumpWaitTime', '10'), ('mPumpAnmSpeed', '0.01f'), + ('mHoverHeadAngle', '0xe000'), ('mFeelDeepHeadAngle', '0x2000'), + ('mFrontWallHeadAngle', '0xe000'), +] +structs['TEParams'] = [ + ('mDamage', '1'), ('mDownType', '0'), ('mWaterEmit', '0'), ('mMotor', '0'), + ('mMinSpeed', '0.0f'), ('mDirty', '0.0f'), ('mInvincibleTime', '0'), +] +structs['TAutoDemoParams'] = [ + ('mWarpInBallsDispTime', '6'), ('mWarpInBallsTime', '0x46'), + ('mWarpInCapturedTime', '0x78'), ('mWarpInTremble', '15.0f'), + ('mWarpInVecBase', '0.3f'), ('mWarpTransTremble', '50.0f'), ('mReadRotSp', '0x400f'), +] +structs['TSoundParams'] = [('mStartFallVoiceSpeed', '60.0f')] +structs['TOptionParams'] = [('mZ', '-1000.0f'), ('mXMin', '846.0f'), ('mXMax', '2000.0f')] + +layout = [ + ('mDeParams', 'TDeParams'), + ('mBodyAngleParamsFree', 'TBodyAngleParams'), + ('mBodyAngleParamsWaterGun', 'TBodyAngleParams'), + ('mAttackParamsFencePunch', 'TAttackParams'), + ('mAttackParamsKickRoof', 'TAttackParams'), + ('mJumpParams', 'TJumpParams'), + ('mRunParams', 'TRunParams'), + ('mSwimParams', 'TSwimParams'), + ('mHangingParams', 'THangingParams'), + ('mHangRoofParams', 'THangRoofParams'), + ('mWireParams', 'TWireParams'), + ('mPullParamsBGBeak', 'TPullParams'), + ('mPullParamsBGTentacle', 'TPullParams'), + ('mPullParamsBGFireWanWanBossTail', 'TPullParams'), + ('mPullParamsFireWanWanTail', 'TPullParams'), + ('mBarParams', 'TBarParams'), + ('mSurfingParamsWaterRed', 'TSurfingParams'), + ('mSurfingParamsGroundRed', 'TSurfingParams'), + ('mSurfingParamsWaterYellow', 'TSurfingParams'), + ('mSurfingParamsGroundYellow', 'TSurfingParams'), + ('mSurfingParamsWaterGreen', 'TSurfingParams'), + ('mSurfingParamsGroundGreen', 'TSurfingParams'), + ('mHoverParams', 'THHoverParams'), + ('mDivingParams', 'TDivingParams'), + ('mYoshiParams', 'TYoshiParams'), + ('mWaterEffectParams', 'TWaterEffectParams'), + ('mControllerParams', 'TControllerParams'), + ('mGraffitoParams', 'TGraffitoParams'), + ('mDirtyParams', 'TDirtyParams'), + ('mMotorParams', 'TMotorParams'), + ('mParticleParams', 'TParticleParams'), + ('mEffectParams', 'TEffectParams'), + ('mSlipParamsNormal', 'TSlipParams'), + ('mSlipParamsOil', 'TSlipParams'), + ('mSlipParamsAll', 'TSlipParams'), + ('mSlipParamsAllSlider', 'TSlipParams'), + ('mSlipParams45', 'TSlipParams'), + ('mSlipParamsWaterSlope', 'TSlipParams'), + ('mSlipParamsWaterGround', 'TSlipParams'), + ('mSlipParamsYoshi', 'TSlipParams'), + ('mUpperBodyParams', 'TUpperParams'), + ('mDmgParamsEnemyCommon', 'TEParams'), + ('mDmgParamsHamakuri', 'TEParams'), + ('mDmgParamsNamekuri', 'TEParams'), + ('mDmgParamsHinokuri', 'TEParams'), + ('mDmgParamsFire', 'TEParams'), + ('mDmgParamsBGTentacle', 'TEParams'), + ('mDmgParamsBossEel', 'TEParams'), + ('mDmgParamsHanachanBoss', 'TEParams'), + ('mDmgParamsPoihana', 'TEParams'), + ('mDmgParamsKiller', 'TEParams'), + ('mDmgParamsLampTrapIron', 'TEParams'), + ('mDmgParamsLampTrapSpike', 'TEParams'), + ('mDmgParamsEnemyMario', 'TEParams'), + ('mDmgParamsCannotBreath', 'TEParams'), + ('mDmgParamsGraffitoFire', 'TEParams'), + ('mDmgParamsGraffitoPoison', 'TEParams'), + ('mDmgParamsGraffitoElec', 'TEParams'), + ('mDmgParamsGraffitoLava', 'TEParams'), + ('mDmgParamsWaterSurface', 'TEParams'), + ('mDmgMapParams0', 'TEParams'), + ('mDmgMapParams1', 'TEParams'), + ('mDmgMapParams2', 'TEParams'), + ('mDmgMapParams3', 'TEParams'), + ('mDmgMapParams4', 'TEParams'), + ('mDmgMapParams5', 'TEParams'), + ('mDmgMapParams6', 'TEParams'), + ('mDmgMapParams7', 'TEParams'), + ('mDmgMapParams8', 'TEParams'), + ('mDmgMapParams9', 'TEParams'), + ('mAutoDemoParams', 'TAutoDemoParams'), + ('mSoundParams', 'TSoundParams'), + ('mOptionParams', 'TOptionParams'), +] + +# Build offset map: value_offset -> (param_name, default_value) +offset = 0x574 +offset_map = {} +struct_base_map = {} # base_offset -> member_name + +for member_name, type_name in layout: + fields = structs[type_name] + struct_base_map[offset] = (member_name, type_name) + for i, (field_name, default_val) in enumerate(fields): + field_base = offset + 0x08 + i * 0x14 + value_offset = field_base + 0x10 + offset_map[value_offset] = (member_name + '.' + field_name, default_val) + offset += params_size(len(fields)) + +# Print complete mapping table +print("=" * 90) +print("COMPLETE TParams OFFSET-TO-NAME MAPPING TABLE") +print("=" * 90) +print(f"{'OFFSET':<8} {'PARAM_NAME':<55} DEFAULT_VALUE") +print(f"{'------':<8} {'----------':<55} -------------") + +current_struct = None +for off in sorted(offset_map.keys()): + name, default = offset_map[off] + struct_name = name.split('.')[0] + if struct_name != current_struct: + current_struct = struct_name + # Find the struct base + for base, (mname, tname) in struct_base_map.items(): + if mname == struct_name: + print(f" --- {mname} ({tname}) @ base 0x{base:04X} ---") + break + print(f"0x{off:04X} {name:<55} {default}") + +print(f"\nTotal TParams value offsets: {len(offset_map)}") +print(f"TMario TParams block: 0x0574 - 0x{offset:04X}") + +# Now scan source files for raw offset patterns +print("\n" + "=" * 90) +print("RAW OFFSET ACCESSES THAT MAP TO TParams FIELDS") +print("=" * 90) + +src_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'src', 'Player') +files = [ + 'MarioMove.cpp', 'MarioSpecial.cpp', 'MarioPhysics.cpp', 'MarioRun.cpp', + 'MarioJump.cpp', 'MarioWait.cpp', 'MarioSwim.cpp', 'MarioCheckCol.cpp', + 'MarioUpper.cpp', 'MarioInit.cpp', +] + +# Pattern: 0xHEX inside (u8*)this + 0xHEX +pat = re.compile(r'\(u8\*\)this\s*\+\s*0x([0-9a-fA-F]+)') + +total_matches = 0 +total_tparams = 0 +unresolved = {} + +for fname in files: + fpath = os.path.join(src_dir, fname) + if not os.path.exists(fpath): + continue + with open(fpath, 'r', encoding='utf-8', errors='replace') as f: + lines = f.readlines() + + file_hits = [] + for lineno, line in enumerate(lines, 1): + for m in pat.finditer(line): + raw_hex = m.group(1) + # Handle base+offset patterns like 0x1BC4 + 0x18 + # Check if there's a + 0x after + after = line[m.end():] + combined_offset = int(raw_hex, 16) + + # Check for "+ 0xNN)" pattern after the first offset + add_match = re.match(r'\s*\+\s*0x([0-9a-fA-F]+)', after) + if add_match: + combined_offset += int(add_match.group(1), 16) + + total_matches += 1 + if combined_offset in offset_map: + name, default = offset_map[combined_offset] + file_hits.append((lineno, combined_offset, name, default, line.rstrip())) + total_tparams += 1 + elif combined_offset in struct_base_map: + mname, tname = struct_base_map[combined_offset] + file_hits.append((lineno, combined_offset, f"{mname} (struct base)", tname, line.rstrip())) + total_tparams += 1 + + if file_hits: + print(f"\n--- {fname} ({len(file_hits)} TParams accesses) ---") + for lineno, off, name, default, line_text in file_hits: + print(f" Line {lineno:4d}: 0x{off:04X} -> {name}") + +print(f"\n{'=' * 90}") +print(f"SUMMARY") +print(f"{'=' * 90}") +print(f"Total raw offset accesses found: {total_matches}") +print(f"Mapped to TParams fields: {total_tparams}") +print(f"Non-TParams offsets: {total_matches - total_tparams}") diff --git a/tools/offset_mapping_results.txt b/tools/offset_mapping_results.txt new file mode 100644 index 00000000..90d49ad7 --- /dev/null +++ b/tools/offset_mapping_results.txt @@ -0,0 +1,1317 @@ +========================================================================================== +COMPLETE TParams OFFSET-TO-NAME MAPPING TABLE +========================================================================================== +OFFSET PARAM_NAME DEFAULT_VALUE +------ ---------- ------------- + --- mDeParams (TDeParams) @ base 0x0574 --- +0x058C mDeParams.mHpMax 8 +0x05A0 mDeParams.mRunningMax 45.0f +0x05B4 mDeParams.mDashMax 60.0f +0x05C8 mDeParams.mDashAcc 0.5f +0x05DC mDeParams.mDashBrake 0.8f +0x05F0 mDeParams.mDashStartTime 0x78 +0x0604 mDeParams.mWaitingRotSp 0x100 +0x0618 mDeParams.mRunningRotSpMin 0x200 +0x062C mDeParams.mRunningRotSpMax 0x400 +0x0640 mDeParams.mRocketRotSp 0x100 +0x0654 mDeParams.mPumpingRotSpMin 0x100 +0x0668 mDeParams.mPumpingRotSpMax 0x200 +0x067C mDeParams.mInvincibleTime 0x14 +0x0690 mDeParams.mFootPrintTimerMax 0x208 +0x06A4 mDeParams.mWaterTriggerRate 1 +0x06B8 mDeParams.mGraffitoNoDmgTime 10 +0x06CC mDeParams.mRestMax 0xff +0x06E0 mDeParams.mShadowSize 200.0f +0x06F4 mDeParams.mShadowErase 0.2f +0x0708 mDeParams.mHoldRadius 100.0f +0x071C mDeParams.mDamageRadius 42.0f +0x0730 mDeParams.mDamageHeight 110.0f +0x0744 mDeParams.mAttackHeight 50.0f +0x0758 mDeParams.mTrampleRadius 100.0f +0x076C mDeParams.mPushupRadius 100.0f +0x0780 mDeParams.mPushupHeight 200.0f +0x0794 mDeParams.mHipdropRadius 250.0f +0x07A8 mDeParams.mQuakeRadius 500.0f +0x07BC mDeParams.mQuakeHeight 500.0f +0x07D0 mDeParams.mTramplePowStep1 30.0f +0x07E4 mDeParams.mTramplePowStep2 40.0f +0x07F8 mDeParams.mTramplePowStep3 50.0f +0x080C mDeParams.mJumpWallHeight 1000.0f +0x0820 mDeParams.mThrowPower 2.0f +0x0834 mDeParams.mSlipStart 0.342f +0x0848 mDeParams.mWasOnWaterSlip 0.97f +0x085C mDeParams.mInWaterSlip 0.9f +0x0870 mDeParams.mToroccoRotSp 1.0f +0x0884 mDeParams.mRecoverTimer 0xf0 +0x0898 mDeParams.mHotTimer 0x4b0 +0x08AC mDeParams.mFeelDeep 500.0f +0x08C0 mDeParams.mDamageFallHeight 1000.0f +0x08D4 mDeParams.mForceSlipAngle 0.5f +0x08E8 mDeParams.mClashSpeed 50.0f +0x08FC mDeParams.mHangWallMovableAngle 0.3f +0x0910 mDeParams.mColMvMax 10.0f +0x0924 mDeParams.mNoFreezeTime 0x78 +0x0938 mDeParams.mKickFreezeTime 5 +0x094C mDeParams.mSurfStartFreezeTime 0x78 +0x0960 mDeParams.mSleepingCheckDist 50.0f +0x0974 mDeParams.mSleepingCheckHeight 30.0f +0x0988 mDeParams.mIllegalPlaneCtInc 4 +0x099C mDeParams.mIllegalPlaneTime 0x1e0 + --- mBodyAngleParamsFree (TBodyAngleParams) @ base 0x09A0 --- +0x09B8 mBodyAngleParamsFree.mHeadRot 0.0f +0x09CC mBodyAngleParamsFree.mWaistRoll 0.0f +0x09E0 mBodyAngleParamsFree.mWaistPitch 80.0f +0x09F4 mBodyAngleParamsFree.mWaistRollMax 0 +0x0A08 mBodyAngleParamsFree.mWaistPitchMax 1000 +0x0A1C mBodyAngleParamsFree.mWaistAngleChangeRate 0.07f + --- mBodyAngleParamsWaterGun (TBodyAngleParams) @ base 0x0A20 --- +0x0A38 mBodyAngleParamsWaterGun.mHeadRot 0.0f +0x0A4C mBodyAngleParamsWaterGun.mWaistRoll 0.0f +0x0A60 mBodyAngleParamsWaterGun.mWaistPitch 80.0f +0x0A74 mBodyAngleParamsWaterGun.mWaistRollMax 0 +0x0A88 mBodyAngleParamsWaterGun.mWaistPitchMax 1000 +0x0A9C mBodyAngleParamsWaterGun.mWaistAngleChangeRate 0.07f + --- mAttackParamsFencePunch (TAttackParams) @ base 0x0AA0 --- +0x0AB8 mAttackParamsFencePunch.mRadius 100.0f +0x0ACC mAttackParamsFencePunch.mHeight 50.0f + --- mAttackParamsKickRoof (TAttackParams) @ base 0x0AD0 --- +0x0AE8 mAttackParamsKickRoof.mRadius 100.0f +0x0AFC mAttackParamsKickRoof.mHeight 50.0f + --- mJumpParams (TJumpParams) @ base 0x0B00 --- +0x0B18 mJumpParams.mGravity 0.8f +0x0B2C mJumpParams.mSpinJumpGravity 0.6f +0x0B40 mJumpParams.mJumpingMax 80.0f +0x0B54 mJumpParams.mJumpSpeedBrake 0.999f +0x0B68 mJumpParams.mJumpAccelControl 0.5f +0x0B7C mJumpParams.mJumpSlideControl 0.5f +0x0B90 mJumpParams.mTurnJumpForce 62.0f +0x0BA4 mJumpParams.mFenceSpeed 2.0f +0x0BB8 mJumpParams.mFireDownForce 80.0f +0x0BCC mJumpParams.mFireDownControl 1.0f +0x0BE0 mJumpParams.mFireBackVelocity 1.0f +0x0BF4 mJumpParams.mBroadJumpForce 80.0f +0x0C08 mJumpParams.mBroadJumpForceY 30.0f +0x0C1C mJumpParams.mRotateJumpForceY 62.0f +0x0C30 mJumpParams.mPopUpSpeedY 5.0f +0x0C44 mJumpParams.mPopUpForceYMult 10.0f +0x0C58 mJumpParams.mBackJumpForce -16.0f +0x0C6C mJumpParams.mBackJumpForceY 62.0f +0x0C80 mJumpParams.mHipAttackSpeedY -50.0f +0x0C94 mJumpParams.mSuperHipAttackSpeedY -80.0f +0x0CA8 mJumpParams.mJumpCatchRotXSp 0x100 +0x0CBC mJumpParams.mJumpCatchRotXMax 0x2aaa +0x0CD0 mJumpParams.mRotBroadEnableV 30.0f +0x0CE4 mJumpParams.mRotBroadJumpForce 60.0f +0x0CF8 mJumpParams.mRotBroadJumpForceY 20.0f +0x0D0C mJumpParams.mTrampolineDec 1.0f +0x0D20 mJumpParams.mSecJumpEnableSp 20.0f +0x0D34 mJumpParams.mSecJumpForce 52.0f +0x0D48 mJumpParams.mSecJumpSpeedMult 0.0f +0x0D5C mJumpParams.mSecJumpXZMult 0.8f +0x0D70 mJumpParams.mTriJumpEnableSp 20.0f +0x0D84 mJumpParams.mUltraJumpForce 70.0f +0x0D98 mJumpParams.mUltraJumpSpeedMult 0.25f +0x0DAC mJumpParams.mUltraJumpXZMult 0.8f +0x0DC0 mJumpParams.mValleyDepth 500.0f +0x0DD4 mJumpParams.mThrownAccel 0.5f +0x0DE8 mJumpParams.mThrownSlide 0.5f +0x0DFC mJumpParams.mThrownBrake 0.98f +0x0E10 mJumpParams.mTremblePower 5.0f +0x0E24 mJumpParams.mTrembleAccele 2.0f +0x0E38 mJumpParams.mTrembleBrake 0.99f +0x0E4C mJumpParams.mTrembleTime 600 +0x0E60 mJumpParams.mClashAngle 0x3555 +0x0E74 mJumpParams.mJumpJumpCatchSp 50.0f +0x0E88 mJumpParams.mGetOffYoshiY 30.0f +0x0E9C mJumpParams.mSuperHipAttackCt 0x32 + --- mRunParams (TRunParams) @ base 0x0EA0 --- +0x0EB8 mRunParams.mMaxSpeed 32.0f +0x0ECC mRunParams.mVelMinusBrake 1.1f +0x0EE0 mRunParams.mAddBase 1.1f +0x0EF4 mRunParams.mAddVelDiv 0.0233f +0x0F08 mRunParams.mDecStartNrmY 0.95f +0x0F1C mRunParams.mDecBrake 1.0f +0x0F30 mRunParams.mSoft2Walk 8.0f +0x0F44 mRunParams.mWalk2Soft 5.0f +0x0F58 mRunParams.mSoftStepAnmMult 0.125f +0x0F6C mRunParams.mRunAnmSpeedBase 1.0f +0x0F80 mRunParams.mRunAnmSpeedMult 0.06f +0x0F94 mRunParams.mMotBlendWalkSp 1.0f +0x0FA8 mRunParams.mMotBlendRunSp 3.0f +0x0FBC mRunParams.mSwimDepth 120.0f +0x0FD0 mRunParams.mInWaterBrake 0.9f +0x0FE4 mRunParams.mInWaterAnmBrake 0.6f +0x0FF8 mRunParams.mPumpingSlideSp 0.1f +0x100C mRunParams.mPumpingSlideAnmSp 0.5f +0x1020 mRunParams.mDoJumpCatchSp 15.0f +0x1034 mRunParams.mTurnNeedSp 10.0f +0x1048 mRunParams.mDashRotSp 100 + --- mSwimParams (TSwimParams) @ base 0x104C --- +0x1064 mSwimParams.mStartSp 1.0f +0x1078 mSwimParams.mMoveSp 0.8f +0x108C mSwimParams.mMoveBrake 1.0f +0x10A0 mSwimParams.mSwimmingRotSpMin 0x200 +0x10B4 mSwimParams.mSwimmingRotSpMax 0x400 +0x10C8 mSwimParams.mPumpingRotSpMin 0x100 +0x10DC mSwimParams.mPumpingRotSpMax 0x200 +0x10F0 mSwimParams.mGravity 0.1f +0x1104 mSwimParams.mWaitBouyancy 1.0f +0x1118 mSwimParams.mMoveBouyancy 1.0f +0x112C mSwimParams.mUpDownBrake 0.95f +0x1140 mSwimParams.mCanJumpDepth 1.0f +0x1154 mSwimParams.mEndDepth 80.0f +0x1168 mSwimParams.mFloatHeight 120.0f +0x117C mSwimParams.mStartVMult 0.1f +0x1190 mSwimParams.mStartVYMult 0.1f +0x11A4 mSwimParams.mRush 3.0f +0x11B8 mSwimParams.mAnmBrake 0.02f +0x11CC mSwimParams.mPaddleSpeedUp 0.3f +0x11E0 mSwimParams.mPaddleJumpUp 1.0f +0x11F4 mSwimParams.mFloatUp 2.0f +0x1208 mSwimParams.mWaterLevelCheckHeight 10.0f +0x121C mSwimParams.mPaddleDown 1.0f +0x1230 mSwimParams.mWaitSinkTime 0x32 +0x1244 mSwimParams.mCanBreathDepth 50.0f +0x1258 mSwimParams.mWaitSinkSpeed 5.0f +0x126C mSwimParams.mAirDec 0.001f +0x1280 mSwimParams.mAirDecDive 0.001f +0x1294 mSwimParams.mAirInc 0.03f + --- mHangingParams (THangingParams) @ base 0x1298 --- +0x12B0 mHangingParams.mMoveSp 0.1f +0x12C4 mHangingParams.mAnmRate 0.5f +0x12D8 mHangingParams.mRapidTime 2000 +0x12EC mHangingParams.mLimitTime 0x960 +0x1300 mHangingParams.mAnmRapid 8.0f +0x1314 mHangingParams.mDescentSp 10.0f + --- mHangRoofParams (THangRoofParams) @ base 0x1318 --- +0x1330 mHangRoofParams.mAnmMult 0.3f + --- mWireParams (TWireParams) @ base 0x1334 --- +0x134C mWireParams.mRotSpeed -8 +0x1360 mWireParams.mRotSpeedTrgHover 8 +0x1374 mWireParams.mRotSpeedTrgTurbo 1000 +0x1388 mWireParams.mRotSpeedTrgRocket 1000 +0x139C mWireParams.mRotSpeedMax 0x578 +0x13B0 mWireParams.mRotStop 100 +0x13C4 mWireParams.mRotGravity 0x14 +0x13D8 mWireParams.mRotBrake 0.98f +0x13EC mWireParams.mJumpRate 0.09f +0x1400 mWireParams.mSwingRate 0.005f +0x1414 mWireParams.mWireJumpAccelControl 0.01f +0x1428 mWireParams.mWireJumpSlideControl 0.3f +0x143C mWireParams.mWireJumpMult 5.0f +0x1450 mWireParams.mWireJumpBase 20.0f +0x1464 mWireParams.mWireSwingBrake 0.99f +0x1478 mWireParams.mWireSwingMax 100.0f + --- mPullParamsBGBeak (TPullParams) @ base 0x147C --- +0x1494 mPullParamsBGBeak.mPullRateV 0.3f +0x14A8 mPullParamsBGBeak.mPullRateH 0.05f +0x14BC mPullParamsBGBeak.mOilPullRateV 0.1f +0x14D0 mPullParamsBGBeak.mOilPullRateH 0.01f + --- mPullParamsBGTentacle (TPullParams) @ base 0x14D4 --- +0x14EC mPullParamsBGTentacle.mPullRateV 0.3f +0x1500 mPullParamsBGTentacle.mPullRateH 0.05f +0x1514 mPullParamsBGTentacle.mOilPullRateV 0.1f +0x1528 mPullParamsBGTentacle.mOilPullRateH 0.01f + --- mPullParamsBGFireWanWanBossTail (TPullParams) @ base 0x152C --- +0x1544 mPullParamsBGFireWanWanBossTail.mPullRateV 0.3f +0x1558 mPullParamsBGFireWanWanBossTail.mPullRateH 0.05f +0x156C mPullParamsBGFireWanWanBossTail.mOilPullRateV 0.1f +0x1580 mPullParamsBGFireWanWanBossTail.mOilPullRateH 0.01f + --- mPullParamsFireWanWanTail (TPullParams) @ base 0x1584 --- +0x159C mPullParamsFireWanWanTail.mPullRateV 0.3f +0x15B0 mPullParamsFireWanWanTail.mPullRateH 0.05f +0x15C4 mPullParamsFireWanWanTail.mOilPullRateV 0.1f +0x15D8 mPullParamsFireWanWanTail.mOilPullRateH 0.01f + --- mBarParams (TBarParams) @ base 0x15DC --- +0x15F4 mBarParams.mClimbSp 0.035f +0x1608 mBarParams.mRotateSp 3.0f +0x161C mBarParams.mClimbAnmRate 0.0039f +0x1630 mBarParams.mCatchRadius 100.0f +0x1644 mBarParams.mCatchAngle 0.8f + --- mSurfingParamsWaterRed (TSurfingParams) @ base 0x1648 --- +0x1660 mSurfingParamsWaterRed.mRotMin 2048.0f +0x1674 mSurfingParamsWaterRed.mRotMax 1024.0f +0x1688 mSurfingParamsWaterRed.mPowMin 24.0f +0x169C mSurfingParamsWaterRed.mPowMax 64.0f +0x16B0 mSurfingParamsWaterRed.mAccel 58.0f +0x16C4 mSurfingParamsWaterRed.mWaistRoll 0.25f +0x16D8 mSurfingParamsWaterRed.mWaistPitch 170.0f +0x16EC mSurfingParamsWaterRed.mWaistRollMax 0x400 +0x1700 mSurfingParamsWaterRed.mWaistPitchMax 0x1555 +0x1714 mSurfingParamsWaterRed.mRoll -0.45f +0x1728 mSurfingParamsWaterRed.mPitch -170.0f +0x173C mSurfingParamsWaterRed.mRollMax 0x4000 +0x1750 mSurfingParamsWaterRed.mPitchMax 0x1555 +0x1764 mSurfingParamsWaterRed.mAngleChangeRate 0.01f +0x1778 mSurfingParamsWaterRed.mWaistAngleChangeRate 0.01f +0x178C mSurfingParamsWaterRed.mScaleMin 0.5f +0x17A0 mSurfingParamsWaterRed.mScaleMax 1.0f +0x17B4 mSurfingParamsWaterRed.mScaleMinSpeed 24.0f +0x17C8 mSurfingParamsWaterRed.mScaleMaxSpeed 60.0f +0x17DC mSurfingParamsWaterRed.mJumpPow 42.0f +0x17F0 mSurfingParamsWaterRed.mJumpXZRatio 0.25f +0x1804 mSurfingParamsWaterRed.mClashSpeed 40.0f +0x1818 mSurfingParamsWaterRed.mClashAngle 0x5555 + --- mSurfingParamsGroundRed (TSurfingParams) @ base 0x181C --- +0x1834 mSurfingParamsGroundRed.mRotMin 2048.0f +0x1848 mSurfingParamsGroundRed.mRotMax 1024.0f +0x185C mSurfingParamsGroundRed.mPowMin 24.0f +0x1870 mSurfingParamsGroundRed.mPowMax 64.0f +0x1884 mSurfingParamsGroundRed.mAccel 58.0f +0x1898 mSurfingParamsGroundRed.mWaistRoll 0.25f +0x18AC mSurfingParamsGroundRed.mWaistPitch 170.0f +0x18C0 mSurfingParamsGroundRed.mWaistRollMax 0x400 +0x18D4 mSurfingParamsGroundRed.mWaistPitchMax 0x1555 +0x18E8 mSurfingParamsGroundRed.mRoll -0.45f +0x18FC mSurfingParamsGroundRed.mPitch -170.0f +0x1910 mSurfingParamsGroundRed.mRollMax 0x4000 +0x1924 mSurfingParamsGroundRed.mPitchMax 0x1555 +0x1938 mSurfingParamsGroundRed.mAngleChangeRate 0.01f +0x194C mSurfingParamsGroundRed.mWaistAngleChangeRate 0.01f +0x1960 mSurfingParamsGroundRed.mScaleMin 0.5f +0x1974 mSurfingParamsGroundRed.mScaleMax 1.0f +0x1988 mSurfingParamsGroundRed.mScaleMinSpeed 24.0f +0x199C mSurfingParamsGroundRed.mScaleMaxSpeed 60.0f +0x19B0 mSurfingParamsGroundRed.mJumpPow 42.0f +0x19C4 mSurfingParamsGroundRed.mJumpXZRatio 0.25f +0x19D8 mSurfingParamsGroundRed.mClashSpeed 40.0f +0x19EC mSurfingParamsGroundRed.mClashAngle 0x5555 + --- mSurfingParamsWaterYellow (TSurfingParams) @ base 0x19F0 --- +0x1A08 mSurfingParamsWaterYellow.mRotMin 2048.0f +0x1A1C mSurfingParamsWaterYellow.mRotMax 1024.0f +0x1A30 mSurfingParamsWaterYellow.mPowMin 24.0f +0x1A44 mSurfingParamsWaterYellow.mPowMax 64.0f +0x1A58 mSurfingParamsWaterYellow.mAccel 58.0f +0x1A6C mSurfingParamsWaterYellow.mWaistRoll 0.25f +0x1A80 mSurfingParamsWaterYellow.mWaistPitch 170.0f +0x1A94 mSurfingParamsWaterYellow.mWaistRollMax 0x400 +0x1AA8 mSurfingParamsWaterYellow.mWaistPitchMax 0x1555 +0x1ABC mSurfingParamsWaterYellow.mRoll -0.45f +0x1AD0 mSurfingParamsWaterYellow.mPitch -170.0f +0x1AE4 mSurfingParamsWaterYellow.mRollMax 0x4000 +0x1AF8 mSurfingParamsWaterYellow.mPitchMax 0x1555 +0x1B0C mSurfingParamsWaterYellow.mAngleChangeRate 0.01f +0x1B20 mSurfingParamsWaterYellow.mWaistAngleChangeRate 0.01f +0x1B34 mSurfingParamsWaterYellow.mScaleMin 0.5f +0x1B48 mSurfingParamsWaterYellow.mScaleMax 1.0f +0x1B5C mSurfingParamsWaterYellow.mScaleMinSpeed 24.0f +0x1B70 mSurfingParamsWaterYellow.mScaleMaxSpeed 60.0f +0x1B84 mSurfingParamsWaterYellow.mJumpPow 42.0f +0x1B98 mSurfingParamsWaterYellow.mJumpXZRatio 0.25f +0x1BAC mSurfingParamsWaterYellow.mClashSpeed 40.0f +0x1BC0 mSurfingParamsWaterYellow.mClashAngle 0x5555 + --- mSurfingParamsGroundYellow (TSurfingParams) @ base 0x1BC4 --- +0x1BDC mSurfingParamsGroundYellow.mRotMin 2048.0f +0x1BF0 mSurfingParamsGroundYellow.mRotMax 1024.0f +0x1C04 mSurfingParamsGroundYellow.mPowMin 24.0f +0x1C18 mSurfingParamsGroundYellow.mPowMax 64.0f +0x1C2C mSurfingParamsGroundYellow.mAccel 58.0f +0x1C40 mSurfingParamsGroundYellow.mWaistRoll 0.25f +0x1C54 mSurfingParamsGroundYellow.mWaistPitch 170.0f +0x1C68 mSurfingParamsGroundYellow.mWaistRollMax 0x400 +0x1C7C mSurfingParamsGroundYellow.mWaistPitchMax 0x1555 +0x1C90 mSurfingParamsGroundYellow.mRoll -0.45f +0x1CA4 mSurfingParamsGroundYellow.mPitch -170.0f +0x1CB8 mSurfingParamsGroundYellow.mRollMax 0x4000 +0x1CCC mSurfingParamsGroundYellow.mPitchMax 0x1555 +0x1CE0 mSurfingParamsGroundYellow.mAngleChangeRate 0.01f +0x1CF4 mSurfingParamsGroundYellow.mWaistAngleChangeRate 0.01f +0x1D08 mSurfingParamsGroundYellow.mScaleMin 0.5f +0x1D1C mSurfingParamsGroundYellow.mScaleMax 1.0f +0x1D30 mSurfingParamsGroundYellow.mScaleMinSpeed 24.0f +0x1D44 mSurfingParamsGroundYellow.mScaleMaxSpeed 60.0f +0x1D58 mSurfingParamsGroundYellow.mJumpPow 42.0f +0x1D6C mSurfingParamsGroundYellow.mJumpXZRatio 0.25f +0x1D80 mSurfingParamsGroundYellow.mClashSpeed 40.0f +0x1D94 mSurfingParamsGroundYellow.mClashAngle 0x5555 + --- mSurfingParamsWaterGreen (TSurfingParams) @ base 0x1D98 --- +0x1DB0 mSurfingParamsWaterGreen.mRotMin 2048.0f +0x1DC4 mSurfingParamsWaterGreen.mRotMax 1024.0f +0x1DD8 mSurfingParamsWaterGreen.mPowMin 24.0f +0x1DEC mSurfingParamsWaterGreen.mPowMax 64.0f +0x1E00 mSurfingParamsWaterGreen.mAccel 58.0f +0x1E14 mSurfingParamsWaterGreen.mWaistRoll 0.25f +0x1E28 mSurfingParamsWaterGreen.mWaistPitch 170.0f +0x1E3C mSurfingParamsWaterGreen.mWaistRollMax 0x400 +0x1E50 mSurfingParamsWaterGreen.mWaistPitchMax 0x1555 +0x1E64 mSurfingParamsWaterGreen.mRoll -0.45f +0x1E78 mSurfingParamsWaterGreen.mPitch -170.0f +0x1E8C mSurfingParamsWaterGreen.mRollMax 0x4000 +0x1EA0 mSurfingParamsWaterGreen.mPitchMax 0x1555 +0x1EB4 mSurfingParamsWaterGreen.mAngleChangeRate 0.01f +0x1EC8 mSurfingParamsWaterGreen.mWaistAngleChangeRate 0.01f +0x1EDC mSurfingParamsWaterGreen.mScaleMin 0.5f +0x1EF0 mSurfingParamsWaterGreen.mScaleMax 1.0f +0x1F04 mSurfingParamsWaterGreen.mScaleMinSpeed 24.0f +0x1F18 mSurfingParamsWaterGreen.mScaleMaxSpeed 60.0f +0x1F2C mSurfingParamsWaterGreen.mJumpPow 42.0f +0x1F40 mSurfingParamsWaterGreen.mJumpXZRatio 0.25f +0x1F54 mSurfingParamsWaterGreen.mClashSpeed 40.0f +0x1F68 mSurfingParamsWaterGreen.mClashAngle 0x5555 + --- mSurfingParamsGroundGreen (TSurfingParams) @ base 0x1F6C --- +0x1F84 mSurfingParamsGroundGreen.mRotMin 2048.0f +0x1F98 mSurfingParamsGroundGreen.mRotMax 1024.0f +0x1FAC mSurfingParamsGroundGreen.mPowMin 24.0f +0x1FC0 mSurfingParamsGroundGreen.mPowMax 64.0f +0x1FD4 mSurfingParamsGroundGreen.mAccel 58.0f +0x1FE8 mSurfingParamsGroundGreen.mWaistRoll 0.25f +0x1FFC mSurfingParamsGroundGreen.mWaistPitch 170.0f +0x2010 mSurfingParamsGroundGreen.mWaistRollMax 0x400 +0x2024 mSurfingParamsGroundGreen.mWaistPitchMax 0x1555 +0x2038 mSurfingParamsGroundGreen.mRoll -0.45f +0x204C mSurfingParamsGroundGreen.mPitch -170.0f +0x2060 mSurfingParamsGroundGreen.mRollMax 0x4000 +0x2074 mSurfingParamsGroundGreen.mPitchMax 0x1555 +0x2088 mSurfingParamsGroundGreen.mAngleChangeRate 0.01f +0x209C mSurfingParamsGroundGreen.mWaistAngleChangeRate 0.01f +0x20B0 mSurfingParamsGroundGreen.mScaleMin 0.5f +0x20C4 mSurfingParamsGroundGreen.mScaleMax 1.0f +0x20D8 mSurfingParamsGroundGreen.mScaleMinSpeed 24.0f +0x20EC mSurfingParamsGroundGreen.mScaleMaxSpeed 60.0f +0x2100 mSurfingParamsGroundGreen.mJumpPow 42.0f +0x2114 mSurfingParamsGroundGreen.mJumpXZRatio 0.25f +0x2128 mSurfingParamsGroundGreen.mClashSpeed 40.0f +0x213C mSurfingParamsGroundGreen.mClashAngle 0x5555 + --- mHoverParams (THHoverParams) @ base 0x2140 --- +0x2158 mHoverParams.mRotSp 0x80 +0x216C mHoverParams.mAccelRate 0.03f +0x2180 mHoverParams.mBrake 0.95f + --- mDivingParams (TDivingParams) @ base 0x2184 --- +0x219C mDivingParams.mRotSp 0x80 +0x21B0 mDivingParams.mGravity 0.5f +0x21C4 mDivingParams.mAccelControl 0.02f +0x21D8 mDivingParams.mSeaBrake 0.999f +0x21EC mDivingParams.mSeaBrakeY 0.98f + --- mYoshiParams (TYoshiParams) @ base 0x21F0 --- +0x2208 mYoshiParams.mRunYoshiMult 1.2f +0x221C mYoshiParams.mJumpYoshiMult 1.0f +0x2230 mYoshiParams.mRotYoshiMult 1.5f +0x2244 mYoshiParams.mHeadFront 80.0f +0x2258 mYoshiParams.mHeadRadius 50.0f +0x226C mYoshiParams.mHoldOutAccCtrlF 0.01f +0x2280 mYoshiParams.mHoldOutAccCtrlB 0.023f +0x2294 mYoshiParams.mHoldOutSldCtrl 0.3f +0x22A8 mYoshiParams.mDecBrake 1.0f + --- mWaterEffectParams (TWaterEffectParams) @ base 0x22AC --- +0x22C4 mWaterEffectParams.mJumpIntoMdlEffectSpY 10.0f +0x22D8 mWaterEffectParams.mJumpIntoMinY 20.0f +0x22EC mWaterEffectParams.mJumpIntoMaxY 50.0f +0x2300 mWaterEffectParams.mJumpIntoScaleMin 0.75f +0x2314 mWaterEffectParams.mJumpIntoScaleWidth 1.0f +0x2328 mWaterEffectParams.mRunningRippleSpeed 30.0f +0x233C mWaterEffectParams.mRunningRippleDepth 30.0f + --- mControllerParams (TControllerParams) @ base 0x2340 --- +0x2358 mControllerParams.mAnalogLRToZeroVal 0x1e +0x236C mControllerParams.mAnalogLRToMiddleVal 0x5a +0x2380 mControllerParams.mAnalogLRToMaxVal 0x96 +0x2394 mControllerParams.mAnalogLRMiddleLevel 0.1f +0x23A8 mControllerParams.mStartToWalkLevel 15.0f +0x23BC mControllerParams.mStickRotateTime 0x18 +0x23D0 mControllerParams.mLengthMultTimes 10 +0x23E4 mControllerParams.mLengthMult 0.935f +0x23F8 mControllerParams.mSquatRotMidAnalog 0.7f +0x240C mControllerParams.mSquatRotMidValue 0.05f + --- mGraffitoParams (TGraffitoParams) @ base 0x2410 --- +0x2428 mGraffitoParams.mSinkTime 0xf0 +0x243C mGraffitoParams.mSinkDmgTime 0xf0 +0x2450 mGraffitoParams.mSinkHeight 150.0f +0x2464 mGraffitoParams.mSinkMoveMin 0.3f +0x2478 mGraffitoParams.mSinkMoveMax 0.5f +0x248C mGraffitoParams.mSinkRecover 0.05f +0x24A0 mGraffitoParams.mSinkJumpRateMin 0.1f +0x24B4 mGraffitoParams.mSinkJumpRateMax 0.3f +0x24C8 mGraffitoParams.mSinkPumpLimit 0.25f +0x24DC mGraffitoParams.mSinkDmgDepth 0.25f +0x24F0 mGraffitoParams.mFireHeight 1000.0f +0x2504 mGraffitoParams.mDizzySlipCtMax 1000 +0x2518 mGraffitoParams.mDizzyWalkCtMax 1000 +0x252C mGraffitoParams.mDizzyAngleY 0x7fff +0x2540 mGraffitoParams.mDizzyAngleRate 400.0f +0x2554 mGraffitoParams.mDizzyPowerRate 120.0f +0x2568 mGraffitoParams.mDizzyPower 20.0f +0x257C mGraffitoParams.mFireInvincibleTime 0x96 +0x2590 mGraffitoParams.mFootEraseTimes 4 +0x25A4 mGraffitoParams.mFootEraseSize 400.0f +0x25B8 mGraffitoParams.mFootEraseFront 200.0f + --- mDirtyParams (TDirtyParams) @ base 0x25BC --- +0x25D4 mDirtyParams.mIncRunning 0.1f +0x25E8 mDirtyParams.mIncCatching 0.3f +0x25FC mDirtyParams.mIncSlipping 0.2f +0x2610 mDirtyParams.mDecSwimming 0.5f +0x2624 mDirtyParams.mDecWaterHit 0.2f +0x2638 mDirtyParams.mDecRotJump 0.1f +0x264C mDirtyParams.mBrakeStartValSlip 0.99f +0x2660 mDirtyParams.mBrakeStartValRun 0.98f +0x2674 mDirtyParams.mDirtyTimeSlip 600 +0x2688 mDirtyParams.mDirtyTimeRun 600 +0x269C mDirtyParams.mPolSizeSlip 200.0f +0x26B0 mDirtyParams.mPolSizeRun 80.0f +0x26C4 mDirtyParams.mPolSizeFootPrint 200.0f +0x26D8 mDirtyParams.mPolSizeJump 200.0f +0x26EC mDirtyParams.mSlopeAngle 0.99f +0x2700 mDirtyParams.mDirtyMax 200.0f +0x2714 mDirtyParams.mSlipAnmSpeed 3.0f +0x2728 mDirtyParams.mSlipRunSp 0.01f +0x273C mDirtyParams.mSlipCatchSp 0.01f +0x2750 mDirtyParams.mSlipRotate 0x100 +0x2764 mDirtyParams.mSlipCatchRotate 0x100 +0x2778 mDirtyParams.mBrakeSlipNoPollute 0.98f +0x278C mDirtyParams.mFogTimeYellow 0xf0 +0x27A0 mDirtyParams.mFogTimeRed 600 + --- mMotorParams (TMotorParams) @ base 0x27A4 --- +0x27BC mMotorParams.mMotorReturn 0x19 +0x27D0 mMotorParams.mMotorTrample 8 +0x27E4 mMotorParams.mMotorHipDrop 0xf +0x27F8 mMotorParams.mMotorWall 6 + --- mParticleParams (TParticleParams) @ base 0x27FC --- +0x2814 mParticleParams.mMeltInWaterMax 0.5f +0x2828 mParticleParams.mWaveEmitSpeed 5.0f +0x283C mParticleParams.mWaveAlphaDec 5 +0x2850 mParticleParams.mBubbleDepth 10.0f +0x2864 mParticleParams.mBodyBubbleSpMin 0.0f +0x2878 mParticleParams.mBodyBubbleSpMax 40.0f +0x288C mParticleParams.mBodyBubbleEmitMin 0.0f +0x28A0 mParticleParams.mBodyBubbleEmitMax 0.5f +0x28B4 mParticleParams.mBubbleToRipple 0.3f +0x28C8 mParticleParams.mToroccoWind 0.001f +0x28DC mParticleParams.mToroccoSpark 0.001f + --- mEffectParams (TEffectParams) @ base 0x28E0 --- +0x28F8 mEffectParams.mDashInc 0.033f +0x290C mEffectParams.mDashDec 0.017f +0x2920 mEffectParams.mDashMaxBlendInBlur 0xb4 +0x2934 mEffectParams.mDashMaxBlendInIris 0xb4 +0x2948 mEffectParams.mDashBlendScale 0.2f + --- mSlipParamsNormal (TSlipParams) @ base 0x294C --- +0x2964 mSlipParamsNormal.mSlipFriction 0.9f +0x2978 mSlipParamsNormal.mSlopeAcceleUp 0.0f +0x298C mSlipParamsNormal.mSlopeAcceleDown 0.0f +0x29A0 mSlipParamsNormal.mSlideAcceleUp 0.0f +0x29B4 mSlipParamsNormal.mSlideAcceleDown 0.0f +0x29C8 mSlipParamsNormal.mSlideStopNormal 15.0f +0x29DC mSlipParamsNormal.mSlideStopCatch 15.0f +0x29F0 mSlipParamsNormal.mJumpEnable 1 +0x2A04 mSlipParamsNormal.mMissJump 1 +0x2A18 mSlipParamsNormal.mSlideAngleYSp 0x200 +0x2A2C mSlipParamsNormal.mStickSlideMult 0.05f + --- mSlipParamsOil (TSlipParams) @ base 0x2A30 --- +0x2A48 mSlipParamsOil.mSlipFriction 0.9f +0x2A5C mSlipParamsOil.mSlopeAcceleUp 0.0f +0x2A70 mSlipParamsOil.mSlopeAcceleDown 0.0f +0x2A84 mSlipParamsOil.mSlideAcceleUp 0.0f +0x2A98 mSlipParamsOil.mSlideAcceleDown 0.0f +0x2AAC mSlipParamsOil.mSlideStopNormal 15.0f +0x2AC0 mSlipParamsOil.mSlideStopCatch 15.0f +0x2AD4 mSlipParamsOil.mJumpEnable 1 +0x2AE8 mSlipParamsOil.mMissJump 1 +0x2AFC mSlipParamsOil.mSlideAngleYSp 0x200 +0x2B10 mSlipParamsOil.mStickSlideMult 0.05f + --- mSlipParamsAll (TSlipParams) @ base 0x2B14 --- +0x2B2C mSlipParamsAll.mSlipFriction 0.9f +0x2B40 mSlipParamsAll.mSlopeAcceleUp 0.0f +0x2B54 mSlipParamsAll.mSlopeAcceleDown 0.0f +0x2B68 mSlipParamsAll.mSlideAcceleUp 0.0f +0x2B7C mSlipParamsAll.mSlideAcceleDown 0.0f +0x2B90 mSlipParamsAll.mSlideStopNormal 15.0f +0x2BA4 mSlipParamsAll.mSlideStopCatch 15.0f +0x2BB8 mSlipParamsAll.mJumpEnable 1 +0x2BCC mSlipParamsAll.mMissJump 1 +0x2BE0 mSlipParamsAll.mSlideAngleYSp 0x200 +0x2BF4 mSlipParamsAll.mStickSlideMult 0.05f + --- mSlipParamsAllSlider (TSlipParams) @ base 0x2BF8 --- +0x2C10 mSlipParamsAllSlider.mSlipFriction 0.9f +0x2C24 mSlipParamsAllSlider.mSlopeAcceleUp 0.0f +0x2C38 mSlipParamsAllSlider.mSlopeAcceleDown 0.0f +0x2C4C mSlipParamsAllSlider.mSlideAcceleUp 0.0f +0x2C60 mSlipParamsAllSlider.mSlideAcceleDown 0.0f +0x2C74 mSlipParamsAllSlider.mSlideStopNormal 15.0f +0x2C88 mSlipParamsAllSlider.mSlideStopCatch 15.0f +0x2C9C mSlipParamsAllSlider.mJumpEnable 1 +0x2CB0 mSlipParamsAllSlider.mMissJump 1 +0x2CC4 mSlipParamsAllSlider.mSlideAngleYSp 0x200 +0x2CD8 mSlipParamsAllSlider.mStickSlideMult 0.05f + --- mSlipParams45 (TSlipParams) @ base 0x2CDC --- +0x2CF4 mSlipParams45.mSlipFriction 0.9f +0x2D08 mSlipParams45.mSlopeAcceleUp 0.0f +0x2D1C mSlipParams45.mSlopeAcceleDown 0.0f +0x2D30 mSlipParams45.mSlideAcceleUp 0.0f +0x2D44 mSlipParams45.mSlideAcceleDown 0.0f +0x2D58 mSlipParams45.mSlideStopNormal 15.0f +0x2D6C mSlipParams45.mSlideStopCatch 15.0f +0x2D80 mSlipParams45.mJumpEnable 1 +0x2D94 mSlipParams45.mMissJump 1 +0x2DA8 mSlipParams45.mSlideAngleYSp 0x200 +0x2DBC mSlipParams45.mStickSlideMult 0.05f + --- mSlipParamsWaterSlope (TSlipParams) @ base 0x2DC0 --- +0x2DD8 mSlipParamsWaterSlope.mSlipFriction 0.9f +0x2DEC mSlipParamsWaterSlope.mSlopeAcceleUp 0.0f +0x2E00 mSlipParamsWaterSlope.mSlopeAcceleDown 0.0f +0x2E14 mSlipParamsWaterSlope.mSlideAcceleUp 0.0f +0x2E28 mSlipParamsWaterSlope.mSlideAcceleDown 0.0f +0x2E3C mSlipParamsWaterSlope.mSlideStopNormal 15.0f +0x2E50 mSlipParamsWaterSlope.mSlideStopCatch 15.0f +0x2E64 mSlipParamsWaterSlope.mJumpEnable 1 +0x2E78 mSlipParamsWaterSlope.mMissJump 1 +0x2E8C mSlipParamsWaterSlope.mSlideAngleYSp 0x200 +0x2EA0 mSlipParamsWaterSlope.mStickSlideMult 0.05f + --- mSlipParamsWaterGround (TSlipParams) @ base 0x2EA4 --- +0x2EBC mSlipParamsWaterGround.mSlipFriction 0.9f +0x2ED0 mSlipParamsWaterGround.mSlopeAcceleUp 0.0f +0x2EE4 mSlipParamsWaterGround.mSlopeAcceleDown 0.0f +0x2EF8 mSlipParamsWaterGround.mSlideAcceleUp 0.0f +0x2F0C mSlipParamsWaterGround.mSlideAcceleDown 0.0f +0x2F20 mSlipParamsWaterGround.mSlideStopNormal 15.0f +0x2F34 mSlipParamsWaterGround.mSlideStopCatch 15.0f +0x2F48 mSlipParamsWaterGround.mJumpEnable 1 +0x2F5C mSlipParamsWaterGround.mMissJump 1 +0x2F70 mSlipParamsWaterGround.mSlideAngleYSp 0x200 +0x2F84 mSlipParamsWaterGround.mStickSlideMult 0.05f + --- mSlipParamsYoshi (TSlipParams) @ base 0x2F88 --- +0x2FA0 mSlipParamsYoshi.mSlipFriction 0.9f +0x2FB4 mSlipParamsYoshi.mSlopeAcceleUp 0.0f +0x2FC8 mSlipParamsYoshi.mSlopeAcceleDown 0.0f +0x2FDC mSlipParamsYoshi.mSlideAcceleUp 0.0f +0x2FF0 mSlipParamsYoshi.mSlideAcceleDown 0.0f +0x3004 mSlipParamsYoshi.mSlideStopNormal 15.0f +0x3018 mSlipParamsYoshi.mSlideStopCatch 15.0f +0x302C mSlipParamsYoshi.mJumpEnable 1 +0x3040 mSlipParamsYoshi.mMissJump 1 +0x3054 mSlipParamsYoshi.mSlideAngleYSp 0x200 +0x3068 mSlipParamsYoshi.mStickSlideMult 0.05f + --- mUpperBodyParams (TUpperParams) @ base 0x306C --- +0x3084 mUpperBodyParams.mPumpWaitTime 10 +0x3098 mUpperBodyParams.mPumpAnmSpeed 0.01f +0x30AC mUpperBodyParams.mHoverHeadAngle 0xe000 +0x30C0 mUpperBodyParams.mFeelDeepHeadAngle 0x2000 +0x30D4 mUpperBodyParams.mFrontWallHeadAngle 0xe000 + --- mDmgParamsEnemyCommon (TEParams) @ base 0x30D8 --- +0x30F0 mDmgParamsEnemyCommon.mDamage 1 +0x3104 mDmgParamsEnemyCommon.mDownType 0 +0x3118 mDmgParamsEnemyCommon.mWaterEmit 0 +0x312C mDmgParamsEnemyCommon.mMotor 0 +0x3140 mDmgParamsEnemyCommon.mMinSpeed 0.0f +0x3154 mDmgParamsEnemyCommon.mDirty 0.0f +0x3168 mDmgParamsEnemyCommon.mInvincibleTime 0 + --- mDmgParamsHamakuri (TEParams) @ base 0x316C --- +0x3184 mDmgParamsHamakuri.mDamage 1 +0x3198 mDmgParamsHamakuri.mDownType 0 +0x31AC mDmgParamsHamakuri.mWaterEmit 0 +0x31C0 mDmgParamsHamakuri.mMotor 0 +0x31D4 mDmgParamsHamakuri.mMinSpeed 0.0f +0x31E8 mDmgParamsHamakuri.mDirty 0.0f +0x31FC mDmgParamsHamakuri.mInvincibleTime 0 + --- mDmgParamsNamekuri (TEParams) @ base 0x3200 --- +0x3218 mDmgParamsNamekuri.mDamage 1 +0x322C mDmgParamsNamekuri.mDownType 0 +0x3240 mDmgParamsNamekuri.mWaterEmit 0 +0x3254 mDmgParamsNamekuri.mMotor 0 +0x3268 mDmgParamsNamekuri.mMinSpeed 0.0f +0x327C mDmgParamsNamekuri.mDirty 0.0f +0x3290 mDmgParamsNamekuri.mInvincibleTime 0 + --- mDmgParamsHinokuri (TEParams) @ base 0x3294 --- +0x32AC mDmgParamsHinokuri.mDamage 1 +0x32C0 mDmgParamsHinokuri.mDownType 0 +0x32D4 mDmgParamsHinokuri.mWaterEmit 0 +0x32E8 mDmgParamsHinokuri.mMotor 0 +0x32FC mDmgParamsHinokuri.mMinSpeed 0.0f +0x3310 mDmgParamsHinokuri.mDirty 0.0f +0x3324 mDmgParamsHinokuri.mInvincibleTime 0 + --- mDmgParamsFire (TEParams) @ base 0x3328 --- +0x3340 mDmgParamsFire.mDamage 1 +0x3354 mDmgParamsFire.mDownType 0 +0x3368 mDmgParamsFire.mWaterEmit 0 +0x337C mDmgParamsFire.mMotor 0 +0x3390 mDmgParamsFire.mMinSpeed 0.0f +0x33A4 mDmgParamsFire.mDirty 0.0f +0x33B8 mDmgParamsFire.mInvincibleTime 0 + --- mDmgParamsBGTentacle (TEParams) @ base 0x33BC --- +0x33D4 mDmgParamsBGTentacle.mDamage 1 +0x33E8 mDmgParamsBGTentacle.mDownType 0 +0x33FC mDmgParamsBGTentacle.mWaterEmit 0 +0x3410 mDmgParamsBGTentacle.mMotor 0 +0x3424 mDmgParamsBGTentacle.mMinSpeed 0.0f +0x3438 mDmgParamsBGTentacle.mDirty 0.0f +0x344C mDmgParamsBGTentacle.mInvincibleTime 0 + --- mDmgParamsBossEel (TEParams) @ base 0x3450 --- +0x3468 mDmgParamsBossEel.mDamage 1 +0x347C mDmgParamsBossEel.mDownType 0 +0x3490 mDmgParamsBossEel.mWaterEmit 0 +0x34A4 mDmgParamsBossEel.mMotor 0 +0x34B8 mDmgParamsBossEel.mMinSpeed 0.0f +0x34CC mDmgParamsBossEel.mDirty 0.0f +0x34E0 mDmgParamsBossEel.mInvincibleTime 0 + --- mDmgParamsHanachanBoss (TEParams) @ base 0x34E4 --- +0x34FC mDmgParamsHanachanBoss.mDamage 1 +0x3510 mDmgParamsHanachanBoss.mDownType 0 +0x3524 mDmgParamsHanachanBoss.mWaterEmit 0 +0x3538 mDmgParamsHanachanBoss.mMotor 0 +0x354C mDmgParamsHanachanBoss.mMinSpeed 0.0f +0x3560 mDmgParamsHanachanBoss.mDirty 0.0f +0x3574 mDmgParamsHanachanBoss.mInvincibleTime 0 + --- mDmgParamsPoihana (TEParams) @ base 0x3578 --- +0x3590 mDmgParamsPoihana.mDamage 1 +0x35A4 mDmgParamsPoihana.mDownType 0 +0x35B8 mDmgParamsPoihana.mWaterEmit 0 +0x35CC mDmgParamsPoihana.mMotor 0 +0x35E0 mDmgParamsPoihana.mMinSpeed 0.0f +0x35F4 mDmgParamsPoihana.mDirty 0.0f +0x3608 mDmgParamsPoihana.mInvincibleTime 0 + --- mDmgParamsKiller (TEParams) @ base 0x360C --- +0x3624 mDmgParamsKiller.mDamage 1 +0x3638 mDmgParamsKiller.mDownType 0 +0x364C mDmgParamsKiller.mWaterEmit 0 +0x3660 mDmgParamsKiller.mMotor 0 +0x3674 mDmgParamsKiller.mMinSpeed 0.0f +0x3688 mDmgParamsKiller.mDirty 0.0f +0x369C mDmgParamsKiller.mInvincibleTime 0 + --- mDmgParamsLampTrapIron (TEParams) @ base 0x36A0 --- +0x36B8 mDmgParamsLampTrapIron.mDamage 1 +0x36CC mDmgParamsLampTrapIron.mDownType 0 +0x36E0 mDmgParamsLampTrapIron.mWaterEmit 0 +0x36F4 mDmgParamsLampTrapIron.mMotor 0 +0x3708 mDmgParamsLampTrapIron.mMinSpeed 0.0f +0x371C mDmgParamsLampTrapIron.mDirty 0.0f +0x3730 mDmgParamsLampTrapIron.mInvincibleTime 0 + --- mDmgParamsLampTrapSpike (TEParams) @ base 0x3734 --- +0x374C mDmgParamsLampTrapSpike.mDamage 1 +0x3760 mDmgParamsLampTrapSpike.mDownType 0 +0x3774 mDmgParamsLampTrapSpike.mWaterEmit 0 +0x3788 mDmgParamsLampTrapSpike.mMotor 0 +0x379C mDmgParamsLampTrapSpike.mMinSpeed 0.0f +0x37B0 mDmgParamsLampTrapSpike.mDirty 0.0f +0x37C4 mDmgParamsLampTrapSpike.mInvincibleTime 0 + --- mDmgParamsEnemyMario (TEParams) @ base 0x37C8 --- +0x37E0 mDmgParamsEnemyMario.mDamage 1 +0x37F4 mDmgParamsEnemyMario.mDownType 0 +0x3808 mDmgParamsEnemyMario.mWaterEmit 0 +0x381C mDmgParamsEnemyMario.mMotor 0 +0x3830 mDmgParamsEnemyMario.mMinSpeed 0.0f +0x3844 mDmgParamsEnemyMario.mDirty 0.0f +0x3858 mDmgParamsEnemyMario.mInvincibleTime 0 + --- mDmgParamsCannotBreath (TEParams) @ base 0x385C --- +0x3874 mDmgParamsCannotBreath.mDamage 1 +0x3888 mDmgParamsCannotBreath.mDownType 0 +0x389C mDmgParamsCannotBreath.mWaterEmit 0 +0x38B0 mDmgParamsCannotBreath.mMotor 0 +0x38C4 mDmgParamsCannotBreath.mMinSpeed 0.0f +0x38D8 mDmgParamsCannotBreath.mDirty 0.0f +0x38EC mDmgParamsCannotBreath.mInvincibleTime 0 + --- mDmgParamsGraffitoFire (TEParams) @ base 0x38F0 --- +0x3908 mDmgParamsGraffitoFire.mDamage 1 +0x391C mDmgParamsGraffitoFire.mDownType 0 +0x3930 mDmgParamsGraffitoFire.mWaterEmit 0 +0x3944 mDmgParamsGraffitoFire.mMotor 0 +0x3958 mDmgParamsGraffitoFire.mMinSpeed 0.0f +0x396C mDmgParamsGraffitoFire.mDirty 0.0f +0x3980 mDmgParamsGraffitoFire.mInvincibleTime 0 + --- mDmgParamsGraffitoPoison (TEParams) @ base 0x3984 --- +0x399C mDmgParamsGraffitoPoison.mDamage 1 +0x39B0 mDmgParamsGraffitoPoison.mDownType 0 +0x39C4 mDmgParamsGraffitoPoison.mWaterEmit 0 +0x39D8 mDmgParamsGraffitoPoison.mMotor 0 +0x39EC mDmgParamsGraffitoPoison.mMinSpeed 0.0f +0x3A00 mDmgParamsGraffitoPoison.mDirty 0.0f +0x3A14 mDmgParamsGraffitoPoison.mInvincibleTime 0 + --- mDmgParamsGraffitoElec (TEParams) @ base 0x3A18 --- +0x3A30 mDmgParamsGraffitoElec.mDamage 1 +0x3A44 mDmgParamsGraffitoElec.mDownType 0 +0x3A58 mDmgParamsGraffitoElec.mWaterEmit 0 +0x3A6C mDmgParamsGraffitoElec.mMotor 0 +0x3A80 mDmgParamsGraffitoElec.mMinSpeed 0.0f +0x3A94 mDmgParamsGraffitoElec.mDirty 0.0f +0x3AA8 mDmgParamsGraffitoElec.mInvincibleTime 0 + --- mDmgParamsGraffitoLava (TEParams) @ base 0x3AAC --- +0x3AC4 mDmgParamsGraffitoLava.mDamage 1 +0x3AD8 mDmgParamsGraffitoLava.mDownType 0 +0x3AEC mDmgParamsGraffitoLava.mWaterEmit 0 +0x3B00 mDmgParamsGraffitoLava.mMotor 0 +0x3B14 mDmgParamsGraffitoLava.mMinSpeed 0.0f +0x3B28 mDmgParamsGraffitoLava.mDirty 0.0f +0x3B3C mDmgParamsGraffitoLava.mInvincibleTime 0 + --- mDmgParamsWaterSurface (TEParams) @ base 0x3B40 --- +0x3B58 mDmgParamsWaterSurface.mDamage 1 +0x3B6C mDmgParamsWaterSurface.mDownType 0 +0x3B80 mDmgParamsWaterSurface.mWaterEmit 0 +0x3B94 mDmgParamsWaterSurface.mMotor 0 +0x3BA8 mDmgParamsWaterSurface.mMinSpeed 0.0f +0x3BBC mDmgParamsWaterSurface.mDirty 0.0f +0x3BD0 mDmgParamsWaterSurface.mInvincibleTime 0 + --- mDmgMapParams0 (TEParams) @ base 0x3BD4 --- +0x3BEC mDmgMapParams0.mDamage 1 +0x3C00 mDmgMapParams0.mDownType 0 +0x3C14 mDmgMapParams0.mWaterEmit 0 +0x3C28 mDmgMapParams0.mMotor 0 +0x3C3C mDmgMapParams0.mMinSpeed 0.0f +0x3C50 mDmgMapParams0.mDirty 0.0f +0x3C64 mDmgMapParams0.mInvincibleTime 0 + --- mDmgMapParams1 (TEParams) @ base 0x3C68 --- +0x3C80 mDmgMapParams1.mDamage 1 +0x3C94 mDmgMapParams1.mDownType 0 +0x3CA8 mDmgMapParams1.mWaterEmit 0 +0x3CBC mDmgMapParams1.mMotor 0 +0x3CD0 mDmgMapParams1.mMinSpeed 0.0f +0x3CE4 mDmgMapParams1.mDirty 0.0f +0x3CF8 mDmgMapParams1.mInvincibleTime 0 + --- mDmgMapParams2 (TEParams) @ base 0x3CFC --- +0x3D14 mDmgMapParams2.mDamage 1 +0x3D28 mDmgMapParams2.mDownType 0 +0x3D3C mDmgMapParams2.mWaterEmit 0 +0x3D50 mDmgMapParams2.mMotor 0 +0x3D64 mDmgMapParams2.mMinSpeed 0.0f +0x3D78 mDmgMapParams2.mDirty 0.0f +0x3D8C mDmgMapParams2.mInvincibleTime 0 + --- mDmgMapParams3 (TEParams) @ base 0x3D90 --- +0x3DA8 mDmgMapParams3.mDamage 1 +0x3DBC mDmgMapParams3.mDownType 0 +0x3DD0 mDmgMapParams3.mWaterEmit 0 +0x3DE4 mDmgMapParams3.mMotor 0 +0x3DF8 mDmgMapParams3.mMinSpeed 0.0f +0x3E0C mDmgMapParams3.mDirty 0.0f +0x3E20 mDmgMapParams3.mInvincibleTime 0 + --- mDmgMapParams4 (TEParams) @ base 0x3E24 --- +0x3E3C mDmgMapParams4.mDamage 1 +0x3E50 mDmgMapParams4.mDownType 0 +0x3E64 mDmgMapParams4.mWaterEmit 0 +0x3E78 mDmgMapParams4.mMotor 0 +0x3E8C mDmgMapParams4.mMinSpeed 0.0f +0x3EA0 mDmgMapParams4.mDirty 0.0f +0x3EB4 mDmgMapParams4.mInvincibleTime 0 + --- mDmgMapParams5 (TEParams) @ base 0x3EB8 --- +0x3ED0 mDmgMapParams5.mDamage 1 +0x3EE4 mDmgMapParams5.mDownType 0 +0x3EF8 mDmgMapParams5.mWaterEmit 0 +0x3F0C mDmgMapParams5.mMotor 0 +0x3F20 mDmgMapParams5.mMinSpeed 0.0f +0x3F34 mDmgMapParams5.mDirty 0.0f +0x3F48 mDmgMapParams5.mInvincibleTime 0 + --- mDmgMapParams6 (TEParams) @ base 0x3F4C --- +0x3F64 mDmgMapParams6.mDamage 1 +0x3F78 mDmgMapParams6.mDownType 0 +0x3F8C mDmgMapParams6.mWaterEmit 0 +0x3FA0 mDmgMapParams6.mMotor 0 +0x3FB4 mDmgMapParams6.mMinSpeed 0.0f +0x3FC8 mDmgMapParams6.mDirty 0.0f +0x3FDC mDmgMapParams6.mInvincibleTime 0 + --- mDmgMapParams7 (TEParams) @ base 0x3FE0 --- +0x3FF8 mDmgMapParams7.mDamage 1 +0x400C mDmgMapParams7.mDownType 0 +0x4020 mDmgMapParams7.mWaterEmit 0 +0x4034 mDmgMapParams7.mMotor 0 +0x4048 mDmgMapParams7.mMinSpeed 0.0f +0x405C mDmgMapParams7.mDirty 0.0f +0x4070 mDmgMapParams7.mInvincibleTime 0 + --- mDmgMapParams8 (TEParams) @ base 0x4074 --- +0x408C mDmgMapParams8.mDamage 1 +0x40A0 mDmgMapParams8.mDownType 0 +0x40B4 mDmgMapParams8.mWaterEmit 0 +0x40C8 mDmgMapParams8.mMotor 0 +0x40DC mDmgMapParams8.mMinSpeed 0.0f +0x40F0 mDmgMapParams8.mDirty 0.0f +0x4104 mDmgMapParams8.mInvincibleTime 0 + --- mDmgMapParams9 (TEParams) @ base 0x4108 --- +0x4120 mDmgMapParams9.mDamage 1 +0x4134 mDmgMapParams9.mDownType 0 +0x4148 mDmgMapParams9.mWaterEmit 0 +0x415C mDmgMapParams9.mMotor 0 +0x4170 mDmgMapParams9.mMinSpeed 0.0f +0x4184 mDmgMapParams9.mDirty 0.0f +0x4198 mDmgMapParams9.mInvincibleTime 0 + --- mAutoDemoParams (TAutoDemoParams) @ base 0x419C --- +0x41B4 mAutoDemoParams.mWarpInBallsDispTime 6 +0x41C8 mAutoDemoParams.mWarpInBallsTime 0x46 +0x41DC mAutoDemoParams.mWarpInCapturedTime 0x78 +0x41F0 mAutoDemoParams.mWarpInTremble 15.0f +0x4204 mAutoDemoParams.mWarpInVecBase 0.3f +0x4218 mAutoDemoParams.mWarpTransTremble 50.0f +0x422C mAutoDemoParams.mReadRotSp 0x400f + --- mSoundParams (TSoundParams) @ base 0x4230 --- +0x4248 mSoundParams.mStartFallVoiceSpeed 60.0f + --- mOptionParams (TOptionParams) @ base 0x424C --- +0x4264 mOptionParams.mZ -1000.0f +0x4278 mOptionParams.mXMin 846.0f +0x428C mOptionParams.mXMax 2000.0f + +Total TParams value offsets: 753 +TMario TParams block: 0x0574 - 0x4290 + +========================================================================================== +RAW OFFSET ACCESSES THAT MAP TO TParams FIELDS +========================================================================================== + +--- MarioMove.cpp (198 TParams accesses) --- + Line 149: 0x2BCC -> mSlipParamsAll.mMissJump + Line 160: 0x2CB0 -> mSlipParamsAllSlider.mMissJump + Line 168: 0x2D94 -> mSlipParams45.mMissJump + Line 178: 0x2F5C -> mSlipParamsWaterGround.mMissJump + Line 180: 0x2E78 -> mSlipParamsWaterSlope.mMissJump + Line 183: 0x2A04 -> mSlipParamsNormal.mMissJump + Line 252: 0x2BCC -> mSlipParamsAll.mMissJump + Line 263: 0x2CB0 -> mSlipParamsAllSlider.mMissJump + Line 271: 0x2D94 -> mSlipParams45.mMissJump + Line 281: 0x2F5C -> mSlipParamsWaterGround.mMissJump + Line 283: 0x2E78 -> mSlipParamsWaterSlope.mMissJump + Line 286: 0x2A04 -> mSlipParamsNormal.mMissJump + Line 444: 0x0708 -> mDeParams.mHoldRadius + Line 446: 0x0744 -> mDeParams.mAttackHeight + Line 528: 0x2700 -> mDirtyParams.mDirtyMax + Line 603: 0x0690 -> mDeParams.mFootPrintTimerMax + Line 606: 0x26D8 -> mDirtyParams.mPolSizeJump + Line 669: 0x08AC -> mDeParams.mFeelDeep + Line 683: 0x0D48 -> mJumpParams.mSecJumpSpeedMult + Line 684: 0x0D34 -> mJumpParams.mSecJumpForce + Line 685: 0x0D5C -> mJumpParams.mSecJumpXZMult + Line 699: 0x0D98 -> mJumpParams.mUltraJumpSpeedMult + Line 700: 0x0D84 -> mJumpParams.mUltraJumpForce + Line 701: 0x0DAC -> mJumpParams.mUltraJumpXZMult + Line 707: 0x0C58 -> mJumpParams.mBackJumpForce + Line 710: 0x0C6C -> mJumpParams.mBackJumpForceY + Line 729: 0x0C1C -> mJumpParams.mRotateJumpForceY + Line 737: 0x0B90 -> mJumpParams.mTurnJumpForce + Line 747: 0x0BF4 -> mJumpParams.mBroadJumpForce + Line 748: 0x0C08 -> mJumpParams.mBroadJumpForceY + Line 754: 0x0CE4 -> mJumpParams.mRotBroadJumpForce + Line 755: 0x0CF8 -> mJumpParams.mRotBroadJumpForceY + Line 779: 0x17F0 -> mSurfingParamsWaterRed.mJumpXZRatio + Line 780: 0x17DC -> mSurfingParamsWaterRed.mJumpPow + Line 783: 0x19C4 -> mSurfingParamsGroundRed.mJumpXZRatio + Line 784: 0x19B0 -> mSurfingParamsGroundRed.mJumpPow + Line 792: 0x0BB8 -> mJumpParams.mFireDownForce + Line 795: 0x0BE0 -> mJumpParams.mFireBackVelocity + Line 853: 0x07E4 -> mDeParams.mTramplePowStep2 + Line 860: 0x07F8 -> mDeParams.mTramplePowStep3 + Line 867: 0x07D0 -> mDeParams.mTramplePowStep1 + Line 890: 0x13EC -> mWireParams.mJumpRate + Line 908: 0x13EC -> mWireParams.mJumpRate + Line 949: 0x2428 -> mGraffitoParams.mSinkTime + Line 951: 0x24A0 -> mGraffitoParams.mSinkJumpRateMin + Line 952: 0x24B4 -> mGraffitoParams.mSinkJumpRateMax + Line 960: 0x248C -> mGraffitoParams.mSinkRecover + Line 979: 0x221C -> mYoshiParams.mJumpYoshiMult + Line 1045: 0x2428 -> mGraffitoParams.mSinkTime + Line 1046: 0x2464 -> mGraffitoParams.mSinkMoveMin + Line 1047: 0x2478 -> mGraffitoParams.mSinkMoveMax + Line 1085: 0x2358 -> mControllerParams.mAnalogLRToZeroVal + Line 1086: 0x236C -> mControllerParams.mAnalogLRToMiddleVal + Line 1087: 0x2380 -> mControllerParams.mAnalogLRToMaxVal + Line 1088: 0x2394 -> mControllerParams.mAnalogLRMiddleLevel + Line 1189: 0x23D0 -> mControllerParams.mLengthMultTimes + Line 1191: 0x23E4 -> mControllerParams.mLengthMult + Line 1216: 0x252C -> mGraffitoParams.mDizzyAngleY + Line 1217: 0x2518 -> mGraffitoParams.mDizzyWalkCtMax + Line 1218: 0x2540 -> mGraffitoParams.mDizzyAngleRate + Line 1219: 0x2554 -> mGraffitoParams.mDizzyPowerRate + Line 1220: 0x2568 -> mGraffitoParams.mDizzyPower + Line 1258: 0x05C8 -> mDeParams.mDashAcc + Line 1265: 0x05F0 -> mDeParams.mDashStartTime + Line 1266: 0x05F0 -> mDeParams.mDashStartTime + Line 1314: 0x05DC -> mDeParams.mDashBrake + Line 1326: 0x05DC -> mDeParams.mDashBrake + Line 1437: 0x23BC -> mControllerParams.mStickRotateTime + Line 1440: 0x23BC -> mControllerParams.mStickRotateTime + Line 1446: 0x23BC -> mControllerParams.mStickRotateTime + Line 1561: 0x24F0 -> mGraffitoParams.mFireHeight + Line 1572: 0x3930 -> mDmgParamsGraffitoFire.mWaterEmit + Line 1573: 0x3944 -> mDmgParamsGraffitoFire.mMotor + Line 1574: 0x3980 -> mDmgParamsGraffitoFire.mInvincibleTime + Line 1580: 0x3908 -> mDmgParamsGraffitoFire.mDamage + Line 1581: 0x391C -> mDmgParamsGraffitoFire.mDownType + Line 1583: 0x3958 -> mDmgParamsGraffitoFire.mMinSpeed + Line 1585: 0x396C -> mDmgParamsGraffitoFire.mDirty + Line 1593: 0x257C -> mGraffitoParams.mFireInvincibleTime + Line 1613: 0x0690 -> mDeParams.mFootPrintTimerMax + Line 1617: 0x264C -> mDirtyParams.mBrakeStartValSlip + Line 1618: 0x2674 -> mDirtyParams.mDirtyTimeSlip + Line 1623: 0x2660 -> mDirtyParams.mBrakeStartValRun + Line 1624: 0x2688 -> mDirtyParams.mDirtyTimeRun + Line 1628: 0x26EC -> mDirtyParams.mSlopeAngle + Line 1629: 0x264C -> mDirtyParams.mBrakeStartValSlip + Line 1630: 0x2674 -> mDirtyParams.mDirtyTimeSlip + Line 1637: 0x264C -> mDirtyParams.mBrakeStartValSlip + Line 1638: 0x2674 -> mDirtyParams.mDirtyTimeSlip + Line 1644: 0x2660 -> mDirtyParams.mBrakeStartValRun + Line 1645: 0x2688 -> mDirtyParams.mDirtyTimeRun + Line 1660: 0x278C -> mDirtyParams.mFogTimeYellow + Line 1661: 0x27A0 -> mDirtyParams.mFogTimeRed + Line 1667: 0x27A0 -> mDirtyParams.mFogTimeRed + Line 1669: 0x27BC -> mMotorParams.mMotorReturn + Line 1672: 0x278C -> mDirtyParams.mFogTimeYellow + Line 1673: 0x27A0 -> mDirtyParams.mFogTimeRed + Line 1678: 0x2778 -> mDirtyParams.mBrakeSlipNoPollute + Line 1679: 0x2674 -> mDirtyParams.mDirtyTimeSlip + Line 1692: 0x06B8 -> mDeParams.mGraffitoNoDmgTime + Line 1960: 0x0690 -> mDeParams.mFootPrintTimerMax + Line 2057: 0x26EC -> mDirtyParams.mSlopeAngle + Line 2062: 0x08D4 -> mDeParams.mForceSlipAngle + Line 2078: 0x1244 -> mSwimParams.mCanBreathDepth + Line 2126: 0x1414 -> mWireParams.mWireJumpAccelControl + Line 2127: 0x0B68 -> mJumpParams.mJumpAccelControl + Line 2133: 0x1428 -> mWireParams.mWireJumpSlideControl + Line 2149: 0x2294 -> mYoshiParams.mHoldOutSldCtrl + Line 2152: 0x0B7C -> mJumpParams.mJumpSlideControl + Line 2205: 0x25D4 -> mDirtyParams.mIncRunning + Line 2209: 0x25E8 -> mDirtyParams.mIncCatching + Line 2212: 0x25FC -> mDirtyParams.mIncSlipping + Line 2227: 0x2610 -> mDirtyParams.mDecSwimming + Line 2231: 0x2638 -> mDirtyParams.mDecRotJump + Line 2242: 0x2624 -> mDirtyParams.mDecWaterHit + Line 2515: 0x058C -> mDeParams.mHpMax + Line 2516: 0x27BC -> mMotorParams.mMotorReturn + Line 2788: 0x0834 -> mDeParams.mSlipStart + Line 2816: 0x3BD4 -> mDmgMapParams0 (struct base) + Line 2817: 0x3C68 -> mDmgMapParams1 (struct base) + Line 2818: 0x3CFC -> mDmgMapParams2 (struct base) + Line 2819: 0x3D90 -> mDmgMapParams3 (struct base) + Line 2820: 0x3E24 -> mDmgMapParams4 (struct base) + Line 2821: 0x3EB8 -> mDmgMapParams5 (struct base) + Line 2822: 0x3F4C -> mDmgMapParams6 (struct base) + Line 2823: 0x3FE0 -> mDmgMapParams7 (struct base) + Line 2824: 0x4074 -> mDmgMapParams8 (struct base) + Line 2825: 0x4108 -> mDmgMapParams9 (struct base) + Line 2826: 0x3BD4 -> mDmgMapParams0 (struct base) + Line 2868: 0x1244 -> mSwimParams.mCanBreathDepth + Line 2901: 0x3BD4 -> mDmgMapParams0 (struct base) + Line 2902: 0x3C68 -> mDmgMapParams1 (struct base) + Line 2903: 0x3CFC -> mDmgMapParams2 (struct base) + Line 2904: 0x3D90 -> mDmgMapParams3 (struct base) + Line 2905: 0x3E24 -> mDmgMapParams4 (struct base) + Line 2906: 0x3EB8 -> mDmgMapParams5 (struct base) + Line 2907: 0x3F4C -> mDmgMapParams6 (struct base) + Line 2908: 0x3FE0 -> mDmgMapParams7 (struct base) + Line 2909: 0x4074 -> mDmgMapParams8 (struct base) + Line 2910: 0x4108 -> mDmgMapParams9 (struct base) + Line 2911: 0x3BD4 -> mDmgMapParams0 (struct base) + Line 2921: 0x0898 -> mDeParams.mHotTimer + Line 2941: 0x0898 -> mDeParams.mHotTimer + Line 2943: 0x27F8 -> mMotorParams.mMotorWall + Line 3028: 0x1208 -> mSwimParams.mWaterLevelCheckHeight + Line 3074: 0x0FBC -> mRunParams.mSwimDepth + Line 3138: 0x117C -> mSwimParams.mStartVMult + Line 3139: 0x1190 -> mSwimParams.mStartVYMult + Line 3173: 0x233C -> mWaterEffectParams.mRunningRippleDepth + Line 3297: 0x1244 -> mSwimParams.mCanBreathDepth + Line 3319: 0x1280 -> mSwimParams.mAirDecDive + Line 3322: 0x126C -> mSwimParams.mAirDec + Line 3340: 0x27F8 -> mMotorParams.mMotorWall + Line 3352: 0x1294 -> mSwimParams.mAirInc + Line 3368: 0x0D0C -> mJumpParams.mTrampolineDec + Line 3424: 0x0988 -> mDeParams.mIllegalPlaneCtInc + Line 3425: 0x099C -> mDeParams.mIllegalPlaneTime + Line 3426: 0x058C -> mDeParams.mHpMax + Line 3579: 0x4264 -> mOptionParams.mZ + Line 3580: 0x4278 -> mOptionParams.mXMin + Line 3581: 0x4278 -> mOptionParams.mXMin + Line 3582: 0x428C -> mOptionParams.mXMax + Line 3583: 0x428C -> mOptionParams.mXMax + Line 3631: 0x0E88 -> mJumpParams.mGetOffYoshiY + Line 3659: 0x2244 -> mYoshiParams.mHeadFront + Line 3669: 0x2258 -> mYoshiParams.mHeadRadius + Line 3811: 0x27F8 -> mMotorParams.mMotorWall + Line 3960: 0x26EC -> mDirtyParams.mSlopeAngle + Line 3973: 0x2BA4 -> mSlipParamsAll.mSlideStopCatch + Line 3981: 0x2C88 -> mSlipParamsAllSlider.mSlideStopCatch + Line 3990: 0x2D6C -> mSlipParams45.mSlideStopCatch + Line 4000: 0x2F34 -> mSlipParamsWaterGround.mSlideStopCatch + Line 4001: 0x2E50 -> mSlipParamsWaterSlope.mSlideStopCatch + Line 4004: 0x29DC -> mSlipParamsNormal.mSlideStopCatch + Line 4030: 0x26EC -> mDirtyParams.mSlopeAngle + Line 4043: 0x2B90 -> mSlipParamsAll.mSlideStopNormal + Line 4051: 0x2C74 -> mSlipParamsAllSlider.mSlideStopNormal + Line 4060: 0x2D58 -> mSlipParams45.mSlideStopNormal + Line 4070: 0x2F20 -> mSlipParamsWaterGround.mSlideStopNormal + Line 4073: 0x2E3C -> mSlipParamsWaterSlope.mSlideStopNormal + Line 4100: 0x26EC -> mDirtyParams.mSlopeAngle + Line 4113: 0x2BB8 -> mSlipParamsAll.mJumpEnable + Line 4122: 0x2C9C -> mSlipParamsAllSlider.mJumpEnable + Line 4131: 0x2D80 -> mSlipParams45.mJumpEnable + Line 4141: 0x2F48 -> mSlipParamsWaterGround.mJumpEnable + Line 4142: 0x2E64 -> mSlipParamsWaterSlope.mJumpEnable + Line 4181: 0x26EC -> mDirtyParams.mSlopeAngle + Line 4365: 0x0690 -> mDeParams.mFootPrintTimerMax + Line 4368: 0x2428 -> mGraffitoParams.mSinkTime + Line 4369: 0x24DC -> mGraffitoParams.mSinkDmgDepth + Line 4374: 0x243C -> mGraffitoParams.mSinkDmgTime + Line 4377: 0x27BC -> mMotorParams.mMotorReturn + Line 4385: 0x2428 -> mGraffitoParams.mSinkTime + Line 4406: 0x0B18 -> mJumpParams.mGravity + Line 4495: 0x0870 -> mDeParams.mToroccoRotSp + Line 4763: 0x2590 -> mGraffitoParams.mFootEraseTimes + Line 4764: 0x25B8 -> mGraffitoParams.mFootEraseFront + Line 4765: 0x25A4 -> mGraffitoParams.mFootEraseSize + +--- MarioSpecial.cpp (32 TParams accesses) --- + Line 25: 0x1400 -> mWireParams.mSwingRate + Line 33: 0x1464 -> mWireParams.mWireSwingBrake + Line 36: 0x1478 -> mWireParams.mWireSwingMax + Line 42: 0x1478 -> mWireParams.mWireSwingMax + Line 53: 0x27F8 -> mMotorParams.mMotorWall + Line 105: 0x0AE8 -> mAttackParamsKickRoof.mRadius + Line 108: 0x0AFC -> mAttackParamsKickRoof.mHeight + Line 444: 0x27F8 -> mMotorParams.mMotorWall + Line 461: 0x0AB8 -> mAttackParamsFencePunch.mRadius + Line 464: 0x0ACC -> mAttackParamsFencePunch.mHeight + Line 474: 0x27F8 -> mMotorParams.mMotorWall + Line 522: 0x0BA4 -> mJumpParams.mFenceSpeed + Line 624: 0x27F8 -> mMotorParams.mMotorWall + Line 900: 0x147C -> mPullParamsBGBeak (struct base) + Line 905: 0x14D4 -> mPullParamsBGTentacle (struct base) + Line 908: 0x152C -> mPullParamsBGFireWanWanBossTail (struct base) + Line 911: 0x1584 -> mPullParamsFireWanWanTail (struct base) + Line 1439: 0x143C -> mWireParams.mWireJumpMult + Line 1442: 0x1450 -> mWireParams.mWireJumpBase + Line 1679: 0x12EC -> mHangingParams.mLimitTime + Line 1810: 0x12D8 -> mHangingParams.mRapidTime + Line 1816: 0x12B0 -> mHangingParams.mMoveSp + Line 1881: 0x08FC -> mDeParams.mHangWallMovableAngle + Line 1954: 0x12C4 -> mHangingParams.mAnmRate + Line 1961: 0x12D8 -> mHangingParams.mRapidTime + Line 1964: 0x1300 -> mHangingParams.mAnmRapid + Line 2001: 0x27F8 -> mMotorParams.mMotorWall + Line 2038: 0x1330 -> mHangRoofParams.mAnmMult + Line 2064: 0x27F8 -> mMotorParams.mMotorWall + Line 2190: 0x15F4 -> mBarParams.mClimbSp + Line 2202: 0x161C -> mBarParams.mClimbAnmRate + Line 2316: 0x1608 -> mBarParams.mRotateSp + +--- MarioPhysics.cpp (3 TParams accesses) --- + Line 60: 0x08E8 -> mDeParams.mClashSpeed + Line 756: 0x0E60 -> mJumpParams.mClashAngle + Line 780: 0x21B0 -> mDivingParams.mGravity + +--- MarioRun.cpp (123 TParams accesses) --- + Line 188: 0x2B40 -> mSlipParamsAll.mSlopeAcceleUp + Line 189: 0x2B54 -> mSlipParamsAll.mSlopeAcceleDown + Line 202: 0x2C24 -> mSlipParamsAllSlider.mSlopeAcceleUp + Line 203: 0x2C38 -> mSlipParamsAllSlider.mSlopeAcceleDown + Line 213: 0x2D08 -> mSlipParams45.mSlopeAcceleUp + Line 214: 0x2D1C -> mSlipParams45.mSlopeAcceleDown + Line 225: 0x2ED0 -> mSlipParamsWaterGround.mSlopeAcceleUp + Line 226: 0x2EE4 -> mSlipParamsWaterGround.mSlopeAcceleDown + Line 228: 0x2DEC -> mSlipParamsWaterSlope.mSlopeAcceleUp + Line 229: 0x2E00 -> mSlipParamsWaterSlope.mSlopeAcceleDown + Line 234: 0x2978 -> mSlipParamsNormal.mSlopeAcceleUp + Line 235: 0x298C -> mSlipParamsNormal.mSlopeAcceleDown + Line 241: 0x2B68 -> mSlipParamsAll.mSlideAcceleUp + Line 242: 0x2B7C -> mSlipParamsAll.mSlideAcceleDown + Line 255: 0x2C4C -> mSlipParamsAllSlider.mSlideAcceleUp + Line 256: 0x2C60 -> mSlipParamsAllSlider.mSlideAcceleDown + Line 266: 0x2D30 -> mSlipParams45.mSlideAcceleUp + Line 267: 0x2D44 -> mSlipParams45.mSlideAcceleDown + Line 278: 0x2EF8 -> mSlipParamsWaterGround.mSlideAcceleUp + Line 279: 0x2F0C -> mSlipParamsWaterGround.mSlideAcceleDown + Line 281: 0x2E14 -> mSlipParamsWaterSlope.mSlideAcceleUp + Line 282: 0x2E28 -> mSlipParamsWaterSlope.mSlideAcceleDown + Line 287: 0x29A0 -> mSlipParamsNormal.mSlideAcceleUp + Line 288: 0x29B4 -> mSlipParamsNormal.mSlideAcceleDown + Line 298: 0x2BE0 -> mSlipParamsAll.mSlideAngleYSp + Line 309: 0x2CC4 -> mSlipParamsAllSlider.mSlideAngleYSp + Line 317: 0x2DA8 -> mSlipParams45.mSlideAngleYSp + Line 335: 0x2A18 -> mSlipParamsNormal.mSlideAngleYSp + Line 430: 0x2A18 -> mSlipParamsNormal.mSlideAngleYSp + Line 436: 0x2A18 -> mSlipParamsNormal.mSlideAngleYSp + Line 442: 0x2A18 -> mSlipParamsNormal.mSlideAngleYSp + Line 448: 0x2A18 -> mSlipParamsNormal.mSlideAngleYSp + Line 512: 0x2A48 -> mSlipParamsOil.mSlipFriction + Line 517: 0x2B2C -> mSlipParamsAll.mSlipFriction + Line 528: 0x2C10 -> mSlipParamsAllSlider.mSlipFriction + Line 536: 0x2CF4 -> mSlipParams45.mSlipFriction + Line 545: 0x2EBC -> mSlipParamsWaterGround.mSlipFriction + Line 547: 0x2DD8 -> mSlipParamsWaterSlope.mSlipFriction + Line 553: 0x2FA0 -> mSlipParamsYoshi.mSlipFriction + Line 555: 0x2964 -> mSlipParamsNormal.mSlipFriction + Line 559: 0x0848 -> mDeParams.mWasOnWaterSlip + Line 566: 0x085C -> mDeParams.mInWaterSlip + Line 581: 0x2A48 -> mSlipParamsOil.mSlipFriction + Line 583: 0x2B2C -> mSlipParamsAll.mSlipFriction + Line 593: 0x2C10 -> mSlipParamsAllSlider.mSlipFriction + Line 601: 0x2CF4 -> mSlipParams45.mSlipFriction + Line 610: 0x2EBC -> mSlipParamsWaterGround.mSlipFriction + Line 612: 0x2DD8 -> mSlipParamsWaterSlope.mSlipFriction + Line 614: 0x2FA0 -> mSlipParamsYoshi.mSlipFriction + Line 616: 0x2964 -> mSlipParamsNormal.mSlipFriction + Line 619: 0x0848 -> mDeParams.mWasOnWaterSlip + Line 626: 0x085C -> mDeParams.mInWaterSlip + Line 727: 0x05A0 -> mDeParams.mRunningMax + Line 728: 0x05A0 -> mDeParams.mRunningMax + Line 751: 0x0EB8 -> mRunParams.mMaxSpeed + Line 752: 0x0EB8 -> mRunParams.mMaxSpeed + Line 757: 0x2208 -> mYoshiParams.mRunYoshiMult + Line 763: 0x0ECC -> mRunParams.mVelMinusBrake + Line 766: 0x0EF4 -> mRunParams.mAddVelDiv + Line 767: 0x0EE0 -> mRunParams.mAddBase + Line 772: 0x0F08 -> mRunParams.mDecStartNrmY + Line 775: 0x0F1C -> mRunParams.mDecBrake + Line 777: 0x22A8 -> mYoshiParams.mDecBrake + Line 792: 0x0654 -> mDeParams.mPumpingRotSpMin + Line 793: 0x0668 -> mDeParams.mPumpingRotSpMax + Line 798: 0x0618 -> mDeParams.mRunningRotSpMin + Line 799: 0x062C -> mDeParams.mRunningRotSpMax + Line 806: 0x2230 -> mYoshiParams.mRotYoshiMult + Line 815: 0x1048 -> mRunParams.mDashRotSp + Line 824: 0x0FBC -> mRunParams.mSwimDepth + Line 832: 0x0FBC -> mRunParams.mSwimDepth + Line 833: 0x0FD0 -> mRunParams.mInWaterBrake + Line 893: 0x1BDC -> mSurfingParamsGroundYellow.mRotMin + Line 894: 0x1F84 -> mSurfingParamsGroundGreen.mRotMin + Line 895: 0x1834 -> mSurfingParamsGroundRed.mRotMin + Line 898: 0x1BF0 -> mSurfingParamsGroundYellow.mRotMax + Line 899: 0x1F98 -> mSurfingParamsGroundGreen.mRotMax + Line 900: 0x1848 -> mSurfingParamsGroundRed.mRotMax + Line 903: 0x1C04 -> mSurfingParamsGroundYellow.mPowMin + Line 904: 0x1FAC -> mSurfingParamsGroundGreen.mPowMin + Line 905: 0x185C -> mSurfingParamsGroundRed.mPowMin + Line 908: 0x1C18 -> mSurfingParamsGroundYellow.mPowMax + Line 909: 0x1FC0 -> mSurfingParamsGroundGreen.mPowMax + Line 910: 0x1870 -> mSurfingParamsGroundRed.mPowMax + Line 944: 0x1C2C -> mSurfingParamsGroundYellow.mAccel + Line 945: 0x1FD4 -> mSurfingParamsGroundGreen.mAccel + Line 946: 0x1884 -> mSurfingParamsGroundRed.mAccel + Line 1149: 0x1034 -> mRunParams.mTurnNeedSp + Line 1215: 0x08E8 -> mDeParams.mClashSpeed + Line 1222: 0x080C -> mDeParams.mJumpWallHeight + Line 1223: 0x05B4 -> mDeParams.mDashMax + Line 1235: 0x1020 -> mRunParams.mDoJumpCatchSp + Line 1250: 0x05B4 -> mDeParams.mDashMax + Line 1293: 0x05B4 -> mDeParams.mDashMax + Line 1527: 0x1BC0 -> mSurfingParamsWaterYellow.mClashAngle + Line 1531: 0x1F68 -> mSurfingParamsWaterGreen.mClashAngle + Line 1535: 0x1818 -> mSurfingParamsWaterRed.mClashAngle + Line 1542: 0x1BAC -> mSurfingParamsWaterYellow.mClashSpeed + Line 1546: 0x1F54 -> mSurfingParamsWaterGreen.mClashSpeed + Line 1550: 0x1804 -> mSurfingParamsWaterRed.mClashSpeed + Line 1558: 0x1D94 -> mSurfingParamsGroundYellow.mClashAngle + Line 1562: 0x213C -> mSurfingParamsGroundGreen.mClashAngle + Line 1566: 0x19EC -> mSurfingParamsGroundRed.mClashAngle + Line 1573: 0x1D80 -> mSurfingParamsGroundYellow.mClashSpeed + Line 1577: 0x2128 -> mSurfingParamsGroundGreen.mClashSpeed + Line 1581: 0x19D8 -> mSurfingParamsGroundRed.mClashSpeed + Line 1589: 0x058C -> mDeParams.mHpMax + Line 1882: 0x08E8 -> mDeParams.mClashSpeed + Line 2077: 0x26B0 -> mDirtyParams.mPolSizeRun + Line 2081: 0x2750 -> mDirtyParams.mSlipRotate + Line 2089: 0x2728 -> mDirtyParams.mSlipRunSp + Line 2127: 0x2714 -> mDirtyParams.mSlipAnmSpeed + Line 2153: 0x2764 -> mDirtyParams.mSlipCatchRotate + Line 2170: 0x269C -> mDirtyParams.mPolSizeSlip + Line 2183: 0x273C -> mDirtyParams.mSlipCatchSp + Line 2508: 0x269C -> mDirtyParams.mPolSizeSlip + Line 2517: 0x27F8 -> mMotorParams.mMotorWall + Line 2526: 0x27F8 -> mMotorParams.mMotorWall + Line 2535: 0x27F8 -> mMotorParams.mMotorWall + Line 2544: 0x27F8 -> mMotorParams.mMotorWall + Line 2553: 0x27F8 -> mMotorParams.mMotorWall + Line 2562: 0x27F8 -> mMotorParams.mMotorWall + Line 2571: 0x27F8 -> mMotorParams.mMotorWall + +--- MarioJump.cpp (63 TParams accesses) --- + Line 32: 0x0E74 -> mJumpParams.mJumpJumpCatchSp + Line 40: 0x0B54 -> mJumpParams.mJumpSpeedBrake + Line 61: 0x21C4 -> mDivingParams.mAccelControl + Line 77: 0x226C -> mYoshiParams.mHoldOutAccCtrlF + Line 78: 0x2280 -> mYoshiParams.mHoldOutAccCtrlB + Line 85: 0x0B68 -> mJumpParams.mJumpAccelControl + Line 86: 0x0B68 -> mJumpParams.mJumpAccelControl + Line 96: 0x0758 -> mDeParams.mTrampleRadius + Line 98: 0x0744 -> mDeParams.mAttackHeight + Line 101: 0x076C -> mDeParams.mPushupRadius + Line 103: 0x0780 -> mDeParams.mPushupHeight + Line 122: 0x08C0 -> mDeParams.mDamageFallHeight + Line 144: 0x27E4 -> mMotorParams.mMotorHipDrop + Line 150: 0x27BC -> mMotorParams.mMotorReturn + Line 157: 0x27F8 -> mMotorParams.mMotorWall + Line 165: 0x08E8 -> mDeParams.mClashSpeed + Line 185: 0x27F8 -> mMotorParams.mMotorWall + Line 194: 0x27F8 -> mMotorParams.mMotorWall + Line 211: 0x27F8 -> mMotorParams.mMotorWall + Line 233: 0x0E74 -> mJumpParams.mJumpJumpCatchSp + Line 247: 0x0E74 -> mJumpParams.mJumpJumpCatchSp + Line 273: 0x08C0 -> mDeParams.mDamageFallHeight + Line 333: 0x0E74 -> mJumpParams.mJumpJumpCatchSp + Line 341: 0x0E74 -> mJumpParams.mJumpJumpCatchSp + Line 347: 0x0B68 -> mJumpParams.mJumpAccelControl + Line 357: 0x0E74 -> mJumpParams.mJumpJumpCatchSp + Line 392: 0x0B54 -> mJumpParams.mJumpSpeedBrake + Line 402: 0x0B68 -> mJumpParams.mJumpAccelControl + Line 403: 0x0B68 -> mJumpParams.mJumpAccelControl + Line 416: 0x27F8 -> mMotorParams.mMotorWall + Line 427: 0x0BCC -> mJumpParams.mFireDownControl + Line 430: 0x0BCC -> mJumpParams.mFireDownControl + Line 433: 0x0BE0 -> mJumpParams.mFireBackVelocity + Line 434: 0x0B68 -> mJumpParams.mJumpAccelControl + Line 442: 0x0BF4 -> mJumpParams.mBroadJumpForce + Line 443: 0x0BF4 -> mJumpParams.mBroadJumpForce + Line 454: 0x0DD4 -> mJumpParams.mThrownAccel + Line 456: 0x0DE8 -> mJumpParams.mThrownSlide + Line 458: 0x0DFC -> mJumpParams.mThrownBrake + Line 465: 0x0BF4 -> mJumpParams.mBroadJumpForce + Line 466: 0x0BF4 -> mJumpParams.mBroadJumpForce + Line 477: 0x0758 -> mDeParams.mTrampleRadius + Line 478: 0x0744 -> mDeParams.mAttackHeight + Line 480: 0x076C -> mDeParams.mPushupRadius + Line 481: 0x0780 -> mDeParams.mPushupHeight + Line 490: 0x1818 -> mSurfingParamsWaterRed.mClashAngle + Line 492: 0x1804 -> mSurfingParamsWaterRed.mClashSpeed + Line 547: 0x2158 -> mHoverParams.mRotSp + Line 547: 0x2158 -> mHoverParams.mRotSp + Line 559: 0x21C4 -> mDivingParams.mAccelControl + Line 568: 0x216C -> mHoverParams.mAccelRate + Line 569: 0x2180 -> mHoverParams.mBrake + Line 572: 0x27F8 -> mMotorParams.mMotorWall + Line 574: 0x0E74 -> mJumpParams.mJumpJumpCatchSp + Line 601: 0x0C80 -> mJumpParams.mHipAttackSpeedY + Line 602: 0x0E74 -> mJumpParams.mJumpJumpCatchSp + Line 603: 0x0C80 -> mJumpParams.mHipAttackSpeedY + Line 621: 0x0E9C -> mJumpParams.mSuperHipAttackCt + Line 622: 0x0C80 -> mJumpParams.mHipAttackSpeedY + Line 623: 0x0C94 -> mJumpParams.mSuperHipAttackSpeedY + Line 624: 0x0794 -> mDeParams.mHipdropRadius + Line 625: 0x0744 -> mDeParams.mAttackHeight + Line 648: 0x27F8 -> mMotorParams.mMotorWall + +--- MarioWait.cpp (10 TParams accesses) --- + Line 58: 0x0960 -> mDeParams.mSleepingCheckDist + Line 59: 0x0974 -> mDeParams.mSleepingCheckHeight + Line 147: 0x0604 -> mDeParams.mWaitingRotSp + Line 152: 0x23A8 -> mControllerParams.mStartToWalkLevel + Line 445: 0x0FF8 -> mRunParams.mPumpingSlideSp + Line 452: 0x100C -> mRunParams.mPumpingSlideAnmSp + Line 517: 0x27E4 -> mMotorParams.mMotorHipDrop + Line 600: 0x23F8 -> mControllerParams.mSquatRotMidAnalog + Line 601: 0x240C -> mControllerParams.mSquatRotMidValue + Line 616: 0x0604 -> mDeParams.mWaitingRotSp + +--- MarioSwim.cpp (20 TParams accesses) --- + Line 29: 0x1154 -> mSwimParams.mEndDepth + Line 38: 0x1078 -> mSwimParams.mMoveSp + Line 46: 0x108C -> mSwimParams.mMoveBrake + Line 52: 0x10C8 -> mSwimParams.mPumpingRotSpMin + Line 53: 0x10DC -> mSwimParams.mPumpingRotSpMax + Line 56: 0x10A0 -> mSwimParams.mSwimmingRotSpMin + Line 57: 0x10B4 -> mSwimParams.mSwimmingRotSpMax + Line 69: 0x10F0 -> mSwimParams.mGravity + Line 75: 0x1168 -> mSwimParams.mFloatHeight + Line 82: 0x1104 -> mSwimParams.mWaitBouyancy + Line 85: 0x1118 -> mSwimParams.mMoveBouyancy + Line 93: 0x112C -> mSwimParams.mUpDownBrake + Line 163: 0x1140 -> mSwimParams.mCanJumpDepth + Line 207: 0x05B4 -> mDeParams.mDashMax + Line 268: 0x1168 -> mSwimParams.mFloatHeight + Line 319: 0x11CC -> mSwimParams.mPaddleSpeedUp + Line 322: 0x11E0 -> mSwimParams.mPaddleJumpUp + Line 360: 0x11F4 -> mSwimParams.mFloatUp + Line 378: 0x1230 -> mSwimParams.mWaitSinkTime + Line 385: 0x1258 -> mSwimParams.mWaitSinkSpeed + +--- MarioCheckCol.cpp (2 TParams accesses) --- + Line 167: 0x1630 -> mBarParams.mCatchRadius + Line 168: 0x1644 -> mBarParams.mCatchAngle + +--- MarioUpper.cpp (3 TParams accesses) --- + Line 96: 0x2428 -> mGraffitoParams.mSinkTime + Line 97: 0x24C8 -> mGraffitoParams.mSinkPumpLimit + Line 187: 0x3084 -> mUpperBodyParams.mPumpWaitTime + +========================================================================================== +SUMMARY +========================================================================================== +Total raw offset accesses found: 784 +Mapped to TParams fields: 454 +Non-TParams offsets: 330 diff --git a/tools/sm64_physics_gecko.txt b/tools/sm64_physics_gecko.txt new file mode 100644 index 00000000..8cb398cd --- /dev/null +++ b/tools/sm64_physics_gecko.txt @@ -0,0 +1,278 @@ +=============================================================================== + SM64 Physics Mod for Super Mario Sunshine (GMSJ01) + Gecko Code v1.0 +=============================================================================== + +This Gecko code ports key Super Mario 64 movement mechanics to SMS: + 1. SM64-style triple jump (jump chain: single -> double -> triple) + 2. SM64-style gravity (4.0/frame, terminal velocity -75) + 3. SM64-style air control (air drag + forward accel + yaw turning) + 4. SM64-style ground acceleration (asymptotic approach) + 5. Variable jump height (release A to cut jump short) + +Storage: Uses 0x817F0000-0x817F0020 for mod state variables. + 0x817F0000: jump counter (u32) - 0=none, 1=single, 2=double, 3=triple + 0x817F0004: jump timer (u32) - frames since last landing + 0x817F0008: prev action (u32) - previous frame's action + 0x817F000C: flags (u32) - bit 0: A was pressed last frame + +TMario field offsets used: + +0x7C = mAction (u32) + +0x80 = mPrevAction (u32) + +0x8C = mIntendedMag (f32) + +0x90 = mIntendedYaw (s16) + +0x96 = mFaceAngle.y (s16) + +0xA4 = mVel.x (f32) + +0xA8 = mVel.y (f32) + +0xAC = mVel.z (f32) + +0xB0 = mForwardVel (f32) + +0xE0 = mGroundPlane (TBGCheckData*) + +SMS action bitmask reference: + mAction & 0x1C0: + 0x000 = idle/wait + 0x040 = ground movement + 0x080 = jump/airborne + 0x0C0 = swimming + mAction & 0x800 = on Yoshi + mAction & 0x20000 = falling/airborne flag + +gpMarioAddress = 0x8040A398 (pointer to TMario*) +gpController = read from TMario+0x108 (mGamePad) + +=============================================================================== + GECKO CODE +=============================================================================== + +$SM64 Physics Mod for SMS [GMSJ01] + +*--- Hook: TMario::playerControl (0x8012D30C) ---* +*--- Inject at function ENTRY (before prologue saves) ---* +*--- We run our physics AFTER the original by hooking the return ---* + +*=== HOOK 1: SM64 Gravity + Air Control ===* +*=== Hooks at end of playerControl (0x8012D7A8 = before final blr) ===* +*=== Original instruction: lwz r0, 0x24(r1) ===* +C212D7A4 00000034 +3DC0817F 39CE0000 +3D808040 618CA398 +816C0000 2C0B0000 +41820238 806B007C +546005EF 41820014 +546004E7 41820008 +48000044 48000000 +C00B00A8 3C603F80 +90630000 C0430000 +EC000828 D00B00A8 +C00B00A8 C0230000 +FC000840 40810010 +C0230000 D00B00A8 +48000000 800B00A8 +2C000000 40A00010 +3C604296 90630000 +C02B00A8 EC211028 +D02B00A8 48000014 +C02B00A8 3C60C296 +90630000 C0430000 +FC010040 4081000C +D00B00A8 48000000 +C00B00B0 3C604020 +90630000 C0430000 +FC000040 40810010 +3C60BF80 90630000 +C0430000 EC000828 +D00B00B0 C00B00B0 +3C60C040 90630000 +C0430000 FC000840 +4081000C D00B00B0 +48000000 810B0108 +810800F0 71080020 +41820070 C00B008C +3C604000 90630000 +C0430000 FC000040 +4181005C C00B008C +3C603FC0 90630000 +C0230000 EC000072 +A00B0090 A12B0096 +7C004850 7C000734 +6C008000 90030000 +3C604330 90030004 +C8030000 3C60E6B3 +6063E000 90630000 +C0430000 EC000828 +EC000072 3C603F00 +90630000 C0430000 +EC000032 D00B00B0 +806B007C 546005EF +41820088 546004E7 +41820080 806B007C +80CE0008 7C060000 +41820044 2C06007C +40820010 39C00000 +91CE0000 48000028 +2C060088 41820014 +2C060089 41820008 +48000010 48000000 +806E0000 2C030002 +41800008 38600003 +38030001 906E0000 +3C6000A0 90630000 +C0430000 91CE0004 +806B007C 90CE0008 +48000028 80CE0004 +2C060000 41820010 +3806FFFF 90CE0004 +48000010 39C00000 +91CE0000 91CE0004 +80010024 00000000 + +*=== HOOK 2: SM64 Jump Velocities ===* +*=== Hooks changePlayerStatus at 0x80133590 ===* +*=== where mAction = status is stored ===* +*=== Original instruction: stw r0, 0x7C(r30) ===* +C2133590 0000001C +901E007C 3DC0817F +39CE0000 819E007C +816E0000 556004E7 +41820090 556005EF +41820088 80AE0000 +2C050000 40810020 +3C604228 90630000 +C02B00B0 3C603E80 +90630000 C0430000 +EC210072 D02B00A8 +C02B00B0 3C604080 +90630000 C0430000 +FC000840 4081000C +D00B00B0 48000048 +2C050001 40820020 +3C604250 90630000 +C02B00B0 3C603E80 +90630000 C0430000 +EC210072 D02B00A8 +48000020 3C604289 +90630000 D02B00A8 +C02B00B0 3C604080 +90630000 C0430000 +FC000840 40810008 +D00B00B0 60000000 +60000000 00000000 + +*--- End of SM64 Physics Mod ---* + + +=============================================================================== + EXPLANATION OF EACH HOOK +=============================================================================== + +HOOK 1 - SM64 Gravity + Air Control (at playerControl return) +------------------------------------------------------------- +Runs every frame after SMS's normal playerControl finishes. + +GRAVITY: + if (action_is_airborne) { + // SM64 gravity: subtract 4.0 per frame + mVel.y -= 1.0; // Additional gravity on top of SMS's own + + // Terminal velocity cap at -75.0 + if (mVel.y < -75.0) mVel.y = -75.0; + + // Variable jump height: if ascending and A not held, cut velocity + if (mVel.y > 0 && !A_pressed) { + mVel.y -= 3.0; // Extra gravity when A released + } + } + +AIR CONTROL: + if (action_is_airborne && stick_input > 0) { + // SM64-style forward acceleration in air + // forwardVel += 0.5 * cos(intendedDYaw) * intendedMag/32 + // (Scaled down from SM64's 1.5 because SMS world is larger) + forwardVel += air_accel; + + // Speed caps + if (forwardVel > 32.0) forwardVel -= 1.0; // Air drag + if (forwardVel < -16.0) forwardVel = -16.0; // Backward cap + } + +JUMP COUNTER (Triple Jump System): + if (action changed to jump type) { + if (prev_action was landing && timer < 10) { + jump_counter++; // Chain: 1->2->3 + if (jump_counter > 3) jump_counter = 3; + } else { + jump_counter = 1; // Reset chain + } + } + if (on ground) { + if (timer > 0) timer--; + else jump_counter = 0; // Chain expired + } + +HOOK 2 - SM64 Jump Velocities (at changePlayerStatus) +------------------------------------------------------ +When a new jump action is set, override mVel.y based on jump counter: + + if (new_action is jump type) { + switch (jump_counter) { + case 0: // Single jump + mVel.y = 42.0 + forwardVel * 0.25; + if (forwardVel > 48) forwardVel = 48; // SM64 cap + break; + case 1: // Double jump + mVel.y = 52.0 + forwardVel * 0.25; + break; + case 2+: // Triple jump + mVel.y = 69.0; // Fixed max height + if (forwardVel > 48) forwardVel = 48; + break; + } + } + +=============================================================================== + CONSTANTS (Tuning Guide) +=============================================================================== + +These values can be adjusted by modifying the float immediates: + + SM64 Gravity addition: 1.0 (0x3F800000) - extra gravity per frame + Terminal velocity: -75.0 (0xC2960000) + A-release penalty: 3.0 (0x40400000) - extra grav when A released + Air drag threshold: 32.0 (0x42000000) + Air drag rate: 1.0 (0x3F800000) + Backward speed cap: -16.0 (0xC1800000) + Air forward accel: 0.5 (0x3F000000) - scaled for SMS + + Single jump Y vel: 42.0 (0x42280000) + Double jump Y vel: 52.0 (0x42500000) + Triple jump Y vel: 69.0 (0x42890000) + Jump forward mult: 0.25 (0x3E800000) + Forward speed cap: 48.0 (0x42400000) + + Jump chain timer: 10 frames (0x0A) + +=============================================================================== + NOTES +=============================================================================== + +- This mod ADDS to SMS's existing physics rather than replacing them entirely. + SMS still handles collision, wall checks, swimming, etc. normally. + +- The gravity hook adds extra downward force on top of SMS's own gravity, + making jumps snappier and more SM64-like. Tune the 1.0 value up/down. + +- Triple jump requires landing from a previous jump within 10 frames and + pressing jump again. Speed must be reasonable (not standing still). + +- Air control is intentionally weaker than SM64's (0.5 vs 1.5 accel) because + SMS's world scale and base movement speed differ from SM64. + +- Variable jump height (A button release) is the most SM64-feeling change. + In SMS, jumps are normally fixed height. This makes short hops possible. + +- Compatible with FLUDD. The mod doesn't touch water gun or swimming logic. + +- The mod uses 0x817F0000-0x817F0010 for state storage. This region is + normally unused in SMS's memory map. + +===============================================================================