#include "d/a/obj/d_a_obj_tumble_weed.h" #include "c/c_math.h" #include "common.h" #include "d/a/d_a_itembase.h" #include "d/a/d_a_player.h" #include "d/a/obj/d_a_obj_base.h" #include "d/col/bg/d_bg_s.h" #include "d/col/c/c_cc_d.h" #include "d/col/c/c_m3d_g_pla.h" #include "d/col/cc/d_cc_d.h" #include "d/col/cc/d_cc_s.h" #include "d/d_light_env.h" #include "d/snd/d_snd_wzsound.h" #include "egg/math/eggMath.h" #include "egg/math/eggQuat.h" #include "m/m_angle.h" #include "m/m_mtx.h" #include "m/m_quat.h" #include "m/m_vec.h" #include "s/s_Math.h" #include "toBeSorted/d_emitter.h" #include "toBeSorted/dowsing_target.h" void float_ordering() { const f32 arr[] = {5.f, 15.f, 7.f, 0.5f, 0.1f}; } SPECIAL_ACTOR_PROFILE(OBJ_TUMBLE_WEED, dAcOTumbleWeed_c, fProfile::OBJ_TUMBLE_WEED, 0x243, 0, 2); STATE_DEFINE(dAcOTumbleWeed_c, Wait); STATE_DEFINE(dAcOTumbleWeed_c, Slope); dCcD_SrcSph dAcOTumbleWeed_c::sSphSrc = { /* mObjInf */ {/* mObjAt */ {0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0}, /* mObjTg */ {~(AT_TYPE_BEETLE | AT_TYPE_GLITTERING_SPORES | AT_TYPE_0x8000), 0x111, {0, 8, 0x40F}, 0, 0}, /* mObjCo */ {0xE9}}, /* mSphInf */ {60.f}, }; bool dAcOTumbleWeed_c::createHeap() { mResFile = nw4r::g3d::ResFile(getOarcResFile("GrassRollDry")); TRY_CREATE(mMdl.create(mResFile.GetResMdl("GrassRollDry"), &mAllocator, 0x120, 1, nullptr)); return true; } int dAcOTumbleWeed_c::actorCreate() { CREATE_ALLOCATOR(dAcOTumbleWeed_c); mObjAcch.Set(this, 1, &mAcchCir); mAcchCir.SetWall(20.f, 60.f); mObjAcch.SetClrSpeedY(); mObjAcch.SetGndThinCellingOff(); mStts.SetRank(3); mSph.Set(sSphSrc); mSph.SetStts(mStts); mDowsingTarget.initialize(DowsingTarget::SLOT_TREASURE, 0, nullptr, 0.0f); mDowsingTarget.doRegister(); mAcceleration = -2.f; mMaxSpeed = -40.f; field_0x974 = 0.0f; field_0x98B = true; mWind = mVec3_c::Zero; mSpeedTarget = cM::rndFX(2.5f) + 15.f; field_0x978 = cM::rndF(40.f); field_0x97C = cM::rndFX(0.25f) + 1.f; mTumbleTimer = (150); field_0x910.set(1.f, 0.f, 0.f, 0.f); mScale.set(1.f, 1.f, 1.f); mStateMgr.changeState(StateID_Wait); mBoundingBox.Set(mVec3_c(-50.f, -20.f, -50.f), mVec3_c(50.f, 60.f, 50.f)); return SUCCEEDED; } int dAcOTumbleWeed_c::actorPostCreate() { return SUCCEEDED; } int dAcOTumbleWeed_c::doDelete() { return SUCCEEDED; } int dAcOTumbleWeed_c::actorExecute() { if (!field_0x98C && !isFullyStopped()) { field_0x968 = mVelocity; field_0x968.y = 0.0f; field_0x968.normalize(); field_0x98C = true; } mStateMgr.executeState(); calcWind(); sLib::chase(&field_0x980, 1.f, 0.1f); dLightEnv_c::GetInstance().setWind(field_0x968, field_0x980); calcVelocity(); mPosition += mVelocity; mPosition += mStts.GetCcMove(); mObjAcch.CrrPos(*dBgS::GetInstance()); field_0x974 += mPosition.y - mOldPosition.y; if (checkCollect()) { dAcPy_c::LINK->bugNetCollectTreasure(ITEM_TUMBLE_WEED); killNoItemDrop(); return SUCCEEDED; } if (checkBreak()) { doBreak(); return SUCCEEDED; } if (mSph.ChkTgHit() && mSph.ChkTgAtHitType(AT_TYPE_BELLOWS | AT_TYPE_WIND)) { field_0x974 = 0.f; adjustTumble(mSph.GetTgAtHitDir() * 0.06f); } adjustTimeScale(); if (checkObjectProperty(OBJ_PROP_0x2)) { // Weak function not being placed right if (sLib::calcTimer(&mTumbleTimer) == 0) { killNoItemDrop(); return SUCCEEDED; } } else { mTumbleTimer = 150; } mSph.SetC(getTumblePosition()); mSph.SetR(mScale.x * 60.f); dCcS::GetInstance()->Set(&mSph); updateMatrix(); calcMatrix(); holdSound(SE_TWeed_ROLL_LV); mMdl.setLocalMtx(mWorldMtx); mMdl.setScale(mScale); mMdl.calc(false); return SUCCEEDED; } int dAcOTumbleWeed_c::draw() { drawModelType1(&mMdl); static mQuat_c shadowRot(0.f, 30.f, 0.f, 50.f); if (0.f < mScale.x) { drawShadow(mShdw, nullptr, mShadowMtx, &shadowRot, -1, -1, -1, -1, -1, mPosition.y - mObjAcch.GetGroundH()); } return SUCCEEDED; } void dAcOTumbleWeed_c::initializeState_Wait() { mbOnGround = false; } void dAcOTumbleWeed_c::executeState_Wait() { if (mObjAcch.ChkGroundLanding() && !mbOnGround) { tumbleBounceMaybe(); return; } if (!mObjAcch.ChkGndHit()) { return; } mVelocity.y = 0.f; field_0x974 = 0.f; if (checkSlope()) { mStateMgr.changeState(StateID_Slope); } else { adjustSpeed(); if (mSpeed < 0.1f) { field_0x98B = false; sLib::chase(&mSpeed, 0.f, 0.05f); } } } void dAcOTumbleWeed_c::finalizeState_Wait() {} void dAcOTumbleWeed_c::initializeState_Slope() {} void dAcOTumbleWeed_c::executeState_Slope() { if (mObjAcch.ChkGroundLanding() && !mbOnGround) { tumbleBounceMaybe(); return; } if (!mObjAcch.ChkGndHit()) { return; } mVelocity.y = 0.f; adjustAngle(); adjustSpeed(); if (!checkSlope()) { mStateMgr.changeState(StateID_Wait); } } void dAcOTumbleWeed_c::finalizeState_Slope() {} bool dAcOTumbleWeed_c::checkBreak() { if (mObjAcch.ChkWallHit(nullptr) && mSpeed > 5.f) { return true; } if (mSph.ChkTgHit() && !mSph.ChkTgAtHitType(AT_TYPE_BUGNET)) { return true; } if (mObjAcch.ChkGndHit() && field_0x974 < -300.f) { return true; } if (fn_475_E40()) { return true; } if (mObjAcch.ChkGndHit() && checkInvalidGround()) { return true; } return false; } bool dAcOTumbleWeed_c::fn_475_E40() const { return field_0x974 < -10000.f; } bool dAcOTumbleWeed_c::checkSlope() { cM3dGPla pla; dBgS::GetInstance()->GetTriPla(mObjAcch.mGnd, &pla); mAng ang = mAng::fromRad(mVec3_c::Ey.angle(pla.GetN())); return ang.absDiff(0) > mAng::d2s(1); } bool dAcOTumbleWeed_c::checkCollect() { return mSph.ChkTgHit() && mSph.ChkTgAtHitType(AT_TYPE_BUGNET) ? true : false; } bool dAcOTumbleWeed_c::checkInvalidGround() const { u32 code = dBgS::GetInstance()->GetSpecialCode(mObjAcch.mGnd); return code == POLY_ATTR_LAVA || code == POLY_ATTR_SAND_MED || code == POLY_ATTR_SAND_DEEP_INSTANT || code == POLY_ATTR_SAND_DEEP_SLOW; } void dAcOTumbleWeed_c::doBreak() { startSound(SE_TWeed_CUT); mVec3_c pos = getTumblePosition(); dJEffManager_c::spawnEffect(PARTICLE_RESOURCE_ID_MAPPING_743_, pos, nullptr, nullptr, nullptr, nullptr, 0, 0); deleteRequest(); } void dAcOTumbleWeed_c::calcMatrix() { if (field_0x98B) { f32 vel_mag = VEC3Len(mVelocity); f32 f1 = mAng(vel_mag * (field_0x978 + 200.f)).radian(); f32 f2 = mAng(vel_mag * 182.0f * 0.2f).radian(); f32 f0 = mAng(mAngle.y - mRotation.y).radian(); mQuat_c q0, q1, q2, q3; q1.setAxisRotation(mVec3_c::Ey, f0); q0.setAxisRotation(mVec3_c::Ey, -f0); q3.setAxisRotation(mVec3_c::Ex, f1); q2.setAxisRotation(mVec3_c::Ey, f2); field_0x910 = q1 * q3 * q2 * q0 * field_0x910; } mMtx_c mtx0, mtx1, mtx2; mShadowMtx.copyFrom(mWorldMtx); mtx1.transS(getTumblePosition() - mPosition); mShadowMtx += mtx1; mtx0.fromQuat(field_0x910); mtx2.transS(0.f, 40.f, 0.f); mWorldMtx += mtx2; mWorldMtx += mtx0; } void dAcOTumbleWeed_c::adjustAngle() { cM3dGPla pla; dBgS::GetInstance()->GetTriPla(mObjAcch.mGnd, &pla); mVec3_c vel = mVelocity; // ok? mAngle.y = mAng::fromVec(mVelocity); } void dAcOTumbleWeed_c::adjustSpeed() { cM3dGPla pla; // @bug // the ground angle calculation happens before retrieving the grounds normal // this is probably hard to notice due to the little effect it has. // Effect: // Tumbleweed slows to the target much quicker than intended // Tumbleweed speeds to the target + 5.0f much quicker than intended mAng gndAngle = mAng::fromRad(mVec3_c::Ey.angle(pla.GetN())); dBgS::GetInstance()->GetTriPla(mObjAcch.GetGnd(), &pla); f32 speedStep = 0.5f; f32 step = gndAngle.cos() * speedStep; // Flat Ground or not in the direction of the slope // Slows down if (gndAngle < mAng::d2s(1) || mAng::fromVec(pla.GetN()).absDiff(mAngle.y) > mAng::d2s(90)) { sLib::chase(&mSpeed, mSpeedTarget, step); } // Sloped Ground and in the direction of the slope // Speeds up else { mSpeed = cM::minMaxLimit(mSpeed + step, mSpeedTarget, mSpeedTarget + 5.0f); } } void dAcOTumbleWeed_c::tumbleBounceMaybe() { mVelocity.y *= -0.75f; mSpeed *= 0.9f; if (mSpeed < 15.f) { mSpeed = mSpeedTarget; } mbOnGround = false; if (mVelocity.y < 5.f) { mVelocity.y = 0.f; mbOnGround = true; } } void dAcOTumbleWeed_c::adjustTimeScale() { mTimeArea.check(getRoomId(), getPosition(), 0, 30.f, 0.1f); if (0.f < mTimeArea.getDistMaybe()) { sLib::chase(&mScale.y, 0.f, 0.07f); mScale.z = mScale.y; mScale.x = mScale.y; } else { sLib::chase(&mScale.y, 1.f, 0.07f); mScale.z = mScale.y; mScale.x = mScale.y; } } void dAcOTumbleWeed_c::adjustTumble(const mVec3_c &dir) { field_0x98B = true; mVelocity.x += dir.x; mVelocity.z += dir.z; mSpeed = field_0x97C * EGG::Math::sqrt(mVelocity.x * mVelocity.x + mVelocity.z * mVelocity.z); mAngle.y = mAng::fromVec(dir); } void dAcOTumbleWeed_c::setWind(const mVec3_c &wind) { mWind = wind; mWindTimer = cM::rndInt(10); } void dAcOTumbleWeed_c::calcWind() { if (!isWindStop() && sLib::calcTimer(&mWindTimer) == 0) { mVelocity.y = cM::rndFX(3.0f) + 15.f; mSpeed = cM::rndFX(5.0f) + 15.f; mWind = mVec3_c::Zero; field_0x980 = 0.f; } } mVec3_c dAcOTumbleWeed_c::getTumblePosition() const { mMtx_c mtx; mVec3_c vec; mtx.ZXYrotS(mRotation.x, mRotation.y, mRotation.z); MTXMultVecSR(mtx, mVec3_c::Ey, vec); return mPosition + vec * 40.f; } void float_ordering2() { const f32 arr[] = {30.f, 0.07f, 20.f, -10000.f, 5.f}; }