mirror of
				https://github.com/AxioDL/metaforce.git
				synced 2025-10-27 15:30:23 +00:00 
			
		
		
		
	Given that we now target C++20, we can make use of bitfield initializers where applicable.
		
			
				
	
	
		
			342 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			342 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "Runtime/Weapon/CAuxWeapon.hpp"
 | |
| 
 | |
| #include <array>
 | |
| 
 | |
| #include "Runtime/CSimplePool.hpp"
 | |
| #include "Runtime/GameGlobalObjects.hpp"
 | |
| #include "Runtime/Weapon/CEnergyProjectile.hpp"
 | |
| #include "Runtime/Weapon/CNewFlameThrower.hpp"
 | |
| #include "Runtime/Weapon/CWaveBuster.hpp"
 | |
| 
 | |
| namespace urde {
 | |
| 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", "IceCombo", "WaveBuster", "FlameThrower", "SuperMissile",
 | |
| };
 | |
| 
 | |
| constexpr std::array<u16, 5> 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<const CWaveBuster*>(ent)->IsFiring();
 | |
|     break;
 | |
|   case CPlayerState::EBeamId::Plasma:
 | |
|     if (const CEntity* ent = mgr.GetObjectById(x6e_flameThrowerId))
 | |
|       return static_cast<const CNewFlameThrower*>(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<CWaveBuster*>(mgr.ObjectById(x70_waveBusterId));
 | |
|     if (wb) {
 | |
|       wb->ResetBeam(deactivate);
 | |
|       DeleteWaveBusterBeam(mgr);
 | |
|     }
 | |
|     break;
 | |
|   }
 | |
|   case CPlayerState::EBeamId::Plasma: {
 | |
|     auto* ft = static_cast<CNewFlameThrower*>(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<CWaveBuster*>(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<CNewFlameThrower*>(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<CAssetId, 8> 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<CElementGen>(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<CElementGen>(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 (auto* wb = static_cast<const CWaveBuster*>(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<CWaveBuster*>(mgr.ObjectById(x70_waveBusterId)))
 | |
|       wb->SetNewTarget(targetId);
 | |
| }
 | |
| 
 | |
| } // namespace urde
 |