#include "Runtime/Weapon/CPlayerGun.hpp" #include "Runtime/CSimplePool.hpp" #include "Runtime/Character/CPrimitive.hpp" #include "Runtime/Camera/CGameCamera.hpp" #include "Runtime/Graphics/CCubeRenderer.hpp" #include "Runtime/Graphics/CGX.hpp" #include "Runtime/Input/ControlMapper.hpp" #include "Runtime/MP1/CSamusHud.hpp" #include "Runtime/MP1/World/CMetroid.hpp" #include "Runtime/MP1/World/CMetroidBeta.hpp" #include "Runtime/World/CGameLight.hpp" #include "Runtime/World/CScriptPlatform.hpp" #include "Runtime/World/CScriptWater.hpp" #include "Runtime/Weapon/CBomb.hpp" #include "Runtime/Weapon/CEnergyProjectile.hpp" #include "Runtime/Weapon/CPowerBomb.hpp" namespace metaforce { namespace { std::array kVerticalAngleTable{-30.f, 0.f, 30.f}; std::array kHorizontalAngleTable{30.f, 30.f, 30.f}; std::array kVerticalVarianceTable{30.f, 30.f, 30.f}; constexpr zeus::CVector3f sGunScale(2.f); constexpr std::array skBeamAnimIds{ 0, 1, 2, 1, }; constexpr std::array skBeamArr{ CPlayerState::EItemType::PowerBeam, CPlayerState::EItemType::IceBeam, CPlayerState::EItemType::WaveBeam, CPlayerState::EItemType::PlasmaBeam, }; constexpr std::array skBeamComboArr{ CPlayerState::EItemType::SuperMissile, CPlayerState::EItemType::IceSpreader, CPlayerState::EItemType::Wavebuster, CPlayerState::EItemType::Flamethrower, }; constexpr std::array mBeamCtrlCmd{ ControlMapper::ECommands::PowerBeam, ControlMapper::ECommands::IceBeam, ControlMapper::ECommands::WaveBeam, ControlMapper::ECommands::PlasmaBeam, }; constexpr std::array skFromMissileSound{ SFXwpn_from_missile_power, SFXwpn_from_missile_ice, SFXwpn_from_missile_wave, SFXwpn_from_missile_plasma, }; constexpr std::array skFromBeamSound{ SFXsfx0000, SFXwpn_from_beam_ice, SFXwpn_from_beam_wave, SFXwpn_from_beam_plasma, }; constexpr std::array skToMissileSound{ SFXwpn_to_missile_power, SFXwpn_to_missile_ice, SFXwpn_to_missile_wave, SFXwpn_to_missile_plasma, }; constexpr std::array skIntoBeamSound{ SFXsfx0000, SFXwpn_into_beam_ice, SFXwpn_into_beam_wave, SFXwpn_into_beam_plasma, }; constexpr float kChargeSpeed = 1.f / CPlayerState::GetMissileComboChargeFactor(); constexpr float kChargeFxStart = 1.f / CPlayerState::GetMissileComboChargeFactor(); constexpr float kChargeAnimStart = 0.25f / CPlayerState::GetMissileComboChargeFactor(); constexpr float kChargeStart = 0.025f / CPlayerState::GetMissileComboChargeFactor(); constexpr std::array skBeamChargeUpSound{ SFXwpn_chargeup_power, SFXwpn_chargeup_ice, SFXwpn_chargeup_wave, SFXwpn_chargeup_plasma, }; constexpr std::array skItemArr{ CPlayerState::EItemType::Invalid, CPlayerState::EItemType::Missiles, }; constexpr std::array skItemEmptySound{ SFXsfx0000, SFXwpn_empty_action, }; constexpr std::array chargeShakeTbl{ -0.001f, 0.f, 0.001f, }; constexpr CMaterialFilter sAimFilter = CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {EMaterialTypes::ProjectilePassthrough}); constexpr std::array kThermalFlags{{ {0, 0, 3, zeus::skWhite}, {5, 0, 3, zeus::CColor(0.f, 0.5f)}, {0, 0, 3, zeus::skWhite}, {0, 0, 3, zeus::skWhite}, }}; constexpr CModelFlags kHandThermalFlag{7, 0, 3, zeus::skWhite}; constexpr CModelFlags kHandHoloFlag{1, 0, 3, zeus::CColor(0.75f, 0.5f, 0.f, 1.f)}; } // Anonymous namespace float CPlayerGun::CMotionState::gGunExtendDistance = 0.125f; float CPlayerGun::skTractorBeamFactor = 0.5f / CPlayerState::GetMissileComboChargeFactor(); CPlayerGun::CPlayerGun(TUniqueId playerId) : x0_lights(8, zeus::skZero3f, 4, 4, false, false, false, 0.1f) , x538_playerId(playerId) , x550_camBob(CPlayerCameraBob::ECameraBobType::One, CPlayerCameraBob::GetCameraBobExtent(), CPlayerCameraBob::GetCameraBobPeriod()) , x678_morph(g_tweakPlayerGun->GetGunTransformTime(), g_tweakPlayerGun->GetHoloHoldTime()) , x6c8_hologramClipCube(zeus::CVector3f(-0.29329199f, 0.f, -0.2481945f), zeus::CVector3f(0.29329199f, 1.292392f, 0.2481945f)) , x6e0_rightHandModel(CAnimRes(g_tweakGunRes->xc_rightHand, 0, zeus::CVector3f(3.f), 0, true)) { x354_bombFuseTime = g_tweakPlayerGun->GetBombFuseTime(); x358_bombDropDelayTime = g_tweakPlayerGun->GetBombDropDelayTime(); x668_aimVerticalSpeed = g_tweakPlayerGun->GetAimVerticalSpeed(); x66c_aimHorizontalSpeed = g_tweakPlayerGun->GetAimHorizontalSpeed(); x73c_gunMotion = std::make_unique(g_tweakGunRes->x4_gunMotion, sGunScale); x740_grappleArm = std::make_unique(sGunScale); x744_auxWeapon = std::make_unique(playerId); x748_rainSplashGenerator = std::make_unique(sGunScale, 20, 2, 0.f, 0.125f); x74c_powerBeam = std::make_unique(g_tweakGunRes->x10_powerBeam, EWeaponType::Power, playerId, EMaterialTypes::Player, sGunScale); x750_iceBeam = std::make_unique(g_tweakGunRes->x14_iceBeam, EWeaponType::Ice, playerId, EMaterialTypes::Player, sGunScale); x754_waveBeam = std::make_unique(g_tweakGunRes->x18_waveBeam, EWeaponType::Wave, playerId, EMaterialTypes::Player, sGunScale); x758_plasmaBeam = std::make_unique(g_tweakGunRes->x1c_plasmaBeam, EWeaponType::Plasma, playerId, EMaterialTypes::Player, sGunScale); x75c_phazonBeam = std::make_unique(g_tweakGunRes->x20_phazonBeam, EWeaponType::Phazon, playerId, EMaterialTypes::Player, sGunScale); x774_holoTransitionGen = std::make_unique( g_SimplePool->GetObj(SObjectTag{FOURCC('PART'), g_tweakGunRes->x24_holoTransition})); x82c_shadow = std::make_unique(256, 256, true); x6e0_rightHandModel.SetSortThermal(true); kVerticalAngleTable[2] = g_tweakPlayerGun->GetUpLookAngle(); kVerticalAngleTable[0] = g_tweakPlayerGun->GetDownLookAngle(); kHorizontalAngleTable[1] = g_tweakPlayerGun->GetHorizontalSpread(); kHorizontalAngleTable[2] = g_tweakPlayerGun->GetHighHorizontalSpread(); kHorizontalAngleTable[0] = g_tweakPlayerGun->GetLowHorizontalSpread(); kVerticalVarianceTable[1] = g_tweakPlayerGun->GetVerticalSpread(); kVerticalVarianceTable[2] = g_tweakPlayerGun->GetHighVerticalSpread(); kVerticalVarianceTable[0] = g_tweakPlayerGun->GetLowVerticalSpread(); CMotionState::SetExtendDistance(g_tweakPlayerGun->GetGunExtendDistance()); InitBeamData(); InitBombData(); InitMuzzleData(); InitCTData(); LoadHandAnimTokens(); x550_camBob.SetPlayerVelocity(zeus::skZero3f); x550_camBob.SetBobMagnitude(0.f); x550_camBob.SetBobTimeScale(0.f); } void CPlayerGun::InitBeamData() { x760_selectableBeams[0] = x74c_powerBeam.get(); x760_selectableBeams[1] = x750_iceBeam.get(); x760_selectableBeams[2] = x754_waveBeam.get(); x760_selectableBeams[3] = x758_plasmaBeam.get(); x72c_currentBeam = x760_selectableBeams[0]; x738_nextBeam = x72c_currentBeam; x774_holoTransitionGen->SetParticleEmission(true); } void CPlayerGun::InitBombData() { x784_bombEffects.resize(2); x784_bombEffects[0].push_back(g_SimplePool->GetObj(SObjectTag{FOURCC('PART'), g_tweakGunRes->x28_bombSet})); x784_bombEffects[0].push_back(g_SimplePool->GetObj(SObjectTag{FOURCC('PART'), g_tweakGunRes->x2c_bombExplode})); TLockedToken pbExplode = g_SimplePool->GetObj(SObjectTag{FOURCC('PART'), g_tweakGunRes->x30_powerBombExplode}); x784_bombEffects[1].push_back(pbExplode); x784_bombEffects[1].push_back(pbExplode); } void CPlayerGun::InitMuzzleData() { for (const auto& muzzleID : g_tweakGunRes->xa4_auxMuzzle) { x7c0_auxMuzzleEffects.push_back(g_SimplePool->GetObj(SObjectTag{FOURCC('PART'), muzzleID})); x800_auxMuzzleGenerators.emplace_back(std::make_unique(x7c0_auxMuzzleEffects.back())); x800_auxMuzzleGenerators.back()->SetParticleEmission(false); } } void CPlayerGun::InitCTData() { x77c_comboXferGen.reset(); } void CPlayerGun::LoadHandAnimTokens() { std::set prims; for (int i = 0; i < 3; ++i) { CAnimPlaybackParms parms(i, -1, 1.f, true); x6e0_rightHandModel.GetAnimationData()->GetAnimationPrimitives(parms, prims); } CAnimData::PrimitiveSetToTokenVector(prims, x540_handAnimTokens, true); } void CPlayerGun::TakeDamage(bool bigStrike, bool notFromMetroid, CStateManager& mgr) { bool hasStrikeAngle = false; float angle = 0.f; if (x398_damageAmt >= 10.f && !bigStrike && (x2f8_stateFlags & 0x10) != 0x10 && !x832_26_comboFiring && x384_gunStrikeDelayTimer <= 0.f) { x384_gunStrikeDelayTimer = 20.f; x364_gunStrikeCoolTimer = 0.75f; if (x678_morph.GetGunState() == CGunMorph::EGunState::OutWipeDone) { zeus::CVector3f localDamageLoc = mgr.GetPlayer().GetTransform().transposeRotate(x3dc_damageLocation); angle = zeus::CRelAngle(std::atan2(localDamageLoc.y(), localDamageLoc.x())).asRel().asDegrees(); hasStrikeAngle = true; } } if (hasStrikeAngle || bigStrike) { if (mgr.GetPlayerState()->GetCurrentVisor() != CPlayerState::EPlayerVisor::Scan) { x73c_gunMotion->PlayPasAnim(SamusGun::EAnimationState::Struck, mgr, angle, bigStrike); if ((bigStrike && notFromMetroid) || x833_31_inFreeLook) x740_grappleArm->EnterStruck(mgr, angle, bigStrike, !x833_31_inFreeLook); } } x398_damageAmt = 0.f; x3dc_damageLocation = zeus::skZero3f; } void CPlayerGun::CreateGunLight(CStateManager& mgr) { if (x53c_lightId != kInvalidUniqueId) return; x53c_lightId = mgr.AllocateUniqueId(); CGameLight* light = new CGameLight(x53c_lightId, kInvalidAreaId, false, "GunLite", x3e8_xf, x538_playerId, CLight::BuildDirectional(zeus::skForward, zeus::skBlack), x53c_lightId.Value(), 0, 0.f); mgr.AddObject(light); } void CPlayerGun::DeleteGunLight(CStateManager& mgr) { if (x53c_lightId == kInvalidUniqueId) return; mgr.FreeScriptObject(x53c_lightId); x53c_lightId = kInvalidUniqueId; } void CPlayerGun::UpdateGunLight(const zeus::CTransform& xf, CStateManager& mgr) { if (x53c_lightId == kInvalidUniqueId || x32c_chargePhase == EChargePhase::NotCharging) return; if (TCastToPtr light = mgr.ObjectById(x53c_lightId)) { if (light->GetActive()) { CElementGen* chargeFx = x72c_currentBeam->GetChargeMuzzleFx(); light->SetTransform(xf); light->SetTranslation(xf.origin); if (chargeFx && chargeFx->SystemHasLight()) { CLight l = chargeFx->GetLight(); l.SetColor(zeus::CColor::lerp(zeus::skClear, l.GetColor(), x340_chargeBeamFactor)); light->SetLight(l); } } } } void CPlayerGun::SetGunLightActive(bool active, CStateManager& mgr) { if (x53c_lightId == kInvalidUniqueId) return; if (TCastToPtr light = mgr.ObjectById(x53c_lightId)) { light->SetActive(active); if (active) { if (CElementGen* gen = x72c_currentBeam->GetChargeMuzzleFx()) { if (gen->SystemHasLight()) { CLight genLight = gen->GetLight(); genLight.SetColor(zeus::skBlack); light->SetLight(genLight); } } } } } void CPlayerGun::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CStateManager& mgr) { const CPlayer& player = mgr.GetPlayer(); bool isUnmorphed = player.GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Unmorphed; switch (msg) { case EScriptObjectMessage::Registered: { CreateGunLight(mgr); x320_currentAuxBeam = x314_nextBeam = x310_currentBeam = mgr.GetPlayerState()->GetCurrentBeam(); x72c_currentBeam = x738_nextBeam = x760_selectableBeams[size_t(x310_currentBeam)]; x72c_currentBeam->Load(mgr, true); x72c_currentBeam->SetRainSplashGenerator(x748_rainSplashGenerator.get()); x744_auxWeapon->Load(x310_currentBeam, mgr); const CAnimPlaybackParms parms(skBeamAnimIds[size_t(mgr.GetPlayerState()->GetCurrentBeam())], -1, 1.f, true); x6e0_rightHandModel.GetAnimationData()->SetAnimation(parms, false); break; } case EScriptObjectMessage::Deleted: DeleteGunLight(mgr); break; case EScriptObjectMessage::UpdateSplashInhabitant: if (mgr.GetPlayerState()->HasPowerUp(CPlayerState::EItemType::PhazonSuit) && isUnmorphed) { if (TCastToConstPtr water = mgr.GetObjectById(sender)) { if (water->GetFluidPlane().GetFluidType() == EFluidType::PhazonFluid) { x835_24_canFirePhazon = true; x835_25_inPhazonBeam = true; } } } if (player.GetDistanceUnderWater() > player.GetEyeHeight()) { x834_27_underwater = true; if (x744_auxWeapon->IsComboFxActive(mgr) && x310_currentBeam != CPlayerState::EBeamId::Wave) StopContinuousBeam(mgr, false); } else { x834_27_underwater = false; } break; case EScriptObjectMessage::RemoveSplashInhabitant: x834_27_underwater = false; x835_24_canFirePhazon = false; break; case EScriptObjectMessage::AddPhazonPoolInhabitant: x835_30_inPhazonPool = true; if (mgr.GetPlayerState()->HasPowerUp(CPlayerState::EItemType::PhazonSuit) && isUnmorphed) x835_24_canFirePhazon = true; break; case EScriptObjectMessage::UpdatePhazonPoolInhabitant: x835_30_inPhazonPool = true; if (mgr.GetPlayerState()->HasPowerUp(CPlayerState::EItemType::PhazonSuit) && isUnmorphed) { x835_24_canFirePhazon = true; x835_25_inPhazonBeam = true; if (x833_28_phazonBeamActive && static_cast(x72c_currentBeam)->IsFiring()) { if (TCastToPtr ent = mgr.ObjectById(sender)) { mgr.SendScriptMsg(ent.GetPtr(), x538_playerId, EScriptObjectMessage::Decrement); } } } break; case EScriptObjectMessage::RemovePhazonPoolInhabitant: x835_30_inPhazonPool = false; x835_24_canFirePhazon = false; break; case EScriptObjectMessage::Damage: { bool bigStrike = false; bool metroidAttached = false; if (TCastToConstPtr proj = mgr.GetObjectById(sender)) { if ((proj->GetAttribField() & EProjectileAttrib::BigStrike) == EProjectileAttrib::BigStrike) { x394_damageTimer = proj->GetDamageDuration(); bigStrike = true; } } else if (TCastToConstPtr ai = mgr.GetObjectById(sender)) { if (ai->IsMakingBigStrike()) { x394_damageTimer = ai->GetDamageDuration(); bigStrike = true; if (player.GetAttachedActor() != kInvalidUniqueId) { metroidAttached = CPatterned::CastTo(mgr.GetObjectById(player.GetAttachedActor())) != nullptr; } } } if (!x834_30_inBigStrike) { if (bigStrike) { x834_31_gunMotionInFidgetBasePosition = false; CancelFiring(mgr); } TakeDamage(bigStrike, !metroidAttached, mgr); x834_30_inBigStrike = bigStrike; } break; } case EScriptObjectMessage::OnFloor: if (player.GetControlsFrozen() && !x834_30_inBigStrike) { x2f4_fireButtonStates = 0; x2ec_lastFireButtonStates = 0; CancelFiring(mgr); TakeDamage(true, false, mgr); x394_damageTimer = 0.75f; x834_30_inBigStrike = true; } break; default: break; } x740_grappleArm->AcceptScriptMsg(msg, sender, mgr); x758_plasmaBeam->AcceptScriptMsg(msg, sender, mgr); x75c_phazonBeam->AcceptScriptMsg(msg, sender, mgr); x744_auxWeapon->AcceptScriptMsg(msg, sender, mgr); } void CPlayerGun::AsyncLoadSuit(CStateManager& mgr) { x72c_currentBeam->AsyncLoadSuitArm(mgr); x740_grappleArm->AsyncLoadSuit(mgr); } void CPlayerGun::TouchModel(const CStateManager& mgr) { if (mgr.GetPlayer().GetMorphballTransitionState() != CPlayer::EPlayerMorphBallState::Morphed) { x73c_gunMotion->GetModelData().Touch(mgr, 0); switch (x33c_phazonBeamState) { case EPhazonBeamState::Entering: if (x75c_phazonBeam) x75c_phazonBeam->Touch(mgr); break; case EPhazonBeamState::Exiting: if (x738_nextBeam) x738_nextBeam->Touch(mgr); break; default: if (!x833_28_phazonBeamActive) x72c_currentBeam->Touch(mgr); else x75c_phazonBeam->Touch(mgr); break; } x72c_currentBeam->TouchHolo(mgr); x740_grappleArm->TouchModel(mgr); x6e0_rightHandModel.Touch(mgr, 0); } if (x734_loadingBeam) { x734_loadingBeam->Touch(mgr); x734_loadingBeam->TouchHolo(mgr); } } void CPlayerGun::DamageRumble(const zeus::CVector3f& location, float damage, const CStateManager& mgr) { x398_damageAmt = damage; x3dc_damageLocation = location; } void CPlayerGun::StopChargeSound(CStateManager& mgr) { if (x2e0_chargeSfx) { CSfxManager::SfxStop(x2e0_chargeSfx); x2e0_chargeSfx.reset(); } if (x830_chargeRumbleHandle != -1) { mgr.GetRumbleManager().StopRumble(x830_chargeRumbleHandle); x830_chargeRumbleHandle = -1; } } void CPlayerGun::ResetCharge(CStateManager& mgr, bool resetBeam) { if (x32c_chargePhase != EChargePhase::NotCharging) StopChargeSound(mgr); if ((x2f8_stateFlags & 0x8) != 0x8 && (x2f8_stateFlags & 0x10) != 0x10) { bool doResetBeam = mgr.GetPlayer().GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Morphed || resetBeam; if (x832_27_chargeAnimStarted || doResetBeam) PlayAnim(NWeaponTypes::EGunAnimType::BasePosition, false); if (doResetBeam) x72c_currentBeam->EnableSecondaryFx(CGunWeapon::ESecondaryFxType::None); if ((x2f8_stateFlags & 0x2) != 0x2 || x330_chargeState != EChargeState::Normal) { if ((x2f8_stateFlags & 0x8) != 0x8) { x2f8_stateFlags |= 0x1; x2f8_stateFlags &= 0xFFE9; } x318_comboAmmoIdx = 0; x31c_missileMode = EMissileMode::Inactive; } } x32c_chargePhase = EChargePhase::NotCharging; x330_chargeState = EChargeState::Normal; x320_currentAuxBeam = x310_currentBeam; x833_30_canShowAuxMuzzleEffect = true; x832_27_chargeAnimStarted = false; x832_26_comboFiring = false; x344_comboXferTimer = 0.f; } bool CPlayerGun::ExitMissile() { if ((x2f8_stateFlags & 0x1) == 0x1) return true; if ((x2f8_stateFlags & 0x10) == 0x10 || x338_nextState == ENextState::ExitMissile) return false; x338_nextState = ENextState::ExitMissile; PlayAnim(NWeaponTypes::EGunAnimType::FromMissile, false); return false; } void CPlayerGun::HandleBeamChange(const CFinalInput& input, CStateManager& mgr) { const CPlayerState& playerState = *mgr.GetPlayerState(); float maxBeamInput = 0.f; CPlayerState::EBeamId selectBeam = CPlayerState::EBeamId::Invalid; for (size_t i = 0; i < skBeamArr.size(); ++i) { if (playerState.HasPowerUp(skBeamArr[i])) { const float inputVal = ControlMapper::GetAnalogInput(mBeamCtrlCmd[i], input); if (inputVal > 0.65f && inputVal > maxBeamInput) { maxBeamInput = inputVal; selectBeam = CPlayerState::EBeamId(i); } } } if (selectBeam == CPlayerState::EBeamId::Invalid) return; x833_25_ = true; if (x310_currentBeam != selectBeam && playerState.HasPowerUp(skBeamArr[size_t(selectBeam)])) { x314_nextBeam = selectBeam; u32 flags = 0; if ((x2f8_stateFlags & 0x10) == 0x10) flags = 0x10; flags |= 0x8; x2f8_stateFlags = flags; PlayAnim(NWeaponTypes::EGunAnimType::FromBeam, false); if (x833_31_inFreeLook || x744_auxWeapon->IsComboFxActive(mgr) || x832_26_comboFiring) { x832_30_requestReturnToDefault = true; x740_grappleArm->EnterIdle(mgr); } x72c_currentBeam->EnableSecondaryFx(CGunWeapon::ESecondaryFxType::None); x338_nextState = ENextState::ChangeWeapon; x2e4_invalidSfx.reset(); } else if (playerState.HasPowerUp(skBeamArr[size_t(selectBeam)])) { if (ExitMissile()) { if (!CSfxManager::IsPlaying(x2e4_invalidSfx)) x2e4_invalidSfx = NWeaponTypes::play_sfx(SFXwpn_empty_action, x834_27_underwater, false, 0.165f); } else { x2e4_invalidSfx.reset(); } } } void CPlayerGun::SetPhazonBeamMorph(bool intoPhazonBeam) { x39c_phazonMorphT = intoPhazonBeam ? 0.f : 1.f; x835_27_intoPhazonBeam = intoPhazonBeam; x835_26_phazonBeamMorphing = true; } void CPlayerGun::Reset(CStateManager& mgr, bool b1) { x72c_currentBeam->Reset(mgr); x832_25_chargeEffectVisible = false; x832_24_coolingCharge = false; x833_26_ = false; x348_chargeCooldownTimer = 0.f; SetGunLightActive(false, mgr); if ((x2f8_stateFlags & 0x10) != 0x10) { if (!b1 && (x2f8_stateFlags & 0x2) != 0x2) { if ((x2f8_stateFlags & 0x8) != 0x8) { x2f8_stateFlags |= 0x1; x2f8_stateFlags &= 0xFFE9; } x318_comboAmmoIdx = 0; x31c_missileMode = EMissileMode::Inactive; } } else { x2f8_stateFlags &= ~0x7; } } void CPlayerGun::ResetBeamParams(CStateManager& mgr, const CPlayerState& playerState, bool playSelectionSfx) { StopContinuousBeam(mgr, true); if (playerState.ItemEnabled(CPlayerState::EItemType::ChargeBeam)) { ResetCharge(mgr, false); } const CAnimPlaybackParms parms(skBeamAnimIds[size_t(x314_nextBeam)], -1, 1.f, true); x6e0_rightHandModel.GetAnimationData()->SetAnimation(parms, false); Reset(mgr, false); if (playSelectionSfx) { CSfxManager::SfxStart(SFXwpn_morph_out_wipe, 1.f, 0.f, true, 0x7f, false, kInvalidAreaId); } x2ec_lastFireButtonStates &= ~0x1; x320_currentAuxBeam = x310_currentBeam; x833_30_canShowAuxMuzzleEffect = true; } void CPlayerGun::PlayAnim(NWeaponTypes::EGunAnimType type, bool loop) { if (x338_nextState != ENextState::ChangeWeapon) x72c_currentBeam->PlayAnim(type, loop); u16 sfx = 0xffff; switch (type) { case NWeaponTypes::EGunAnimType::FromMissile: x2f8_stateFlags &= ~0x4; sfx = skFromMissileSound[size_t(x310_currentBeam)]; break; case NWeaponTypes::EGunAnimType::MissileReload: sfx = SFXwpn_reload_missile; break; case NWeaponTypes::EGunAnimType::FromBeam: sfx = skFromBeamSound[size_t(x310_currentBeam)]; break; case NWeaponTypes::EGunAnimType::ToMissile: x2f8_stateFlags &= ~0x1; sfx = skToMissileSound[size_t(x310_currentBeam)]; break; default: break; } if (sfx != 0xffff) NWeaponTypes::play_sfx(sfx, x834_27_underwater, false, 0.165f); } void CPlayerGun::CancelCharge(CStateManager& mgr, bool withEffect) { if (withEffect) { x32c_chargePhase = EChargePhase::ChargeCooldown; x72c_currentBeam->EnableSecondaryFx(CGunWeapon::ESecondaryFxType::CancelCharge); } else { x72c_currentBeam->EnableSecondaryFx(CGunWeapon::ESecondaryFxType::None); } x834_24_charging = false; x348_chargeCooldownTimer = 0.f; x72c_currentBeam->ActivateCharge(false, false); SetGunLightActive(false, mgr); } void CPlayerGun::HandlePhazonBeamChange(CStateManager& mgr) { bool inMorph = false; switch (x33c_phazonBeamState) { case EPhazonBeamState::Inactive: SetPhazonBeamMorph(true); x338_nextState = ENextState::EnterPhazonBeam; inMorph = true; break; case EPhazonBeamState::Active: if (!x835_24_canFirePhazon) { SetPhazonBeamMorph(true); x338_nextState = ENextState::ExitPhazonBeam; inMorph = true; if (x75c_phazonBeam) { x75c_phazonBeam->SetClipWipeActive(false); x75c_phazonBeam->SetVeinsAlphaActive(true); } } break; default: break; } if (inMorph) { ResetBeamParams(mgr, *mgr.GetPlayerState(), true); x2f8_stateFlags = 0x8; PlayAnim(NWeaponTypes::EGunAnimType::FromBeam, false); if (x833_31_inFreeLook) { x832_30_requestReturnToDefault = true; x740_grappleArm->EnterIdle(mgr); } CancelCharge(mgr, false); } } void CPlayerGun::HandleWeaponChange(const CFinalInput& input, CStateManager& mgr) { x833_25_ = false; if (ControlMapper::GetPressInput(ControlMapper::ECommands::Morph, input)) { StopContinuousBeam(mgr, true); } if ((x2f8_stateFlags & 0x8) != 0x8) { if (!x835_25_inPhazonBeam) { HandleBeamChange(input, mgr); } else { HandlePhazonBeamChange(mgr); } } } void CPlayerGun::ProcessInput(const CFinalInput& input, CStateManager& mgr) { CPlayerState& state = *mgr.GetPlayerState(); bool damageNotMorphed = (x834_30_inBigStrike && mgr.GetPlayer().GetMorphballTransitionState() != CPlayer::EPlayerMorphBallState::Morphed); if (x832_24_coolingCharge || damageNotMorphed || (x2f8_stateFlags & 0x8) == 0x8) { return; } if (state.HasPowerUp(CPlayerState::EItemType::ChargeBeam)) { if (!state.ItemEnabled(CPlayerState::EItemType::ChargeBeam)) state.EnableItem(CPlayerState::EItemType::ChargeBeam); } else if (state.ItemEnabled(CPlayerState::EItemType::ChargeBeam)) { state.DisableItem(CPlayerState::EItemType::ChargeBeam); ResetCharge(mgr, false); } switch (mgr.GetPlayer().GetMorphballTransitionState()) { default: x2f4_fireButtonStates = 0; break; case CPlayer::EPlayerMorphBallState::Unmorphed: if ((x2f8_stateFlags & 0x10) != 0x10) HandleWeaponChange(input, mgr); [[fallthrough]]; case CPlayer::EPlayerMorphBallState::Morphed: x2f4_fireButtonStates = ControlMapper::GetDigitalInput(ControlMapper::ECommands::FireOrBomb, input) ? 1 : 0; x2f4_fireButtonStates |= ControlMapper::GetDigitalInput(ControlMapper::ECommands::MissileOrPowerBomb, input) ? 2 : 0; break; } } void CPlayerGun::UnLoadFidget() { if ((x2fc_fidgetAnimBits & 0x1) == 0x1) x73c_gunMotion->GunController().UnLoadFidget(); if ((x2fc_fidgetAnimBits & 0x2) == 0x2) x72c_currentBeam->UnLoadFidget(); if ((x2fc_fidgetAnimBits & 0x4) == 0x4) if (CGunController* gc = x740_grappleArm->GunController()) gc->UnLoadFidget(); x2fc_fidgetAnimBits = 0; } void CPlayerGun::ReturnArmAndGunToDefault(CStateManager& mgr, bool returnToDefault) { if (returnToDefault || !x833_31_inFreeLook) { x73c_gunMotion->ReturnToDefault(mgr, false); x740_grappleArm->ReturnToDefault(mgr, 0.f, false); } if (!x834_25_gunMotionFidgeting) x72c_currentBeam->ReturnToDefault(mgr); x834_25_gunMotionFidgeting = false; } void CPlayerGun::ReturnToRestPose() { if (x832_31_inRestPose) return; if ((x2f8_stateFlags & 0x1) == 0x1) PlayAnim(NWeaponTypes::EGunAnimType::BasePosition, false); else if ((x2f8_stateFlags & 0x4) == 0x4) PlayAnim(NWeaponTypes::EGunAnimType::ToMissile, false); x832_31_inRestPose = true; } void CPlayerGun::ResetIdle(CStateManager& mgr) { x550_camBob.SetState(CPlayerCameraBob::ECameraBobState::GunFireNoBob, mgr); if (x3a4_fidget.GetState() != CFidget::EState::NoFidget) { if (x3a4_fidget.GetState() == CFidget::EState::Loading) UnLoadFidget(); ReturnArmAndGunToDefault(mgr, true); } x3a4_fidget.ResetAll(); ReturnToRestPose(); if (x324_idleState != EIdleState::NotIdle) x324_idleState = EIdleState::NotIdle; if (!x740_grappleArm->GetActive()) x834_26_animPlaying = false; } void CPlayerGun::CancelFiring(CStateManager& mgr) { if (x32c_chargePhase == EChargePhase::ComboFireDone) ReturnArmAndGunToDefault(mgr, true); if ((x2f8_stateFlags & 0x10) == 0x10) { StopContinuousBeam(mgr, true); if ((x2f8_stateFlags & 0x8) == 0x8) { x2f8_stateFlags |= 0x1; x2f8_stateFlags &= 0xFFE9; } x318_comboAmmoIdx = 0; x31c_missileMode = EMissileMode::Inactive; } if (x32c_chargePhase != EChargePhase::NotCharging) { x72c_currentBeam->ActivateCharge(false, false); SetGunLightActive(false, mgr); ResetCharge(mgr, true); } Reset(mgr, (x2f8_stateFlags & 0x2) == 0x2); } float CPlayerGun::GetBeamVelocity() const { if (x72c_currentBeam->IsLoaded()) return x72c_currentBeam->GetVelocityInfo().GetVelocity(int(x330_chargeState)).y(); return 10.f; } void CPlayerGun::StopContinuousBeam(CStateManager& mgr, bool b1) { if ((x2f8_stateFlags & 0x10) == 0x10) { ReturnArmAndGunToDefault(mgr, false); x744_auxWeapon->StopComboFx(mgr, b1); switch (x310_currentBeam) { case CPlayerState::EBeamId::Power: case CPlayerState::EBeamId::Wave: case CPlayerState::EBeamId::Plasma: // All except ice if (x310_currentBeam != CPlayerState::EBeamId::Power || x833_28_phazonBeamActive) { x72c_currentBeam->EnableSecondaryFx(b1 ? CGunWeapon::ESecondaryFxType::None : CGunWeapon::ESecondaryFxType::CancelCharge); } break; default: break; } } else if (x833_28_phazonBeamActive) { if (static_cast(x72c_currentBeam)->IsFiring()) static_cast(x72c_currentBeam)->StopBeam(mgr, b1); } else if (x310_currentBeam == CPlayerState::EBeamId::Plasma) { if (static_cast(x72c_currentBeam)->IsFiring()) static_cast(x72c_currentBeam)->StopBeam(mgr, b1); } } void CPlayerGun::CMotionState::Update(bool firing, float dt, zeus::CTransform& xf, CStateManager& mgr) { if (firing) { x24_fireState = EFireState::StartFire; x8_fireTime = 0.f; } else if (x24_fireState != EFireState::NotFiring) { if (x8_fireTime > dt) x24_fireState = EFireState::Firing; x8_fireTime += dt; } if (x0_24_extendParabola && x20_state == EMotionState::LockOn) { float extendT = xc_curExtendDist / gGunExtendDistance; xf = xf * zeus::CTransform(zeus::CMatrix3f::RotateZ(zeus::degToRad(extendT * -4.f * (extendT - 1.f) * 15.f)), {0.f, xc_curExtendDist, 0.f}); } else if (x24_fireState == EFireState::StartFire || x24_fireState == EFireState::Firing) { if (std::fabs(x14_rotationT - 1.f) < 0.1f) { x18_startRotation = x1c_endRotation; x14_rotationT = 0.f; if (x24_fireState == EFireState::StartFire) { x1c_endRotation = mgr.GetActiveRandom()->Next() % 15; x1c_endRotation *= (mgr.GetActiveRandom()->Next() % 100) > 45 ? 1.f : -1.f; } else { x1c_endRotation = 0.f; if (x18_startRotation == x1c_endRotation) { x10_curRotation = x1c_endRotation; x24_fireState = EFireState::NotFiring; } } } else { x10_curRotation = (x1c_endRotation - x18_startRotation) * x14_rotationT + x18_startRotation; } x14_rotationT += (1.f - x14_rotationT) * 0.8f * (10.f * dt); zeus::CTransform tmpXf = zeus::CQuaternion::fromAxisAngle(xf.frontVector(), zeus::degToRad(x10_curRotation)).toTransform() * xf.getRotation(); tmpXf.origin = xf.origin; xf = tmpXf * zeus::CTransform::Translate(0.f, xc_curExtendDist, 0.f); } else { xf = xf * zeus::CTransform::Translate(0.f, xc_curExtendDist, 0.f); } switch (x20_state) { case EMotionState::LockOn: xc_curExtendDist += 3.f * dt; if (xc_curExtendDist > gGunExtendDistance) { xc_curExtendDist = gGunExtendDistance; x20_state = EMotionState::One; x0_24_extendParabola = false; } break; case EMotionState::CancelLockOn: xc_curExtendDist -= 3.f * dt; if (xc_curExtendDist < 0.f) { xc_curExtendDist = 0.f; x20_state = EMotionState::Zero; } break; default: break; } if (!x0_24_extendParabola) { if (x4_extendParabolaDelayTimer < 30.f) { x4_extendParabolaDelayTimer += dt; } else { x0_24_extendParabola = true; x4_extendParabolaDelayTimer = 0.f; } } } void CPlayerGun::ChangeWeapon(const CPlayerState& playerState, CStateManager& mgr) { if (x730_outgoingBeam != nullptr && x72c_currentBeam != x730_outgoingBeam) x730_outgoingBeam->Unload(mgr); x734_loadingBeam = x760_selectableBeams[size_t(x314_nextBeam)]; if (x734_loadingBeam && x72c_currentBeam != x734_loadingBeam) { x734_loadingBeam->Load(mgr, false); x744_auxWeapon->Load(x314_nextBeam, mgr); } x72c_currentBeam->EnableFx(false); x834_28_requestImmediateRecharge = x32c_chargePhase != EChargePhase::NotCharging; ResetBeamParams(mgr, playerState, true); x678_morph.StartWipe(CGunMorph::EDir::In); } void CPlayerGun::GetLctrWithShake(zeus::CTransform& xfOut, const CModelData& mData, std::string_view lctrName, bool shake, bool dyn) const { if (dyn) xfOut = mData.GetScaledLocatorTransformDynamic(lctrName, nullptr); else xfOut = mData.GetScaledLocatorTransform(lctrName); if (x834_24_charging && shake) xfOut.origin += zeus::CVector3f(x34c_shakeX, 0.f, x350_shakeZ); } void CPlayerGun::UpdateLeftArmTransform(const CModelData& mData, const CStateManager& mgr) { if (x834_26_animPlaying) x740_grappleArm->AuxTransform() = zeus::CTransform(); else GetLctrWithShake(x740_grappleArm->AuxTransform(), mData, "elbow", true, false); x740_grappleArm->AuxTransform().origin = x740_grappleArm->AuxTransform() * zeus::CVector3f(-0.9f, -0.4f, 0.4f); x740_grappleArm->SetTransform(x3e8_xf); } CPlayerGun::CGunMorph::EMorphEvent CPlayerGun::CGunMorph::Update(float inY, float outY, float dt) { EMorphEvent ret = EMorphEvent::None; if (x20_gunState == EGunState::InWipeDone) { x14_remHoldTime -= dt; if (x14_remHoldTime <= 0.f && x24_25_weaponChanged) { StartWipe(EDir::Out); x24_25_weaponChanged = false; x14_remHoldTime = 0.f; ret = EMorphEvent::InWipeDone; } } if (x24_24_morphing) { float omt = x8_remTime * xc_speed; float t = 1.f - omt; if (x1c_dir == EDir::In) { x0_yLerp = omt * outY + t * inY; x18_transitionFactor = omt; } else { x0_yLerp = omt * inY + t * outY; x18_transitionFactor = t; } if (x8_remTime <= 0.f) { x24_24_morphing = false; x8_remTime = 0.f; if (x1c_dir == EDir::In) { x20_gunState = EGunState::InWipeDone; x18_transitionFactor = 0.f; } else { x18_transitionFactor = 1.f; x20_gunState = EGunState::OutWipeDone; x1c_dir = EDir::Done; ret = EMorphEvent::OutWipeDone; } } else { x8_remTime -= dt; } } return ret; } void CPlayerGun::CGunMorph::StartWipe(EDir dir) { x14_remHoldTime = x10_holoHoldTime; if (dir == EDir::In && x20_gunState == EGunState::InWipeDone) return; if (dir != x1c_dir && x20_gunState != EGunState::OutWipe) { x8_remTime = x4_gunTransformTime; xc_speed = 1.f / x4_gunTransformTime; } else if (x20_gunState != EGunState::InWipe) { x8_remTime = x4_gunTransformTime - x8_remTime; } x1c_dir = dir; x20_gunState = x1c_dir == EDir::In ? EGunState::InWipe : EGunState::OutWipe; x24_24_morphing = true; } void CPlayerGun::ProcessGunMorph(float dt, CStateManager& mgr) { bool isUnmorphed = mgr.GetPlayer().GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Unmorphed; switch (x678_morph.GetGunState()) { case CGunMorph::EGunState::InWipeDone: if (x310_currentBeam != x314_nextBeam && x734_loadingBeam != nullptr) { if (!isUnmorphed) x734_loadingBeam->Touch(mgr); if (x734_loadingBeam->IsLoaded() && x744_auxWeapon->IsLoaded()) { x730_outgoingBeam = (x734_loadingBeam == x72c_currentBeam ? nullptr : x72c_currentBeam); x734_loadingBeam = nullptr; x310_currentBeam = x314_nextBeam; x320_currentAuxBeam = x314_nextBeam; x833_30_canShowAuxMuzzleEffect = true; x72c_currentBeam = x760_selectableBeams[size_t(x314_nextBeam)]; x738_nextBeam = x72c_currentBeam; x678_morph.SetWeaponChanged(); mgr.GetPlayerState()->SetCurrentBeam(x314_nextBeam); } } break; case CGunMorph::EGunState::InWipe: case CGunMorph::EGunState::OutWipe: x774_holoTransitionGen->SetGlobalScale(sGunScale); x774_holoTransitionGen->SetGlobalTranslation(zeus::CVector3f(0.f, x678_morph.GetYLerp(), 0.f)); x774_holoTransitionGen->Update(dt); break; default: break; } switch (x678_morph.Update(0.2f, 1.292392f, dt)) { case CGunMorph::EMorphEvent::InWipeDone: CSfxManager::SfxStart(SFXwpn_morph_in_wipe_done, 1.f, 0.f, true, 0x74, false, kInvalidAreaId); break; case CGunMorph::EMorphEvent::OutWipeDone: if (x730_outgoingBeam != nullptr && x72c_currentBeam != x730_outgoingBeam) { x730_outgoingBeam->Unload(mgr); x730_outgoingBeam = nullptr; } if (isUnmorphed) { NWeaponTypes::play_sfx(skIntoBeamSound[size_t(x310_currentBeam)], x834_27_underwater, false, 0.165f); } x72c_currentBeam->SetRainSplashGenerator(x748_rainSplashGenerator.get()); x72c_currentBeam->EnableFx(true); PlayAnim(NWeaponTypes::EGunAnimType::ToBeam, false); if (x833_31_inFreeLook) EnterFreeLook(mgr); else if (x832_30_requestReturnToDefault) ReturnArmAndGunToDefault(mgr, false); if (x834_28_requestImmediateRecharge || (x2ec_lastFireButtonStates & 0x1) != 0) { if (mgr.GetPlayerState()->GetCurrentVisor() != CPlayerState::EPlayerVisor::Scan) x32c_chargePhase = EChargePhase::ChargeRequested; x834_28_requestImmediateRecharge = false; } x832_30_requestReturnToDefault = false; x338_nextState = ENextState::SetupBeam; break; default: break; } } void CPlayerGun::SetPhazonBeamFeedback(bool active) { const char16_t* str = g_MainStringTable->GetString(21); // Hyper-mode CHUDMemoParms parms(5.f, true, !active, false); MP1::CSamusHud::DisplayHudMemo(str, parms); if (CSfxManager::IsPlaying(x2e8_phazonBeamSfx)) CSfxManager::SfxStop(x2e8_phazonBeamSfx); x2e8_phazonBeamSfx.reset(); if (active) x2e8_phazonBeamSfx = NWeaponTypes::play_sfx(SFXphg_charge_lp, x834_27_underwater, false, 0.165f); } void CPlayerGun::StartPhazonBeamTransition(bool active, CStateManager& mgr, CPlayerState& playerState) { if (x833_28_phazonBeamActive == active) { return; } x760_selectableBeams[size_t(x310_currentBeam)]->Unload(mgr); x760_selectableBeams[size_t(x310_currentBeam)] = active ? x75c_phazonBeam.get() : x738_nextBeam; ResetBeamParams(mgr, playerState, false); x72c_currentBeam = x760_selectableBeams[size_t(x310_currentBeam)]; x833_28_phazonBeamActive = active; SetPhazonBeamFeedback(active); x72c_currentBeam->SetRainSplashGenerator(x748_rainSplashGenerator.get()); x72c_currentBeam->EnableFx(true); x72c_currentBeam->SetDrawHologram(false); PlayAnim(NWeaponTypes::EGunAnimType::ToBeam, false); if (x833_31_inFreeLook) EnterFreeLook(mgr); else if (x832_30_requestReturnToDefault) ReturnArmAndGunToDefault(mgr, false); x832_30_requestReturnToDefault = false; } void CPlayerGun::ProcessPhazonGunMorph(float dt, CStateManager& mgr) { if (x835_26_phazonBeamMorphing) { if (x835_27_intoPhazonBeam) { x39c_phazonMorphT += 15.f * dt; if (x39c_phazonMorphT > 1.f) x39c_phazonMorphT = 1.f; } else { x39c_phazonMorphT -= 2.f * dt; if (x39c_phazonMorphT < 0.f) { x835_26_phazonBeamMorphing = false; x39c_phazonMorphT = 0.f; } } } switch (x33c_phazonBeamState) { case EPhazonBeamState::Entering: if (x75c_phazonBeam) { x75c_phazonBeam->Update(dt, mgr); if (x75c_phazonBeam->IsLoaded()) { StartPhazonBeamTransition(true, mgr, *mgr.GetPlayerState()); SetPhazonBeamMorph(false); x33c_phazonBeamState = EPhazonBeamState::Active; x338_nextState = ENextState::SetupBeam; } } break; case EPhazonBeamState::Exiting: if (x738_nextBeam) { x738_nextBeam->Update(dt, mgr); if (x738_nextBeam->IsLoaded()) { x835_25_inPhazonBeam = false; StartPhazonBeamTransition(false, mgr, *mgr.GetPlayerState()); SetPhazonBeamMorph(false); x33c_phazonBeamState = EPhazonBeamState::Inactive; x338_nextState = ENextState::SetupBeam; } } break; default: break; } } void CPlayerGun::EnableChargeFx(EChargeState state, CStateManager& mgr) { x72c_currentBeam->ActivateCharge(true, false); SetGunLightActive(true, mgr); x72c_currentBeam->EnableSecondaryFx(CGunWeapon::ESecondaryFxType::Charge); StopContinuousBeam(mgr, false); switch (x310_currentBeam) { case CPlayerState::EBeamId::Plasma: case CPlayerState::EBeamId::Power: x832_25_chargeEffectVisible = true; break; default: break; } x2f8_stateFlags |= 0x7; x318_comboAmmoIdx = 1; x338_nextState = ENextState::StatusQuo; x833_30_canShowAuxMuzzleEffect = true; x800_auxMuzzleGenerators[size_t(x320_currentAuxBeam)] = std::make_unique(x7c0_auxMuzzleEffects[size_t(x320_currentAuxBeam)]); x800_auxMuzzleGenerators[size_t(x320_currentAuxBeam)]->SetParticleEmission(true); } void CPlayerGun::UpdateChargeState(float dt, CStateManager& mgr) { switch (x32c_chargePhase) { case EChargePhase::ChargeRequested: x340_chargeBeamFactor = 0.f; x330_chargeState = EChargeState::Normal; x832_27_chargeAnimStarted = false; x834_24_charging = true; x32c_chargePhase = EChargePhase::AnimAndSfx; break; case EChargePhase::AnimAndSfx: if (!x832_27_chargeAnimStarted) { if (x340_chargeBeamFactor > kChargeStart && x832_25_chargeEffectVisible) { x832_25_chargeEffectVisible = false; } if (x340_chargeBeamFactor > kChargeAnimStart) { PlayAnim(NWeaponTypes::EGunAnimType::ChargeUp, false); if (!x2e0_chargeSfx) { x2e0_chargeSfx = NWeaponTypes::play_sfx(skBeamChargeUpSound[size_t(x310_currentBeam)], x834_27_underwater, true, 0.165f); } if (x830_chargeRumbleHandle == -1) { x830_chargeRumbleHandle = mgr.GetRumbleManager().Rumble(mgr, ERumbleFxId::PlayerGunCharge, 1.f, ERumblePriority::Three); } x832_27_chargeAnimStarted = true; } } else { if (x340_chargeBeamFactor >= kChargeFxStart && (x2f8_stateFlags & 0x8) != 0x8) { x832_25_chargeEffectVisible = true; x832_27_chargeAnimStarted = false; x32c_chargePhase = EChargePhase::FxGrowing; x330_chargeState = EChargeState::Charged; EnableChargeFx(EChargeState::Charged, mgr); PlayAnim(NWeaponTypes::EGunAnimType::ChargeLoop, true); } } break; case EChargePhase::FxGrowing: if (x340_chargeBeamFactor >= 1.f) x32c_chargePhase = EChargePhase::FxGrown; break; case EChargePhase::ComboXfer: if (x344_comboXferTimer >= 1.f) { x32c_chargePhase = EChargePhase::ComboXferDone; x832_25_chargeEffectVisible = false; } break; case EChargePhase::ComboXferDone: x32c_chargePhase = EChargePhase::ComboFire; x348_chargeCooldownTimer = 0.f; break; case EChargePhase::ComboFire: x740_grappleArm->EnterComboFire(s32(x310_currentBeam), mgr); x73c_gunMotion->PlayPasAnim(SamusGun::EAnimationState::ComboFire, mgr, 0.f, false); x72c_currentBeam->PlayPasAnim(SamusGun::EAnimationState::ComboFire, mgr, 0.f); x833_31_inFreeLook = false; x32c_chargePhase = EChargePhase::ComboFireDone; break; case EChargePhase::ChargeCooldown: if ((x2f8_stateFlags & 0x10) != 0x10) { x348_chargeCooldownTimer += dt; if (x348_chargeCooldownTimer >= 0.3f && x72c_currentBeam->IsChargeAnimOver()) x32c_chargePhase = EChargePhase::ChargeDone; } else { x832_24_coolingCharge = false; } break; case EChargePhase::ChargeDone: ResetCharge(mgr, false); Reset(mgr, false); break; default: break; } if (x2e0_chargeSfx) CSfxManager::PitchBend(x2e0_chargeSfx, x834_27_underwater ? -1.f : 0.f); if (x32c_chargePhase > EChargePhase::NotCharging && x32c_chargePhase < EChargePhase::FxGrown) { x340_chargeBeamFactor += kChargeSpeed * dt; if (x340_chargeBeamFactor > 1.f) x340_chargeBeamFactor = 1.f; } } void CPlayerGun::UpdateAuxWeapons(float dt, const zeus::CTransform& targetXf, CStateManager& mgr) { zeus::CVector3f firePoint = x4a8_gunWorldXf * x418_beamLocalXf.origin + mgr.GetCameraManager()->GetGlobalCameraTranslation(mgr); bool done = x744_auxWeapon->UpdateComboFx(dt, sGunScale, firePoint, targetXf, mgr); if ((x2f8_stateFlags & 0x10) == 0x10) { if (x310_currentBeam == CPlayerState::EBeamId::Wave && x744_auxWeapon->HasTarget(mgr) == kInvalidUniqueId) { TUniqueId targetId = GetTargetId(mgr); if (targetId == kInvalidUniqueId) targetId = mgr.GetPlayer().GetAimTarget(); x744_auxWeapon->SetNewTarget(targetId, mgr); } if (done) return; done = x310_currentBeam == CPlayerState::EBeamId::Wave || x310_currentBeam == CPlayerState::EBeamId::Plasma; if (!done) if (x72c_currentBeam->ComboFireOver()) done = true; x72c_currentBeam->EnableSecondaryFx(CGunWeapon::ESecondaryFxType::CancelCharge); if (done) { x32c_chargePhase = EChargePhase::ChargeDone; ReturnArmAndGunToDefault(mgr, false); if ((x2f8_stateFlags & 0x8) != 0x8) { x2f8_stateFlags |= 0x1; x2f8_stateFlags &= 0xFFE9; } x318_comboAmmoIdx = 0; x31c_missileMode = EMissileMode::Inactive; } } else if (x833_28_phazonBeamActive) { static_cast(x72c_currentBeam)->UpdateBeam(dt, targetXf, x418_beamLocalXf.origin, mgr); } else if (x310_currentBeam == CPlayerState::EBeamId::Plasma) { static_cast(x72c_currentBeam)->UpdateBeam(dt, targetXf, x418_beamLocalXf.origin, mgr); } } void CPlayerGun::DoUserAnimEvent(float dt, CStateManager& mgr, const CInt32POINode& node, EUserEventType type) { switch (type) { case EUserEventType::Projectile: if (x32c_chargePhase != EChargePhase::ComboFireDone) return; bool doFireSecondary; if (x310_currentBeam != CPlayerState::EBeamId::Wave && x310_currentBeam != CPlayerState::EBeamId::Plasma) doFireSecondary = true; else doFireSecondary = (x2ec_lastFireButtonStates & 0x1) != 0; if (doFireSecondary) FireSecondary(dt, mgr); if ((x2f8_stateFlags & 0x10) != 0x10) x2f8_stateFlags |= 0x10; CancelCharge(mgr, true); if (doFireSecondary) x72c_currentBeam->EnableSecondaryFx(CGunWeapon::ESecondaryFxType::ToCombo); break; default: break; } } void CPlayerGun::DoUserAnimEvents(float dt, CStateManager& mgr) { zeus::CVector3f posToCam = mgr.GetCameraManager()->GetCurrentCamera(mgr)->GetTranslation() - x3e8_xf.origin; const CAnimData& animData = *x72c_currentBeam->GetSolidModelData().GetAnimationData(); for (size_t i = 0; i < animData.GetPassedSoundPOICount(); ++i) { const CSoundPOINode& node = CAnimData::g_SoundPOINodes[i]; if (node.GetPoiType() != EPOIType::Sound || (node.GetCharacterIndex() != -1 && animData.x204_charIdx != node.GetCharacterIndex())) { continue; } NWeaponTypes::do_sound_event(x670_animSfx, x328_animSfxPitch, false, node.GetSfxId(), node.GetWeight(), node.GetFlags(), node.GetFalloff(), node.GetMaxDist(), 0.16f, 1.f, posToCam, x3e8_xf.origin, mgr.GetPlayer().GetAreaIdAlways(), mgr); } for (size_t i = 0; i < animData.GetPassedIntPOICount(); ++i) { const CInt32POINode& node = CAnimData::g_Int32POINodes[i]; switch (node.GetPoiType()) { case EPOIType::UserEvent: DoUserAnimEvent(dt, mgr, node, EUserEventType(node.GetValue())); break; case EPOIType::SoundInt32: if (node.GetCharacterIndex() != -1 && animData.x204_charIdx != node.GetCharacterIndex()) break; NWeaponTypes::do_sound_event(x670_animSfx, x328_animSfxPitch, false, u32(node.GetValue()), node.GetWeight(), node.GetFlags(), 0.1f, 150.f, 0.16f, 1.f, posToCam, x3e8_xf.origin, mgr.GetPlayer().GetAreaIdAlways(), mgr); break; default: break; } } } TUniqueId CPlayerGun::GetTargetId(CStateManager& mgr) const { TUniqueId ret = mgr.GetPlayer().GetOrbitTargetId(); if (x832_26_comboFiring && ret == kInvalidUniqueId && x310_currentBeam == CPlayerState::EBeamId::Wave) ret = mgr.GetPlayer().GetOrbitNextTargetId(); if (ret == kInvalidUniqueId) return ret; if (TCastToConstPtr act = mgr.GetObjectById(ret)) if (!act->GetMaterialList().HasMaterial(EMaterialTypes::Target)) ret = kInvalidUniqueId; return ret; } void CPlayerGun::CancelLockOn() { if (x832_29_lockedOn) { x832_29_lockedOn = false; x6a0_motionState.SetState(CMotionState::EMotionState::CancelLockOn); if (x32c_chargePhase == EChargePhase::NotCharging && x318_comboAmmoIdx != 1) PlayAnim(NWeaponTypes::EGunAnimType::BasePosition, false); } } void CPlayerGun::FireSecondary(float dt, CStateManager& mgr) { if (mgr.GetCameraManager()->IsInCinematicCamera()) { return; } if (x835_25_inPhazonBeam || x318_comboAmmoIdx == 0 || !mgr.GetPlayerState()->HasPowerUp(skItemArr[x318_comboAmmoIdx]) || (x2f8_stateFlags & 0x4) != 0x4) { NWeaponTypes::play_sfx(SFXwpn_invalid_action, x834_27_underwater, false, 0.165f); return; } bool comboFired = false; if (x318_comboAmmoIdx == 1) { x300_remainingMissiles = mgr.GetPlayerState()->GetItemAmount(CPlayerState::EItemType::Missiles); if (mgr.GetWeaponIdCount(x538_playerId, EWeaponType::Missile) < 3 && x300_remainingMissiles != 0) { mgr.GetPlayerState()->DecrPickup(CPlayerState::EItemType::Missiles, x832_26_comboFiring ? mgr.GetPlayerState()->GetMissileCostForAltAttack() : 1); comboFired = true; } if (x300_remainingMissiles > 5) { x300_remainingMissiles = 5; } else { x300_remainingMissiles -= 1; } } if (comboFired) { TUniqueId targetId = GetTargetId(mgr); if (x832_26_comboFiring && targetId == kInvalidUniqueId && x310_currentBeam == CPlayerState::EBeamId::Wave) { targetId = mgr.GetPlayer().GetAimTarget(); } zeus::CTransform fireXf = x833_29_pointBlankWorldSurface ? x448_elbowWorldXf : x4a8_gunWorldXf * x418_beamLocalXf; if (!x833_29_pointBlankWorldSurface && x364_gunStrikeCoolTimer <= 0.f) { zeus::CVector3f backupOrigin = fireXf.origin; fireXf = x478_assistAimXf; fireXf.origin = backupOrigin; } fireXf.origin += mgr.GetCameraManager()->GetGlobalCameraTranslation(mgr); x744_auxWeapon->Fire(dt, x834_27_underwater, x310_currentBeam, x330_chargeState, fireXf, mgr, x72c_currentBeam->GetWeaponType(), targetId); mgr.InformListeners(x4a8_gunWorldXf.origin, EListenNoiseType::PlayerFire); x3a0_missileExitTimer = 7.f; if (!x832_26_comboFiring) { PlayAnim(NWeaponTypes::EGunAnimType::MissileShoot, false); x338_nextState = x300_remainingMissiles > 0 ? ENextState::MissileReload : ENextState::MissileShotDone; x2f8_stateFlags &= ~0x4; } } else { NWeaponTypes::play_sfx(skItemEmptySound[x318_comboAmmoIdx], x834_27_underwater, false, 0.165f); } } void CPlayerGun::ResetCharged(float dt, CStateManager& mgr) { if (x832_26_comboFiring) return; if (x32c_chargePhase >= EChargePhase::FxGrowing) { x833_30_canShowAuxMuzzleEffect = false; UpdateNormalShotCycle(dt, mgr); x832_24_coolingCharge = true; CancelCharge(mgr, true); } else if (x32c_chargePhase != EChargePhase::NotCharging) { x320_currentAuxBeam = x310_currentBeam; x833_30_canShowAuxMuzzleEffect = true; x32c_chargePhase = EChargePhase::ChargeDone; } StopChargeSound(mgr); } void CPlayerGun::ActivateCombo(CStateManager& mgr) { if (x832_26_comboFiring) return; if (mgr.GetPlayerState()->GetItemAmount(skItemArr[x318_comboAmmoIdx]) >= mgr.GetPlayerState()->GetMissileCostForAltAttack()) { bool canFire = true; if (x310_currentBeam == CPlayerState::EBeamId::Plasma) canFire = !x834_27_underwater; if (canFire) { x832_26_comboFiring = true; const auto& xferEffect = x72c_currentBeam->GetComboXferDescr(); if (xferEffect.IsLoaded()) { x77c_comboXferGen = std::make_unique(xferEffect); x77c_comboXferGen->SetGlobalScale(sGunScale); } x72c_currentBeam->EnableCharge(true); StopChargeSound(mgr); NWeaponTypes::play_sfx(SFXwpn_combo_xfer, x834_27_underwater, false, 0.165f); x32c_chargePhase = EChargePhase::ComboXfer; } } else { NWeaponTypes::play_sfx(SFXwpn_invalid_action, x834_27_underwater, false, 0.165f); } } void CPlayerGun::ProcessChargeState(u32 releasedStates, u32 pressedStates, CStateManager& mgr, float dt) { if ((releasedStates & 0x1) != 0) ResetCharged(dt, mgr); if ((pressedStates & 0x1) != 0) { if (x32c_chargePhase == EChargePhase::NotCharging && (pressedStates & 0x1) != 0 && x348_chargeCooldownTimer == 0.f && x832_28_readyForShot) { UpdateNormalShotCycle(dt, mgr); x32c_chargePhase = EChargePhase::ChargeRequested; } } else if (mgr.GetPlayerState()->HasPowerUp(CPlayerState::EItemType::Missiles) && (pressedStates & 0x2) != 0) { if (x32c_chargePhase >= EChargePhase::FxGrown) { if (mgr.GetPlayerState()->HasPowerUp(skBeamComboArr[size_t(x310_currentBeam)])) ActivateCombo(mgr); } else if (x32c_chargePhase == EChargePhase::NotCharging) { FireSecondary(dt, mgr); } } } void CPlayerGun::ResetNormal(CStateManager& mgr) { Reset(mgr, false); x832_28_readyForShot = false; } void CPlayerGun::UpdateNormalShotCycle(float dt, CStateManager& mgr) { if (!ExitMissile()) return; if (mgr.GetCameraManager()->IsInCinematicCamera()) return; x832_25_chargeEffectVisible = x833_28_phazonBeamActive || x310_currentBeam != CPlayerState::EBeamId::Plasma || x32c_chargePhase != EChargePhase::NotCharging; x30c_rapidFireShots += 1; zeus::CTransform xf = x833_29_pointBlankWorldSurface ? x448_elbowWorldXf : x4a8_gunWorldXf * x418_beamLocalXf; if (!x833_29_pointBlankWorldSurface && x364_gunStrikeCoolTimer <= 0.f) { zeus::CVector3f oldOrigin = xf.origin; xf = x478_assistAimXf; xf.origin = oldOrigin; } xf.origin += mgr.GetCameraManager()->GetGlobalCameraTranslation(mgr); x38c_muzzleEffectVisTimer = 0.0625f; TUniqueId homingTarget; if (x72c_currentBeam->GetVelocityInfo().GetTargetHoming(int(x330_chargeState))) homingTarget = GetTargetId(mgr); else homingTarget = kInvalidUniqueId; x72c_currentBeam->Fire(x834_27_underwater, dt, x330_chargeState, xf, mgr, homingTarget, x340_chargeBeamFactor, x340_chargeBeamFactor); mgr.InformListeners(x4a8_gunWorldXf.origin, EListenNoiseType::PlayerFire); } void CPlayerGun::ProcessNormalState(u32 releasedStates, u32 pressedStates, CStateManager& mgr, float dt) { if ((releasedStates & 0x1) != 0) ResetNormal(mgr); if ((pressedStates & 0x1) != 0 && x348_chargeCooldownTimer == 0.f && x832_28_readyForShot) UpdateNormalShotCycle(dt, mgr); else if ((pressedStates & 0x2) != 0) FireSecondary(dt, mgr); } void CPlayerGun::UpdateWeaponFire(float dt, const CPlayerState& playerState, CStateManager& mgr) { u32 oldFiring = x2ec_lastFireButtonStates; x2ec_lastFireButtonStates = x2f4_fireButtonStates; u32 pressedStates = x2f4_fireButtonStates & (oldFiring ^ x2f4_fireButtonStates); x2f0_pressedFireButtonStates = pressedStates; u32 releasedStates = oldFiring & (oldFiring ^ x2f4_fireButtonStates); x832_28_readyForShot = false; CPlayer& player = mgr.GetPlayer(); if (!x832_24_coolingCharge && !x834_30_inBigStrike) { float coolDown = x72c_currentBeam->GetWeaponInfo().x0_coolDown; if ((pressedStates & 0x1) == 0) { if (x390_cooldown >= coolDown) { x390_cooldown = coolDown; if (player.GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Unmorphed && mgr.GetPlayerState()->ItemEnabled(CPlayerState::EItemType::ChargeBeam) && player.GetGunHolsterState() == CPlayer::EGunHolsterState::Drawn && player.GetGrappleState() == CPlayer::EGrappleState::None && mgr.GetPlayerState()->GetTransitioningVisor() != CPlayerState::EPlayerVisor::Scan && mgr.GetPlayerState()->GetCurrentVisor() != CPlayerState::EPlayerVisor::Scan && (x2ec_lastFireButtonStates & 0x1) != 0 && x32c_chargePhase == EChargePhase::NotCharging) { x832_28_readyForShot = true; pressedStates |= 0x1; x390_cooldown = 0.f; } } } else if (x390_cooldown >= coolDown) { x832_28_readyForShot = true; x390_cooldown = 0.f; } x390_cooldown += dt; } if (x834_28_requestImmediateRecharge) x834_28_requestImmediateRecharge = (x2ec_lastFireButtonStates & 0x1) != 0; if (player.GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Morphed) { x835_28_bombReady = false; x835_29_powerBombReady = false; if (!x835_31_actorAttached) { x835_28_bombReady = true; if (x53a_powerBomb != kInvalidUniqueId && !mgr.CanCreateProjectile(x538_playerId, EWeaponType::PowerBomb, 1)) { const auto* pb = static_cast(mgr.GetObjectById(x53a_powerBomb)); if (pb && pb->GetCurTime() <= 4.25f) { x835_28_bombReady = false; } else { x53a_powerBomb = kInvalidUniqueId; } } if (((pressedStates & 0x1) != 0 || x32c_chargePhase != EChargePhase::NotCharging) && mgr.GetPlayerState()->HasPowerUp(CPlayerState::EItemType::MorphBallBombs)) { if (x835_28_bombReady) DropBomb(EBWeapon::Bomb, mgr); } else if (mgr.GetPlayerState()->HasPowerUp(CPlayerState::EItemType::PowerBombs) && mgr.GetPlayerState()->GetItemAmount(CPlayerState::EItemType::PowerBombs) > 0) { x835_29_powerBombReady = mgr.CanCreateProjectile(x538_playerId, EWeaponType::PowerBomb, 1) && mgr.CanCreateProjectile(x538_playerId, EWeaponType::Bomb, 1); if ((pressedStates & 0x2) != 0 && x835_29_powerBombReady) DropBomb(EBWeapon::PowerBomb, mgr); } } } else if ((x2f8_stateFlags & 0x8) != 0x8 && player.GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Unmorphed) { if ((pressedStates & 0x2) != 0 && x318_comboAmmoIdx == 0 && (x2f8_stateFlags & 0x2) != 0x2 && x32c_chargePhase == EChargePhase::NotCharging) { u32 missileCount = mgr.GetPlayerState()->GetItemAmount(CPlayerState::EItemType::Missiles); if (x338_nextState != ENextState::EnterMissile && x338_nextState != ENextState::ExitMissile) { if (mgr.GetPlayerState()->HasPowerUp(CPlayerState::EItemType::Missiles) && missileCount > 0) { x300_remainingMissiles = missileCount; if (x300_remainingMissiles > 5) x300_remainingMissiles = 5; if (!x835_25_inPhazonBeam) { x2f8_stateFlags &= ~0x1; x2f8_stateFlags |= 0x6; x318_comboAmmoIdx = 1; x31c_missileMode = EMissileMode::Active; } FireSecondary(dt, mgr); } else { if (!CSfxManager::IsPlaying(x2e4_invalidSfx)) x2e4_invalidSfx = NWeaponTypes::play_sfx(SFXwpn_invalid_action, x834_27_underwater, false, 0.165f); else x2e4_invalidSfx.reset(); } } } else { if (x3a4_fidget.GetState() == CFidget::EState::NoFidget) { if ((x2f8_stateFlags & 0x10) == 0x10 && x744_auxWeapon->IsComboFxActive(mgr)) { if (x2ec_lastFireButtonStates == 0 || (x310_currentBeam == CPlayerState::EBeamId::Wave && x833_29_pointBlankWorldSurface)) { StopContinuousBeam(mgr, (x2f8_stateFlags & 0x8) == 0x8); } } else { if (mgr.GetPlayerState()->ItemEnabled(CPlayerState::EItemType::ChargeBeam) && x33c_phazonBeamState == EPhazonBeamState::Inactive) ProcessChargeState(releasedStates, pressedStates, mgr, dt); else ProcessNormalState(releasedStates, pressedStates, mgr, dt); } } } } } void CPlayerGun::EnterFreeLook(CStateManager& mgr) { if (!x832_30_requestReturnToDefault) x73c_gunMotion->PlayPasAnim(SamusGun::EAnimationState::FreeLook, mgr, 0.f, false); x740_grappleArm->EnterFreeLook(x835_25_inPhazonBeam ? 1 : s32(x310_currentBeam), x73c_gunMotion->GetFreeLookSetId(), mgr); } void CPlayerGun::SetFidgetAnimBits(int animSet, bool beamOnly) { x2fc_fidgetAnimBits = 0; if (beamOnly) { x2fc_fidgetAnimBits = 2; return; } switch (x3a4_fidget.GetType()) { case SamusGun::EFidgetType::Minor: x2fc_fidgetAnimBits = 1; if (animSet != 1) return; x2fc_fidgetAnimBits |= 4; break; case SamusGun::EFidgetType::Major: if (animSet >= 6 || animSet < 4) x2fc_fidgetAnimBits = 2; else x2fc_fidgetAnimBits = 1; x2fc_fidgetAnimBits |= 4; break; default: break; } } void CPlayerGun::AsyncLoadFidget(CStateManager& mgr) { SetFidgetAnimBits(x3a4_fidget.GetAnimSet(), x3a4_fidget.GetState() == CFidget::EState::HolsterBeam); if ((x2fc_fidgetAnimBits & 0x1) == 0x1) x73c_gunMotion->GunController().LoadFidgetAnimAsync(mgr, s32(x3a4_fidget.GetType()), s32(x310_currentBeam), x3a4_fidget.GetAnimSet()); if ((x2fc_fidgetAnimBits & 0x2) == 0x2) { x72c_currentBeam->AsyncLoadFidget( mgr, (x3a4_fidget.GetState() == CFidget::EState::HolsterBeam ? SamusGun::EFidgetType::Minor : x3a4_fidget.GetType()), x3a4_fidget.GetAnimSet()); x832_31_inRestPose = false; } if ((x2fc_fidgetAnimBits & 0x4) == 0x4) if (CGunController* gc = x740_grappleArm->GunController()) gc->LoadFidgetAnimAsync(mgr, s32(x3a4_fidget.GetType()), x3a4_fidget.GetType() != SamusGun::EFidgetType::Minor ? s32(x310_currentBeam) : 0, x3a4_fidget.GetAnimSet()); } bool CPlayerGun::IsFidgetLoaded() const { u32 loadFlags = 0; if ((x2fc_fidgetAnimBits & 0x1) == 0x1 && x73c_gunMotion->GunController().IsFidgetLoaded()) { loadFlags |= 0x1; } if ((x2fc_fidgetAnimBits & 0x2) == 0x2 && x72c_currentBeam->IsFidgetLoaded()) { loadFlags |= 0x2; } if ((x2fc_fidgetAnimBits & 0x4) == 0x4) { if (CGunController* gc = x740_grappleArm->GunController()) { if (gc->IsFidgetLoaded()) { loadFlags |= 0x4; } } } return x2fc_fidgetAnimBits == loadFlags; } void CPlayerGun::EnterFidget(CStateManager& mgr) { if ((x2fc_fidgetAnimBits & 0x1) == 0x1) { x73c_gunMotion->EnterFidget(mgr, x3a4_fidget.GetType(), x3a4_fidget.GetAnimSet()); x834_25_gunMotionFidgeting = true; } else { x834_25_gunMotionFidgeting = false; } if ((x2fc_fidgetAnimBits & 0x2) == 0x2) x72c_currentBeam->EnterFidget(mgr, x3a4_fidget.GetType(), x3a4_fidget.GetAnimSet()); if ((x2fc_fidgetAnimBits & 0x4) == 0x4) x740_grappleArm->EnterFidget(mgr, x3a4_fidget.GetType(), x3a4_fidget.GetType() != SamusGun::EFidgetType::Minor ? s32(x310_currentBeam) : 0, x3a4_fidget.GetAnimSet()); UnLoadFidget(); x3a4_fidget.DoneLoading(); } void CPlayerGun::UpdateGunIdle(bool inStrikeCooldown, float camBobT, float dt, CStateManager& mgr) { CPlayer& player = mgr.GetPlayer(); if (player.IsInFreeLook() && !x832_29_lockedOn && !x740_grappleArm->IsGrappling() && x3a4_fidget.GetState() != CFidget::EState::HolsterBeam && player.GetGunHolsterState() == CPlayer::EGunHolsterState::Drawn && !x834_30_inBigStrike) { if ((x2f8_stateFlags & 0x8) != 0x8) { if (!x833_31_inFreeLook && !x834_26_animPlaying) { if (x388_enterFreeLookDelayTimer < 0.25f) x388_enterFreeLookDelayTimer += dt; if (x388_enterFreeLookDelayTimer >= 0.25f && !x740_grappleArm->IsSuitLoading()) { EnterFreeLook(mgr); x833_31_inFreeLook = true; } } else { x388_enterFreeLookDelayTimer = 0.f; if (x834_26_animPlaying) ResetIdle(mgr); } } } else { if (x833_31_inFreeLook) { if ((x2f8_stateFlags & 0x10) != 0x10) { x73c_gunMotion->ReturnToDefault(mgr, x834_30_inBigStrike); x740_grappleArm->ReturnToDefault(mgr, 0.f, false); } x833_31_inFreeLook = false; } x388_enterFreeLookDelayTimer = 0.f; if (player.GetMorphballTransitionState() != CPlayer::EPlayerMorphBallState::Morphed) { x833_24_notFidgeting = !(player.GetSurfaceRestraint() != CPlayer::ESurfaceRestraints::Water && mgr.GetPlayerState()->GetCurrentVisor() != CPlayerState::EPlayerVisor::Scan && (x2f4_fireButtonStates & 0x3) == 0 && x32c_chargePhase == EChargePhase::NotCharging && !x832_29_lockedOn && (x2f8_stateFlags & 0x8) != 0x8 && x364_gunStrikeCoolTimer <= 0.f && player.GetPlayerMovementState() == CPlayer::EPlayerMovementState::OnGround && !player.IsInFreeLook() && !player.GetFreeLookStickState() && player.GetOrbitState() == CPlayer::EPlayerOrbitState::NoOrbit && std::fabs(player.GetAngularVelocityOR().angle()) <= 0.1f && camBobT <= 0.01f && !mgr.GetCameraManager()->IsInCinematicCamera() && player.GetGunHolsterState() == CPlayer::EGunHolsterState::Drawn && player.GetGrappleState() == CPlayer::EGrappleState::None && !x834_30_inBigStrike && !x835_25_inPhazonBeam); if (x833_24_notFidgeting) { if (!x834_30_inBigStrike) { bool doWander = camBobT > 0.01f && (x2f4_fireButtonStates & 0x3) == 0; if (doWander) { x370_gunMotionSpeedMult = 1.f; x374_ = 0.f; if (x364_gunStrikeCoolTimer <= 0.f && x368_idleWanderDelayTimer <= 0.f) { x368_idleWanderDelayTimer = 8.f; x73c_gunMotion->PlayPasAnim(SamusGun::EAnimationState::Wander, mgr, 0.f, false); x324_idleState = EIdleState::Wander; x550_camBob.SetState(CPlayerCameraBob::ECameraBobState::Walk, mgr); } x368_idleWanderDelayTimer -= dt; x360_ -= dt; } if (!doWander || x834_26_animPlaying) ResetIdle(mgr); } else if (x394_damageTimer > 0.f) { x394_damageTimer -= dt; } else if (!x834_31_gunMotionInFidgetBasePosition) { x394_damageTimer = 0.f; x834_31_gunMotionInFidgetBasePosition = true; x73c_gunMotion->BasePosition(true); } else if (!x73c_gunMotion->GetModelData().GetAnimationData()->IsAnimTimeRemaining(0.001f, "Whole Body")) { x834_30_inBigStrike = false; x834_31_gunMotionInFidgetBasePosition = false; } } else { switch (x3a4_fidget.Update(x2ec_lastFireButtonStates, camBobT > 0.01f, inStrikeCooldown, dt, mgr)) { case CFidget::EState::NoFidget: if (x324_idleState != EIdleState::Idle) { x73c_gunMotion->PlayPasAnim(SamusGun::EAnimationState::Idle, mgr, 0.f, false); x324_idleState = EIdleState::Idle; } x550_camBob.SetState(CPlayerCameraBob::ECameraBobState::WalkNoBob, mgr); break; case CFidget::EState::MinorFidget: case CFidget::EState::MajorFidget: case CFidget::EState::HolsterBeam: if (x324_idleState != EIdleState::NotIdle) { x73c_gunMotion->BasePosition(false); x324_idleState = EIdleState::NotIdle; } AsyncLoadFidget(mgr); break; case CFidget::EState::Loading: if (IsFidgetLoaded()) EnterFidget(mgr); break; case CFidget::EState::StillMinorFidget: case CFidget::EState::StillMajorFidget: x550_camBob.SetState(CPlayerCameraBob::ECameraBobState::Walk, mgr); x833_24_notFidgeting = false; x834_26_animPlaying = x834_25_gunMotionFidgeting ? x73c_gunMotion->IsAnimPlaying() : x72c_currentBeam->GetSolidModelData().GetAnimationData()->IsAnimTimeRemaining(0.001f, "Whole Body"); if (!x834_26_animPlaying) { x3a4_fidget.ResetMinor(); ReturnToRestPose(); } break; default: break; } } x550_camBob.Update(dt, mgr); } } } void CPlayerGun::Update(float grappleSwingT, float cameraBobT, float dt, CStateManager& mgr) { CPlayer& player = mgr.GetPlayer(); CPlayerState& playerState = *mgr.GetPlayerState(); const bool isUnmorphed = player.GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Unmorphed; bool becameFrozen = false; if (isUnmorphed) { becameFrozen = !x834_29_frozen && player.GetFrozenState(); } bool becameThawed = false; if (isUnmorphed) { becameThawed = x834_29_frozen && !player.GetFrozenState(); } x834_29_frozen = isUnmorphed && player.GetFrozenState(); float advDt = dt; if (x834_29_frozen) { advDt = 0.f; } const bool r23 = x678_morph.GetGunState() != CGunMorph::EGunState::OutWipeDone; if (mgr.GetPlayerState()->GetCurrentVisor() == CPlayerState::EPlayerVisor::XRay || r23) { x6e0_rightHandModel.AdvanceAnimation(advDt, mgr, kInvalidAreaId, true); } if (r23 && x734_loadingBeam != nullptr && x734_loadingBeam != x72c_currentBeam) { x744_auxWeapon->LoadIdle(); x734_loadingBeam->Update(advDt, mgr); } if (!x744_auxWeapon->IsLoaded()) { x744_auxWeapon->LoadIdle(); } if (becameFrozen) { x72c_currentBeam->EnableSecondaryFx(CGunWeapon::ESecondaryFxType::None); x72c_currentBeam->EnableFrozenEffect(CGunWeapon::EFrozenFxType::Frozen); } else if (becameThawed) { x72c_currentBeam->EnableFrozenEffect(CGunWeapon::EFrozenFxType::Thawed); } if (becameFrozen || becameThawed) { x2f4_fireButtonStates = 0; x2ec_lastFireButtonStates = 0; CancelFiring(mgr); } x72c_currentBeam->Update(advDt, mgr); x73c_gunMotion->Update(advDt * x370_gunMotionSpeedMult, mgr); x740_grappleArm->Update(grappleSwingT, advDt, mgr); if (x338_nextState != ENextState::StatusQuo) { if (x678_morph.GetGunState() == CGunMorph::EGunState::InWipeDone) { if (x338_nextState == ENextState::ChangeWeapon) { ChangeWeapon(playerState, mgr); x338_nextState = ENextState::StatusQuo; } } else if (!x72c_currentBeam->GetSolidModelData().GetAnimationData()->IsAnimTimeRemaining(0.001f, "Whole Body") || x832_30_requestReturnToDefault) { bool statusQuo = true; switch (x338_nextState) { case ENextState::EnterMissile: x2f8_stateFlags &= ~0x1; x2f8_stateFlags |= 0x6; x318_comboAmmoIdx = 1; x31c_missileMode = EMissileMode::Active; break; case ENextState::ExitMissile: if ((x2f8_stateFlags & 0x8) != 0x8) { x2f8_stateFlags |= 0x1; x2f8_stateFlags &= 0xFFE9; } x318_comboAmmoIdx = 0; x31c_missileMode = EMissileMode::Inactive; x390_cooldown = x72c_currentBeam->GetWeaponInfo().x0_coolDown; break; case ENextState::MissileReload: PlayAnim(NWeaponTypes::EGunAnimType::MissileReload, false); x338_nextState = ENextState::MissileShotDone; statusQuo = false; break; case ENextState::MissileShotDone: x2f8_stateFlags |= 0x4; break; case ENextState::ChangeWeapon: ChangeWeapon(playerState, mgr); break; case ENextState::SetupBeam: x390_cooldown = x72c_currentBeam->GetWeaponInfo().x0_coolDown; x2f8_stateFlags &= ~0x8; if ((x2f8_stateFlags & 0x8) != 0x8) { x2f8_stateFlags |= 0x1; x2f8_stateFlags &= 0xFFE9; } x318_comboAmmoIdx = 0; x31c_missileMode = EMissileMode::Inactive; break; case ENextState::EnterPhazonBeam: if (x75c_phazonBeam->IsLoaded()) break; x72c_currentBeam->SetDrawHologram(true); x75c_phazonBeam->Load(mgr, false); x33c_phazonBeamState = EPhazonBeamState::Entering; break; case ENextState::ExitPhazonBeam: if (x738_nextBeam->IsLoaded()) break; x72c_currentBeam->SetDrawHologram(true); x738_nextBeam->Load(mgr, false); x33c_phazonBeamState = EPhazonBeamState::Exiting; break; default: break; } if (statusQuo) x338_nextState = ENextState::StatusQuo; } } if (x37c_rapidFireShotsDecayTimer < 0.2f) { x37c_rapidFireShotsDecayTimer += advDt; } else { x37c_rapidFireShotsDecayTimer = 0.f; if (x30c_rapidFireShots > 0) x30c_rapidFireShots -= 1; } if (x32c_chargePhase != EChargePhase::NotCharging && !player.GetFrozenState()) { x34c_shakeX = chargeShakeTbl[mgr.GetActiveRandom()->Next() % 3] * x340_chargeBeamFactor; x350_shakeZ = chargeShakeTbl[mgr.GetActiveRandom()->Next() % 3] * x340_chargeBeamFactor; } if (!x72c_currentBeam->IsLoaded()) { return; } GetLctrWithShake(x4d8_gunLocalXf, x73c_gunMotion->GetModelData(), "GBSE_SDK", true, true); GetLctrWithShake(x418_beamLocalXf, x72c_currentBeam->GetSolidModelData(), "LBEAM", false, true); GetLctrWithShake(x508_elbowLocalXf, x72c_currentBeam->GetSolidModelData(), "elbow", false, false); x4a8_gunWorldXf = x3e8_xf * x4d8_gunLocalXf * x550_camBob.GetCameraBobTransformation(); if (x740_grappleArm->GetActive() && !x740_grappleArm->IsGrappling()) UpdateLeftArmTransform(x72c_currentBeam->GetSolidModelData(), mgr); x6a0_motionState.Update(x2f0_pressedFireButtonStates != 0 && x832_28_readyForShot && x32c_chargePhase < EChargePhase::AnimAndSfx && !player.IsInFreeLook(), advDt, x4a8_gunWorldXf, mgr); x72c_currentBeam->GetSolidModelData().AdvanceParticles(x4a8_gunWorldXf, advDt, mgr); x72c_currentBeam->UpdateGunFx(x380_shotSmokeTimer > 2.f && x378_shotSmokeStartTimer > 0.15f, dt, mgr, x508_elbowLocalXf); zeus::CTransform beamWorldXf = x4a8_gunWorldXf * x418_beamLocalXf; if (player.GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Unmorphed && !mgr.GetCameraManager()->IsInCinematicCamera()) { EntityList nearList; zeus::CAABox aabb = x72c_currentBeam->GetBounds().getTransformedAABox(x4a8_gunWorldXf); mgr.BuildNearList(nearList, aabb, sAimFilter, &player); TUniqueId bestId = kInvalidUniqueId; zeus::CVector3f dir = x4a8_gunWorldXf.frontVector().normalized(); zeus::CVector3f pos = dir * -0.5f + x4a8_gunWorldXf.origin; CRayCastResult result = mgr.RayWorldIntersection(bestId, pos, dir, 3.5f, sAimFilter, nearList); x833_29_pointBlankWorldSurface = result.IsValid(); if (result.IsValid()) { x448_elbowWorldXf = x4a8_gunWorldXf * x508_elbowLocalXf; x448_elbowWorldXf.origin += dir * -0.5f; beamWorldXf.origin = result.GetPoint(); } } else { x833_29_pointBlankWorldSurface = false; } zeus::CTransform beamTargetXf = x833_29_pointBlankWorldSurface ? x448_elbowWorldXf : beamWorldXf; zeus::CVector3f camTrans = mgr.GetCameraManager()->GetGlobalCameraTranslation(mgr); beamWorldXf.origin += camTrans; beamTargetXf.origin += camTrans; if (x832_25_chargeEffectVisible) { bool emitting = x833_30_canShowAuxMuzzleEffect ? x344_comboXferTimer < 1.f : false; zeus::CVector3f scale((emitting && x832_26_comboFiring) ? (1.f - x344_comboXferTimer) * 2.f : 2.f); x72c_currentBeam->UpdateMuzzleFx(advDt, scale, x418_beamLocalXf.origin, emitting); CElementGen& gen = *x800_auxMuzzleGenerators[size_t(x320_currentAuxBeam)]; gen.SetGlobalOrientAndTrans(x418_beamLocalXf); gen.SetGlobalScale(scale); gen.SetParticleEmission(emitting); gen.Update(advDt); } if (x748_rainSplashGenerator) x748_rainSplashGenerator->Update(advDt, mgr); UpdateGunLight(beamWorldXf, mgr); ProcessGunMorph(advDt, mgr); if (x835_26_phazonBeamMorphing) ProcessPhazonGunMorph(advDt, mgr); if (x832_26_comboFiring && x77c_comboXferGen) { x77c_comboXferGen->SetGlobalTranslation(x418_beamLocalXf.origin); x77c_comboXferGen->SetGlobalOrientation(x418_beamLocalXf.getRotation()); x77c_comboXferGen->Update(advDt); x344_comboXferTimer += advDt * 4.f; } if (x35c_bombTime > 0.f) { x35c_bombTime -= advDt; if (x35c_bombTime <= 0.f) x308_bombCount = 3; } if (playerState.ItemEnabled(CPlayerState::EItemType::ChargeBeam) && x32c_chargePhase != EChargePhase::NotCharging) { UpdateChargeState(advDt, mgr); } else { x340_chargeBeamFactor -= advDt; if (x340_chargeBeamFactor < 0.f) x340_chargeBeamFactor = 0.f; } UpdateAuxWeapons(advDt, beamTargetXf, mgr); DoUserAnimEvents(advDt, mgr); if (player.GetOrbitState() == CPlayer::EPlayerOrbitState::OrbitObject && GetTargetId(mgr) != kInvalidUniqueId) { if (!x832_29_lockedOn && !x832_26_comboFiring && (x2f8_stateFlags & 0x10) != 0x10) { x832_29_lockedOn = true; x6a0_motionState.SetState(CMotionState::EMotionState::LockOn); ReturnArmAndGunToDefault(mgr, true); } } else { CancelLockOn(); } UpdateWeaponFire(advDt, playerState, mgr); UpdateGunIdle(x364_gunStrikeCoolTimer > 0.f, cameraBobT, advDt, mgr); if ((x2ec_lastFireButtonStates & 0x1) == 0x1) { x378_shotSmokeStartTimer = 0.f; } else if (x378_shotSmokeStartTimer < 2.f) { x378_shotSmokeStartTimer += advDt; if (x378_shotSmokeStartTimer > 1.f) { x30c_rapidFireShots = 0; x380_shotSmokeTimer = 0.f; } } if (x38c_muzzleEffectVisTimer > 0.f) x38c_muzzleEffectVisTimer -= advDt; if (x30c_rapidFireShots > 5 && x380_shotSmokeTimer < 2.f) x380_shotSmokeTimer += advDt; if (x384_gunStrikeDelayTimer > 0.f) x384_gunStrikeDelayTimer -= advDt; if (x364_gunStrikeCoolTimer > 0.f) { x2f4_fireButtonStates = 0; x364_gunStrikeCoolTimer -= advDt; } if (isUnmorphed && (x2f8_stateFlags & 0x4) == 0x4) { x3a0_missileExitTimer -= advDt; if (x3a0_missileExitTimer < 0.f) { x3a0_missileExitTimer = 0.f; ExitMissile(); } } } void CPlayerGun::PreRender(const CStateManager& mgr, const zeus::CFrustum& frustum, const zeus::CVector3f& camPos) { SCOPED_GRAPHICS_DEBUG_GROUP("CPlayerGun::PreRender", zeus::skBlue); const CPlayerState& playerState = *mgr.GetPlayerState(); if (playerState.GetCurrentVisor() == CPlayerState::EPlayerVisor::Scan) return; CPlayerState::EPlayerVisor activeVisor = playerState.GetActiveVisor(mgr); switch (activeVisor) { case CPlayerState::EPlayerVisor::Thermal: x0_lights.BuildConstantAmbientLighting( zeus::CColor(zeus::clamp(0.6f, 0.5f * x380_shotSmokeTimer + 0.6f - x378_shotSmokeStartTimer, 1.f), 1.f)); break; case CPlayerState::EPlayerVisor::Combat: { zeus::CAABox aabb = x72c_currentBeam->GetBounds(zeus::CTransform::Translate(camPos) * x4a8_gunWorldXf); if (mgr.GetNextAreaId() != kInvalidAreaId) { x0_lights.SetFindShadowLight(true); x0_lights.SetShadowDynamicRangeThreshold(0.25f); x0_lights.BuildAreaLightList(mgr, *mgr.GetWorld()->GetAreaAlways(mgr.GetNextAreaId()), aabb); } x0_lights.BuildDynamicLightList(mgr, aabb); if (x0_lights.HasShadowLight()) { if (x72c_currentBeam->IsLoaded()) { x82c_shadow->BuildLightShadowTexture(mgr, mgr.GetNextAreaId(), x0_lights.GetShadowLightIndex(), aabb, true, false); } } else { x82c_shadow->ResetBlur(); } break; } default: break; } if (x740_grappleArm->GetActive()) x740_grappleArm->PreRender(mgr, frustum, camPos); if (x678_morph.GetGunState() != CGunMorph::EGunState::OutWipeDone || activeVisor == CPlayerState::EPlayerVisor::XRay) x6e0_rightHandModel.GetAnimationData()->PreRender(); if (x833_28_phazonBeamActive) g_Renderer->AllocatePhazonSuitMaskTexture(); } void CPlayerGun::RenderEnergyDrainEffects(const CStateManager& mgr) const { if (const TCastToConstPtr player = mgr.GetObjectById(x538_playerId)) { for (const auto& source : player->GetEnergyDrain().GetEnergyDrainSources()) { if (const auto* metroid = CPatterned::CastTo(mgr.GetObjectById(source.GetEnergyDrainSourceId()))) { metroid->RenderHitGunEffect(); return; } } } } void CPlayerGun::DrawArm(const CStateManager& mgr, const zeus::CVector3f& pos, const CModelFlags& flags) const { const CPlayer& player = mgr.GetPlayer(); if (!x740_grappleArm->GetActive() || x740_grappleArm->GetAnimState() == CGrappleArm::EArmState::Done) return; if (player.GetGrappleState() != CPlayer::EGrappleState::None || x740_grappleArm->GetTransform().basis[1].dot(player.GetTransform().basis[1]) > 0.1f) { CModelFlags useFlags; if (x740_grappleArm->IsArmMoving()) useFlags = flags; else useFlags = CModelFlags(0, 0, 3, zeus::skWhite); x740_grappleArm->Render(mgr, pos, useFlags, &x0_lights); } } zeus::CVector3f CPlayerGun::ConvertToScreenSpace(const zeus::CVector3f& pos, const CGameCamera& cam) const { return cam.ConvertToScreenSpace(pos); } void CPlayerGun::CopyScreenTex() { // Copy lower right quadrant to gpCopyTexBuf as RGBA8 u16 width = CGraphics::g_Viewport.x8_width / 2; u16 height = CGraphics::g_Viewport.xc_height / 2; GXSetTexCopySrc(width, height, width, height); GXSetTexCopyDst(width, height, GX_TF_RGBA8, false); GXCopyTex(CGraphics::sSpareTextureData, false); GXPixModeSync(); } void CPlayerGun::DrawScreenTex(float z) { // Use CopyScreenTex rendering to draw over framebuffer pixels in front of `z` // This is accomplished using orthographic projection quad with sweeping `y` coordinates // Depth is set to GEQUAL to obscure pixels in front rather than behind const auto backupViewMatrix = CGraphics::g_ViewMatrix; const auto backupProjectionState = CGraphics::GetProjectionState(); g_Renderer->SetViewportOrtho(false, -1.f, 1.f); g_Renderer->SetBlendMode_AlphaBlended(); CGraphics::SetDepthWriteMode(true, ERglEnum::GEqual, true); u16 width = CGraphics::g_Viewport.x8_width / 2; u16 height = CGraphics::g_Viewport.xc_height / 2; CGraphics::LoadDolphinSpareTexture(width, height, GX_TF_RGBA8, nullptr, GX_TEXMAP7); constexpr std::array vtxDescList{ GXVtxDescList{GX_VA_POS, GX_DIRECT}, GXVtxDescList{GX_VA_TEX0, GX_DIRECT}, GXVtxDescList{GX_VA_NULL, GX_NONE}, }; CGX::SetVtxDescv(vtxDescList.data()); CGX::SetNumChans(0); CGX::SetNumTexGens(1); CGX::SetNumTevStages(1); CGX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP7, GX_COLOR_NULL); CGX::SetChanCtrl(CGX::EChannelId::Channel0, false, GX_SRC_REG, GX_SRC_REG, {}, GX_DF_NONE, GX_AF_NONE); CGX::Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, 4); GXPosition3f32(CGraphics::g_Viewport.x10_halfWidth, z, 0.f); GXTexCoord2f32(0.f, 1.f); GXPosition3f32(CGraphics::g_Viewport.x8_width, z, 0.f); GXTexCoord2f32(1.f, 1.f); GXPosition3f32(CGraphics::g_Viewport.x10_halfWidth, z, CGraphics::g_Viewport.x14_halfHeight); GXTexCoord2f32(0.f, 0.f); GXPosition3f32(CGraphics::g_Viewport.x8_width, z, CGraphics::g_Viewport.x14_halfHeight); GXTexCoord2f32(1.f, 0.f); CGX::End(); CGraphics::SetDepthWriteMode(true, ERglEnum::LEqual, true); CGraphics::SetViewPointMatrix(backupViewMatrix); CGraphics::SetProjectionState(backupProjectionState); } void CPlayerGun::DrawClipCube(const zeus::CAABox& aabb) { // Render AABB as completely transparent object, only modifying Z-buffer const zeus::CColor color{1.f, 0.f}; g_Renderer->SetBlendMode_AlphaBlended(); CGraphics::SetCullMode(ERglCullMode::None); g_Renderer->BeginTriangleStrip(4); g_Renderer->PrimColor(color); g_Renderer->PrimVertex(zeus::CVector3f{aabb.max.x(), aabb.max.y(), aabb.min.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.max.x(), aabb.min.y(), aabb.min.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.max.x(), aabb.max.y(), aabb.max.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.max.x(), aabb.min.y(), aabb.max.z()}); g_Renderer->EndPrimitive(); g_Renderer->BeginTriangleStrip(4); g_Renderer->PrimColor(color); g_Renderer->PrimVertex(zeus::CVector3f{aabb.min.x(), aabb.max.y(), aabb.min.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.max.x(), aabb.max.y(), aabb.min.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.min.x(), aabb.max.y(), aabb.max.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.max.x(), aabb.max.y(), aabb.max.z()}); g_Renderer->EndPrimitive(); g_Renderer->BeginTriangleStrip(4); g_Renderer->PrimColor(color); g_Renderer->PrimVertex(zeus::CVector3f{aabb.min.x(), aabb.max.y(), aabb.min.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.min.x(), aabb.min.y(), aabb.min.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.min.x(), aabb.max.y(), aabb.max.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.min.x(), aabb.min.y(), aabb.max.z()}); g_Renderer->EndPrimitive(); g_Renderer->BeginTriangleStrip(4); g_Renderer->PrimColor(color); g_Renderer->PrimVertex(zeus::CVector3f{aabb.min.x(), aabb.min.y(), aabb.min.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.max.x(), aabb.min.y(), aabb.min.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.min.x(), aabb.min.y(), aabb.max.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.max.x(), aabb.min.y(), aabb.max.z()}); g_Renderer->EndPrimitive(); g_Renderer->BeginTriangleStrip(4); g_Renderer->PrimColor(color); g_Renderer->PrimVertex(zeus::CVector3f{aabb.min.x(), aabb.min.y(), aabb.max.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.max.x(), aabb.min.y(), aabb.max.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.min.x(), aabb.max.y(), aabb.max.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.max.x(), aabb.max.y(), aabb.max.z()}); g_Renderer->EndPrimitive(); g_Renderer->BeginTriangleStrip(4); g_Renderer->PrimColor(color); g_Renderer->PrimVertex(zeus::CVector3f{aabb.min.x(), aabb.min.y(), aabb.min.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.max.x(), aabb.min.y(), aabb.min.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.min.x(), aabb.max.y(), aabb.min.z()}); g_Renderer->PrimVertex(zeus::CVector3f{aabb.max.x(), aabb.max.y(), aabb.min.z()}); g_Renderer->EndPrimitive(); CGraphics::SetCullMode(ERglCullMode::Front); } void CPlayerGun::Render(const CStateManager& mgr, const zeus::CVector3f& pos, const CModelFlags& flags) { SCOPED_GRAPHICS_DEBUG_GROUP("CPlayerGun::Render", zeus::skMagenta); CGraphics::CProjectionState projState = CGraphics::GetProjectionState(); CModelFlags beamFlags = flags; if (mgr.GetPlayerState()->GetCurrentVisor() == CPlayerState::EPlayerVisor::Thermal) { beamFlags = kThermalFlags[size_t(x310_currentBeam)]; } else if (x835_26_phazonBeamMorphing) { beamFlags = CModelFlags{1, 0, 3, zeus::CColor::lerp(zeus::skWhite, zeus::skBlack, x39c_phazonMorphT)}; } const CGameCamera* cam = mgr.GetCameraManager()->GetCurrentCamera(mgr); CGraphics::SetDepthRange(DEPTH_GUN, DEPTH_WORLD); zeus::CTransform offsetWorldXf = zeus::CTransform::Translate(pos) * x4a8_gunWorldXf; zeus::CTransform elbowOffsetXf = offsetWorldXf * x508_elbowLocalXf; if (x32c_chargePhase != EChargePhase::NotCharging && (x2f8_stateFlags & 0x10) != 0x10) { offsetWorldXf.origin += zeus::CVector3f(x34c_shakeX, 0.f, x350_shakeZ); } zeus::CTransform oldViewMtx = CGraphics::g_ViewMatrix; CGraphics::SetViewPointMatrix(offsetWorldXf.inverse() * oldViewMtx); CGraphics::SetModelMatrix(zeus::CTransform()); if (x32c_chargePhase >= EChargePhase::FxGrown && x32c_chargePhase < EChargePhase::ComboXfer) { x800_auxMuzzleGenerators[size_t(x320_currentAuxBeam)]->Render(); } if (x832_25_chargeEffectVisible && (x38c_muzzleEffectVisTimer > 0.f || x32c_chargePhase > EChargePhase::AnimAndSfx)) { x72c_currentBeam->DrawMuzzleFx(mgr); } if (x678_morph.GetGunState() == CGunMorph::EGunState::InWipe || x678_morph.GetGunState() == CGunMorph::EGunState::OutWipe) { x774_holoTransitionGen->Render(); } CGraphics::SetViewPointMatrix(oldViewMtx); if ((x2f8_stateFlags & 0x10) == 0x10) { x744_auxWeapon->RenderMuzzleFx(); } x72c_currentBeam->PreRenderGunFx(mgr, offsetWorldXf); bool drawSuitArm = !x740_grappleArm->IsGrappling() && mgr.GetPlayer().GetGunHolsterState() == CPlayer::EGunHolsterState::Drawn; x73c_gunMotion->Draw(mgr, offsetWorldXf); switch (x678_morph.GetGunState()) { case CGunMorph::EGunState::OutWipeDone: if (x0_lights.HasShadowLight()) x82c_shadow->EnableModelProjectedShadow(offsetWorldXf, x0_lights.GetShadowLightArrIndex(), 2.15f); if (mgr.GetPlayerState()->GetCurrentVisor() == CPlayerState::EPlayerVisor::XRay) { x6e0_rightHandModel.Render(mgr, elbowOffsetXf * zeus::CTransform::Translate(0.f, -0.2f, 0.02f), &x0_lights, mgr.GetPlayerState()->GetCurrentVisor() == CPlayerState::EPlayerVisor::Thermal ? kHandThermalFlag : kHandHoloFlag); } DrawArm(mgr, pos, flags); x72c_currentBeam->Draw(drawSuitArm, mgr, offsetWorldXf, beamFlags, &x0_lights); x82c_shadow->DisableModelProjectedShadow(); break; case CGunMorph::EGunState::InWipeDone: case CGunMorph::EGunState::InWipe: case CGunMorph::EGunState::OutWipe: if (x678_morph.GetGunState() != CGunMorph::EGunState::InWipeDone) { zeus::CTransform morphXf = elbowOffsetXf * zeus::CTransform::Translate(0.f, x678_morph.GetYLerp(), 0.f); CopyScreenTex(); x6e0_rightHandModel.Render(mgr, elbowOffsetXf * zeus::CTransform::Translate(0.f, -0.2f, 0.02f), &x0_lights, mgr.GetPlayerState()->GetCurrentVisor() == CPlayerState::EPlayerVisor::Thermal ? kHandThermalFlag : kHandHoloFlag); x72c_currentBeam->DrawHologram(mgr, offsetWorldXf, CModelFlags(0, 0, 3, zeus::skWhite)); DrawScreenTex(ConvertToScreenSpace(morphXf.origin, *cam).z()); if (x0_lights.HasShadowLight()) x82c_shadow->EnableModelProjectedShadow(offsetWorldXf, x0_lights.GetShadowLightArrIndex(), 2.15f); CGraphics::SetModelMatrix(morphXf); DrawClipCube(x6c8_hologramClipCube); x72c_currentBeam->Draw(drawSuitArm, mgr, offsetWorldXf, beamFlags, &x0_lights); x82c_shadow->DisableModelProjectedShadow(); } else { x6e0_rightHandModel.Render(mgr, elbowOffsetXf * zeus::CTransform::Translate(0.f, -0.2f, 0.02f), &x0_lights, mgr.GetPlayerState()->GetCurrentVisor() == CPlayerState::EPlayerVisor::Thermal ? kHandThermalFlag : kHandHoloFlag); x72c_currentBeam->DrawHologram(mgr, offsetWorldXf, CModelFlags(0, 0, 3, zeus::skWhite)); if (x0_lights.HasShadowLight()) x82c_shadow->EnableModelProjectedShadow(offsetWorldXf, x0_lights.GetShadowLightArrIndex(), 2.15f); DrawArm(mgr, pos, flags); x82c_shadow->DisableModelProjectedShadow(); } break; } oldViewMtx = CGraphics::g_ViewMatrix; CGraphics::SetViewPointMatrix(offsetWorldXf.inverse() * oldViewMtx); CGraphics::SetModelMatrix(zeus::CTransform()); x72c_currentBeam->PostRenderGunFx(mgr, offsetWorldXf); if (x832_26_comboFiring && x77c_comboXferGen) x77c_comboXferGen->Render(); CGraphics::SetViewPointMatrix(oldViewMtx); RenderEnergyDrainEffects(mgr); CGraphics::SetDepthRange(DEPTH_WORLD, DEPTH_FAR); CGraphics::SetProjectionState(projState); } void CPlayerGun::AddToRenderer(const zeus::CFrustum& frustum, const CStateManager& mgr) const { if (x72c_currentBeam->HasSolidModelData()) x72c_currentBeam->GetSolidModelData().RenderParticles(frustum); } void CPlayerGun::DropBomb(EBWeapon weapon, CStateManager& mgr) { if (weapon == EBWeapon::Bomb) { if (x32c_chargePhase != EChargePhase::NotCharging) { x32c_chargePhase = EChargePhase::ChargeDone; return; } if (x308_bombCount <= 0) return; const zeus::CVector3f plPos = mgr.GetPlayer().GetTranslation(); const zeus::CTransform xf = zeus::CTransform::Translate({plPos.x(), plPos.y(), plPos.z() + g_tweakPlayer->GetPlayerBallHalfExtent()}); CBomb* bomb = new CBomb(x784_bombEffects[u32(weapon)][0], x784_bombEffects[u32(weapon)][1], mgr.AllocateUniqueId(), mgr.GetPlayer().GetAreaId(), x538_playerId, x354_bombFuseTime, xf, CDamageInfo{g_tweakPlayerGun->GetBombInfo()}); mgr.AddObject(bomb); if (x308_bombCount == 3) x35c_bombTime = x358_bombDropDelayTime; --x308_bombCount; if (TCastToPtr plat = mgr.ObjectById(mgr.GetPlayer().GetRidingPlatformId())) plat->AddSlave(bomb->GetUniqueId(), mgr); } else if (weapon == EBWeapon::PowerBomb) { mgr.GetPlayerState()->DecrPickup(CPlayerState::EItemType::PowerBombs, 1); x53a_powerBomb = DropPowerBomb(mgr); } } TUniqueId CPlayerGun::DropPowerBomb(CStateManager& mgr) { const auto dInfo = mgr.GetPlayer().GetDeathTime() <= 0.f ? CDamageInfo{g_tweakPlayerGun->GetPowerBombInfo()} : CDamageInfo{CWeaponMode::PowerBomb(), 0.f, 0.f, 0.f}; TUniqueId uid = mgr.AllocateUniqueId(); zeus::CVector3f plVec = mgr.GetPlayer().GetTranslation(); zeus::CTransform xf = zeus::CTransform::Translate({plVec.x(), plVec.y(), plVec.z() + g_tweakPlayer->GetPlayerBallHalfExtent()}); CPowerBomb* pBomb = new CPowerBomb(x784_bombEffects[1][0], uid, kInvalidAreaId, x538_playerId, xf, dInfo); mgr.AddObject(*pBomb); return uid; } } // namespace metaforce