#include "d/a/obj/d_a_obj_arrow.h" #include "c/c_lib.h" #include "c/c_math.h" #include "common.h" #include "d/a/d_a_item.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_pc.h" #include "d/col/bg/d_bg_s.h" #include "d/col/bg/d_bg_s_gnd_chk.h" #include "d/col/bg/d_bg_s_lin_chk.h" #include "d/col/c/c_bg_s_poly_info.h" #include "d/col/c/c_cc_d.h" #include "d/col/c/c_m3d_g_pla.h" #include "d/col/cc/d_cc_s.h" #include "d/d_jnt_col.h" #include "d/d_light_env.h" #include "d/d_linkage.h" #include "d/d_vec.h" #include "d/snd/d_snd_source_if.h" #include "d/snd/d_snd_wzsound.h" #include "f/f_profile_name.h" #include "m/m_mtx.h" #include "m/m_vec.h" #include "nw4r/math/math_types.h" #include "nw4r/types_nw4r.h" #include "s/s_Math.h" #include "toBeSorted/d_emitter.h" SPECIAL_ACTOR_PROFILE(ARROW, dAcArrow_c, fProfile::ARROW, 0x126, 0, 0x80); static dBgS_ArrowLinChk sArrowLinChk; u16 dAcArrow_c::sCounter; inline static u32 getDamage1() { return 16; } inline static u32 getDamage2() { return 16; } inline static u32 getDamage3() { return 24; } inline static u32 getDamage4() { return 32; } const u8 dAcArrow_c::sDamageArrMaybe[] = { getDamage1(), getDamage2(), getDamage3(), getDamage4(), }; STATE_DEFINE(dAcArrow_c, Wait); STATE_DEFINE(dAcArrow_c, Move); STATE_DEFINE(dAcArrow_c, ActorStop); STATE_DEFINE(dAcArrow_c, BgStop); STATE_DEFINE(dAcArrow_c, Bound); // clang-format off cCcD_SrcGObj dAcArrow_c::sCcSrcInf = { {AT_TYPE_ARROW, 0x8BB, {0x15,0, 0}, sDamageArrMaybe[0], 0, 0, 0, 0, 0}, { ~(AT_TYPE_BUGNET | AT_TYPE_BEETLE | AT_TYPE_GLITTERING_SPORES | AT_TYPE_0x8000 | AT_TYPE_ARROW | AT_TYPE_0x100 | AT_TYPE_WIND | AT_TYPE_SLINGSHOT), 0x210, {0, 0, 0x407}, 0, 0 }, {0}, }; dCcD_SrcCps dAcArrow_c::sCc1 = { dAcArrow_c::sCcSrcInf, {5.0f}, }; const dCcD_SrcSph dAcArrow_c::sCc2 = { {{0x0, 0x0, 0x0, 0, 0, 0, 0, 0, 0}, {AT_TYPE_SWORD, 0x211, {0, 0, 0x407}, 0, 0}, {0xC28}}, {100.0f}, }; // clang-format on bool dAcArrow_atHitCallback(dAcObjBase_c *i_actorA, cCcD_Obj *i_objInfA, dAcObjBase_c *i_actorB, cCcD_Obj *i_objInfB) { static_cast(i_actorA)->hitCallback(i_objInfA, i_actorB, i_objInfB); return true; } bool dAcArrow_c::createHeap() { mResFile = nw4r::g3d::ResFile(getOarcResFile("Alink")); nw4r::g3d::ResMdl mdl(nullptr); if ((isSlingshotProjectile()) != 0) { mdl = mResFile.GetResMdl("EquipPachinkoBullet"); } else { mdl = mResFile.GetResMdl("EquipArrow"); } // Looks weird; the more likely explanation here is that they did an `if (!create) return false;` // forgetting the `return true;` case, but this matches without UB so let's keep it that way? bool ok = mModel.create(mdl, &mAllocator, 0x120); if (!ok) { ok = false; } return ok; } int dAcArrow_c::create() { mArrowType = mParams & 0xFF; CREATE_ALLOCATOR(dAcArrow_c); setArrowFlag(ARROW_INITIAL_SPAWN); mStts.SetRank(2); mCcCps.Set(sCc1); mCcCps.SetStts(mStts); mCcCps.OnAt_0x2000(); mCcCps.SetAtCallback(dAcArrow_atHitCallback); mCcSph.Set(sCc2); mCcSph.SetStts(mStts); if (mArrowType == 17) { mStateMgr.changeState(StateID_Move); } else { mStateMgr.changeState(StateID_Wait); } mBoundingBox.Set(mVec3_c(-6.0f, -6.0f, 0.0f), mVec3_c(6.0f, 6.0f, 110.0f)); mCounterIdx = -1; field_0x68A = 300; field_0x688 = 50.0f - cM::rndF(20.0f); field_0x6A8 = 80.0f; mDespawnTimer = 10; updateRoomId(); return SUCCEEDED; } dAcArrow_c::~dAcArrow_c() {} void dAcArrow_c::hitCallback(cCcD_Obj *i_objInfA, dAcObjBase_c *i_actorB, cCcD_Obj *i_objInfB) { dJntCol_c *col = i_actorB->getLinkage().getJntCol(); if (col != nullptr) { mBoundJntIdx = col->getArrowOffsetPosAndAngle( &i_objInfA->GetAtHitPos(), &mRotation, &field_0x6B0, &field_0x6BC, i_objInfB->ChkTg_0x4C(0x2000) ); if (mBoundJntIdx >= 0) { mRef1.link(i_actorB); } else { mRef1.unlink(); } } } bool dAcArrow_c::checkWaterHit(const mVec3_c &v1, mVec3_c &v2, bool noEffect) { if (dBgS_WtrLinChk::SetIsWater(&v1, &v2, this)) { const mVec3_c &end = dBgS_WtrLinChk::GetInstance().GetLinEnd(); if (isSlingshotProjectile()) { v2 = end; } else { mVec3_c t1 = v2 - end; f32 sc; f32 dist = v2.distance(v1); if (dist > 0.0001f) { sc = t1.mag() / dist; } else { sc = 1.0f; } t1.normalize(); v2 = end + t1 * field_0x6A8 * sc; } if (!noEffect) { dAcPy_c::fn_801E2FC0(end, dBgS_WtrLinChk::GetInstance(), 0.5f); startSound(SE_AW_HIT_WATER); } return true; } return false; } void dAcArrow_c::fn_8025DED0(f32 f) { f32 f1; dAcPy_c *link = dAcPy_c::GetLink2(); if (isSlingshotProjectile()) { f1 = 12.0f; } else { f1 = 5.0f; f32 f2 = link->fn_802097C0(); if (f > f2) { f32 f3 = link->fn_802097D0(); if (f3 <= f2) { f1 += link->fn_802097B0(); } else { f32 f4 = (f - f2) / (f3 - f2); if (f4 > 1.0f) { f4 = 1.0f; } f1 += f4 * link->fn_802097B0(); } } } mVec3_c next = mVec3_c(mPosition + mVelocity * (field_0x6A0 + 1.0f)); mCcCps.OnAtSet(); if (!checkArrowFlag(ARROW_WATER_HIT)) { if (!isSlingshotProjectile()) { holdSound(SE_AW_FLY_LV); } checkWaterHit(mPosition, next, true); } else { f32 diff = next.y - field_0x6D4.y; if (diff < -300.0f) { diff = -300.0f - diff; f32 diff2 = next.y - mPosition.y; diff2 = nw4r::math::FAbs(diff2); if (diff2 > diff) { next = mPosition + mVelocity * ((field_0x6A0 + 1.0f) * ((diff2 - diff) / diff2)); } } } sArrowLinChk.Set(&mPosition, &next, this); if (dBgS::GetInstance()->LineCross(&sArrowLinChk)) { next = sArrowLinChk.GetLinEnd(); } mCcCps.Set(mPosition, next, f1); mCcCps.unknownCalc(); dCcS::GetInstance()->Set(&mCcCps); dCcS::GetInstance()->GetMassMng().SetObj(&mCcCps, 1); } void dAcArrow_c::fn_8025E160() { // NONMATCHING dAcPy_c *link = dAcPy_c::GetLink2(); if (isSlingshotProjectile()) { field_0x6A0 = 0.0f; if (mArrowType != 17) { mAngle.set(mAng(-link->getField_0x1268()), mAng(link->getRotation().y + link->getField_0x126A()), mAng(0)); } else { mAngle.y = mRotation.y; mAngle.x = -mRotation.x; } link->fn_80209700(field_0x6A4, field_0x69C, false); } else { link->fn_80209700(field_0x6A4, field_0x69C, false); if (checkArrowFlag(ARROW_FULLY_CHARGED)) { const mVec3_c *v = link->fn_802097E0(mRotation.x, mRotation.y); if (v != nullptr) { mPosition = *v; mAngle.x = -mRotation.x; mAngle.y = mRotation.y; } } field_0x683 = 30; field_0x6A0 = 90.0f / field_0x69C; startSound(SE_AW_FLY); } if (checkArrowFlag(ARROW_0x40)) { unsetArrowFlag(ARROW_0x40); mVec3_c tmp = field_0x6EC - mPosition; mAngle.x = tmp.atan2sY_XZ() + field_0x6AC * cM::rndFX(400.0f); mAngle.y = tmp.atan2sX_Z() + field_0x6AC * cM::rndFX(400.0f); mRotation.x = -mAngle.x; mRotation.y = mAngle.y; } // Spawn additional projectiles for Mighty Scattershot if (isSlingshotProjectile() && checkArrowFlag(ARROW_FULLY_CHARGED)) { mVec3_c tmp = mVec3_c::Ez; tmp.rotX(mAngle.x); tmp.rotY(mAngle.y); mAng3_c ang(0, 0, 0); for (int i = 0; i < 8; i++) { mVec3_c tmp2 = mVec3_c::Ez; // TODO eliminate temps? mAng a1 = link->fn_80208CA0(); mAng a2 = mAng::fromDeg(cM::rndF(2.5f)); tmp2.rotX(mAngle.x + a1 - a2); tmp2.rotY(mAngle.y); mMtx_c mtx; mtx.setAxisRotation(tmp, i * mAng::s2r_c(0x2000) + mAng::d2r_c(cM::rndFX(12.0f))); MTXMultVecSR(mtx, tmp2, tmp2); ang.x = tmp2.atan2sY_XZ(); ang.y = tmp2.atan2sX_Z(); dAcObjBase_c::create(fProfile::ARROW, -1, 17, &mPosition, &ang, 0, -1); } } mSpeed = 100.0f; field_0x6C8 = mPosition; mVelocity.fromXY(mAngle.x, mAngle.y, field_0x69C); if (!isSlingshotProjectile()) { mCounterIdx = sCounter; sCounter++; if (sCounter == 6) { sCounter = 0; } } fn_8025DED0(0.0f); } void dAcArrow_c::updateRoomId() { if (dBgS_ObjGndChk::CheckPos(mPosition)) { mRoomID = dBgS_ObjGndChk::GetRoomID(); } else if (mRoomID == -1) { mRoomID = dAcPy_c::GetLink2()->getRoomId(); } } s16 dAcArrow_c::fn_8025E640() { if (sLib::calcTimer(&field_0x688)) { field_0x690 += (s16)(21243.0f - cM::rndF(0x1000)); f32 a = field_0x688 * 0.02f; return a * 0x400 * a * field_0x690.sin(); } else { return 0; } } void dAcArrow_c::fn_8025E720(dAcObjBase_c *obj, const mVec3_c &v) { mRef1.link(obj); mMtx_c mtx; obj->mWorldMtx.inverseTo(mtx); MTXMultVec(mtx, v, field_0x6B0); mVec3_c v1 = mVec3_c::createProjectionXZ(mRotation, 1.0f); v1.y *= -1.0f; MTXMultVecSR(mtx, v1, field_0x6BC); } void dAcArrow_c::updateMtx() { mWorldMtx.transS(mPosition); mWorldMtx.ZXYrotM(mRotation); if (mStateMgr.isState(StateID_BgStop)) { mMtx_c mtx; mtx.transS(0.0f, 0.0f, -90.0f); MTXConcat(mWorldMtx, mtx, mWorldMtx); } else if (mStateMgr.isState(StateID_Bound)) { mMtx_c mtx; mtx.transS(0.0f, 0.0f, -45.0f); MTXConcat(mWorldMtx, mtx, mWorldMtx); } mModel.setLocalMtx(mWorldMtx); } void dAcArrow_c::setInitialPosition() { dAcPy_c::GetLink2()->getWeaponLMtx(mWorldMtx); mModel.setLocalMtx(mWorldMtx); mWorldMtx.getTranslation(mPosition); mWorldMtx.toRot(mRotation); mAngle.y = mRotation.y; mAngle.x = -mRotation.x; } dAcObjBase_c *dAcArrow_c::fn_8025E960() { s16 a1 = fn_8025E640(); dAcObjBase_c *o1 = mRef1.get(); if (o1 == nullptr || o1->checkObjectProperty(OBJ_PROP_0x100)) { return nullptr; } else if (checkArrowFlag(ARROW_0x2000)) { if (checkArrowFlag(ARROW_0x10000)) { mWorldMtx.getTranslation(mPosition); mWorldMtx.toRot(mRotation); mMtx_c mtx; mtx.transS(0.0f, 0.0f, -90.0f); MTXConcat(mWorldMtx, mtx, mWorldMtx); mModel.setLocalMtx(mWorldMtx); } return o1; } else { dJntCol_c *col = o1->getLinkage().getJntCol(); if (col != nullptr) { col->setArrowPosAndAngle(&field_0x6B0, &field_0x6BC, mBoundJntIdx, &mPosition, &mRotation); } else { MTXMultVec(o1->mWorldMtx, field_0x6B0, mPosition); mVec3_c tmp; MTXMultVecSR(o1->mWorldMtx, field_0x6BC, tmp); mRotation.x = tmp.atan2snY_XZ(); mRotation.y = tmp.atan2sX_Z(); } mWorldMtx.transS(mPosition); mWorldMtx.ZXYrotM(mRotation.x + a1, mRotation.y, mRotation.z); mMtx_c mtx; mtx.transS(0.0f, 0.0f, -90.0f); MTXConcat(mWorldMtx, mtx, mWorldMtx); mModel.setLocalMtx(mWorldMtx); return o1; } } void dAcArrow_c::initPickupCc() { mCcSph.SetR(25.0f); mCcSph.ClrTgSet(); mCcSph.OnCoSet(); } bool dAcArrow_c::checkPickup() { if (dAcPy_c::getCurrentBowType() != 0) { if (mCcSph.ChkCoHit()) { dAcItem_c::giveItem(ITEM_SINGLE_ARROW, 0, -1); setArrowFlag(ARROW_SHOULD_DESPAWN); return true; } mCcSph.SetC(mPosition); dCcS::GetInstance()->Set(&mCcSph); } return false; } void dAcArrow_c::initializeState_Wait() { setInitialPosition(); } void dAcArrow_c::executeState_Wait() { setInitialPosition(); if (checkArrowFlag(ARROW_0x800)) { mStateMgr.changeState(StateID_Move); } else if (isSlingshotProjectile()) { mHiddenTimer = 1; } } void dAcArrow_c::finalizeState_Wait() {} void dAcArrow_c::initializeState_Move() { setArrowFlag(ARROW_0x1000); setActorProperty(AC_PROP_0x4); if (isSlingshotProjectile()) { mCcCps.SetAtType(AT_TYPE_SLINGSHOT); mCcCps.SetAtInfo_0x0(20); mCcCps.SetAtDamage(0); mEffectId = PARTICLE_RESOURCE_ID_MAPPING_207_; } else { u8 dmg = sDamageArrMaybe[dAcPy_c::getCurrentBowType()]; if (checkArrowFlag(ARROW_FULLY_CHARGED)) { mCcCps.SetAtFlagsUpper(0x2000000); mEffectId = PARTICLE_RESOURCE_ID_MAPPING_2_; dmg = (s32)dmg * 1.5f + 0.5f; } else { mEffectId = PARTICLE_RESOURCE_ID_MAPPING_1_; } mCcCps.SetAtDamage(dmg); mCcCps.OnAt_0x1000(); } mCcCps.OnTgSet(); fn_8025E160(); updateMtx(); } void dAcArrow_c::executeState_Move() { mPosition += mVelocity; mVec3_c next = mVec3_c(mPosition + mVelocity * field_0x6A0); if (!checkArrowFlag(ARROW_WATER_HIT)) { sArrowLinChk.Set(&mOldPosition, &next, this); mVec3_c t; if (dBgS::GetInstance()->LineCross(&sArrowLinChk)) { t = sArrowLinChk.GetLinEnd(); } else { t = next; } if (checkWaterHit(mOldPosition, t, false)) { setArrowFlag(ARROW_WATER_HIT); unsetArrowFlag(ARROW_0x200); field_0x6D4 = dBgS_WtrLinChk::GetInstance().GetLinEnd(); mVelocity.normalize(); mPosition = t - mVelocity * 90.0f; mVelocity *= field_0x6A8; if (!isSlingshotProjectile()) { field_0x6A0 = 90.0f / field_0x6A8; } next = t; } } sArrowLinChk.Set(&mOldPosition, &next, this); bool isCross = dBgS::GetInstance()->LineCross(&sArrowLinChk); cCcD_Obj *obj = nullptr; s32 flags = -1; if (mCcCps.ChkTgHit()) { obj = &mCcCps; } if (obj != nullptr) { flags = obj->GetTgDamageFlags(); } // TODO magic numbers if (flags == 8) { mPosition = obj->GetTgHitPos(); const mVec3_c &hitDir = obj->GetTgAtHitDir(); if (hitDir.squareMagXZ() < 1.0f) { mAngle.y = cLib::targetAngleY(mPosition, obj->GetTgActor()->mPosition); } else { mAngle.y = hitDir.atan2snX_nZ(); } mRotation.y = mAngle.y; setArrowFlag(ARROW_0x400); mStateMgr.changeState(StateID_Bound); } else if (obj != nullptr) { mPosition = obj->GetTgHitPos(); mStateMgr.changeState(StateID_Bound); startSound(SE_AW_FLIP); } else { dAcObjBase_c *atActor; s32 what; if (mCcCps.ChkAtHit()) { if (mCcCps.GetAtActor()->mProfileName == fProfile::PUMPKIN) { field_0x683 = 0; } dLightEnv_c::GetPInstance()->setBPM8_Type6(&mCcCps.GetAtHitPos()); if (isSlingshotProjectile()) { onSlingshotHit(mCcCps.GetAtHitPos(), 0); return; } atActor = mCcCps.GetAtActor(); what = 1; if (atActor != nullptr && atActor->getLinkage().hasJntCol()) { bool b = false; if (atActor != mRef1.get()) { mBoundJntIdx = atActor->getLinkage().getArrowOffsetPosAndAngle( &mCcCps.GetAtHitPos(), &mRotation, &field_0x6B0, &field_0x6BC, mCcCps.GetAtFlag0x2() ); b = true; } if (mBoundJntIdx >= 0) { what = 4; if (b) { mRef1.link(atActor); } } else if (mBoundJntIdx == -2 || mCcCps.GetAtFlag0x2()) { what = 2; } } else { if (mCcCps.GetAtFlag0x2()) { what = 2; } else if (mCcCps.ChkAtArrowStick()) { fn_8025E720(atActor, mCcCps.GetAtHitPos()); what = 4; } } if (what == 1 && atActor != nullptr && atActor->getLinkage().checkFlag(0x1000) && !isSlingshotProjectile()) { if (!mRef2.isLinked()) { atActor->getLinkage().tryAttach(atActor, this, &mRef2, dLinkage_c::CONNECTION_8, false); } what = 0; } } else { what = 0; } switch (what) { default: { if (what == 2) { if (checkArrowFlag(ARROW_WATER_HIT)) { setArrowFlag(ARROW_SHOULD_DESPAWN); } else { if (mCcCps.ChkAtHit()) { mPosition = mCcCps.GetAtHitPos(); } mStateMgr.changeState(StateID_Bound); } } else if (what == 4) { mStateMgr.changeState(StateID_ActorStop); } else { setArrowFlag(ARROW_SHOULD_DESPAWN); if (!checkArrowFlag(ARROW_WATER_HIT) && mCcCps.ChkAtHit()) { mPosition = mCcCps.GetAtHitPos(); } } break; } case 0: case 3: { if (isCross) { mPosition = sArrowLinChk.GetLinEnd(); if (checkArrowFlag(ARROW_WATER_HIT) && (isSlingshotProjectile() || mPosition.y - field_0x6D4.y < -300.0f)) { setArrowFlag(ARROW_SHOULD_DESPAWN); } else { s32 polyAtt0 = dBgS::GetInstance()->GetPolyAtt0(sArrowLinChk); s32 polyAtt1 = dBgS::GetInstance()->GetPolyAtt1(sArrowLinChk); s32 specialCode = dBgS::GetInstance()->GetSpecialCode(sArrowLinChk); if (isSlingshotProjectile() || specialCode == 19 || specialCode == 20 || specialCode == 16 || specialCode == 10 || specialCode == 11 || polyAtt0 == 6 || polyAtt0 == POLY_ATT_0_ROCK || polyAtt0 == POLY_ATT_0_NONE || polyAtt0 == POLY_ATT_0_NUMA || polyAtt0 == POLY_ATT_0_STONE || polyAtt0 == POLY_ATT_0_METAL || (polyAtt0 == POLY_ATT_0_LIFE && (polyAtt1 == 2 || polyAtt1 == 3))) { if (checkArrowFlag(ARROW_WATER_HIT)) { setArrowFlag(ARROW_SHOULD_DESPAWN); } else { dAcPy_c::fn_801E2FC0(mPosition, sArrowLinChk, 0.5f); if (isSlingshotProjectile()) { onSlingshotHit(mPosition, polyAtt0 == POLY_ATT_0_LAVA); } else { startBgHitSound(SE_AW_HIT, sArrowLinChk, nullptr); if (polyAtt0 == POLY_ATT_0_LAVA || polyAtt0 == POLY_ATT_0_NUMA || polyAtt0 == POLY_ATT_0_NONE || specialCode == 16 || specialCode == 10 || specialCode == 11) { setArrowFlag(ARROW_SHOULD_DESPAWN); } else { mStateMgr.changeState(StateID_Bound); } } } } else { mStateMgr.changeState(StateID_BgStop); } } } else if (checkArrowFlag(ARROW_WATER_HIT)) { if (isSlingshotProjectile()) { mPosition = next; setArrowFlag(ARROW_SHOULD_DESPAWN); } else { f32 diff = mPosition.y - field_0x6D4.y; if (diff < -300.0f) { mCcCps.ClrAtSet(); mCcCps.ClrAtHit(); if (sLib::chase(&mScale.x, 0.0f, 0.1f)) { setArrowFlag(ARROW_SHOULD_DESPAWN); } else { mScale.y = mScale.z = mScale.x; mModel.setScale(mScale); } } else if (diff > 90.0f) { setArrowFlag(ARROW_SHOULD_DESPAWN); } else { fn_8025DED0(mPosition.distance(field_0x6C8)); } } } else { f32 diff = mPosition.distance(field_0x6C8); if (diff > field_0x6A4 || mAcceleration < 0.0f) { if (isSlingshotProjectile()) { mAcceleration = -5.0f; } else { mAcceleration = -2.0f; } mVelocity.y += mAcceleration; if (isSlingshotProjectile()) { dAcPy_c *link = dAcPy_c::GetLink2(); f32 xz = mVelocity.absXZ(); f32 t1 = link->fn_80208CE0(); if (xz > t1) { f32 orig = xz; sLib::chase(&xz, t1, link->fn_80208CF0()); xz /= orig; mVelocity.x *= xz; mVelocity.z *= xz; } } if (field_0x68A != 0) { field_0x68A--; } else { setArrowFlag(ARROW_SHOULD_DESPAWN); return; } mRotation.x = mVelocity.atan2snY_XZ(); mAngle.x = -mRotation.x; if (!isSlingshotProjectile()) { field_0x6A0 = 90.0f / mVelocity.mag(); } } fn_8025DED0(diff); } break; } } } } void dAcArrow_c::finalizeState_Move() {} void dAcArrow_c::initializeState_Bound() { if (isSlingshotProjectile()) { onSlingshotHit(mPosition, false); } else { mSpeed = 0.0f; fn_802601C0(); f32 rnd = cM::rndFX(0x2000); f32 ang = mAngle.y + 0x8000; mAng ay = ang + rnd; mAng ax; if (checkArrowFlag(ARROW_0x400)) { ax = 0x2000 - cM::rndF(0x1000); } else { ax = -cM::rndF(0x1000); } mVelocity.fromXY(ax, ay, 15.0f + cM::rndF(15.0f)); field_0x690 = 0x2C00 - cM::rndF(0x1000); setArrowFlag(ARROW_0x100); unsetArrowFlag(ARROW_0x200); updateMtx(); } } void dAcArrow_c::executeState_Bound() { mVelocity.y -= 2.0f; mPosition += mVelocity; mRotation.x += field_0x690; mVec3_c next = mPosition + mVelocity * field_0x6A0; sArrowLinChk.Set(&mOldPosition, &next, this); if (dBgS::GetInstance()->LineCross(&sArrowLinChk)) { cM3dGPla pla; dBgS::GetInstance()->GetTriPla(sArrowLinChk, &pla); f32 halfMag = mVelocity.mag() * 0.5f; VECReflect(mVelocity, pla.mNormal, mVelocity); mVelocity *= halfMag; field_0x690.mVal >>= 1; if (dBgS_CheckBGroundPoly(sArrowLinChk)) { setArrowFlag(ARROW_0x1); } } else if ((checkArrowFlag(ARROW_0x1) && mVelocity.y <= 0.0f) || field_0x6C8.y - 5000.0f > mPosition.y) { setArrowFlag(ARROW_SHOULD_DESPAWN); } updateMtx(); } void dAcArrow_c::finalizeState_Bound() {} void dAcArrow_c::initializeState_BgStop() { fn_802601C0(); cM3dGPla pla; dBgS::GetInstance()->GetTriPla(sArrowLinChk, &pla); mVec3_c vz; mWorldMtx.getBase(2, vz); if (pla.mNormal.dot(vz) > mAng(19115).cos()) { mVec3_c c; vecCross(c, pla.mNormal, vz); if (nw4r::math::VEC3LenSq(c) > 0.1f) { mMtx_c mtx; mtx.setAxisRotation(c, mAng::s2r_c(19115)); MTXMultVecSR(mtx.m, pla.mNormal, vz); mRotation.y = vz.atan2sX_Z(); mRotation.x = vz.atan2snY_XZ(); } } unsetArrowFlag(ARROW_0x200); setArrowFlag(ARROW_0x100); mSpeed = 0.0f; mAngle.x = -mRotation.x; field_0x68A = 300; updateMtx(); mPolyInfo.SetPolyInfo(sArrowLinChk); startBgHitSound(SE_AW_STICK, sArrowLinChk, nullptr); // TODO - constness dBgS::GetInstance()->ArrowStickCallBack(sArrowLinChk, this, const_cast(sArrowLinChk.GetLinEnd())); initPickupCc(); dLightEnv_c::GetPInstance()->setBPM8_Type6(&mPosition); const dAcObjBase_c *ac = dBgS::GetInstance()->GetActorPointer(sArrowLinChk); if (ac != nullptr) { // TODO - constness fn_8025E720(const_cast(ac), mPosition); setArrowFlag(ARROW_0x8000); } } void dAcArrow_c::executeState_BgStop() { checkPickup(); dAcObjBase_c *ref = this; if (checkArrowFlag(ARROW_0x8000)) { ref = fn_8025E960(); } if (checkArrowFlag(ARROW_OLD) || ref == nullptr || ((!ref->checkObjectProperty(OBJ_PROP_0x10000) || !checkArrowFlag(ARROW_0x8000)) && !dBgS::GetInstance()->ChkPolySafe(mPolyInfo))) { setArrowFlag(ARROW_SHOULD_DESPAWN); } else if (!checkArrowFlag(ARROW_0x8000)) { bool b = false; s16 s1 = fn_8025E640(); if (field_0x688 != 0) { b = true; } if (dBgS::GetInstance()->ChkMoveBG(mPolyInfo, true)) { dBgS::GetInstance()->MoveBgTransPos(mPolyInfo, true, &mPosition, &mAngle, &mRotation); b = true; } if (b != 0) { mRotation.x = s1 - mAngle.x; updateMtx(); } } } void dAcArrow_c::finalizeState_BgStop() {} void dAcArrow_c::initializeState_ActorStop() { fn_802601C0(); unsetArrowFlag(ARROW_0x200); mSpeed = 0.0f; dAcObjBase_c *obj = fn_8025E960(); if (obj != nullptr) { getSoundSource()->startObjHitSound(SE_AW_STICK, obj->getSoundSource(), mPosition); } initPickupCc(); } void dAcArrow_c::executeState_ActorStop() { dAcObjBase_c *obj = fn_8025E960(); checkPickup(); if (checkArrowFlag(ARROW_OLD) || obj == nullptr) { setArrowFlag(ARROW_SHOULD_DESPAWN); } unsetArrowFlag(ARROW_0x10000); } void dAcArrow_c::finalizeState_ActorStop() { unsetArrowFlag(ARROW_0x10000); } void dAcArrow_c::onSlingshotHit(const mVec3_c &v, bool b) { if (b) { startSound(SE_PC_HIT_LAVA); } else { startSound(SE_PC_HIT); } setArrowFlag(ARROW_SHOULD_DESPAWN); setArrowFlag(ARROW_0x100); dJEffManager_c::spawnEffect(PARTICLE_RESOURCE_ID_MAPPING_538_, v, nullptr, nullptr, nullptr, nullptr, 0, 0); } void dAcArrow_c::fn_802600D0(bool b) { if (b) { mWorldMtx.getTranslation(mEffectTranslation); mEffectRotation.set(mAng(0), mRotation.y, mEffectRotationZ.mVal); if (mEffectId == PARTICLE_RESOURCE_ID_MAPPING_2_) { mEffectRotationZ += 0x888; } mMtx_c mtx; mtx.transS(mEffectTranslation); mtx.ZXYrotM(mEffectRotation); mEffects.holdEffect(mEffectId, mtx, nullptr, nullptr); } else { mEffects.setFading(5); unsetArrowFlag(ARROW_0x1000); } unsetArrowFlag(ARROW_0x100); } void dAcArrow_c::fn_802601C0() { if (field_0x683 != 0) { if (!(mCcCps.ChkAtHit() && mCcCps.GetAtActor()->mProfileName == fProfile::PUMPKIN)) { setArrowFlag(ARROW_0x4000); } field_0x683 = 0; } } bool dAcArrow_c::fn_80260250(dAcObjBase_c *o1, dAcObjBase_c *o2) { dAcObjBase_c *o = mRef2.get(); if (o == nullptr || o1 == o) { o1->getLinkage().forceRemove(o1); o2->getLinkage().tryAttach(o2, this, &mRef2, dLinkage_c::CONNECTION_8, false); return true; } else { return false; } } int dAcArrow_c::actorExecute() { unsetArrowFlag(ARROW_0x4000); if (field_0x683 != 0) { field_0x683--; if (field_0x683 == 0) { setArrowFlag(ARROW_0x4000); } } if (checkArrowFlag(ARROW_SHOULD_DESPAWN)) { // TODO - works but maybe temps or inlines? if (mRef2.isLinked()) { mRef2.get()->getLinkage().forceRemove(mRef2.get()); } fn_802601C0(); if (mDespawnTimer != 0) { mDespawnTimer--; } else { deleteRequest(); return SUCCEEDED; } } else { if (mCounterIdx == sCounter) { setArrowFlag(ARROW_OLD); } if (mHiddenTimer != 0) { mHiddenTimer--; } mStateMgr.executeState(); if (mStateMgr.isState(StateID_Move)) { updateMtx(); } } mPositionCopy2 = mPositionCopy3 = mPosition; if (checkArrowFlag(ARROW_0x1000)) { fn_802600D0( (mStateMgr.isState(StateID_Move) && !checkArrowFlag(ARROW_SHOULD_DESPAWN)) || checkArrowFlag(ARROW_0x100) ); } updateRoomId(); dAcObjBase_c *obj = mRef2.get(); if (obj != nullptr) { static const mVec3_c v(0.0f, 0.0f, 45.0f); mVec3_c v2; MTXMultVec(mWorldMtx, v, v2); obj->mPosition = v2; obj->mPosition.y -= obj->getLinkage().getField_0x1C(); } return SUCCEEDED; } int dAcArrow_c::draw() { if (mHiddenTimer != 0) { return SUCCEEDED; } if (checkArrowFlag(ARROW_SHOULD_DESPAWN)) { return SUCCEEDED; } if (mStateMgr.isState(StateID_Wait) && checkArrowFlag(ARROW_INITIAL_SPAWN)) { setInitialPosition(); unsetArrowFlag(ARROW_INITIAL_SPAWN); } drawModelType1(&mModel); return SUCCEEDED; }