#include "Particle/CElementGen.hpp" #include "CDependencyGroup.hpp" #include "CGrappleArm.hpp" #include "GameGlobalObjects.hpp" #include "CSimplePool.hpp" #include "World/CPlayer.hpp" #include "TCastTo.hpp" #include "Camera/CGameCamera.hpp" #include "Graphics/CSkinnedModel.hpp" #include "Graphics/CVertexMorphEffect.hpp" namespace urde { float CGrappleArm::g_GrappleBeamAnglePhaseDelta = 0.875f; float CGrappleArm::g_GrappleBeamXWaveAmplitude = 0.25f; float CGrappleArm::g_GrappleBeamZWaveAmplitude = 0.125f; float CGrappleArm::g_GrappleBeamSpeed = 5.f; CGrappleArm::CGrappleArm(const zeus::CVector3f& scale) : x0_grappleArmModel(CAnimRes(g_tweakGunRes->x8_grappleArm, 0, scale, 41, false)), xa0_grappleGearModel(CStaticRes(NWeaponTypes::get_asset_id_from_name("GrappleGear"), scale)), xec_grapNoz1Model(CStaticRes(NWeaponTypes::get_asset_id_from_name("GrapNoz1"), scale)), x138_grapNoz2Model(CStaticRes(NWeaponTypes::get_asset_id_from_name("GrapNoz2"), scale)), x184_grappleArm(g_SimplePool->GetObj(SObjectTag{FOURCC('ANCS'), g_tweakGunRes->x8_grappleArm})), x31c_scale(scale), x354_grappleSegmentDesc(g_SimplePool->GetObj(SObjectTag{FOURCC('PART'), g_tweakGunRes->xb4_grappleSegment})), x360_grappleClawDesc(g_SimplePool->GetObj(SObjectTag{FOURCC('PART'), g_tweakGunRes->xb8_grappleClaw})), x36c_grappleHitDesc(g_SimplePool->GetObj(SObjectTag{FOURCC('PART'), g_tweakGunRes->xbc_grappleHit})), x378_grappleMuzzleDesc(g_SimplePool->GetObj(SObjectTag{FOURCC('PART'), g_tweakGunRes->xc0_grappleMuzzle})), x384_grappleSwooshDesc(g_SimplePool->GetObj(SObjectTag{FOURCC('SWHC'), g_tweakGunRes->xc4_grappleSwoosh})), x390_grappleSegmentGen(std::make_unique(x354_grappleSegmentDesc)), x394_grappleClawGen(std::make_unique(x360_grappleClawDesc)), x398_grappleHitGen(std::make_unique(x36c_grappleHitDesc)), x39c_grappleMuzzleGen(std::make_unique(x378_grappleMuzzleDesc)), x3a0_grappleSwooshGen(std::make_unique(x384_grappleSwooshDesc, 0)), x3a4_rainSplashGenerator(std::make_unique(scale, 20, 2, 0.f, 0.125f)) { x0_grappleArmModel->SetSortThermal(true); xa0_grappleGearModel.SetSortThermal(true); xec_grapNoz1Model.SetSortThermal(true); x138_grapNoz2Model.SetSortThermal(true); g_GrappleBeamAnglePhaseDelta = g_tweakPlayer->GetGrappleBeamAnglePhaseDelta(); g_GrappleBeamXWaveAmplitude = g_tweakPlayer->GetGrappleBeamXWaveAmplitude(); g_GrappleBeamZWaveAmplitude = g_tweakPlayer->GetGrappleBeamZWaveAmplitude(); g_GrappleBeamSpeed = g_tweakPlayer->GetGrappleBeamSpeed(); x39c_grappleMuzzleGen->SetParticleEmission(false); x390_grappleSegmentGen->SetParticleEmission(false); x3a0_grappleSwooshGen->DoGrappleWarmup(); BuildSuitDependencyList(); LoadAnimations(); } static const char* skDependencyNames[] = { "PowerSuit_DGRP", "GravitySuit_DGRP", "VariaSuit_DGRP", "PhazonSuit_DGRP", "FusionSuit_DGRP", "FusionSuitG_DGRP", "FusionSuitV_DGRP", "FusionSuitP_DGRP", }; void CGrappleArm::FillTokenVector(const std::vector& tags, std::vector& objects) { objects.reserve(tags.size()); for (const SObjectTag& tag : tags) objects.push_back(g_SimplePool->GetObj(tag)); } void CGrappleArm::BuildSuitDependencyList() { x184_grappleArm.Lock(); for (const char* name : skDependencyNames) { TLockedToken dgrp = g_SimplePool->GetObj(name); x19c_suitDeps.emplace_back(); std::vector& depsOut = x19c_suitDeps.back(); FillTokenVector(dgrp->GetObjectTagVector(), depsOut); } } void CGrappleArm::LoadAnimations() { NWeaponTypes::get_token_vector(*x0_grappleArmModel->GetAnimationData(), 0, 42, x18c_anims, true); x0_grappleArmModel = std::experimental::nullopt; } void CGrappleArm::AsyncLoadSuit(CStateManager& mgr) { CPlayerState::EPlayerSuit suit = NWeaponTypes::get_current_suit(mgr); if (suit == x3a8_loadedSuit) return; x0_grappleArmModel = std::experimental::nullopt; x3b2_29_suitLoading = true; if (x3a8_loadedSuit != CPlayerState::EPlayerSuit::Invalid) { NWeaponTypes::unlock_tokens(x19c_suitDeps[int(x3a8_loadedSuit)]); x19c_suitDeps[int(x3a8_loadedSuit)].clear(); } if (suit < CPlayerState::EPlayerSuit::Power || suit > CPlayerState::EPlayerSuit::FusionPhazon) x3a8_loadedSuit = CPlayerState::EPlayerSuit::Power; else x3a8_loadedSuit = suit; NWeaponTypes::lock_tokens(x19c_suitDeps[int(x3a8_loadedSuit)]); } void CGrappleArm::ResetAuxParams(bool resetGunController) { x3b2_24_active = false; x3b2_27_armMoving = false; x2e0_auxXf = zeus::CTransform::Identity(); if (resetGunController) x328_gunController->Reset(); } void CGrappleArm::DisconnectGrappleBeam() { x394_grappleClawGen->SetParticleEmission(false); x3b2_25_beamActive = false; GrappleBeamDisconnected(); } void CGrappleArm::SetAnimState(EArmState state) { if (x334_animState == state) return; x0_grappleArmModel->AnimationData()->EnableLooping(false); x3b2_28_isGrappling = true; switch (state) { case EArmState::IntoGrapple: { ResetAuxParams(true); CAnimPlaybackParms parms(0, -1, 1.f, true); x0_grappleArmModel->AnimationData()->SetAnimation(parms, false); x3b2_25_beamActive = false; x3b2_24_active = true; break; } case EArmState::IntoGrappleIdle: { CAnimPlaybackParms parms(1, -1, 1.f, true); x0_grappleArmModel->AnimationData()->EnableLooping(true); x0_grappleArmModel->AnimationData()->SetAnimation(parms, false); break; } case EArmState::FireGrapple: { CAnimPlaybackParms parms(2, -1, 1.f, true); x0_grappleArmModel->AnimationData()->SetAnimation(parms, false); break; } case EArmState::ConnectGrapple: { CAnimPlaybackParms parms(3, -1, 1.f, true); x0_grappleArmModel->AnimationData()->SetAnimation(parms, false); break; } case EArmState::Connected: { CAnimPlaybackParms parms(3, -1, 1.f, true); x0_grappleArmModel->AnimationData()->SetAnimation(parms, false); break; } case EArmState::OutOfGrapple: { CAnimPlaybackParms parms(4, -1, 1.f, true); x0_grappleArmModel->AnimationData()->SetAnimation(parms, false); DisconnectGrappleBeam(); break; } case EArmState::Done: x3b2_28_isGrappling = false; break; default: break; } x334_animState = state; } void CGrappleArm::Activate(bool intoGrapple) { SetAnimState(intoGrapple ? EArmState::IntoGrapple : EArmState::OutOfGrapple); } void CGrappleArm::GrappleBeamDisconnected() { if (x32c_grappleLoopSfx) { CSfxManager::SfxStop(x32c_grappleLoopSfx); x32c_grappleLoopSfx.reset(); } } void CGrappleArm::GrappleBeamConnected() { if (!x32c_grappleLoopSfx) x32c_grappleLoopSfx = NWeaponTypes::play_sfx(1527, false, true, -0.15f); } void CGrappleArm::RenderGrappleBeam(const CStateManager& mgr, const zeus::CVector3f& pos) { if (x3b2_24_active && !x3b2_29_suitLoading) { zeus::CTransform tmpXf = zeus::CTransform::Translate(pos) * x220_xf; if (x3b2_25_beamActive) { if (x3b2_26_grappleHit) x398_grappleHitGen->Render(); x394_grappleClawGen->Render(); x3a0_grappleSwooshGen->Render(); x390_grappleSegmentGen->Render(); zeus::CTransform backupViewMtx = CGraphics::g_ViewMatrix; CGraphics::SetViewPointMatrix(tmpXf.inverse() * backupViewMtx); CGraphics::SetModelMatrix(zeus::CTransform::Identity()); x39c_grappleMuzzleGen->Render(); CGraphics::SetViewPointMatrix(backupViewMtx); } } } void CGrappleArm::TouchModel(const CStateManager& mgr) const { if (x3b2_24_active && !x3b2_29_suitLoading) { x0_grappleArmModel->Touch(mgr, 0); if (x50_grappleArmSkeletonModel) x50_grappleArmSkeletonModel->Touch(mgr, 0); if (mgr.GetPlayerState()->HasPowerUp(CPlayerState::EItemType::GrappleBeam)) { xa0_grappleGearModel.Touch(mgr, 0); xec_grapNoz1Model.Touch(mgr, 0); x138_grapNoz2Model.Touch(mgr, 0); } } } void CGrappleArm::LoadSuitPoll() { if (NWeaponTypes::are_tokens_ready(x19c_suitDeps[int(x3a8_loadedSuit)])) { x0_grappleArmModel.emplace(CAnimRes(g_tweakGunRes->x8_grappleArm, 0, x31c_scale, 41, false)); x0_grappleArmModel->SetSortThermal(true); x328_gunController = std::make_unique(*x0_grappleArmModel); x3b2_29_suitLoading = false; } } void CGrappleArm::BuildXRayModel() { x50_grappleArmSkeletonModel.emplace(CAnimRes(g_tweakGunRes->x8_grappleArm, 8, x31c_scale, !x328_gunController ? 41 : x328_gunController->GetCurAnimId(), false)); x50_grappleArmSkeletonModel->SetSortThermal(true); } void CGrappleArm::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type) { switch (type) { case EUserEventType::Projectile: if (x3b2_27_armMoving) return; x3b2_25_beamActive = true; x398_grappleHitGen = std::make_unique(x36c_grappleHitDesc); x39c_grappleMuzzleGen = std::make_unique(x378_grappleMuzzleDesc); x338_beamT = 0.f; x33c_beamDist = 0.f; x340_anglePhase = 0.f; x344_xAmplitude = g_GrappleBeamXWaveAmplitude; x348_zAmplitude = g_GrappleBeamZWaveAmplitude; x398_grappleHitGen->SetParticleEmission(false); x394_grappleClawGen->SetParticleEmission(true); NWeaponTypes::play_sfx(1526, false, false, -0.15f); mgr.GetRumbleManager().Rumble(mgr, ERumbleFxId::PlayerGrappleFire, 1.f, ERumblePriority::Three); break; default: break; } } void CGrappleArm::DoUserAnimEvents(CStateManager& mgr) { zeus::CVector3f armToCam = mgr.GetCameraManager()->GetCurrentCamera(mgr)->GetTranslation() - x220_xf.origin; const CAnimData& animData = *x0_grappleArmModel->GetAnimationData(); for (int i=0 ; iUpdate(dt, mgr)) ResetAuxParams(false); } void CGrappleArm::UpdateGrappleBeamFx(const zeus::CVector3f& beamGunPos, const zeus::CVector3f& beamAirPos, CStateManager& mgr) { x394_grappleClawGen->SetTranslation(beamAirPos); x390_grappleSegmentGen->SetParticleEmission(true); zeus::CVector3f segmentDelta = beamAirPos - beamGunPos; zeus::CVector3f swooshSegmentDelta = segmentDelta * 0.02f; int numSegments = int(2.f * segmentDelta.magnitude() + 1.f); segmentDelta = (1.f / float(numSegments)) * segmentDelta; zeus::CVector3f segmentPos = beamGunPos; zeus::CTransform rotation = x220_xf.getRotation(); for (int i=0 ; i 0) vec = rotation * zeus::CVector3f(std::cos(i + x340_anglePhase) * x344_xAmplitude, 0.f, std::sin(float(i)) * x348_zAmplitude); x390_grappleSegmentGen->SetTranslation(vec * segmentPos); x390_grappleSegmentGen->ForceParticleCreation(1); segmentPos += segmentDelta; } x390_grappleSegmentGen->SetParticleEmission(false); x3a0_grappleSwooshGen->DoGrappleUpdate(beamGunPos, rotation, x340_anglePhase, x344_xAmplitude, x348_zAmplitude, swooshSegmentDelta); } bool CGrappleArm::UpdateGrappleBeam(float dt, const zeus::CTransform& beamLoc, CStateManager& mgr) { bool beamConnected = false; if (TCastToConstPtr act = mgr.GetObjectById(mgr.GetPlayer().GetOrbitTargetId())) x310_grapplePointPos = act->GetTranslation(); else x310_grapplePointPos = x220_xf.origin; zeus::CVector3f beamGunPos = (x220_xf * beamLoc).origin; zeus::CVector3f beamAirPos = beamGunPos * (1.f - x338_beamT) + x310_grapplePointPos * x338_beamT; switch (x334_animState) { case EArmState::FireGrapple: case EArmState::Three: { float gunToPointMag = (x310_grapplePointPos - beamGunPos).magnitude(); if (gunToPointMag > 0.f) x338_beamT = x33c_beamDist / gunToPointMag; else x338_beamT = 1.f; float speedMult = mgr.GetPlayer().GetPlayerMovementState() != CPlayer::EPlayerMovementState::OnGround ? 2.f : 1.f; x33c_beamDist += speedMult * (dt * g_GrappleBeamSpeed); if (x338_beamT >= 1.f) { x338_beamT = 1.f; beamConnected = true; } break; } case EArmState::ConnectGrapple: { float delta = 4.f * dt; x344_xAmplitude -= delta; x348_zAmplitude -= delta; if (x344_xAmplitude < 0.f) x344_xAmplitude = 0.f; if (x348_zAmplitude < 0.f) x348_zAmplitude = 0.f; break; } default: break; } if (x3b2_25_beamActive) { x340_anglePhase += g_GrappleBeamAnglePhaseDelta; UpdateGrappleBeamFx(beamGunPos, beamAirPos, mgr); x394_grappleClawGen->Update(dt); x390_grappleSegmentGen->Update(dt); } return beamConnected; } void CGrappleArm::UpdateSwingAction(float grappleSwingT, float dt, CStateManager& mgr) { if (x3b2_29_suitLoading) return; if (x334_animState == EArmState::FireGrapple) DoUserAnimEvents(mgr); zeus::CTransform beamLocXf = x0_grappleArmModel->GetScaledLocatorTransform("LGBeam"); bool grappleConnected = UpdateGrappleBeam(dt, beamLocXf, mgr); if ((grappleSwingT > 0.175f && grappleSwingT < 0.3f) || (grappleSwingT > 0.7f && grappleSwingT < 0.9f)) { if (!CSfxManager::IsPlaying(x330_swooshSfx)) { x330_swooshSfx = NWeaponTypes::play_sfx(1528, false, false, -0.15f); if (x3b0_rumbleHandle != -1) mgr.GetRumbleManager().StopRumble(x3b0_rumbleHandle); x3b0_rumbleHandle = mgr.GetRumbleManager().Rumble(mgr, ERumbleFxId::PlayerGrappleSwoosh, 1.f, ERumblePriority::Three); } } if (!x0_grappleArmModel->GetAnimationData()->IsAnimTimeRemaining(dt, "Whole Body")) { switch (x334_animState) { case EArmState::IntoGrapple: case EArmState::Seven: SetAnimState(EArmState::IntoGrappleIdle); break; case EArmState::FireGrapple: if (grappleConnected) { SetAnimState(EArmState::ConnectGrapple); x3b2_26_grappleHit = true; x398_grappleHitGen->SetParticleEmission(true); GrappleBeamConnected(); if (x3b0_rumbleHandle != -1) mgr.GetRumbleManager().StopRumble(x3b0_rumbleHandle); } break; case EArmState::ConnectGrapple: if (x344_xAmplitude == 0.f) SetAnimState(EArmState::Connected); break; case EArmState::OutOfGrapple: if (x3b0_rumbleHandle != -1) mgr.GetRumbleManager().StopRumble(x3b0_rumbleHandle); SetAnimState(EArmState::Done); x3b2_24_active = false; break; default: break; } } if (x3b2_25_beamActive) { x39c_grappleMuzzleGen->SetTranslation(beamLocXf.origin); x39c_grappleMuzzleGen->Update(dt); if (x3b2_26_grappleHit) { x3b2_26_grappleHit = !x398_grappleHitGen->IsSystemDeletable(); x398_grappleHitGen->SetTranslation(x310_grapplePointPos); x398_grappleHitGen->Update(dt); } } } void CGrappleArm::Update(float grappleSwingT, float dt, CStateManager& mgr) { if (!(x3b2_24_active && !x3b2_29_suitLoading)) { if (x3b2_29_suitLoading) LoadSuitPoll(); return; } if (mgr.GetPlayerState()->GetCurrentVisor() == CPlayerState::EPlayerVisor::XRay) { if (!x50_grappleArmSkeletonModel) BuildXRayModel(); } else { if (x50_grappleArmSkeletonModel) x50_grappleArmSkeletonModel = std::experimental::nullopt; } float speed = 1.f; if (!x3b2_27_armMoving) speed = (mgr.GetPlayer().GetPlayerMovementState() != CPlayer::EPlayerMovementState::OnGround && x334_animState != EArmState::OutOfGrapple) ? 4.f : 1.f; x0_grappleArmModel->AdvanceAnimation(speed * dt, mgr, kInvalidAreaId, true); if (mgr.GetPlayerState()->HasPowerUp(CPlayerState::EItemType::GrappleBeam)) { x250_grapLocatorXf = x0_grappleArmModel->GetScaledLocatorTransformDynamic("grapLocator_SDK", nullptr); x280_grapNozLoc1Xf = x0_grappleArmModel->GetScaledLocatorTransform("gNozLoc1_SDK"); x2b0_grapNozLoc2Xf = x0_grappleArmModel->GetScaledLocatorTransform("gNozLoc1_SDK"); } if (x3b2_27_armMoving) UpdateArmMovement(dt, mgr); else UpdateSwingAction(grappleSwingT, dt, mgr); if (x3a4_rainSplashGenerator) x3a4_rainSplashGenerator->Update(dt, mgr); } void CGrappleArm::PreRender(const CStateManager& mgr, const zeus::CFrustum& frustum, const zeus::CVector3f& camPos) { if (x3b2_24_active && !x3b2_29_suitLoading) { x0_grappleArmModel->AnimationData()->PreRender(); if (x50_grappleArmSkeletonModel) x50_grappleArmSkeletonModel->AnimationData()->PreRender(); } } void CGrappleArm::RenderXRayModel(const CStateManager& mgr, const zeus::CTransform& modelXf, const CModelFlags& flags) const { CGraphics::SetModelMatrix(modelXf * zeus::CTransform::Scale(x0_grappleArmModel->GetScale())); //CGraphics::DisableAllLights(); //g_Renderer->SetAmbientColor(zeus::CColor::skWhite); CSkinnedModel& model = const_cast(*x50_grappleArmSkeletonModel->GetAnimationData()->GetModelData()); model.GetModelInst()->ActivateLights({CLight::BuildLocalAmbient({}, zeus::CColor::skWhite)}); const_cast(this)->x0_grappleArmModel->AnimationData()->Render(model, flags, {}, nullptr); //g_Renderer->SetAmbientColor(zeus::CColor::skWhite); //CGraphics::DisableAllLights(); } void CGrappleArm::PointGenerator(void* ctx, const std::vector>& vn) { reinterpret_cast(ctx)->GeneratePoints(vn); } void CGrappleArm::Render(const CStateManager& mgr, const zeus::CVector3f& pos, const CModelFlags& flags, const CActorLights* lights) const { if (x3b2_24_active && !x3b2_29_suitLoading) { zeus::CTransform modelXf = zeus::CTransform::Translate(pos) * x220_xf * x2e0_auxXf; if (x50_grappleArmSkeletonModel) RenderXRayModel(mgr, modelXf, flags); CModelFlags useFlags; const CActorLights* useLights; if (mgr.GetPlayerState()->GetCurrentVisor() == CPlayerState::EPlayerVisor::XRay) { useFlags = CModelFlags(5, 0, 3, zeus::CColor(1.f, 0.25f)); useLights = nullptr; } else { useFlags = flags; useLights = lights; } if (x3a4_rainSplashGenerator && x3a4_rainSplashGenerator->IsRaining()) CSkinnedModel::SetPointGeneratorFunc(x3a4_rainSplashGenerator.get(), PointGenerator); x0_grappleArmModel->Render(mgr, modelXf, useLights, useFlags); if (x3a4_rainSplashGenerator && x3a4_rainSplashGenerator->IsRaining()) { CSkinnedModel::ClearPointGeneratorFunc(); x3a4_rainSplashGenerator->Draw(modelXf); } if (mgr.GetPlayerState()->HasPowerUp(CPlayerState::EItemType::GrappleBeam)) { xa0_grappleGearModel.Render(mgr, modelXf * x250_grapLocatorXf, useLights, useFlags); xec_grapNoz1Model.Render(mgr, modelXf * x280_grapNozLoc1Xf, useLights, useFlags); x138_grapNoz2Model.Render(mgr, modelXf * x2b0_grapNozLoc2Xf, useLights, useFlags); } } } void CGrappleArm::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CStateManager& mgr) { if (msg == EScriptObjectMessage::Registered) AsyncLoadSuit(mgr); } void CGrappleArm::EnterStruck(CStateManager& mgr, float angle, bool bigStrike, bool notInFreeLook) { if (x3b2_29_suitLoading) return; if (x3b2_28_isGrappling) { DisconnectGrappleBeam(); x3b2_28_isGrappling = false; } if (!x3b2_27_armMoving) { x3b2_24_active = true; x3b2_27_armMoving = true; x334_animState = EArmState::GunControllerAnimation; } x328_gunController->EnterStruck(mgr, angle, bigStrike, notInFreeLook); } void CGrappleArm::EnterIdle(CStateManager& mgr) { if (x3b2_29_suitLoading) return; x328_gunController->EnterIdle(mgr); } void CGrappleArm::EnterFidget(CStateManager& mgr, SamusGun::EFidgetType type, s32 gunId, s32 animSet) { if (x3b2_29_suitLoading) return; x3b2_24_active = true; x3b2_27_armMoving = true; x334_animState = EArmState::GunControllerAnimation; x328_gunController->EnterFidget(mgr, s32(type), gunId, animSet); } void CGrappleArm::EnterFreeLook(s32 gunId, s32 setId, CStateManager& mgr) { if (x3b2_29_suitLoading) return; x3b2_24_active = true; x3b2_27_armMoving = true; x334_animState = EArmState::GunControllerAnimation; x328_gunController->EnterFreeLook(mgr, gunId, setId); } void CGrappleArm::EnterComboFire(s32 gunId, CStateManager& mgr) { if (x3b2_29_suitLoading) return; x3b2_24_active = true; x3b2_27_armMoving = true; x334_animState = EArmState::GunControllerAnimation; x328_gunController->EnterComboFire(mgr, gunId); } void CGrappleArm::ReturnToDefault(CStateManager& mgr, float dt, bool setState) { if (x3b2_29_suitLoading) return; x328_gunController->ReturnToDefault(mgr, dt, setState); } }