metaforce/Runtime/MP1/World/CChozoGhost.cpp

769 lines
29 KiB
C++

#include "Runtime/MP1/World/CChozoGhost.hpp"
#include "Runtime/GameGlobalObjects.hpp"
#include "Runtime/CStateManager.hpp"
#include "Runtime/CSimplePool.hpp"
#include "Runtime/CRandom16.hpp"
#include "Runtime/Character/CPASAnimParmData.hpp"
#include "Runtime/Weapon/CGameProjectile.hpp"
#include "Runtime/World/CPlayer.hpp"
#include "Runtime/World/CScriptCoverPoint.hpp"
#include "Runtime/World/CScriptWaypoint.hpp"
#include "Runtime/World/CTeamAiMgr.hpp"
#include "TCastTo.hpp" // Generated file, do not modify include path
namespace metaforce::MP1 {
CChozoGhost::CBehaveChance::CBehaveChance(CInputStream& in)
: x0_propertyCount(in.ReadLong())
, x4_lurk(in.ReadFloat())
, x8_taunt(in.ReadFloat())
, xc_attack(in.ReadFloat())
, x10_move(in.ReadFloat())
, x14_lurkTime(in.ReadFloat())
, x18_chargeAttack(x0_propertyCount < 6 ? 0.5f : in.ReadFloat() * .01f)
, x1c_numBolts(x0_propertyCount < 7 ? 2 : in.ReadLong()) {
float f2 = 1.f / (x10_move + xc_attack + x4_lurk + x8_taunt);
x4_lurk *= f2;
x8_taunt *= f2;
xc_attack *= f2;
x10_move *= f2;
}
EBehaveType CChozoGhost::CBehaveChance::GetBehave(EBehaveType type, CStateManager& mgr) const {
float lurkChance = x4_lurk;
float tauntChance = x8_taunt;
float attackChance = xc_attack;
if (type == EBehaveType::Lurk) {
float delta = lurkChance / 3.f;
lurkChance = 0.f;
tauntChance += delta;
attackChance += delta;
} else if (type == EBehaveType::Taunt) {
float delta = tauntChance / 3.f;
tauntChance = 0.f;
lurkChance += delta;
attackChance += delta;
} else if (type == EBehaveType::Attack) {
float delta = attackChance / 3.f;
attackChance = 0.f;
lurkChance += delta;
tauntChance += delta;
} else if (type == EBehaveType::Move) {
float delta = x10_move / 3.f;
lurkChance += delta;
tauntChance += delta;
attackChance += delta;
}
float rnd = mgr.GetActiveRandom()->Float();
if (lurkChance > rnd)
return EBehaveType::Lurk;
else if (tauntChance > rnd - lurkChance)
return EBehaveType::Taunt;
else if (attackChance > rnd - lurkChance - tauntChance)
return EBehaveType::Attack;
return EBehaveType::Move;
}
CChozoGhost::CChozoGhost(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf,
CModelData&& mData, const CActorParameters& actParms, const CPatternedInfo& pInfo,
float hearingRadius, float fadeOutDelay, float attackDelay, float freezeTime, CAssetId wpsc1,
const CDamageInfo& dInfo1, CAssetId wpsc2, const CDamageInfo& dInfo2,
const CBehaveChance& chance1, const CBehaveChance& chance2, const CBehaveChance& chance3,
u16 soundImpact, float f5, u16 sId2, u16 sId3, u32 w1, float f6, u32 w2, float hurlRecoverTime,
CAssetId projectileVisorEffect, s16 soundProjectileVisor, float f8, float f9, u32 nearChance,
u32 midChance)
: CPatterned(ECharacter::ChozoGhost, uid, name, EFlavorType::Zero, info, xf, std::move(mData), pInfo,
EMovementType::Flyer, EColliderType::Zero, EBodyType::BiPedal, actParms, EKnockBackVariant::Medium)
, x568_hearingRadius(hearingRadius)
, x56c_fadeOutDelay(fadeOutDelay)
, x570_attackDelay(attackDelay)
, x574_freezeTime(freezeTime)
, x578_(wpsc1, dInfo1)
, x5a0_(wpsc2, dInfo2)
, x5c8_(chance1)
, x5e8_(chance2)
, x608_(chance3)
, x628_soundImpact(soundImpact)
, x62c_(f5)
, x630_sfxFadeIn(sId2)
, x632_sfxFadeOut(sId3)
, x634_(f6)
, x638_hurlRecoverTime(hurlRecoverTime)
, x63c_(w2)
, x650_sound_ProjectileVisor(soundProjectileVisor)
, x654_(f8)
, x658_(f9)
, x65c_nearChance(nearChance)
, x660_midChance(midChance)
, x664_24_behaviorEnabled(w1)
, x664_25_flinch(!w1)
, x680_behaveType(x664_24_behaviorEnabled ? EBehaveType::Attack : EBehaveType::None)
, x68c_boneTracking(*GetModelData()->GetAnimationData(), "Head_1"sv, zeus::degToRad(80.f), zeus::degToRad(180.f),
EBoneTrackingFlags::None) {
x578_.Token().Lock();
x5a0_.Token().Lock();
x668_ = GetModelData()->GetScale().z() *
GetAnimationDistance(
CPASAnimParmData(pas::EAnimationState::Jump, CPASAnimParm::FromEnum(3), CPASAnimParm::FromEnum(0)));
x66c_ = GetModelData()->GetScale().z() *
GetAnimationDistance(
CPASAnimParmData(pas::EAnimationState::Slide, CPASAnimParm::FromEnum(1), CPASAnimParm::FromReal32(90.f)));
x670_ = GetModelData()->GetScale().z() *
GetAnimationDistance(CPASAnimParmData(pas::EAnimationState::MeleeAttack, CPASAnimParm::FromEnum(2),
CPASAnimParm::FromEnum(1)));
if (projectileVisorEffect.IsValid())
x640_projectileVisor = g_SimplePool->GetObj({SBIG('PART'), projectileVisorEffect});
x460_knockBackController.SetEnableBurn(false);
x460_knockBackController.SetEnableLaggedBurnDeath(false);
x460_knockBackController.SetEnableShock(false);
x460_knockBackController.SetEnableFreeze(false);
CreateShadow(false);
MakeThermalColdAndHot();
}
void CChozoGhost::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) {
CPatterned::AcceptScriptMsg(msg, uid, mgr);
switch (msg) {
case EScriptObjectMessage::Activate:
AddToTeam(mgr);
break;
case EScriptObjectMessage::Deactivate:
case EScriptObjectMessage::Deleted:
RemoveFromTeam(mgr);
break;
case EScriptObjectMessage::Action:
if (x664_25_flinch)
x665_29_aggressive = true;
break;
case EScriptObjectMessage::Alert:
if (x664_26_alert)
break;
x664_26_alert = true;
x400_24_hitByPlayerProjectile = true;
break;
case EScriptObjectMessage::Falling:
case EScriptObjectMessage::Jumped: {
if (!x328_25_verticalMovement)
x150_momentum = {0.f, 0.f, -(GetGravityConstant() * GetMass())};
break;
}
case EScriptObjectMessage::InitializedInArea:
if (GetActive())
AddToTeam(mgr);
break;
default:
break;
}
}
void CChozoGhost::Think(float dt, CStateManager& mgr) {
if (!GetActive())
return;
CPatterned::Think(dt, mgr);
UpdateThermalFrozenState(false);
x68c_boneTracking.Update(dt);
x6c8_spaceWarpTime = std::max(0.f, x6c8_spaceWarpTime - dt);
xe7_31_targetable = IsVisibleEnough(mgr);
}
void CChozoGhost::PreRender(CStateManager& mgr, const zeus::CFrustum& frustum) {
x402_29_drawParticles = mgr.GetPlayerState()->GetActiveVisor(mgr) == CPlayerState::EPlayerVisor::XRay;
if (mgr.GetPlayerState()->GetActiveVisor(mgr) == CPlayerState::EPlayerVisor::Thermal) {
SetCalculateLighting(false);
x90_actorLights->BuildConstantAmbientLighting(zeus::skWhite);
} else {
SetCalculateLighting(true);
}
u8 alpha = GetModelAlphau8(mgr);
u8 r, g, b, a;
x42c_color.toRGBA8(r, g, b, a);
if (alpha < 255 || r == 0) {
zeus::CColor col;
if (r == 0) {
col = zeus::skWhite;
} else {
float v = std::max<s8>(0, r - 255) * (1.f / 255.f);
col.r() = 1.f;
col.g() = v;
col.b() = v;
}
col.a() = alpha * (1.f / 255.f);
xb4_drawFlags = CModelFlags(5, 0, 3, col);
} else {
xb4_drawFlags = CModelFlags(0, 0, 3, zeus::skWhite);
}
CPatterned::PreRender(mgr, frustum);
x68c_boneTracking.PreRender(mgr, *GetModelData()->GetAnimationData(), GetTransform(), GetModelData()->GetScale(),
*GetBodyController());
}
void CChozoGhost::Render(CStateManager& mgr) {
if (x6c8_spaceWarpTime > 0.f)
mgr.DrawSpaceWarp(x6cc_spaceWarpPosition, std::sin((M_PIF * x6c8_spaceWarpTime) / x56c_fadeOutDelay));
if (mgr.GetPlayerState()->GetActiveVisor(mgr) == CPlayerState::EPlayerVisor::XRay) {
CElementGen::SetSubtractBlend(true);
CElementGen::SetMoveRedToAlphaBuffer(true);
CGraphics::SetFog(ERglFogMode::PerspLin, 0.f, 75.f, zeus::skBlack);
GetModelData()->GetAnimationData()->GetParticleDB().RenderSystemsToBeDrawnFirst();
mgr.SetupFogForArea3XRange(GetAreaIdAlways());
}
CPatterned::Render(mgr);
if (mgr.GetPlayerState()->GetActiveVisor(mgr) == CPlayerState::EPlayerVisor::XRay) {
CGraphics::SetFog(ERglFogMode::PerspLin, 0.f, 75.f, zeus::skBlack);
GetModelData()->GetAnimationData()->GetParticleDB().RenderSystemsToBeDrawnLast();
mgr.SetupFogForArea(GetAreaIdAlways());
CElementGen::SetSubtractBlend(false);
CElementGen::SetMoveRedToAlphaBuffer(false);
}
}
void CChozoGhost::Touch(CActor& act, CStateManager& mgr) {
if (IsVisibleEnough(mgr)) {
if (TCastToPtr<CPlayer> pl = act) {
if (x420_curDamageRemTime <= 0.f) {
mgr.ApplyDamage(GetUniqueId(), pl->GetUniqueId(), GetUniqueId(), GetContactDamage(),
CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {}), {});
x420_curDamageRemTime = x424_damageWaitTime;
}
}
}
CPatterned::Touch(act, mgr);
}
EWeaponCollisionResponseTypes CChozoGhost::GetCollisionResponseType(const zeus::CVector3f& pos,
const zeus::CVector3f& dir, const CWeaponMode& mode,
EProjectileAttrib attrib) const {
return EWeaponCollisionResponseTypes::ChozoGhost;
}
void CChozoGhost::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt) {
if (type == EUserEventType::FadeIn) {
if (x664_30_fadedOut) {
x3e8_alphaDelta = 2.f;
CSfxManager::AddEmitter(x630_sfxFadeIn, GetTranslation(), {}, false, false, 127, -1);
}
AddMaterial(EMaterialTypes::Target, mgr);
x664_30_fadedOut = false;
x664_29_fadedIn = true;
return;
} else if (type == EUserEventType::Projectile) {
const zeus::CTransform& xf =
zeus::lookAt(GetLctrTransform(node.GetLocatorName()).origin, mgr.GetPlayer().GetAimPosition(mgr, 0.f));
if (x67c_attackType == 2) {
CGameProjectile* proj =
LaunchProjectile(xf, mgr, 2, EProjectileAttrib::BigStrike | EProjectileAttrib::StaticInterference, true,
x640_projectileVisor, x650_sound_ProjectileVisor, false, zeus::skOne3f);
if (proj) {
proj->AddAttrib(EProjectileAttrib::BigStrike);
proj->SetDamageDuration(x62c_);
proj->AddAttrib(EProjectileAttrib::StaticInterference);
proj->SetInterferenceDuration(x62c_);
proj->SetMinHomingDistance(x634_);
}
} else {
CGameProjectile* proj =
LaunchProjectile(xf, mgr, 5, EProjectileAttrib::DamageFalloff | EProjectileAttrib::StaticInterference, true,
{x640_projectileVisor}, x650_sound_ProjectileVisor, false, zeus::skOne3f);
if (proj) {
if (GetProjectileInfo()->GetProjectileSpeed() > 0.f) {
proj->SetDamageFalloffSpeed(80.f / GetProjectileInfo()->GetProjectileSpeed());
}
proj->AddAttrib(EProjectileAttrib::BigStrike);
proj->SetDamageDuration(x62c_);
proj->AddAttrib(EProjectileAttrib::StaticInterference);
proj->SetInterferenceDuration(x62c_);
proj->SetMinHomingDistance(x634_);
}
}
return;
} else if (type == EUserEventType::FadeOut) {
if (x664_29_fadedIn) {
x3e8_alphaDelta = -2.f;
CSfxManager::AddEmitter(x632_sfxFadeOut, GetTranslation(), {}, false, false, 127, -1);
}
RemoveMaterial(EMaterialTypes::Target, mgr);
x664_29_fadedIn = false;
x664_30_fadedOut = true;
x665_26_shouldSwoosh = true;
return;
}
CPatterned::DoUserAnimEvent(mgr, node, type, dt);
if (type == EUserEventType::Delete)
x3e8_alphaDelta = -1.f;
}
void CChozoGhost::KnockBack(const zeus::CVector3f& dir, CStateManager& mgr, const CDamageInfo& info,
EKnockBackType type, bool inDeferred, float magnitude) {
if (!IsAlive()) {
x460_knockBackController.SetAvailableState(EKnockBackAnimationState::Hurled, false);
} else if (!x460_knockBackController.TestAvailableState(EKnockBackAnimationState::KnockBack) &&
info.GetWeaponMode().IsCharged()) {
x460_knockBackController.SetAnimationStateRange(EKnockBackAnimationState::Hurled, EKnockBackAnimationState::Fall);
}
CPatterned::KnockBack(dir, mgr, info, type, inDeferred, magnitude);
x460_knockBackController.SetAnimationStateRange(EKnockBackAnimationState::Flinch, EKnockBackAnimationState::Fall);
if (!IsAlive()) {
Stop();
x150_momentum.zeroOut();
} else if (x460_knockBackController.GetActiveParms().x0_animState == EKnockBackAnimationState::Hurled) {
x330_stateMachineState.SetState(mgr, *this, GetStateMachine(), "Hurled"sv);
}
}
bool CChozoGhost::CanBeShot(const CStateManager& mgr, int) { return IsVisibleEnough(mgr); }
void CChozoGhost::Dead(CStateManager& mgr, EStateMsg msg, float) {
if (msg == EStateMsg::Activate) {
ReleaseCoverPoint(mgr, x674_coverPoint);
x3e8_alphaDelta = 4.f;
x664_30_fadedOut = false;
x664_29_fadedIn = false;
x68c_boneTracking.SetActive(false);
Stop();
}
}
void CChozoGhost::SelectTarget(CStateManager& mgr, EStateMsg msg, float) {
if (msg == EStateMsg::Activate)
FindBestAnchor(mgr);
}
void CChozoGhost::Run(CStateManager& mgr, EStateMsg msg, float dt) {
if (msg == EStateMsg::Activate) {
GetBodyController()->SetLocomotionType(pas::ELocomotionType::Lurk);
x400_24_hitByPlayerProjectile = false;
x460_knockBackController.SetAvailableState(EKnockBackAnimationState::KnockBack, false);
x665_28_inRange = false;
} else if (msg == EStateMsg::Update) {
GetBodyController()->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(x688_.Seek(*this, x2e0_destPos), {}, 1.f));
if (x665_26_shouldSwoosh) {
x678_floorLevel = x2e0_destPos.z();
FloatToLevel(x678_floorLevel, dt);
GetModelData()->GetAnimationData()->SetParticleEffectState("SpeedSwoosh", true, mgr);
x665_24_ = false;
if (!x665_28_inRange) {
const float range = 2.5f * (dt * x138_velocity.magnitude()) + x66c_;
x665_28_inRange = (GetTranslation() - x2e0_destPos).magSquared() < range * range;
}
}
} else if (msg == EStateMsg::Deactivate) {
GetBodyController()->SetLocomotionType(pas::ELocomotionType::Crouch);
SetDestPos(mgr.GetPlayer().GetTranslation());
GetModelData()->GetAnimationData()->SetParticleEffectState("SpeedSwoosh", false, mgr);
x460_knockBackController.SetAvailableState(EKnockBackAnimationState::KnockBack, true);
x665_28_inRange = false;
}
}
void CChozoGhost::Generate(CStateManager& mgr, EStateMsg msg, float dt) {
if (msg == EStateMsg::Activate) {
x330_stateMachineState.SetDelay(x56c_fadeOutDelay);
x32c_animState = EAnimState::Ready;
x664_27_onGround = false;
const CRayCastResult& res = mgr.RayStaticIntersection(GetTranslation(), zeus::skDown, 100.f,
CMaterialFilter::MakeInclude({EMaterialTypes::Floor}));
if (res.IsInvalid()) {
x678_floorLevel = mgr.GetPlayer().GetTranslation().z();
} else {
x678_floorLevel = res.GetPoint().z();
}
x3e8_alphaDelta = 1.f;
x664_29_fadedIn = true;
if (x56c_fadeOutDelay > 0.f) {
x6c8_spaceWarpTime = x56c_fadeOutDelay;
FindSpaceWarpPosition(mgr, zeus::skDown);
}
} else if (msg == EStateMsg::Update) {
TryCommand(mgr, pas::EAnimationState::Jump, &CPatterned::TryJump, 0);
if (x32c_animState == EAnimState::Over) {
x68c_boneTracking.SetActive(true);
x68c_boneTracking.SetTarget(mgr.GetPlayer().GetUniqueId());
FloatToLevel(x678_floorLevel, dt);
} else if (x32c_animState == EAnimState::Repeat) {
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Crouch);
if (!x664_27_onGround) {
const zeus::CVector3f& pos = GetTranslation();
SetTranslation({pos.x(), pos.y(), x678_floorLevel + x668_});
x664_27_onGround = true;
}
}
} else if (msg == EStateMsg::Deactivate) {
x32c_animState = EAnimState::NotReady;
x665_24_ = false;
x664_27_onGround = false;
}
}
void CChozoGhost::Deactivate(CStateManager& mgr, EStateMsg msg, float) {
if (msg == EStateMsg::Activate) {
x68c_boneTracking.SetActive(false);
ReleaseCoverPoint(mgr, x674_coverPoint);
x32c_animState = EAnimState::Ready;
x665_24_ = true;
} else if (msg == EStateMsg::Update) {
TryCommand(mgr, pas::EAnimationState::Generate, &CPatterned::TryGenerateNoXf, 1);
if (x32c_animState == EAnimState::Repeat)
GetBodyController()->SetLocomotionType(pas::ELocomotionType::Relaxed);
} else if (msg == EStateMsg::Deactivate) {
x32c_animState = EAnimState::NotReady;
}
}
void CChozoGhost::Attack(CStateManager& mgr, EStateMsg msg, float dt) {
if (msg == EStateMsg::Activate) {
CTeamAiMgr::AddAttacker(CTeamAiMgr::EAttackType::Melee, mgr, x6c4_teamMgr, GetUniqueId());
x32c_animState = EAnimState::Ready;
if (x6d8_ == 1)
x67c_attackType = 3;
else if (x6d8_ == 2)
x67c_attackType = 4;
else if (x6d8_ == 3)
x67c_attackType = 5;
const CRayCastResult& res = mgr.RayStaticIntersection(GetTranslation() + (zeus::skUp * 0.5f), zeus::skUp, x670_,
CMaterialFilter::MakeInclude({EMaterialTypes::Solid}));
if (x665_26_shouldSwoosh && res.IsValid()) {
x67c_attackType = 2;
x460_knockBackController.SetAvailableState(EKnockBackAnimationState::KnockBack, false);
}
x150_momentum.zeroOut();
xfc_constantForce.zeroOut();
} else if (msg == EStateMsg::Update) {
TryCommand(mgr, pas::EAnimationState::MeleeAttack, &CPatterned::TryMeleeAttack, x67c_attackType);
GetBodyController()->GetCommandMgr().DeliverTargetVector(mgr.GetPlayer().GetTranslation() - GetTranslation());
if (x67c_attackType != 2)
FloatToLevel(x678_floorLevel, dt);
} else if (msg == EStateMsg::Deactivate) {
x32c_animState = EAnimState::NotReady;
x665_26_shouldSwoosh = false;
x460_knockBackController.SetAvailableState(EKnockBackAnimationState::KnockBack, true);
CTeamAiMgr::ResetTeamAiRole(CTeamAiMgr::EAttackType::Ranged, mgr, x6c4_teamMgr, GetUniqueId(), true);
}
}
void CChozoGhost::Shuffle(CStateManager& mgr, EStateMsg msg, float) {
if (msg != EStateMsg::Activate)
return;
const CBehaveChance& chance1 = ChooseBehaveChanceRange(mgr);
CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x6c4_teamMgr, GetUniqueId());
if (role && role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Ranged &&
!CTeamAiMgr::CanAcceptAttacker(CTeamAiMgr::EAttackType::Ranged, mgr, x6c4_teamMgr, GetUniqueId())) {
x680_behaveType = EBehaveType::Attack;
}
const CBehaveChance& chance2 = ChooseBehaveChanceRange(mgr);
x680_behaveType = chance2.GetBehave(x680_behaveType, mgr);
if (x680_behaveType == EBehaveType::Lurk)
x684_lurkDelay = chance1.GetLurkTime();
else if (x680_behaveType == EBehaveType::Attack) {
x665_25_ = mgr.GetActiveRandom()->Float() < chance1.GetChargeAttack();
const int rnd = mgr.GetActiveRandom()->Next();
x6d8_ = (rnd - (rnd / chance1.GetNumBolts()) * chance1.GetNumBolts()) + 1;
}
x664_31_ = false;
x665_27_playerInLeashRange = false;
}
void CChozoGhost::InActive(CStateManager& mgr, EStateMsg msg, float) {
if (msg == EStateMsg::Activate) {
if (!x450_bodyController->GetActive())
x450_bodyController->Activate(mgr);
if (x63c_ == 3) {
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Crouch);
x42c_color.a() = 1.f;
} else {
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed);
x42c_color.a() = 0.f;
}
RemoveMaterial(EMaterialTypes::Solid, mgr);
x150_momentum.zeroOut();
x665_24_ = true;
}
}
void CChozoGhost::Taunt(CStateManager& mgr, EStateMsg msg, float dt) {
if (msg == EStateMsg::Activate) {
x32c_animState = EAnimState::Ready;
} else if (msg == EStateMsg::Update) {
TryCommand(mgr, pas::EAnimationState::Taunt, &CPatterned::TryTaunt, 0);
FloatToLevel(x678_floorLevel, dt);
} else {
x32c_animState = EAnimState::NotReady;
x665_26_shouldSwoosh = false;
}
}
void CChozoGhost::Hurled(CStateManager& mgr, EStateMsg msg, float) {
if (msg == EStateMsg::Activate) {
x328_25_verticalMovement = false;
x664_27_onGround = false;
x665_24_ = true;
} else if (msg == EStateMsg::Update) {
x3e8_alphaDelta = 2.f;
if (x664_27_onGround)
return;
if (x138_velocity.z() < 0.f) {
const CRayCastResult& res = mgr.RayStaticIntersection(GetTranslation() + zeus::skUp, zeus::skDown, 2.f,
CMaterialFilter::MakeInclude({EMaterialTypes::Floor}));
if (res.IsValid()) {
x664_27_onGround = true;
x150_momentum.zeroOut();
SetVelocityWR({x138_velocity.x(), x138_velocity.y(), 0.f});
x678_floorLevel = res.GetPoint().z();
x330_stateMachineState.SetCodeTrigger();
}
}
if (!x664_27_onGround && x638_hurlRecoverTime < x330_stateMachineState.GetTime()) {
GetBodyController()->GetCommandMgr().DeliverCmd(CBodyStateCmd(EBodyStateCmd::ExitState));
GetBodyController()->SetLocomotionType(pas::ELocomotionType::Lurk);
x330_stateMachineState.SetCodeTrigger();
}
} else if (msg == EStateMsg::Deactivate) {
x328_25_verticalMovement = true;
x150_momentum.zeroOut();
}
}
void CChozoGhost::WallDetach(CStateManager& mgr, EStateMsg msg, float) {
if (msg == EStateMsg::Activate) {
x330_stateMachineState.SetDelay(x56c_fadeOutDelay);
x3e8_alphaDelta = 0.f;
x664_29_fadedIn = false;
if (x56c_fadeOutDelay > 0.f) {
x6c8_spaceWarpTime = x56c_fadeOutDelay;
FindSpaceWarpPosition(mgr, GetTransform().basis[1]);
}
TUniqueId wpId = GetWaypointForState(mgr, EScriptObjectState::Attack, EScriptObjectMessage::Follow);
TCastToConstPtr<CScriptWaypoint> wp;
if (wpId != kInvalidUniqueId) {
wp = TCastToConstPtr<CScriptWaypoint>(mgr.GetObjectById(wpId));
}
if (wp)
SetDestPos(wp->GetTranslation());
else
SetDestPos(GetTranslation() + (2.f * x66c_) * GetTranslation());
SendScriptMsgs(EScriptObjectState::Attack, mgr, EScriptObjectMessage::Follow);
} else if (msg == EStateMsg::Deactivate) {
x68c_boneTracking.SetActive(true);
x68c_boneTracking.SetTarget(mgr.GetPlayer().GetUniqueId());
x665_24_ = false;
x680_behaveType = EBehaveType::Move;
}
}
void CChozoGhost::Growth(CStateManager& mgr, EStateMsg msg, float) {
if (msg == EStateMsg::Activate) {
x330_stateMachineState.SetDelay(x56c_fadeOutDelay);
GetBodyController()->SetLocomotionType(pas::ELocomotionType::Crouch);
x3e8_alphaDelta = 1.f;
x664_29_fadedIn = true;
if (x56c_fadeOutDelay > 0.f) {
x6c8_spaceWarpTime = x56c_fadeOutDelay;
FindSpaceWarpPosition(mgr, zeus::skUp);
}
} else if (msg == EStateMsg::Deactivate) {
x665_24_ = false;
x68c_boneTracking.SetActive(false);
x68c_boneTracking.SetTarget(mgr.GetPlayer().GetUniqueId());
}
}
void CChozoGhost::Land(CStateManager& mgr, EStateMsg msg, float dt) {
if (msg == EStateMsg::Update) {
FloatToLevel(x678_floorLevel, dt);
if (std::fabs(x678_floorLevel - GetTranslation().z()) < 0.05f) {
x330_stateMachineState.SetCodeTrigger();
}
}
}
void CChozoGhost::Lurk(CStateManager& mgr, EStateMsg msg, float dt) {
if (msg == EStateMsg::Activate) {
x330_stateMachineState.SetDelay(x684_lurkDelay);
} else if (msg == EStateMsg::Update) {
FloatToLevel(x678_floorLevel, dt);
}
}
bool CChozoGhost::Leash(CStateManager& mgr, float arg) {
return x665_27_playerInLeashRange || CPatterned::Leash(mgr, arg);
}
bool CChozoGhost::InRange(CStateManager& mgr, float arg) { return x665_28_inRange; }
bool CChozoGhost::ShouldAttack(CStateManager& mgr, float arg) { return x680_behaveType == EBehaveType::Attack; }
bool CChozoGhost::AggressionCheck(CStateManager& mgr, float arg) { return x665_29_aggressive; }
bool CChozoGhost::ShouldTaunt(CStateManager& mgr, float arg) { return x680_behaveType == EBehaveType::Taunt; }
bool CChozoGhost::ShouldFlinch(CStateManager& mgr, float arg) { return x664_25_flinch; }
bool CChozoGhost::ShouldMove(CStateManager& mgr, float arg) { return x680_behaveType == EBehaveType::Move; }
bool CChozoGhost::AIStage(CStateManager& mgr, float arg) { return arg == x63c_; }
u8 CChozoGhost::GetModelAlphau8(const CStateManager& mgr) const {
if (mgr.GetPlayerState()->GetActiveVisor(mgr) == CPlayerState::EPlayerVisor::XRay && IsAlive())
return 255;
return u8(x42c_color.a() * 255);
}
bool CChozoGhost::IsOnGround() const { return x664_27_onGround; }
CProjectileInfo* CChozoGhost::GetProjectileInfo() { return x67c_attackType == 2 ? &x578_ : &x5a0_; }
void CChozoGhost::AddToTeam(CStateManager& mgr) {
if (x6c4_teamMgr == kInvalidUniqueId)
x6c4_teamMgr = CTeamAiMgr::GetTeamAiMgr(*this, mgr);
if (x6c4_teamMgr == kInvalidUniqueId)
return;
if (TCastToPtr<CTeamAiMgr> teamMgr = mgr.ObjectById(x6c4_teamMgr))
teamMgr->AssignTeamAiRole(*this, CTeamAiRole::ETeamAiRole::Ranged, CTeamAiRole::ETeamAiRole::Unknown,
CTeamAiRole::ETeamAiRole::Invalid);
}
void CChozoGhost::RemoveFromTeam(CStateManager& mgr) {
if (x6c4_teamMgr == kInvalidUniqueId)
return;
if (TCastToPtr<CTeamAiMgr> teamMgr = mgr.ObjectById(x6c4_teamMgr)) {
if (teamMgr->IsPartOfTeam(GetUniqueId())) {
teamMgr->RemoveTeamAiRole(GetUniqueId());
x6c4_teamMgr = kInvalidUniqueId;
}
}
}
void CChozoGhost::FloatToLevel(float level, float dt) {
const zeus::CVector3f& pos = GetTranslation();
SetTranslation({pos.x(), pos.y(), 4.f * (level - pos.z()) * dt + pos.z()});
}
const CChozoGhost::CBehaveChance& CChozoGhost::ChooseBehaveChanceRange(CStateManager& mgr) {
const float dist = (GetTranslation() - mgr.GetPlayer().GetTranslation()).magnitude();
if (x654_ <= dist && x658_ > dist)
return x5e8_;
else if (x658_ <= dist)
return x608_;
else
return x5c8_;
}
void CChozoGhost::FindSpaceWarpPosition(CStateManager& mgr, const zeus::CVector3f& dir) {
const zeus::CVector3f& center = GetBoundingBox().center();
const CRayCastResult& res =
mgr.RayStaticIntersection(center + (dir * 8.f), -dir, 8.f, CMaterialFilter::MakeInclude({EMaterialTypes::Solid}));
if (res.IsInvalid()) {
x6cc_spaceWarpPosition = center + dir;
} else {
x6cc_spaceWarpPosition = res.GetPoint();
}
}
void CChozoGhost::FindBestAnchor(CStateManager& mgr) {
x665_27_playerInLeashRange = false;
u32 chance = mgr.GetActiveRandom()->Next() % 100;
chance = chance < x65c_nearChance ? 0 : (chance < (x65c_nearChance + x660_midChance)) + 2;
float dVar15 = 10.f * x658_;
float dVar14 = dVar15;
float dVar13 = dVar15;
if (chance == 0) {
dVar13 = dVar15 * 10.f;
dVar14 = dVar15 * 5.f;
} else if (chance == 1) {
dVar13 = dVar15 * 5.f;
dVar15 *= 10.f;
} else if (chance == 2) {
dVar14 = dVar15 * 5.f;
dVar15 *= 10.f;
}
float prevDist = FLT_MAX;
CScriptCoverPoint* target = nullptr;
for (CEntity* ent : mgr.GetAiWaypointObjectList()) {
if (TCastToPtr<CScriptCoverPoint> cover = ent) {
if (cover->GetActive() && !cover->GetInUse(kInvalidUniqueId) && cover->GetAreaIdAlways() == GetAreaId()) {
float fVar17 = (cover->GetTranslation() - GetTranslation()).magnitude();
if (2.f * x66c_ <= fVar17) {
float dist = std::max(0.f, x654_ - fVar17);
zeus::CVector3f diff = cover->GetTranslation() - mgr.GetPlayer().GetTranslation();
fVar17 = diff.magnitude();
if (x2fc_minAttackRange <= fVar17) {
if (std::fabs(diff.z()) / fVar17 < 0.2f) {
dist = (20.f * x658_) * ((std::fabs(diff.z()) / fVar17) - 0.2f) + dist;
}
if (x654_ <= fVar17) {
if (x658_ <= fVar17) {
dist = (dist + dVar13);
} else {
dist = (dist + dVar14);
if (dist < prevDist) {
fVar17 = 1.f / fVar17;
diff = diff * fVar17;
dist += (10.f * x658_) * (1.f - mgr.GetPlayer().GetTransform().basis[1].dot(diff));
}
}
} else {
dist += dVar15;
if (dist < prevDist) {
fVar17 = 1.f / fVar17;
diff = diff * fVar17;
dist += (10.f * x658_) * (1.f - mgr.GetPlayer().GetTransform().basis[1].dot(diff));
}
}
if (dist < prevDist) {
dist += x658_ * mgr.GetActiveRandom()->Float();
if (dist < prevDist) {
x665_27_playerInLeashRange = false;
target = cover;
prevDist = dist;
}
}
}
}
}
}
}
if (target) {
x2dc_destObj = target->GetUniqueId();
SetDestPos(target->GetTranslation());
ReleaseCoverPoint(mgr, x674_coverPoint);
SetCoverPoint(target, x674_coverPoint);
} else if (mgr.GetPlayer().GetAreaIdAlways() == GetAreaIdAlways()) {
x2dc_destObj = mgr.GetPlayer().GetUniqueId();
zeus::CVector3f destPos =
mgr.GetPlayer().GetTranslation() - x654_ * (mgr.GetPlayer().GetTranslation() - GetTranslation()).normalized();
const CRayCastResult& res =
mgr.RayStaticIntersection(destPos, zeus::skDown, 8.f, CMaterialFilter::MakeInclude(EMaterialTypes::Floor));
if (res.IsValid())
destPos = res.GetPoint();
SetDestPos(destPos);
} else {
x2dc_destObj = kInvalidUniqueId;
x2e0_destPos = GetTranslation();
}
}
} // namespace metaforce::MP1