2
0
mirror of https://github.com/AxioDL/metaforce.git synced 2025-05-13 17:51:21 +00:00
metaforce/Runtime/World/CScriptGunTurret.cpp
Lioncash 5d209c8dc8 General: Mark a handful of deduced const variables as const explicitly
Same behavior, but makes it explicit to the reader that these are const.

Prevents cases where the reader might assume that just because the
variable isn't const qualified that it must be mutable, when it actually
isn't.
2020-05-08 18:41:29 -04:00

1308 lines
51 KiB
C++

#include "Runtime/World/CScriptGunTurret.hpp"
#include <array>
#include "Runtime/CSimplePool.hpp"
#include "Runtime/GameGlobalObjects.hpp"
#include "Runtime/Character/CPASAnimParmData.hpp"
#include "Runtime/Collision/CCollisionActor.hpp"
#include "Runtime/Collision/CCollisionActorManager.hpp"
#include "Runtime/Graphics/CBooRenderer.hpp"
#include "Runtime/Particle/CElementGen.hpp"
#include "Runtime/Particle/CGenDescription.hpp"
#include "Runtime/Weapon/CEnergyProjectile.hpp"
#include "Runtime/Weapon/CGameProjectile.hpp"
#include "Runtime/World/CAiFuncMap.hpp"
#include "Runtime/World/CGameLight.hpp"
#include "Runtime/World/CPlayer.hpp"
#include "TCastTo.hpp" // Generated file, do not modify include path
namespace urde {
namespace {
constexpr CMaterialList skGunMaterialList = {EMaterialTypes::Solid, EMaterialTypes::Character, EMaterialTypes::Orbit,
EMaterialTypes::Target};
constexpr CMaterialList skTurretMaterialList = {EMaterialTypes::Character};
constexpr std::array<SBurst, 6> skBurst2InfoTemplate{{
{3, {1, 2, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{3, {7, 6, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{4, {3, 5, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{60, {16, 4, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{30, {4, 4, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{0, {0, 0, 0, 0, 0, 0, 0, 0}, 0.000000, 0.000000},
}};
constexpr std::array<SBurst, 6> skBurst3InfoTemplate{{
{30, {4, 5, 4, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{30, {2, 3, 4, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{30, {3, 4, 5, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{5, {16, 1, 2, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{5, {8, 7, 6, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{0, {0, 0, 0, 0, 0, 0, 0, 0}, 0.000000, 0.000000},
}};
constexpr std::array<SBurst, 8> skBurst4InfoTemplate{{
{5, {16, 1, 2, 3, 0, 0, 0, 0}, 0.150000, 0.050000},
{5, {9, 8, 7, 6, 0, 0, 0, 0}, 0.150000, 0.050000},
{15, {2, 3, 4, 5, 0, 0, 0, 0}, 0.150000, 0.050000},
{15, {5, 4, 3, 2, 0, 0, 0, 0}, 0.150000, 0.050000},
{15, {10, 11, 4, 13, 0, 0, 0, 0}, 0.150000, 0.050000},
{15, {14, 13, 4, 11, 0, 0, 0, 0}, 0.150000, 0.050000},
{30, {2, 4, 4, 6, 0, 0, 0, 0}, 0.150000, 0.050000},
{0, {0, 0, 0, 0, 0, 0, 0, 0}, 0.000000, 0.000000},
}};
constexpr std::array<SBurst, 6> skOOVBurst2InfoTemplate{{
{20, {16, 15, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{20, {8, 9, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{20, {13, 11, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{20, {2, 6, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{20, {3, 4, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{0, {0, 0, 0, 0, 0, 0, 0, 0}, 0.000000, 0.000000},
}};
constexpr std::array<SBurst, 6> skOOVBurst3InfoTemplate{{
{10, {14, 4, 10, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{10, {15, 13, 4, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{10, {9, 11, 4, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{35, {15, 13, 11, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{35, {9, 11, 13, -1, 0, 0, 0, 0}, 0.150000, 0.050000},
{0, {0, 0, 0, 0, 0, 0, 0, 0}, 0.000000, 0.000000},
}};
constexpr std::array<SBurst, 7> skOOVBurst4InfoTemplate{{
{10, {14, 13, 4, 11, 0, 0, 0, 0}, 0.150000, 0.050000},
{30, {1, 15, 13, 11, 0, 0, 0, 0}, 0.150000, 0.050000},
{20, {16, 15, 14, 13, 0, 0, 0, 0}, 0.150000, 0.050000},
{10, {8, 9, 11, 4, 0, 0, 0, 0}, 0.150000, 0.050000},
{10, {1, 15, 13, 4, 0, 0, 0, 0}, 0.150000, 0.050000},
{20, {8, 9, 10, 11, 0, 0, 0, 0}, 0.150000, 0.050000},
{0, {0, 0, 0, 0, 0, 0, 0, 0}, 0.000000, 0.000000},
}};
constexpr std::array<const SBurst*, 7> skBursts{
skBurst2InfoTemplate.data(),
skBurst3InfoTemplate.data(),
skBurst4InfoTemplate.data(),
skOOVBurst2InfoTemplate.data(),
skOOVBurst3InfoTemplate.data(),
skOOVBurst4InfoTemplate.data(),
nullptr,
};
constexpr std::array StateNames{
"Destroyed", "Deactive", "DeactiveFromReady", "Deactivating", "DeactivatingFromReady", "Inactive", "Ready",
"PanningA", "PanningB", "Targeting", "Firing", "ExitTargeting", "Frenzy",
};
constexpr std::array<u32, 13> skStateToLocoTypeLookup{
5, 7, 9, 0, 1, 0, 1, 2, 3, 1, 1, 1, 1,
};
} // Anonymous namespace
CScriptGunTurretData::CScriptGunTurretData(CInputStream& in, s32 propCount)
: x0_intoDeactivateDelay(in.readFloatBig())
, x4_intoActivateDelay(in.readFloatBig())
, x8_reloadTime(in.readFloatBig())
, xc_reloadTimeVariance(in.readFloatBig())
, x10_panStartTime(in.readFloatBig())
, x14_panHoldTime(in.readFloatBig())
, x1c_leftMaxAngle(zeus::degToRad(in.readFloatBig()))
, x20_rightMaxAngle(zeus::degToRad(in.readFloatBig()))
, x24_downMaxAngle(zeus::degToRad(in.readFloatBig()))
, x28_turnSpeed(zeus::degToRad(in.readFloatBig()))
, x2c_detectionRange(in.readFloatBig())
, x30_detectionZRange(in.readFloatBig())
, x34_freezeDuration(in.readFloatBig())
, x38_freezeVariance(in.readFloatBig())
, x3c_freezeTimeout(propCount >= 48 ? in.readBool() : false)
, x40_projectileRes(in)
, x44_projectileDamage(in)
, x60_idleLightRes(in.readUint32Big())
, x64_deactivateLightRes(in.readUint32Big())
, x68_targettingLightRes(in.readUint32Big())
, x6c_frozenEffectRes(in.readUint32Big())
, x70_chargingEffectRes(in.readUint32Big())
, x74_panningEffectRes(in.readUint32Big())
, x78_visorEffectRes(propCount >= 44 ? in.readUint32Big() : -1)
, x7c_trackingSoundId(CSfxManager::TranslateSFXID(u16(in.readUint32Big())))
, x7e_lockOnSoundId(CSfxManager::TranslateSFXID(u16(in.readUint32Big())))
, x80_unfreezeSoundId(CSfxManager::TranslateSFXID(u16(in.readUint32Big())))
, x82_stopClankSoundId(CSfxManager::TranslateSFXID(u16(in.readUint32Big())))
, x84_chargingSoundId(CSfxManager::TranslateSFXID(u16(in.readUint32Big())))
, x86_visorSoundId(propCount >= 45 ? CSfxManager::TranslateSFXID(u16(in.readUint32Big())) : u16(0xFFFF))
, x88_extensionModelResId(in.readUint32Big())
, x8c_extensionDropDownDist(in.readFloatBig())
, x90_numInitialShots(in.readUint32Big())
, x94_initialShotTableIndex(in.readUint32Big())
, x98_numSubsequentShots(in.readUint32Big())
, x9c_frenzyDuration(propCount >= 47 ? in.readFloatBig() : 3.f)
, xa0_scriptedStartOnly(propCount >= 46 ? in.readBool() : false) {}
CScriptGunTurret::CScriptGunTurret(TUniqueId uid, std::string_view name, ETurretComponent comp, const CEntityInfo& info,
const zeus::CTransform& xf, CModelData&& mData, const zeus::CAABox& aabb,
const CHealthInfo& hInfo, const CDamageVulnerability& dVuln,
const CActorParameters& aParms, const CScriptGunTurretData& turretData)
: CPhysicsActor(uid, true, name, info, xf, std::move(mData),
comp == ETurretComponent::Base ? skTurretMaterialList : skGunMaterialList, aabb, SMoverData(1000.f),
aParms, 0.3f, 0.1f)
, x258_type(comp)
, x264_healthInfo(hInfo)
, x26c_damageVuln(dVuln)
, x2d4_data(turretData)
, x37c_projectileInfo(turretData.GetProjectileRes(), turretData.GetProjectileDamage())
, x3a4_burstFire(skBursts.data(), 1)
, x410_idleLightDesc(g_SimplePool->GetObj({SBIG('PART'), turretData.GetIdleLightRes()}))
, x41c_deactivateLightDesc(g_SimplePool->GetObj({SBIG('PART'), turretData.GetDeactivateLightRes()}))
, x428_targettingLightDesc(g_SimplePool->GetObj({SBIG('PART'), turretData.GetTargettingLightRes()}))
, x434_frozenEffectDesc(g_SimplePool->GetObj({SBIG('PART'), turretData.GetFrozenEffectRes()}))
, x440_chargingEffectDesc(g_SimplePool->GetObj({SBIG('PART'), turretData.GetChargingEffectRes()}))
, x44c_panningEffectDesc(g_SimplePool->GetObj({SBIG('PART'), turretData.GetPanningEffectRes()})) {
if (turretData.GetVisorEffectRes().IsValid()) {
x458_visorEffectDesc = g_SimplePool->GetObj({SBIG('PART'), turretData.GetVisorEffectRes()});
}
x468_idleLight = std::make_unique<CElementGen>(x410_idleLightDesc);
x470_deactivateLight = std::make_unique<CElementGen>(x41c_deactivateLightDesc);
x478_targettingLight = std::make_unique<CElementGen>(x428_targettingLightDesc);
x480_frozenEffect = std::make_unique<CElementGen>(x434_frozenEffectDesc);
x488_chargingEffect = std::make_unique<CElementGen>(x440_chargingEffectDesc);
x490_panningEffect = std::make_unique<CElementGen>(x44c_panningEffectDesc);
x4fc_extensionOffset = xf.origin;
x514_lastFrontVector = xf.frontVector();
x544_originalFrontVec = xf.frontVector();
x550_originalRightVec = xf.rightVector();
if (comp == ETurretComponent::Base && HasModelData() && GetModelData()->HasAnimData()) {
GetModelData()->EnableLooping(true);
}
x37c_projectileInfo.Token().Lock();
}
CScriptGunTurret::~CScriptGunTurret() = default;
void CScriptGunTurret::Accept(IVisitor& visitor) { visitor.Visit(this); }
void CScriptGunTurret::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) {
CActor::AcceptScriptMsg(msg, uid, mgr);
switch (msg) {
case EScriptObjectMessage::Activate:
if (x49c_collisionManager) {
x49c_collisionManager->SetActive(mgr, true);
}
break;
case EScriptObjectMessage::Deactivate:
if (x49c_collisionManager) {
x49c_collisionManager->SetActive(mgr, false);
}
break;
case EScriptObjectMessage::Registered:
if (x258_type == ETurretComponent::Gun) {
if (x478_targettingLight->SystemHasLight()) {
x498_lightId = mgr.AllocateUniqueId();
mgr.AddObject(new CGameLight(x498_lightId, GetAreaIdAlways(), GetActive(),
std::string("ParticleLight_").append(GetName()), GetTransform(), GetUniqueId(),
x478_targettingLight->GetLight(), 0, 1, 0.f));
}
SetupCollisionManager(mgr);
} else if (x258_type == ETurretComponent::Base) {
const zeus::CVector3f scale = GetModelData()->GetScale();
if (x2d4_data.GetExtensionModelResId().IsValid()) {
CModelData mData(CStaticRes(x2d4_data.GetExtensionModelResId(), scale));
x4a4_extensionModel.emplace(std::move(mData));
x4f4_extensionRange = x4a4_extensionModel->GetBounds().max.z() - x4a4_extensionModel->GetBounds().min.z();
}
SetTurretState(ETurretState::Inactive, mgr);
}
break;
case EScriptObjectMessage::Deleted: {
if (x258_type == ETurretComponent::Gun) {
if (x498_lightId != kInvalidUniqueId) {
mgr.FreeScriptObject(x498_lightId);
}
}
if (x50c_targetingEmitter) {
CSfxManager::RemoveEmitter(x50c_targetingEmitter);
}
if (x49c_collisionManager) {
x49c_collisionManager->Destroy(mgr);
}
break;
}
case EScriptObjectMessage::Start:
if (x258_type == ETurretComponent::Base && x520_state == ETurretState::Inactive) {
x560_29_scriptedStart = true;
}
break;
case EScriptObjectMessage::Stop:
if (x258_type == ETurretComponent::Base && x520_state != ETurretState::Deactive &&
x520_state != ETurretState::DeactiveFromReady && x520_state != ETurretState::Deactivating) {
SetTurretState((x560_28_hasBeenActivated ? ETurretState::DeactivatingFromReady : ETurretState::Deactivating),
mgr);
}
break;
case EScriptObjectMessage::Action: {
if (x258_type == ETurretComponent::Gun) {
LaunchProjectile(mgr);
} else if (x258_type == ETurretComponent::Base) {
PlayAdditiveFlinchAnimation(mgr);
}
break;
}
case EScriptObjectMessage::SetToMax: {
x560_25_frozen = false;
SetMuted(false);
break;
}
case EScriptObjectMessage::SetToZero: {
x560_25_frozen = true;
SetMuted(true);
break;
}
case EScriptObjectMessage::InitializedInArea: {
if (x258_type == ETurretComponent::Base) {
for (const SConnection& conn : x20_conns) {
if (conn.x0_state != EScriptObjectState::Play || conn.x4_msg != EScriptObjectMessage::Activate) {
continue;
}
if (TCastToConstPtr<CScriptGunTurret> gun = mgr.GetObjectById(mgr.GetIdForScript(conn.x8_objId))) {
x25c_gunId = mgr.GetIdForScript(conn.x8_objId);
x260_lastGunHP = gun->GetHealthInfo(mgr)->GetHP();
return;
}
}
}
break;
}
case EScriptObjectMessage::Damage: {
if (x258_type == ETurretComponent::Gun && GetHealthInfo(mgr)->GetHP() > 0.f) {
if (const TCastToConstPtr<CGameProjectile> proj = mgr.GetObjectById(uid)) {
if ((proj->GetAttribField() & EProjectileAttrib::Wave) == EProjectileAttrib::Wave) {
x520_state = ETurretState::Frenzy;
RemoveMaterial(EMaterialTypes::Target, EMaterialTypes::Orbit, mgr);
mgr.GetPlayer().SetOrbitRequestForTarget(GetUniqueId(), CPlayer::EPlayerOrbitRequest::ActivateOrbitSource,
mgr);
x53c_freezeRemTime = 0.f;
}
}
}
break;
}
default:
break;
}
}
void CScriptGunTurret::Think(float dt, CStateManager& mgr) {
if (!GetActive()) {
return;
}
if (x258_type == ETurretComponent::Base) {
if (!x560_25_frozen) {
ProcessGunStateMachine(dt, mgr);
UpdateTurretAnimation();
UpdateGunOrientation(dt, mgr);
const zeus::CVector3f vec = UpdateExtensionModelState(dt);
const SAdvancementDeltas advancementDeltas = UpdateAnimation(dt, mgr, true);
SetTranslation(vec + advancementDeltas.x0_posDelta + GetTranslation());
RotateToOR(advancementDeltas.xc_rotDelta, dt);
} else {
Stop();
}
UpdateTargettingSound(dt);
} else if (x258_type == ETurretComponent::Gun) {
UpdateGunParticles(dt, mgr);
const SAdvancementDeltas deltas = UpdateAnimation(dt, mgr, true);
MoveToOR(deltas.x0_posDelta, dt);
RotateToOR(deltas.xc_rotDelta, dt);
UpdateGunCollisionManager(dt, mgr);
UpdateFrozenState(dt, mgr);
}
UpdateHealthInfo(mgr);
}
void CScriptGunTurret::Touch(CActor& act, CStateManager& mgr) {
if (x258_type != ETurretComponent::Gun) {
return;
}
if (const TCastToConstPtr<CGameProjectile> proj = act) {
const CPlayer& player = mgr.GetPlayer();
if (proj->GetOwnerId() == player.GetUniqueId()) {
const CDamageVulnerability* dVuln = GetDamageVulnerability();
if (!x560_24_dead && x520_state != ETurretState::Frenzy &&
(proj->GetAttribField() & EProjectileAttrib::Ice) == EProjectileAttrib::Ice &&
dVuln->WeaponHits(CWeaponMode::Ice(), false)) {
x560_25_frozen = true;
SendScriptMsgs(EScriptObjectState::Zero, mgr, EScriptObjectMessage::None);
x53c_freezeRemTime = mgr.GetActiveRandom()->Float() *
x2d4_data.GetFreezeVariance() + x2d4_data.GetFreezeDuration();
SetMuted(true);
}
SendScriptMsgs(EScriptObjectState::Damage, mgr, EScriptObjectMessage::None);
}
}
}
std::optional<zeus::CAABox> CScriptGunTurret::GetTouchBounds() const {
if (GetActive() && GetMaterialList().HasMaterial(EMaterialTypes::Solid)) {
return GetBoundingBox();
}
return std::nullopt;
}
zeus::CVector3f CScriptGunTurret::GetOrbitPosition(const CStateManager& mgr) const { return GetAimPosition(mgr, 0.f); }
zeus::CVector3f CScriptGunTurret::GetAimPosition(const CStateManager&, float) const {
if (x258_type == ETurretComponent::Base) {
return GetTranslation() + x34_transform.rotate(GetLocatorTransform("Gun_SDK"sv).origin);
}
return GetTranslation();
}
void CScriptGunTurret::SetupCollisionManager(CStateManager& mgr) {
std::vector<CJointCollisionDescription> jointDescs;
jointDescs.reserve(2);
const CAnimData* animData = GetModelData()->GetAnimationData();
x508_gunSDKSeg = animData->GetLocatorSegId("Gun_SDK"sv);
const CSegId blastLCTR = animData->GetLocatorSegId("Blast_LCTR"sv);
jointDescs.push_back(CJointCollisionDescription::SphereSubdivideCollision(
x508_gunSDKSeg, blastLCTR, 0.6f, 1.f, CJointCollisionDescription::EOrientationType::One, "Gun_SDK"sv, 1000.f));
jointDescs.push_back(CJointCollisionDescription::SphereCollision(blastLCTR, 0.3f, "Blast_LCTR"sv, 1000.f));
x49c_collisionManager = std::make_unique<CCollisionActorManager>(mgr, GetUniqueId(), GetAreaIdAlways(), jointDescs, true);
x49c_collisionManager->SetActive(mgr, GetActive());
for (int i = 0; i < x49c_collisionManager->GetNumCollisionActors(); ++i) {
const auto& desc = x49c_collisionManager->GetCollisionDescFromIndex(i);
if (const TCastToPtr<CCollisionActor> cAct = mgr.ObjectById(desc.GetCollisionActorId())) {
cAct->AddMaterial(EMaterialTypes::ProjectilePassthrough, mgr);
cAct->SetMaterialFilter(CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Player},
{EMaterialTypes::Character, EMaterialTypes::NoStaticCollision, EMaterialTypes::NoPlatformCollision}));
if (desc.GetName().find("Blast_LCTR"sv) != std::string_view::npos) {
x4a0_collisionActor = desc.GetCollisionActorId();
}
}
}
}
void CScriptGunTurret::SetTurretState(ETurretState state, CStateManager& mgr) {
if (state < ETurretState::Destroyed || state > ETurretState::Frenzy) {
return;
}
if (x520_state != ETurretState::Invalid) {
ProcessCurrentState(EStateMsg::Deactivate, mgr, 0.f);
}
if (state != ETurretState::Invalid && x520_state != state) {
#ifndef NDEBUG
fmt::print(FMT_STRING("{} {} {} - {}\n"), GetUniqueId(), GetEditorId(), GetName(), StateNames[size_t(state)]);
#endif
}
x520_state = state;
x524_curStateTime = 0.f;
ProcessCurrentState(EStateMsg::Activate, mgr, 0.f);
}
void CScriptGunTurret::LaunchProjectile(CStateManager& mgr) {
if (!x37c_projectileInfo.Token().IsLoaded() || !mgr.CanCreateProjectile(GetUniqueId(), EWeaponType::AI, 8)) {
return;
}
const zeus::CTransform xf = GetLocatorTransform("Blast_LCTR"sv);
const zeus::CVector3f projPt = GetTranslation() + GetTransform().rotate(xf.origin);
zeus::CVector3f lookPt = x404_targetPosition;
const zeus::CVector3f aimDelta = x404_targetPosition - projPt;
if (zeus::CVector3f::getAngleDiff(GetTransform().frontVector(), aimDelta) > zeus::degToRad(20.f)) {
if (aimDelta.canBeNormalized()) {
const zeus::CVector3f lookDir =
zeus::CVector3f::slerp(GetTransform().frontVector(), aimDelta.normalized(), zeus::degToRad(20.f));
lookPt = lookDir * aimDelta.magnitude() + projPt;
} else {
lookPt = projPt + GetTransform().frontVector();
}
} else if (!aimDelta.canBeNormalized()) {
lookPt = projPt + GetTransform().frontVector();
}
const zeus::CTransform useXf = zeus::lookAt(projPt, lookPt);
auto* proj = new CEnergyProjectile(true, x37c_projectileInfo.Token(), EWeaponType::AI, useXf,
EMaterialTypes::Character, x37c_projectileInfo.GetDamage(), mgr.AllocateUniqueId(),
GetAreaIdAlways(), GetUniqueId(), kInvalidUniqueId, EProjectileAttrib::None, false,
zeus::skOne3f, x458_visorEffectDesc, x2d4_data.GetVisorSoundId(), false);
mgr.AddObject(proj);
const auto pair =
x64_modelData->GetAnimationData()->GetCharacterInfo().GetPASDatabase().FindBestAnimation(
CPASAnimParmData(18, CPASAnimParm::FromEnum(1), CPASAnimParm::FromReal32(90.f),
CPASAnimParm::FromEnum(skStateToLocoTypeLookup[size_t(x520_state)])), -1);
if (pair.first > 0.f) {
x64_modelData->EnableLooping(false);
x64_modelData->GetAnimationData()->SetAnimation(CAnimPlaybackParms(pair.second, -1, 1.f, true), false);
}
}
void CScriptGunTurret::PlayAdditiveFlinchAnimation(CStateManager& mgr) {
const auto pair = GetModelData()->GetAnimationData()->GetCharacterInfo().GetPASDatabase().FindBestAnimation(
CPASAnimParmData(23), *mgr.GetActiveRandom(), -1);
if (pair.first > 0.f) {
GetModelData()->GetAnimationData()->AddAdditiveAnimation(pair.second, 1.f, false, true);
}
}
void CScriptGunTurret::AddToRenderer(const zeus::CFrustum& frustum, CStateManager& mgr) {
CActor::AddToRenderer(frustum, mgr);
if (x258_type != ETurretComponent::Gun) {
return;
}
if (!x560_25_frozen) {
switch (x520_state) {
case ETurretState::Deactive:
case ETurretState::DeactiveFromReady:
case ETurretState::Deactivating:
case ETurretState::DeactivatingFromReady:
g_Renderer->AddParticleGen(*x470_deactivateLight);
break;
case ETurretState::Inactive:
g_Renderer->AddParticleGen(*x468_idleLight);
break;
case ETurretState::PanningA:
case ETurretState::PanningB:
g_Renderer->AddParticleGen(*x490_panningEffect);
break;
case ETurretState::Ready:
case ETurretState::Targeting:
case ETurretState::Firing:
case ETurretState::ExitTargeting:
case ETurretState::Frenzy:
g_Renderer->AddParticleGen(*x478_targettingLight);
if (x520_state == ETurretState::Firing || x520_state == ETurretState::Frenzy) {
g_Renderer->AddParticleGen(*x488_chargingEffect);
}
break;
default:
break;
}
} else {
g_Renderer->AddParticleGen(*x480_frozenEffect);
}
}
void CScriptGunTurret::Render(CStateManager& mgr) {
CPhysicsActor::Render(mgr);
if (x258_type == ETurretComponent::Gun) {
if (!x560_25_frozen) {
switch (x520_state) {
case ETurretState::Deactive:
case ETurretState::DeactiveFromReady:
case ETurretState::Deactivating:
case ETurretState::DeactivatingFromReady:
x470_deactivateLight->Render(x90_actorLights.get());
break;
case ETurretState::Inactive:
x468_idleLight->Render(x90_actorLights.get());
break;
case ETurretState::PanningA:
case ETurretState::PanningB:
x490_panningEffect->Render(x90_actorLights.get());
break;
case ETurretState::Ready:
case ETurretState::Targeting:
case ETurretState::Firing:
case ETurretState::ExitTargeting:
case ETurretState::Frenzy:
x478_targettingLight->Render(x90_actorLights.get());
if (x520_state == ETurretState::Firing) {
x488_chargingEffect->Render(x90_actorLights.get());
}
break;
default:
break;
}
} else {
x480_frozenEffect->Render(x90_actorLights.get());
}
} else if (x258_type == ETurretComponent::Base) {
if (x4a4_extensionModel && x4f8_extensionT > 0.f) {
zeus::CTransform xf = GetTransform();
xf.origin = x4fc_extensionOffset + (x4f4_extensionRange * 0.5f * zeus::skDown);
CModelFlags flags;
flags.x2_flags = 3;
flags.x1_matSetIdx = 0;
flags.x4_color = zeus::skWhite;
x4a4_extensionModel->Render(mgr, xf, x90_actorLights.get(), flags);
}
}
}
void CScriptGunTurret::UpdateGunCollisionManager(float dt, CStateManager& mgr) {
if (TCastToPtr<CCollisionActor> colAct = mgr.ObjectById(x4a0_collisionActor)) {
colAct->SetActive(mgr.GetPlayer().GetMorphballTransitionState() != CPlayer::EPlayerMorphBallState::Morphed);
}
x49c_collisionManager->Update(dt, mgr, CCollisionActorManager::EUpdateOptions::ObjectSpace);
}
void CScriptGunTurret::UpdateFrozenState(float dt, CStateManager& mgr) {
if (x560_25_frozen) {
if (x53c_freezeRemTime <= 0.f) {
x560_25_frozen = false;
SendScriptMsgs(EScriptObjectState::UnFrozen, mgr, EScriptObjectMessage::None);
CSfxManager::AddEmitter(x2d4_data.GetUnFreezeSoundId(), GetTranslation(), zeus::skUp, false, false, 0x7f,
GetAreaIdAlways());
SetMuted(false);
} else if (x2d4_data.GetFreezeTimeout()) {
x53c_freezeRemTime -= dt;
}
} else {
x53c_freezeRemTime = 0.f;
}
}
void CScriptGunTurret::UpdateGunParticles(float dt, CStateManager& mgr) {
CGameLight* light = nullptr;
if (x498_lightId != kInvalidUniqueId) {
light = TCastToPtr<CGameLight>(mgr.ObjectById(x498_lightId));
}
if (!x560_25_frozen) {
const zeus::CTransform lightXf = GetLocatorTransform("light_LCTR"sv);
zeus::CVector3f pos = x34_transform.rotate(lightXf.origin);
pos += GetTranslation();
if (light) {
light->SetActive(true);
}
switch (x520_state) {
case ETurretState::Deactive:
case ETurretState::DeactiveFromReady:
case ETurretState::Deactivating:
case ETurretState::DeactivatingFromReady:
x468_idleLight->SetParticleEmission(false);
x470_deactivateLight->SetParticleEmission(true);
x478_targettingLight->SetParticleEmission(false);
x480_frozenEffect->SetParticleEmission(false);
x488_chargingEffect->SetParticleEmission(false);
x490_panningEffect->SetParticleEmission(false);
x470_deactivateLight->SetOrientation(GetTransform().getRotation());
x470_deactivateLight->SetGlobalTranslation(pos);
x470_deactivateLight->SetGlobalScale(GetModelData()->GetScale());
x470_deactivateLight->Update(dt);
if (light) {
if (x470_deactivateLight->SystemHasLight()) {
light->SetLight(x470_deactivateLight->GetLight());
} else {
light->SetActive(false);
}
}
break;
case ETurretState::Inactive:
x468_idleLight->SetParticleEmission(true);
x470_deactivateLight->SetParticleEmission(false);
x478_targettingLight->SetParticleEmission(false);
x480_frozenEffect->SetParticleEmission(false);
x488_chargingEffect->SetParticleEmission(false);
x490_panningEffect->SetParticleEmission(false);
x468_idleLight->SetOrientation(GetTransform().getRotation());
x468_idleLight->SetGlobalTranslation(pos);
x468_idleLight->SetGlobalScale(GetModelData()->GetScale());
x468_idleLight->Update(dt);
if (light) {
light->SetActive(false);
}
break;
case ETurretState::PanningA:
case ETurretState::PanningB:
x468_idleLight->SetParticleEmission(false);
x470_deactivateLight->SetParticleEmission(false);
x478_targettingLight->SetParticleEmission(false);
x480_frozenEffect->SetParticleEmission(false);
x488_chargingEffect->SetParticleEmission(false);
x490_panningEffect->SetParticleEmission(true);
x490_panningEffect->SetOrientation(GetTransform().getRotation());
x490_panningEffect->SetGlobalTranslation(GetTranslation());
x490_panningEffect->SetGlobalScale(GetModelData()->GetScale());
x490_panningEffect->Update(dt);
if (light) {
light->SetActive(false);
}
break;
case ETurretState::Targeting:
case ETurretState::Firing:
case ETurretState::ExitTargeting:
case ETurretState::Frenzy: {
const bool doEmission = x520_state == ETurretState::Firing || x520_state == ETurretState::Frenzy;
x468_idleLight->SetParticleEmission(false);
x470_deactivateLight->SetParticleEmission(false);
x478_targettingLight->SetParticleEmission(true);
x480_frozenEffect->SetParticleEmission(false);
x488_chargingEffect->SetParticleEmission(doEmission);
x478_targettingLight->SetOrientation(GetTransform().getRotation());
x478_targettingLight->SetGlobalTranslation(pos);
x478_targettingLight->SetGlobalScale(GetModelData()->GetScale());
x478_targettingLight->Update(dt);
if (x478_targettingLight->SystemHasLight()) {
light->SetLight(x478_targettingLight->GetLight());
} else {
light->SetActive(false);
}
if (doEmission) {
const zeus::CTransform blastXf = GetLocatorTransform("Blast_LCTR"sv);
zeus::CVector3f blastPos = GetTransform().rotate(blastXf.origin);
blastPos += GetTranslation();
x488_chargingEffect->SetOrientation(GetTransform().getRotation());
x488_chargingEffect->SetGlobalTranslation(blastPos);
x488_chargingEffect->SetGlobalScale(GetModelData()->GetScale());
x488_chargingEffect->Update(dt);
}
break;
}
default:
x468_idleLight->SetParticleEmission(false);
x470_deactivateLight->SetParticleEmission(false);
x478_targettingLight->SetParticleEmission(false);
x480_frozenEffect->SetParticleEmission(false);
x488_chargingEffect->SetParticleEmission(false);
x490_panningEffect->SetParticleEmission(false);
x490_panningEffect->Update(dt);
if (light) {
light->SetActive(false);
}
break;
}
} else {
x468_idleLight->SetParticleEmission(false);
x470_deactivateLight->SetParticleEmission(false);
x478_targettingLight->SetParticleEmission(false);
x480_frozenEffect->SetParticleEmission(true);
x488_chargingEffect->SetParticleEmission(false);
x490_panningEffect->SetParticleEmission(false);
x480_frozenEffect->SetOrientation(GetTransform().getRotation());
x480_frozenEffect->SetGlobalTranslation(GetTranslation());
x480_frozenEffect->SetGlobalScale(GetModelData()->GetScale());
x480_frozenEffect->Update(dt);
if (light) {
light->SetActive(false);
}
}
}
void CScriptGunTurret::ProcessGunStateMachine(float dt, CStateManager& mgr) {
ProcessCurrentState(EStateMsg::Update, mgr, dt);
x524_curStateTime += dt;
PlayAdditiveChargingAnimation(mgr);
if (x25c_gunId == kInvalidUniqueId) {
return;
}
if (const TCastToPtr<CScriptGunTurret> gunTurret = mgr.ObjectById(x25c_gunId)) {
if (gunTurret->x520_state != ETurretState::Frenzy) {
gunTurret->x520_state = x520_state;
} else if (x520_state != ETurretState::Frenzy) {
SetTurretState(ETurretState::Frenzy, mgr);
gunTurret->RemoveMaterial(EMaterialTypes::Target, EMaterialTypes::Orbit, mgr);
mgr.GetPlayer().SetOrbitRequestForTarget(GetUniqueId(), CPlayer::EPlayerOrbitRequest::ActivateOrbitSource, mgr);
}
}
}
void CScriptGunTurret::UpdateTurretAnimation() {
if (!HasModelData() || !GetModelData()->HasAnimData()) {
return;
}
if (x520_state > ETurretState::Frenzy) {
return;
}
const auto parmData = CPASAnimParmData(5, CPASAnimParm::FromEnum(0),
CPASAnimParm::FromEnum(skStateToLocoTypeLookup[size_t(x520_state)]));
const auto pair =
GetModelData()->GetAnimationData()->GetCharacterInfo().GetPASDatabase().FindBestAnimation(parmData, -1);
if (pair.first > 0.f && pair.second != x540_turretAnim) {
GetModelData()->GetAnimationData()->SetAnimation(CAnimPlaybackParms(pair.second, -1, 1.f, true), false);
GetModelData()->GetAnimationData()->EnableLooping(true);
x540_turretAnim = pair.second;
}
}
void CScriptGunTurret::ProcessCurrentState(EStateMsg msg, CStateManager& mgr, float dt) {
switch (x520_state) {
case ETurretState::Deactivating:
case ETurretState::DeactivatingFromReady:
ProcessDeactivatingState(msg, mgr);
break;
case ETurretState::Inactive:
ProcessInactiveState(msg, mgr, dt);
break;
case ETurretState::Ready:
ProcessReadyState(msg, mgr, dt);
break;
case ETurretState::PanningA:
case ETurretState::PanningB:
ProcessPanningState(msg, mgr, dt);
break;
case ETurretState::Targeting:
case ETurretState::Firing:
ProcessTargettingState(msg, mgr, dt);
break;
case ETurretState::ExitTargeting:
ProcessExitTargettingState(msg, mgr);
break;
case ETurretState::Frenzy:
ProcessFrenzyState(msg, mgr, dt);
break;
default:
break;
}
}
void CScriptGunTurret::ProcessDeactivatingState(EStateMsg msg, CStateManager& mgr) {
if (msg == EStateMsg::Update && x524_curStateTime >= x2d4_data.GetIntoDeactivateDelay()) {
SetTurretState(x560_28_hasBeenActivated ? ETurretState::DeactiveFromReady : ETurretState::Deactive, mgr);
}
}
void CScriptGunTurret::ProcessInactiveState(EStateMsg msg, CStateManager& mgr, float dt) {
if (msg == EStateMsg::Activate) {
x528_curInactiveTime = 0.f;
x560_27_burstSet = false;
if (const TCastToPtr<CScriptGunTurret> gunTurret = mgr.ObjectById(x25c_gunId)) {
x260_lastGunHP = gunTurret->HealthInfo(mgr)->GetHP();
}
} else if (msg == EStateMsg::Update) {
bool forceActivate = false;
if (x25c_gunId != kInvalidUniqueId)
if (const TCastToPtr<CScriptGunTurret> gun = mgr.ObjectById(x25c_gunId)) {
forceActivate = gun->HealthInfo(mgr)->GetHP() < x260_lastGunHP;
}
if (x2d4_data.GetScriptedStartOnly() ? (forceActivate || x560_29_scriptedStart) :
(forceActivate || x560_29_scriptedStart || InDetectionRange(mgr))) {
x528_curInactiveTime += dt;
if (forceActivate || x528_curInactiveTime >= x2d4_data.GetIntoActivateDelay())
SetTurretState(ETurretState::Ready, mgr);
} else {
x468_idleLight->SetParticleEmission(true);
}
} else if (msg == EStateMsg::Deactivate) {
x560_28_hasBeenActivated = true;
x468_idleLight->SetParticleEmission(false);
if (const TCastToConstPtr<CScriptGunTurret> gunTurret = mgr.ObjectById(x25c_gunId)) {
x260_lastGunHP = gunTurret->GetHealthInfo(mgr)->GetHP();
}
}
}
void CScriptGunTurret::ProcessReadyState(EStateMsg msg, CStateManager& mgr, float dt) {
if (msg == EStateMsg::Activate) {
x52c_curActiveTime = 0.f;
} else if (msg == EStateMsg::Update) {
x52c_curActiveTime += dt;
if (x52c_curActiveTime < x2d4_data.GetPanStartTime()) {
return;
}
if (IsPlayerInFiringRange(mgr) && InDetectionRange(mgr)) {
SetTurretState(ETurretState::Targeting, mgr);
CSfxManager::AddEmitter(x2d4_data.GetLockOnSoundId(), GetTranslation(), zeus::skUp, false, false, 0x7f,
GetAreaIdAlways());
} else {
SetTurretState(ETurretState::PanningA, mgr);
x530_curPanTime = 0.f;
}
}
}
void CScriptGunTurret::ProcessPanningState(EStateMsg msg, CStateManager& mgr, float dt) {
if (msg == EStateMsg::Activate) {
x52c_curActiveTime = 0.f;
} else if (msg == EStateMsg::Update) {
if (IsPlayerInFiringRange(mgr) && InDetectionRange(mgr)) {
SetTurretState(ETurretState::Targeting, mgr);
CSfxManager::AddEmitter(x2d4_data.GetLockOnSoundId(), GetTranslation(), zeus::skUp, false, false, 0x7f,
GetAreaIdAlways());
} else {
x52c_curActiveTime += dt;
x530_curPanTime += dt;
if (x530_curPanTime >= x2d4_data.GetTotalPanSearchTime() && !x4a4_extensionModel &&
!x2d4_data.GetScriptedStartOnly()) {
SetTurretState(ETurretState::Inactive, mgr);
} else if (x52c_curActiveTime >= x2d4_data.GetPanHoldTime()) {
SetTurretState(x520_state != ETurretState::PanningA ? ETurretState::PanningA : ETurretState::PanningB, mgr);
}
}
}
}
void CScriptGunTurret::ProcessTargettingState(EStateMsg msg, CStateManager& mgr, float dt) {
if (msg == EStateMsg::Activate) {
x52c_curActiveTime = 0.f;
} else if (msg == EStateMsg::Update) {
if (x560_26_firedWithSetBurst || InDetectionRange(mgr)) {
UpdateTargettingMode(dt, mgr);
if (x25c_gunId != kInvalidUniqueId) {
if (TCastToPtr<CScriptGunTurret> gun = mgr.ObjectById(x25c_gunId)) {
zeus::CVector3f vec = x404_targetPosition;
if (IsPlayerInFiringRange(mgr)) {
const zeus::CTransform blastXf = gun->GetLocatorTransform("Blast_LCTR"sv);
const zeus::CVector3f rotatedBlastVec = GetTransform().rotate(blastXf.origin) + GetTranslation();
x404_targetPosition = mgr.GetPlayer().GetAimPosition(mgr, 0.f);
vec = x37c_projectileInfo.PredictInterceptPos(rotatedBlastVec, mgr.GetPlayer().GetAimPosition(mgr, dt),
mgr.GetPlayer(), false, dt);
}
zeus::CVector3f compensated =
x3a4_burstFire.GetDistanceCompensatedError(
(x404_targetPosition - gun->GetTranslation()).magnitude(), 20.f);
compensated = gun->GetTransform().rotate(compensated);
gun->x404_targetPosition = x404_targetPosition + (vec - x404_targetPosition) + compensated;
}
}
zeus::CVector3f diffVec = x404_targetPosition - GetTranslation();
diffVec.z() = 0.f;
if (diffVec.canBeNormalized()) {
const zeus::CVector3f normDiff = diffVec.normalized();
const float angDif = zeus::CVector3f::getAngleDiff(normDiff, GetTransform().frontVector());
zeus::CQuaternion quat = zeus::CQuaternion::lookAt(GetTransform().frontVector(), normDiff,
std::min(angDif, (dt * x2d4_data.GetTurnSpeed())));
quat.setImaginary(GetTransform().transposeRotate(quat.getImaginary()));
RotateInOneFrameOR(quat, dt);
}
if (ShouldFire(mgr)) {
SendScriptMsgs(EScriptObjectState::Attack, mgr, EScriptObjectMessage::None);
x560_26_firedWithSetBurst = true;
}
x52c_curActiveTime = 0.f;
} else {
x52c_curActiveTime += dt;
if (x52c_curActiveTime >= 10.f) {
SetTurretState(ETurretState::ExitTargeting, mgr);
}
}
} else if (msg == EStateMsg::Deactivate) {
x560_30_needsStopClankSound = true;
}
}
void CScriptGunTurret::ProcessExitTargettingState(EStateMsg msg, CStateManager& mgr) {
if (msg != EStateMsg::Update || x25c_gunId == kInvalidUniqueId)
return;
if (TCastToPtr<CScriptGunTurret> gun = mgr.ObjectById(x25c_gunId)) {
//zeus::CTransform gunXf = GetTransform() * GetLocatorTransform("Gun_SDK"sv);
if (zeus::CVector3f::getAngleDiff(gun->GetTransform().frontVector(), x544_originalFrontVec) < zeus::degToRad(0.9f))
SetTurretState(ETurretState::Ready, mgr);
}
}
void CScriptGunTurret::ProcessFrenzyState(EStateMsg msg, CStateManager& mgr, float dt) {
if (msg == EStateMsg::Activate) {
x560_31_frenzyReverse = mgr.GetActiveRandom()->Float() < 0.f;
x534_fireCycleRemTime = 0.15f;
RemoveMaterial(EMaterialTypes::Target, EMaterialTypes::Orbit, mgr);
mgr.GetPlayer().SetOrbitRequestForTarget(GetUniqueId(), CPlayer::EPlayerOrbitRequest::ActivateOrbitSource, mgr);
} else if (msg == EStateMsg::Update) {
if (x524_curStateTime >= x2d4_data.GetFrenzyDuration()) {
SetTurretState(ETurretState::Destroyed, mgr);
if (const TCastToPtr<CScriptGunTurret> gun = mgr.ObjectById(x25c_gunId)) {
gun->x520_state = ETurretState::Destroyed;
}
return;
}
const zeus::CVector3f frontVec = GetTransform().frontVector();
if (x560_31_frenzyReverse && x550_originalRightVec.magSquared() < 0.f &&
zeus::CVector3f::getAngleDiff(x544_originalFrontVec, frontVec) >= zeus::degToRad(45.f)) {
x560_31_frenzyReverse = false;
} else if (!x560_31_frenzyReverse && x550_originalRightVec.magSquared() < 0.f &&
zeus::CVector3f::getAngleDiff(x544_originalFrontVec, frontVec) >= zeus::degToRad(45.f)) {
x560_31_frenzyReverse = true;
}
if (const TCastToConstPtr<CScriptGunTurret> gun = mgr.ObjectById(x25c_gunId)) {
x534_fireCycleRemTime -= dt;
if (x534_fireCycleRemTime >= 0.f) {
return;
}
x404_targetPosition = gun->GetTranslation() + (100.f * gun->GetTransform().frontVector());
SendScriptMsgs(EScriptObjectState::Attack, mgr, EScriptObjectMessage::None);
x534_fireCycleRemTime = 0.15f;
}
}
}
bool CScriptGunTurret::IsPlayerInFiringRange(CStateManager& mgr) const {
const zeus::CVector3f posDif = mgr.GetPlayer().GetTranslation() - GetTranslation();
const zeus::CVector3f someVec(posDif.x(), posDif.y(), 0.f);
if (x550_originalRightVec.dot(posDif) >= 0.f) {
return zeus::CVector3f::getAngleDiff(x544_originalFrontVec, someVec) <= x2d4_data.GetRightMaxAngle();
}
if (zeus::CVector3f::getAngleDiff(x544_originalFrontVec, someVec) <= x2d4_data.GetLeftMaxAngle()) {
return true;
}
const float biasedAngle = zeus::CVector3f::getAngleDiff(posDif, zeus::skUp) - zeus::degToRad(90.f);
return biasedAngle >= zeus::degToRad(-20.f) && biasedAngle <= x2d4_data.GetDownMaxAngle();
}
bool CScriptGunTurret::LineOfSightTest(CStateManager& mgr) const {
if (x25c_gunId == kInvalidUniqueId) {
return false;
}
if (const TCastToConstPtr<CScriptGunTurret> gun = mgr.ObjectById(x25c_gunId)) {
if (x560_27_burstSet || (x520_state == ETurretState::Inactive && x4a4_extensionModel)) {
return true;
}
const zeus::CTransform xf = GetLocatorTransform("Blast_LCTR"sv);
const zeus::CVector3f muzzlePos = gun->GetTransform().rotate(xf.origin) + gun->GetTranslation();
zeus::CVector3f dir = mgr.GetPlayer().GetAimPosition(mgr, 0.f) - muzzlePos;
const float mag = dir.magnitude();
dir = dir / mag;
rstl::reserved_vector<TUniqueId, 1024> nearList;
constexpr auto filter = CMaterialFilter::MakeIncludeExclude(
{EMaterialTypes::Solid}, {EMaterialTypes::Player, EMaterialTypes::CollisionActor});
mgr.BuildNearList(nearList, muzzlePos, dir, mag, filter, gun.GetPtr());
TUniqueId id = kInvalidUniqueId;
return mgr.RayWorldIntersection(id, muzzlePos, dir, mag, filter, nearList).IsInvalid();
}
return false;
}
bool CScriptGunTurret::InDetectionRange(CStateManager& mgr) const {
const zeus::CVector3f delta = mgr.GetPlayer().GetTranslation() - GetTranslation();
if (delta.dot(zeus::skDown) < 0.f &&
zeus::CVector3f::getAngleDiff(GetTransform().frontVector(), delta) > zeus::degToRad(20.f)) {
return false;
}
if (delta.magSquared() > x2d4_data.GetDetectionRange() * x2d4_data.GetDetectionRange()) {
return false;
}
if (x2d4_data.GetDetectionZRange() != 0.f && std::fabs(delta.z()) >= x2d4_data.GetDetectionZRange()) {
return false;
}
return LineOfSightTest(mgr);
}
zeus::CVector3f CScriptGunTurret::UpdateExtensionModelState(float dt) {
if (!x4a4_extensionModel) {
return {};
}
switch (x520_state) {
case ETurretState::PanningA:
case ETurretState::PanningB:
case ETurretState::Targeting:
case ETurretState::Firing:
case ETurretState::ExitTargeting:
x4f8_extensionT = std::min(0.9f, x4f8_extensionT + 1.5f * dt);
break;
default:
x4f8_extensionT = std::max(0.f, x4f8_extensionT - 1.5f * dt);
break;
case ETurretState::Ready:
case ETurretState::Deactivating:
case ETurretState::DeactivatingFromReady:
case ETurretState::Frenzy:
break;
}
return (x4fc_extensionOffset + (x2d4_data.GetExtensionDropDownDist() * x4f8_extensionT * zeus::skDown)) -
GetTranslation();
}
void CScriptGunTurret::UpdateHealthInfo(CStateManager& mgr) {
switch (x258_type) {
case ETurretComponent::Base:
if (x25c_gunId != kInvalidUniqueId) {
if (!TCastToConstPtr<CScriptGunTurret>(mgr.ObjectById(x25c_gunId))) {
SetTurretState(ETurretState::Destroyed, mgr);
x560_25_frozen = false;
x25c_gunId = kInvalidUniqueId;
if (x50c_targetingEmitter) {
CSfxManager::RemoveEmitter(x50c_targetingEmitter);
x50c_targetingEmitter.reset();
}
}
} else {
SetTurretState(ETurretState::Destroyed, mgr);
}
break;
case ETurretComponent::Gun:
if (!x560_24_dead && x520_state != ETurretState::Frenzy && HealthInfo(mgr)->GetHP() <= 0.f) {
x560_24_dead = true;
SendScriptMsgs(EScriptObjectState::Dead, mgr, EScriptObjectMessage::None);
mgr.FreeScriptObject(GetUniqueId());
}
break;
default:
break;
}
}
bool CScriptGunTurret::PlayerInsideTurretSphere(CStateManager& mgr) const {
if (const TCastToConstPtr<CCollisionActor> cAct = mgr.GetObjectById(x4a0_collisionActor)) {
if (cAct->GetActive()) {
zeus::CVector3f delta = mgr.GetPlayer().GetAimPosition(mgr, 0.f) - GetTranslation();
if (delta.z() < 0.f) {
const float rad = cAct->GetSphereRadius() * 2.f +
(cAct->GetTranslation() - GetTranslation()).magnitude();
return delta.magSquared() < rad * rad;
}
}
}
return false;
}
void CScriptGunTurret::UpdateGunOrientation(float dt, CStateManager& mgr) {
if (x25c_gunId == kInvalidUniqueId) {
return;
}
if (const TCastToPtr<CScriptGunTurret> gun = mgr.ObjectById(x25c_gunId)) {
zeus::CTransform xf = GetLocatorTransform("Gun_SDK"sv);
xf = GetTransform() * xf;
switch (x520_state) {
case ETurretState::Targeting:
case ETurretState::Firing: {
const float xyMagSq = xf.frontVector().toVec2f().magSquared();
float useYaw = 0.f;
if (std::sqrt(xyMagSq) > 0.001f) {
useYaw = -std::atan2(xf.frontVector().x(), xf.frontVector().y());
}
const float oldPitch = gun->GetPitch();
float usePitch = 0.f;
if (!gun->PlayerInsideTurretSphere(mgr)) {
zeus::CTransform newXf;
if ((x404_targetPosition - xf.origin).canBeNormalized()) {
newXf = zeus::lookAt(xf.origin, x404_targetPosition);
} else {
newXf = GetTransform();
}
const float newPitch = -std::atan2(-newXf.frontVector().z(), newXf.frontVector().toVec2f().magnitude());
const float newPitchDelta = newPitch - oldPitch;
const float f2 = newPitchDelta > 0.f ? dt * x2d4_data.GetTurnSpeed() : dt * -x2d4_data.GetTurnSpeed();
usePitch = std::max(std::fabs(newPitchDelta) <= std::fabs(f2) ? newPitch : oldPitch + f2,
-x2d4_data.GetDownMaxAngle());
}
zeus::CQuaternion qy, qx, qz;
qy.rotateY(0.f);
qx.rotateX(usePitch);
qz.rotateZ(useYaw);
gun->SetTransform((qz * qx * qy).toTransform(xf.origin));
break;
}
case ETurretState::ExitTargeting: {
const zeus::CVector3f frontVec = GetTransform().frontVector();
const zeus::CVector3f gunFrontVec = gun->GetTransform().frontVector();
const float rotAngle = 0.3f * dt * x2d4_data.GetTurnSpeed();
zeus::CQuaternion quat = zeus::CQuaternion::lookAt(gunFrontVec, frontVec, rotAngle);
quat.setImaginary(gun->GetTransform().transposeRotate(quat.getImaginary()));
gun->RotateInOneFrameOR(quat, dt);
zeus::CQuaternion quat2 = zeus::CQuaternion::lookAt(frontVec, x544_originalFrontVec, rotAngle);
quat2.setImaginary(GetTransform().transposeRotate(quat2.getImaginary()));
RotateInOneFrameOR(quat2, dt);
break;
}
case ETurretState::Frenzy: {
const float xyMagSq = xf.frontVector().toVec2f().magSquared();
float useYaw = 0.f;
if (std::sqrt(xyMagSq) > 0.001f) {
useYaw = -std::atan2(xf.frontVector().x(), xf.frontVector().y());
}
const float f28 = -0.5f * x2d4_data.GetDownMaxAngle() *
(1.f - std::cos(2.f * x524_curStateTime * x2d4_data.GetTurnSpeed()));
const float pitch = gun->GetPitch();
const float f2 = f28 - pitch;
const float f31 = x2d4_data.GetTurnSpeed() * dt;
const float f3 = f2 > 0.f ? f31 : -f31;
float usePitch = std::fabs(f2) <= std::fabs(f3) ? f28 : pitch + f3;
usePitch = std::max(usePitch, -x2d4_data.GetDownMaxAngle());
zeus::CQuaternion qy, qx, qz;
qy.rotateY(0.f);
qx.rotateX(usePitch);
qz.rotateZ(useYaw);
gun->SetTransform((qz * qx * qy).toTransform(xf.origin));
zeus::CQuaternion rot = zeus::CQuaternion::lookAt(
GetTransform().frontVector(), x560_31_frenzyReverse ? -x550_originalRightVec : x550_originalRightVec, f31);
rot.setImaginary(GetTransform().transposeRotate(rot.getImaginary()));
RotateInOneFrameOR(rot, dt);
break;
}
default:
gun->SetTransform(xf);
break;
}
}
}
void CScriptGunTurret::UpdateTargettingSound(float dt) {
x510_timeSinceLastTargetSfx += dt;
const float angleDiff2D =
zeus::CVector2f::getAngleDiff(x514_lastFrontVector.toVec2f(), GetTransform().frontVector().toVec2f());
if (x560_30_needsStopClankSound && angleDiff2D < zeus::degToRad(20.f) &&
(x520_state == ETurretState::Targeting || x520_state == ETurretState::Firing)) {
if (!x560_25_frozen) {
CSfxManager::AddEmitter(x2d4_data.GetStopClankSoundId(), GetTranslation(), zeus::skUp, false, false, 127,
GetAreaIdAlways());
}
x560_30_needsStopClankSound = false;
}
if (x510_timeSinceLastTargetSfx >= 0.5f && !x560_25_frozen) {
if (x520_state == ETurretState::Targeting || x520_state == ETurretState::Firing ||
x520_state == ETurretState::Frenzy) {
const bool insignificant = IsInsignificantRotation(dt);
if (!insignificant && !x50c_targetingEmitter) {
x50c_targetingEmitter = CSfxManager::AddEmitter(x2d4_data.GetTrackingSoundId(), GetTranslation(), zeus::skUp,
false, true, 127, GetAreaIdAlways());
} else if (insignificant && x50c_targetingEmitter) {
CSfxManager::RemoveEmitter(x50c_targetingEmitter);
x50c_targetingEmitter.reset();
x510_timeSinceLastTargetSfx = 0.f;
}
if (x50c_targetingEmitter) {
const float bendScale = dt * x2d4_data.GetTurnSpeed();
CSfxManager::PitchBend(x50c_targetingEmitter, std::max(0.f, (bendScale > 0.f ? angleDiff2D / bendScale : 0.f)));
}
}
} else if (x560_25_frozen && x50c_targetingEmitter) {
CSfxManager::RemoveEmitter(x50c_targetingEmitter);
x50c_targetingEmitter.reset();
}
x514_lastFrontVector = GetTransform().frontVector();
}
void CScriptGunTurret::PlayAdditiveChargingAnimation(CStateManager& mgr) {
if (x520_state == ETurretState::Firing) {
if (x55c_additiveChargeAnim != -1) {
return;
}
const auto pair = GetModelData()->GetAnimationData()->GetCharacterInfo().GetPASDatabase().FindBestAnimation(
CPASAnimParmData(24, CPASAnimParm::FromEnum(2)), *mgr.GetActiveRandom(), -1);
if (pair.first > 0.f) {
x55c_additiveChargeAnim = pair.second;
GetModelData()->GetAnimationData()->AddAdditiveAnimation(pair.second, 1.f, true, false);
}
} else if (x55c_additiveChargeAnim != -1) {
GetModelData()->GetAnimationData()->DelAdditiveAnimation(x55c_additiveChargeAnim);
x55c_additiveChargeAnim = -1;
}
}
void CScriptGunTurret::UpdateTargettingMode(float dt, CStateManager& mgr) {
if (mgr.GetCameraManager()->IsInCinematicCamera()) {
x534_fireCycleRemTime =
mgr.GetActiveRandom()->Float() * x2d4_data.GetReloadTimeVariance() + x2d4_data.GetReloadTime();
x538_halfFireCycleDur = 0.5f * x534_fireCycleRemTime;
}
if (x534_fireCycleRemTime > 0.f) {
x534_fireCycleRemTime -= dt;
if (x534_fireCycleRemTime < x538_halfFireCycleDur && x520_state != ETurretState::Firing) {
CSfxManager::AddEmitter(x2d4_data.GetChargingSoundId(), GetTranslation(), zeus::skUp, false, false, 0x7f,
GetAreaIdAlways());
SetTurretState(ETurretState::Firing, mgr);
return;
}
} else {
if (x520_state != ETurretState::Targeting) {
SetTurretState(ETurretState::Targeting, mgr);
}
if (!x3a4_burstFire.IsBurstSet()) {
UpdateBurstType(mgr);
x534_fireCycleRemTime =
mgr.GetActiveRandom()->Float() * x2d4_data.GetReloadTimeVariance() + x2d4_data.GetReloadTime();
x538_halfFireCycleDur = 0.5f * x534_fireCycleRemTime;
} else {
x3a4_burstFire.Update(mgr, dt);
}
}
}
void CScriptGunTurret::UpdateBurstType(CStateManager& mgr) {
if (x560_27_burstSet) {
bool inView = true;
if (mgr.GetPlayer().GetMorphballTransitionState() != CPlayer::EPlayerMorphBallState::Morphed) {
const zeus::CVector3f frontVec = GetTransform().frontVector();
const zeus::CVector3f plFrontVec = mgr.GetPlayer().GetTransform().frontVector();
const float dot = frontVec.dot(plFrontVec);
if (dot >= 0.f) {
inView = false;
}
}
u32 r3 = mgr.GetActiveRandom()->Range(0, 3);
r3 += 2;
u32 type = 1;
if (r3 <= 2 || x2d4_data.GetNumSubsequentShots() < 3) {
type = 0;
} else if (r3 >= 5 && x2d4_data.GetNumSubsequentShots() > 3) {
type = 2;
}
x3a4_burstFire.SetBurstType(type + (inView ? 0 : 3));
} else {
const u32 type = x2d4_data.GetNumInitialShots() - 2;
x3a4_burstFire.SetBurstType(type);
x3a4_burstFire.SetFirstBurstIndex(x2d4_data.GetInitialShotTableIndex());
}
x3a4_burstFire.Start(mgr);
x560_26_firedWithSetBurst = false;
x560_27_burstSet = true;
}
bool CScriptGunTurret::ShouldFire(CStateManager& mgr) const {
if (x520_state == ETurretState::Targeting && x534_fireCycleRemTime <= 0.f && x3a4_burstFire.ShouldFire()) {
return IsPlayerInFiringRange(mgr);
}
return false;
}
bool CScriptGunTurret::IsInsignificantRotation(float dt) const {
return zeus::CVector2f::getAngleDiff(x514_lastFrontVector.toVec2f(), GetTransform().frontVector().toVec2f()) <
zeus::degToRad(2.f) * dt;
}
} // namespace urde