#include "Runtime/Weapon/CAuxWeapon.hpp" #include #include "Runtime/CSimplePool.hpp" #include "Runtime/GameGlobalObjects.hpp" #include "Runtime/Weapon/CEnergyProjectile.hpp" #include "Runtime/Weapon/CNewFlameThrower.hpp" #include "Runtime/Weapon/CWaveBuster.hpp" namespace metaforce { constexpr CCameraShakeData skHardShake{ 0.3f, 100.f, 0, zeus::skZero3f, {}, { true, {false, 0.f, 0.f, 0.3f, -2.f}, {true, 0.f, 0.f, 0.05f, 0.5f}, }, {}, }; constexpr std::array skComboNames{ "SuperMissile"sv, "IceCombo"sv, "WaveBuster"sv, "FlameThrower"sv, "SuperMissile"sv, }; constexpr std::array skSoundId{ SFXsfx0712, SFXsfx072D, SFXwpn_combo_wavebuster, SFXwpn_combo_flamethrower, SFXsfx0712, }; CAuxWeapon::CAuxWeapon(TUniqueId playerId) : x0_missile(g_SimplePool->GetObj("Missile")) , xc_flameMuzzle(g_SimplePool->GetObj("FlameMuzzle")) , x18_busterMuzzle(g_SimplePool->GetObj("BusterMuzzle")) , x6c_playerId(playerId) { x0_missile.GetObj(); xc_flameMuzzle.GetObj(); x18_busterMuzzle.GetObj(); InitComboData(); } void CAuxWeapon::InitComboData() { for (const auto& comboName : skComboNames) { x28_combos.push_back(g_SimplePool->GetObj(comboName)); } } void CAuxWeapon::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CStateManager& mgr) { if (msg == EScriptObjectMessage::Deleted) { DeleteFlameThrower(mgr); DeleteWaveBusterBeam(mgr); } } bool CAuxWeapon::IsComboFxActive(const CStateManager& mgr) const { switch (x74_firingBeamId) { case CPlayerState::EBeamId::Wave: if (const CEntity* ent = mgr.GetObjectById(x70_waveBusterId)) return static_cast(ent)->IsFiring(); break; case CPlayerState::EBeamId::Plasma: if (const CEntity* ent = mgr.GetObjectById(x6e_flameThrowerId)) return static_cast(ent)->IsFiring(); break; default: break; } return false; } void CAuxWeapon::Load(CPlayerState::EBeamId curBeam, CStateManager& mgr) { x80_24_isLoaded = false; switch (x78_loadBeamId) { case CPlayerState::EBeamId::Wave: DeleteWaveBusterBeam(mgr); break; case CPlayerState::EBeamId::Plasma: DeleteFlameThrower(mgr); break; default: break; } x28_combos[int(x78_loadBeamId)].Unlock(); x28_combos[int(curBeam)].Lock(); x78_loadBeamId = curBeam; LoadIdle(); } void CAuxWeapon::StopComboFx(CStateManager& mgr, bool deactivate) { switch (x74_firingBeamId) { case CPlayerState::EBeamId::Wave: { auto* wb = static_cast(mgr.ObjectById(x70_waveBusterId)); if (wb) { wb->ResetBeam(deactivate); DeleteWaveBusterBeam(mgr); } break; } case CPlayerState::EBeamId::Plasma: { auto* ft = static_cast(mgr.ObjectById(x6e_flameThrowerId)); if (ft) { mgr.GetPlayerState()->SetFiringComboBeam(false); if (ft->IsFiring()) { ft->Reset(mgr, deactivate); FreeComboVoiceId(); } else if (ft->GetActive() && deactivate) { ft->Reset(mgr, deactivate); } } break; } default: break; } if (deactivate) { x74_firingBeamId = CPlayerState::EBeamId::Invalid; x68_ammoConsumeTimer = 0.f; } } bool CAuxWeapon::UpdateComboFx(float dt, const zeus::CVector3f& scale, const zeus::CVector3f& pos, const zeus::CTransform& xf, CStateManager& mgr) { if (!x80_24_isLoaded || x74_firingBeamId == CPlayerState::EBeamId::Invalid) return false; bool firing = false; if (x7c_comboSfx && !CSfxManager::IsPlaying(x7c_comboSfx)) FreeComboVoiceId(); switch (x74_firingBeamId) { case CPlayerState::EBeamId::Wave: case CPlayerState::EBeamId::Plasma: { bool firingFx = false; if (x74_firingBeamId == CPlayerState::EBeamId::Wave) { auto* wb = static_cast(mgr.ObjectById(x70_waveBusterId)); if (wb && wb->IsFiring()) { wb->UpdateFx(xf, dt, mgr); firing = true; firingFx = true; } else { DeleteWaveBusterBeam(mgr); mgr.GetPlayerState()->SetFiringComboBeam(false); } } else { auto* ft = static_cast(mgr.ObjectById(x6e_flameThrowerId)); bool needsDelete = true; if (ft) { firingFx = ft->CanRenderAuxEffects(); if (ft->GetActive()) { ft->UpdateFx(xf, dt, mgr); firing = ft->IsFiring(); } if (x6e_flameThrowerId != kInvalidUniqueId) needsDelete = ft->AreEffectsFinished(); } if (needsDelete) { DeleteFlameThrower(mgr); mgr.GetPlayerState()->SetFiringComboBeam(false); } } if (firingFx) { x68_ammoConsumeTimer += dt; if (mgr.GetPlayerState()->GetItemAmount(CPlayerState::EItemType::Missiles) > 0) { if (x68_ammoConsumeTimer >= mgr.GetPlayerState()->GetComboFireAmmoPeriod()) { mgr.GetPlayerState()->DecrPickup(CPlayerState::EItemType::Missiles, 1); x68_ammoConsumeTimer = 0.f; } } } if (mgr.GetPlayerState()->GetItemAmount(CPlayerState::EItemType::Missiles) == 0) StopComboFx(mgr, false); x24_muzzleFxGen->SetGlobalTranslation(pos); x24_muzzleFxGen->SetGlobalScale(scale); x24_muzzleFxGen->SetParticleEmission(firingFx); x24_muzzleFxGen->Update(dt); break; } default: break; } return firing; } void CAuxWeapon::FreeComboVoiceId() { CSfxManager::SfxStop(x7c_comboSfx); x7c_comboSfx.reset(); } void CAuxWeapon::DeleteFlameThrower(CStateManager& mgr) { FreeComboVoiceId(); if (x6e_flameThrowerId != kInvalidUniqueId) { mgr.FreeScriptObject(x6e_flameThrowerId); x6e_flameThrowerId = kInvalidUniqueId; x74_firingBeamId = CPlayerState::EBeamId::Invalid; mgr.GetPlayerState()->SetFiringComboBeam(false); } } void CAuxWeapon::CreateFlameThrower(const zeus::CTransform& xf, CStateManager& mgr, float dt) { DeleteFlameThrower(mgr); if (x6e_flameThrowerId != kInvalidUniqueId) return; const std::array resInfo{ NWeaponTypes::get_asset_id_from_name("NFTMainFire"), NWeaponTypes::get_asset_id_from_name("NFTMainSmoke"), NWeaponTypes::get_asset_id_from_name("NFTSwooshCenter"), NWeaponTypes::get_asset_id_from_name("NFTSwooshFire"), NWeaponTypes::get_asset_id_from_name("NFTSecondarySmoke"), NWeaponTypes::get_asset_id_from_name("NFTSecondaryFire"), NWeaponTypes::get_asset_id_from_name("NFTSecondarySparks"), {}, }; x6e_flameThrowerId = mgr.AllocateUniqueId(); auto* ft = new CNewFlameThrower(x28_combos[3], "Player_FlameThrower", EWeaponType::Plasma, resInfo, xf, EMaterialTypes::Player, CGunWeapon::GetShotDamageInfo(g_tweakPlayerGun->GetComboShotInfo(3), mgr), x6e_flameThrowerId, kInvalidAreaId, x6c_playerId, EProjectileAttrib::None); mgr.AddObject(ft); ft->Think(dt, mgr); ft->StartFiring(xf, mgr); x24_muzzleFxGen = std::make_unique(xc_flameMuzzle); x7c_comboSfx = NWeaponTypes::play_sfx(SFXwpn_combo_flamethrower, false, true, 0.165f); mgr.GetCameraManager()->AddCameraShaker(skHardShake, false); mgr.GetPlayerState()->SetFiringComboBeam(true); x74_firingBeamId = CPlayerState::EBeamId::Plasma; } void CAuxWeapon::DeleteWaveBusterBeam(CStateManager& mgr) { FreeComboVoiceId(); if (x70_waveBusterId != kInvalidUniqueId) { mgr.FreeScriptObject(x70_waveBusterId); x70_waveBusterId = kInvalidUniqueId; x74_firingBeamId = CPlayerState::EBeamId::Invalid; mgr.GetPlayerState()->SetFiringComboBeam(false); } } void CAuxWeapon::CreateWaveBusterBeam(EProjectileAttrib attribs, TUniqueId homingTarget, const zeus::CTransform& xf, CStateManager& mgr) { DeleteFlameThrower(mgr); if (x70_waveBusterId != kInvalidUniqueId) return; x70_waveBusterId = mgr.AllocateUniqueId(); CWaveBuster* wb = new CWaveBuster(x28_combos[2], EWeaponType::Wave, xf, EMaterialTypes::Player, CGunWeapon::GetShotDamageInfo(g_tweakPlayerGun->GetComboShotInfo(2), mgr), x70_waveBusterId, kInvalidAreaId, x6c_playerId, homingTarget, attribs); mgr.AddObject(wb); x24_muzzleFxGen = std::make_unique(x18_busterMuzzle); x7c_comboSfx = NWeaponTypes::play_sfx(SFXwpn_combo_wavebuster, false, true, 0.165f); mgr.GetCameraManager()->AddCameraShaker(CCameraShakeData::skChargedShotCameraShakeData, false); mgr.GetPlayerState()->SetFiringComboBeam(true); x74_firingBeamId = CPlayerState::EBeamId::Wave; } void CAuxWeapon::LaunchMissile(float dt, bool underwater, bool charged, CPlayerState::EBeamId currentBeam, EProjectileAttrib attrib, const zeus::CTransform& xf, TUniqueId homingId, CStateManager& mgr) { const SShotParam& info = charged ? g_tweakPlayerGun->GetComboShotInfo(int(currentBeam)) : g_tweakPlayerGun->GetMissileInfo(); u16 sfxId = charged ? skSoundId[int(currentBeam)] : u16(SFXwpn_fire_missile); CEnergyProjectile* proj = new CEnergyProjectile( true, charged ? x28_combos[int(currentBeam)] : x0_missile, charged ? EWeaponType::Power : EWeaponType::Missile, xf, EMaterialTypes::Player, CGunWeapon::GetShotDamageInfo(info, mgr), mgr.AllocateUniqueId(), kInvalidAreaId, x6c_playerId, homingId, attrib | EProjectileAttrib::ArmCannon, underwater, zeus::skOne3f, {}, -1, false); mgr.AddObject(proj); proj->Think(dt, mgr); if (charged) { proj->SetCameraShake(CCameraShakeData::BuildMissileCameraShake(0.25f, 0.75f, 50.f, proj->GetTranslation())); mgr.GetCameraManager()->AddCameraShaker(skHardShake, false); } else { mgr.GetRumbleManager().Rumble(mgr, ERumbleFxId::PlayerMissileFire, 0.5f, ERumblePriority::One); } x7c_comboSfx = NWeaponTypes::play_sfx(sfxId, underwater, false, 0.165f); } void CAuxWeapon::Fire(float dt, bool underwater, CPlayerState::EBeamId currentBeam, EChargeState chargeState, const zeus::CTransform& xf, CStateManager& mgr, EWeaponType type, TUniqueId homingId) { if (!x80_24_isLoaded) return; EProjectileAttrib attrib = EProjectileAttrib::None; if (chargeState == EChargeState::Charged) attrib = CGameProjectile::GetBeamAttribType(type) | EProjectileAttrib::ComboShot; if (chargeState == EChargeState::Normal) { LaunchMissile(dt, underwater, false, currentBeam, attrib, xf, homingId, mgr); } else { switch (currentBeam) { case CPlayerState::EBeamId::Power: case CPlayerState::EBeamId::Ice: LaunchMissile(dt, underwater, chargeState == EChargeState::Charged, currentBeam, attrib, xf, homingId, mgr); break; case CPlayerState::EBeamId::Wave: CreateWaveBusterBeam(attrib, homingId, xf, mgr); break; case CPlayerState::EBeamId::Plasma: CreateFlameThrower(xf, mgr, dt); break; default: break; } } } void CAuxWeapon::LoadIdle() { x80_24_isLoaded = x28_combos[int(x78_loadBeamId)].IsLoaded(); } void CAuxWeapon::RenderMuzzleFx() const { switch (x74_firingBeamId) { case CPlayerState::EBeamId::Wave: case CPlayerState::EBeamId::Plasma: x24_muzzleFxGen->Render(); break; default: break; } } TUniqueId CAuxWeapon::HasTarget(const CStateManager& mgr) const { if (x74_firingBeamId == CPlayerState::EBeamId::Wave) { if (const auto* wb = static_cast(mgr.GetObjectById(x70_waveBusterId))) { return wb->GetHomingTargetId(); } } return kInvalidUniqueId; } void CAuxWeapon::SetNewTarget(TUniqueId targetId, CStateManager& mgr) { if (x74_firingBeamId == CPlayerState::EBeamId::Wave) if (auto* wb = static_cast(mgr.ObjectById(x70_waveBusterId))) wb->SetNewTarget(targetId, mgr); } } // namespace metaforce