diff --git a/include/d/a/obj/d_a_obj_fairy.h b/include/d/a/obj/d_a_obj_fairy.h index 43f715c5..e9283b46 100644 --- a/include/d/a/obj/d_a_obj_fairy.h +++ b/include/d/a/obj/d_a_obj_fairy.h @@ -32,6 +32,15 @@ public: STATE_FUNC_DECLARE(dAcOFairy_c, CatchDemo); private: + enum SpawnType_e { + SPAWN_0, + SPAWN_1, + /** The player released the fairy from a bottle or the bug net */ + SPAWN_MANUAL_RELEASE, + /** The previously bottled fairy saves the player from death */ + SPAWN_AUTO_RELEASE, + }; + bool shouldAvoidBugNet() const; bool isCuring() const; bool canTargetWithBugNet() const; @@ -61,7 +70,7 @@ private: /* 0xA8C */ EffectsStruct mEffects[2]; /* 0xAF4 */ mVec3_c mOrigPosition; ///< The original position of the actor around which it is moving /* 0xB00 */ mVec3_c mSpawnPosition; ///< The (slightly randomized) spawn position - /* 0xB0C */ mVec3_c field_0xB0C; + /* 0xB0C */ u8 _0xB0C[0xB18 - 0xB0C]; /* 0xB18 */ mVec3_c mCurePosition; ///< When curing the player, holds the calculated ///< position that is applied in actorExecute /* 0xB24 */ mMtx_c field_0xB24; @@ -72,23 +81,22 @@ private: ///< circular path around the player /* 0xB5C */ mAng mCureAngularSpeed; ///< When curing the player, holds the speed with which ///< the fairy is circling around the player - /* 0xB5E */ mAng field_0xB5E; /* 0xB60 */ u32 mPosYWaveAmplitude; /* 0xB64 */ f32 mMaxSpeedY; /* 0xB68 */ f32 targetSpeedY; - /* 0xB6C */ f32 field_0xB6C; + /* 0xB6C */ f32 mOriginalGndHeight; /* 0xB70 */ f32 mCurePosXZOffset; - /* 0xB74 */ f32 field_0xB74; + /* 0xB74 */ f32 mCurePosXZOffsetTarget; /* 0xB78 */ f32 mCurePosYOffset; - /* 0xB7C */ f32 field_0xB7C; - /* 0xB80 */ u8 field_0xB80; + /* 0xB7C */ f32 mAutoReleaseProgress; + /* 0xB80 */ u8 mSpawnType; /* 0xB81 */ u8 field_0xB81; - /* 0xB82 */ u8 field_0xB82; + /* 0xB82 */ u8 mPreventAvoidTimer; /* 0xB83 */ u8 mAvoidTimer; - /* 0xB84 */ u8 field_0xB84; + /* 0xB84 */ u8 mPreventCatchAfterSpawnTimer; /* 0xB85 */ bool mHasSetTurnSpeedY; /* 0xB86 */ bool field_0xB86; - /* 0xB87 */ u8 field_0xB87; + /* 0xB87 */ u8 _0xB87; /* 0xB88 */ bool field_0xB88; /* 0xB89 */ bool field_0xB89; }; diff --git a/src/d/a/obj/d_a_obj_fairy.cpp b/src/d/a/obj/d_a_obj_fairy.cpp index e5cb70e1..4155855a 100644 --- a/src/d/a/obj/d_a_obj_fairy.cpp +++ b/src/d/a/obj/d_a_obj_fairy.cpp @@ -89,7 +89,7 @@ int dAcOFairy_c::create() { mMaxSpeedY = randMaxSpeedY(); mSpeed = mMaxSpeedY; - field_0xB80 = getFromParams(0, 0xF); + mSpawnType = getFromParams(0, 0xF); field_0xB81 = getFromParams(4, 0x3); setSpawnPosition(calcInitialSpawnPosition()); @@ -110,7 +110,7 @@ int dAcOFairy_c::create() { mOldPosition = mPosition; } - if (field_0xB80 == 2 || field_0xB80 == 3) { + if (mSpawnType == SPAWN_MANUAL_RELEASE || mSpawnType == SPAWN_AUTO_RELEASE) { mStateMgr.changeState(StateID_CureStart); } else { mStateMgr.changeState(StateID_Wait); @@ -123,10 +123,10 @@ int dAcOFairy_c::create() { dBgS_ObjGndChk chk; pos.y += 10.0f; if (chk.CheckPos(pos)) { - field_0xB6C = chk.GetGroundHeight(); - mOrigPosition.y = field_0xB6C + 20.0f + 100.0f; + mOriginalGndHeight = chk.GetGroundHeight(); + mOrigPosition.y = mOriginalGndHeight + 20.0f + 100.0f; } - field_0xB84 = 0x2D; + mPreventCatchAfterSpawnTimer = 45; if (isWithinPlayerRadius(200.0f)) { s16 ang = mVelocity.atan2sX_Z(); @@ -165,7 +165,7 @@ int dAcOFairy_c::actorExecute() { return SUCCEEDED; } - if (!sLib::calcTimer(&field_0xB84)) { + if (!sLib::calcTimer(&mPreventCatchAfterSpawnTimer)) { if (mCcSph2.ChkCoHit() && !isCuring() && !dAcPy_c::GetLink()->checkCurrentAction(/* SCOOP */ 0x9E)) { if (!dAcItem_c::hasAnyFairy()) { for (int i = 0; i < (int)ARRAY_LENGTH(mEffects); i++) { @@ -276,7 +276,7 @@ int dAcOFairy_c::draw() { void dAcOFairy_c::initializeState_Wait() { mSpeed = mMaxSpeedY; - field_0xB82 = cM::rndInt(60) + 120; + mPreventAvoidTimer = cM::rndInt(60) + 120; mHasSetTurnSpeedY = false; field_0xB86 = false; mModel.setRate(1.0f); @@ -307,7 +307,7 @@ void dAcOFairy_c::executeState_Wait() { } } else { mHasSetTurnSpeedY = false; - if (!sLib::calcTimer(&field_0xB82)) { + if (!sLib::calcTimer(&mPreventAvoidTimer)) { mAvoidTimer = cM::rndInt(30); mStateMgr.changeState(StateID_Avoid); return; @@ -341,7 +341,8 @@ void dAcOFairy_c::executeState_Wait() { field_0xB86 = false; } - if (mVelocity.y < 0.0f && mPosition.y - field_0xB6C <= 50.0f) { + if (mVelocity.y < 0.0f && mPosition.y - mOriginalGndHeight <= 50.0f) { + // slow down when approaching ground mVelocity.y *= 0.7f; } @@ -418,8 +419,11 @@ void dAcOFairy_c::finalizeState_PlayerAvoid() { void dAcOFairy_c::initializeState_CureStart() { mCureAngularSpeed = cM::rndInt(0x100) + 0x2000; mCureAngle = getXZAngleToPlayer(); - mCurePosXZOffset = field_0xB80 == 3 ? 0.0f : 50.0f; - mCurePosYOffset = field_0xB80 == 3 ? 0.0f : 50.0f; + // If the fairy saves the player from death, it spawns directly + // at the player's position and circles outwards. Otherwise it directly + // starts 50 units away. + mCurePosXZOffset = mSpawnType == SPAWN_AUTO_RELEASE ? 0.0f : 50.0f; + mCurePosYOffset = mSpawnType == SPAWN_AUTO_RELEASE ? 0.0f : 50.0f; mCcSph2.ClrCoSet(); mCcSph2.ClrTgSet(); @@ -427,14 +431,15 @@ void dAcOFairy_c::initializeState_CureStart() { mSpeed = 0.0f; mVelocity = mVec3_c::Zero; dAcPy_c::fairyHeal(this); - if (field_0xB80 != 3) { + if (mSpawnType != SPAWN_AUTO_RELEASE) { dSndSmallEffectMgr_c::GetInstance()->playSound(SE_O_FAIRY_RECOVER); } } void dAcOFairy_c::executeState_CureStart() { - if (field_0xB80 == 3) { - sLib::chase(&field_0xB7C, 1.0f, 0.04f); - f32 f = cLib::easeInOut(field_0xB7C, 3.0f) * 50.0f; + if (mSpawnType == SPAWN_AUTO_RELEASE) { + // Start circling away from the player up to the normal distance + sLib::chase(&mAutoReleaseProgress, 1.0f, 0.04f); + f32 f = cLib::easeInOut(mAutoReleaseProgress, 3.0f) * 50.0f; mCurePosXZOffset = f; mCurePosYOffset = f; @@ -451,7 +456,7 @@ void dAcOFairy_c::executeState_CureStart() { } holdSound(SE_O_FAIRY_FLY_LEV); - if (field_0xB7C == 1.0f) { + if (mAutoReleaseProgress == 1.0f) { mStateMgr.changeState(StateID_Cure); } } else { @@ -470,15 +475,15 @@ void dAcOFairy_c::initializeState_Cure() { mCurePosXZOffset = 50.0f; mCurePosYOffset = 50.0f; mModel.setRate(15.0f); - field_0xB74 = cM::rnd() * 80.0f + 20.0f; - if (field_0xB80 == 3) { + mCurePosXZOffsetTarget = cM::rnd() * 80.0f + 20.0f; + if (mSpawnType == SPAWN_AUTO_RELEASE) { dSndSmallEffectMgr_c::GetInstance()->playSound(SE_O_FAIRY_RECOVER); } } void dAcOFairy_c::executeState_Cure() { - calcCurePosition(field_0xB74, 160.0f); + calcCurePosition(mCurePosXZOffsetTarget, 160.0f); if (mCureAngle < mCureAngularSpeed) { - field_0xB74 = cM::rnd() * 80.0f + 20.0f; + mCurePosXZOffsetTarget = cM::rnd() * 80.0f + 20.0f; } if (mCurePosYOffset == 160.0f) { mStateMgr.changeState(StateID_CureEnd); @@ -488,7 +493,7 @@ void dAcOFairy_c::finalizeState_Cure() {} void dAcOFairy_c::initializeState_CureEnd() {} void dAcOFairy_c::executeState_CureEnd() { - calcCurePosition(field_0xB74, 180.0f); + calcCurePosition(mCurePosXZOffsetTarget, 180.0f); sLib::chase(&mScale.x, 0.0f, 0.07f); mScale.y = mScale.z = mScale.x; if (mScale.x <= 0.0f) { @@ -582,7 +587,7 @@ void dAcOFairy_c::calcCurePosition(const f32 &xzOffsetTarget, const f32 &yOffset mCurePosition.y += mCurePosYOffset; vecCylCalc(mCurePosition, mCureAngle, mCurePosXZOffset); - sLib::chase(&mCurePosXZOffset, xzOffsetTarget, field_0xB74 / 10.0f); + sLib::chase(&mCurePosXZOffset, xzOffsetTarget, mCurePosXZOffsetTarget / 10.0f); sLib::chase(&mCurePosYOffset, yOffsetTarget, 2.0f); mCureAngle += mCureAngularSpeed;