mirror of https://github.com/AxioDL/metaforce.git
866 lines
31 KiB
C++
866 lines
31 KiB
C++
#include "Runtime/MP1/World/CParasite.hpp"
|
|
|
|
#include "Runtime/Factory/CSimplePool.hpp"
|
|
#include "Runtime/CStateManager.hpp"
|
|
#include "Runtime/GameGlobalObjects.hpp"
|
|
#include "Runtime/Character/CModelData.hpp"
|
|
#include "Runtime/Collision/CCollisionActor.hpp"
|
|
#include "Runtime/Collision/CGameCollision.hpp"
|
|
#include "Runtime/Graphics/CSkinnedModel.hpp"
|
|
#include "Runtime/World/CActorParameters.hpp"
|
|
#include "Runtime/World/CGameArea.hpp"
|
|
#include "Runtime/World/CPatternedInfo.hpp"
|
|
#include "Runtime/World/CPlayer.hpp"
|
|
#include "Runtime/World/CScriptDoor.hpp"
|
|
#include "Runtime/World/CScriptWaypoint.hpp"
|
|
#include "Runtime/World/CWorld.hpp"
|
|
|
|
#include "TCastTo.hpp" // Generated file, do not modify include path
|
|
|
|
namespace metaforce::MP1 {
|
|
|
|
const float CParasite::flt_805A8FB0 = 2.f * std::sqrt(2.5f / CPhysicsActor::GravityConstant());
|
|
const float CParasite::skAttackVelocity = 15.f / 2.f * (std::sqrt(2.5f / CPhysicsActor::GravityConstant()));
|
|
short CParasite::word_805A8FC0 = 0;
|
|
const float CParasite::flt_805A8FB8 = 2.f * std::sqrt(2.5f / CPhysicsActor::GravityConstant());
|
|
const float CParasite::skRetreatVelocity = 3.f / 2.f * std::sqrt(2.5f / CPhysicsActor::GravityConstant());
|
|
|
|
CParasite::CParasite(TUniqueId uid, std::string_view name, EFlavorType flavor, const CEntityInfo& info,
|
|
const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo, EBodyType bodyType,
|
|
float maxTelegraphReactDist, float advanceWpRadius, float f3, float alignAngVel, float f5,
|
|
float stuckTimeThreshold, float collisionCloseMargin, float parasiteSearchRadius,
|
|
float parasiteSeparationDist, float parasiteSeparationWeight, float parasiteAlignmentWeight,
|
|
float parasiteCohesionWeight, float destinationSeekWeight, float forwardMoveWeight,
|
|
float playerSeparationDist, float playerSeparationWeight, float playerObstructionMinDist,
|
|
float haltDelay, bool disableMove, EWalkerType wType, const CDamageVulnerability& dVuln,
|
|
const CDamageInfo& parInfo, u16 haltSfx, u16 getUpSfx, u16 crouchSfx, CAssetId modelRes,
|
|
CAssetId skinRes, float iceZoomerJointHP, const CActorParameters& aParams)
|
|
: CWallWalker(ECharacter::Parasite, uid, name, flavor, info, xf, std::move(mData), pInfo, EMovementType::Flyer,
|
|
EColliderType::Zero, bodyType, aParams, collisionCloseMargin, alignAngVel, EKnockBackVariant::Small,
|
|
advanceWpRadius, wType, playerObstructionMinDist, disableMove)
|
|
, x64c_oculusHaltDVuln(dVuln)
|
|
, x6b4_oculusHaltDInfo(parInfo)
|
|
, x6d0_maxTelegraphReactDist(maxTelegraphReactDist)
|
|
, x6d4_(f3)
|
|
, x6dc_(f5)
|
|
, x6e0_stuckTimeThreshold(stuckTimeThreshold)
|
|
, x6e4_parasiteSearchRadius(parasiteSearchRadius)
|
|
, x6e8_parasiteSeparationDist(parasiteSeparationDist)
|
|
, x6ec_parasiteSeparationWeight(parasiteSeparationWeight)
|
|
, x6f0_parasiteAlignmentWeight(parasiteAlignmentWeight)
|
|
, x6f4_parasiteCohesionWeight(parasiteCohesionWeight)
|
|
, x6f8_destinationSeekWeight(destinationSeekWeight)
|
|
, x6fc_forwardMoveWeight(forwardMoveWeight)
|
|
, x700_playerSeparationDist(playerSeparationDist)
|
|
, x704_playerSeparationWeight(playerSeparationWeight)
|
|
, x708_unmorphedRadius(pInfo.GetHeight() * 0.5f)
|
|
, x710_haltDelay(haltDelay)
|
|
, x714_iceZoomerJointHP(iceZoomerJointHP)
|
|
, x73c_haltSfx(CSfxManager::TranslateSFXID(haltSfx))
|
|
, x73e_getUpSfx(CSfxManager::TranslateSFXID(getUpSfx))
|
|
, x740_crouchSfx(CSfxManager::TranslateSFXID(crouchSfx)) {
|
|
switch (x5d0_walkerType) {
|
|
case EWalkerType::Geemer:
|
|
x460_knockBackController.SetEnableFreeze(false);
|
|
[[fallthrough]];
|
|
case EWalkerType::Oculus:
|
|
x460_knockBackController.SetAutoResetImpulse(false);
|
|
break;
|
|
case EWalkerType::IceZoomer: {
|
|
TLockedToken<CModel> model = g_SimplePool->GetObj({FOURCC('CMDL'), modelRes});
|
|
TLockedToken<CModel> skin = g_SimplePool->GetObj({FOURCC('CSKR'), skinRes});
|
|
x624_extraModel =
|
|
CToken(TObjOwnerDerivedFromIObj<CSkinnedModel>::GetNewDerivedObject(std::make_unique<CSkinnedModel>(
|
|
model, skin, x64_modelData->GetAnimationData()->GetModelData()->GetLayoutInfo())));
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
if (x5d0_walkerType == EWalkerType::Oculus) {
|
|
x460_knockBackController.SetEnableShock(false);
|
|
x460_knockBackController.SetEnableBurn(false);
|
|
x460_knockBackController.SetEnableBurnDeath(false);
|
|
x460_knockBackController.SetEnableExplodeDeath(false);
|
|
x460_knockBackController.SetX82_24(false);
|
|
}
|
|
}
|
|
|
|
void CParasite::Accept(IVisitor& visitor) { visitor.Visit(this); }
|
|
|
|
void CParasite::SetupIceZoomerCollision(CStateManager& mgr) {
|
|
std::vector<CJointCollisionDescription> descs;
|
|
descs.reserve(2);
|
|
descs.push_back(CJointCollisionDescription::SphereCollision(
|
|
x64_modelData->GetAnimationData()->GetLocatorSegId("Ice_LCTR"sv), 0.4f, "Ice_LCTR"sv, 0.001f));
|
|
RemoveMaterial(EMaterialTypes::Solid, mgr);
|
|
AddMaterial(EMaterialTypes::ProjectilePassthrough, mgr);
|
|
x620_collisionActorManager =
|
|
std::make_unique<CCollisionActorManager>(mgr, GetUniqueId(), GetAreaIdAlways(), descs, GetActive());
|
|
}
|
|
|
|
void CParasite::SetupIceZoomerVulnerability(CStateManager& mgr, const CDamageVulnerability& dVuln,
|
|
const CHealthInfo& hInfo) {
|
|
for (u32 i = 0; i < x620_collisionActorManager->GetNumCollisionActors(); ++i) {
|
|
const CJointCollisionDescription& cDesc = x620_collisionActorManager->GetCollisionDescFromIndex(i);
|
|
if (TCastToPtr<CCollisionActor> act = mgr.ObjectById(cDesc.GetCollisionActorId())) {
|
|
act->SetDamageVulnerability(dVuln);
|
|
*act->HealthInfo(mgr) = hInfo;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CParasite::AddDoorRepulsors(CStateManager& mgr) {
|
|
u32 doorCount = 0;
|
|
for (CEntity* ent : mgr.GetPhysicsActorObjectList())
|
|
if (TCastToPtr<CScriptDoor> door = ent)
|
|
if (door->GetAreaIdAlways() == GetAreaIdAlways())
|
|
++doorCount;
|
|
x5d8_doorRepulsors.reserve(doorCount);
|
|
for (CEntity* ent : mgr.GetPhysicsActorObjectList())
|
|
if (TCastToPtr<CScriptDoor> door = ent)
|
|
if (door->GetAreaIdAlways() == GetAreaIdAlways()) {
|
|
if (auto tb = door->GetTouchBounds()) {
|
|
float diagMag = (tb->min - tb->max).magnitude() * 0.75f;
|
|
x5d8_doorRepulsors.emplace_back(tb->center(), diagMag);
|
|
}
|
|
}
|
|
}
|
|
|
|
static TUniqueId lastParasite = kInvalidUniqueId;
|
|
|
|
void CParasite::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) {
|
|
CPatterned::AcceptScriptMsg(msg, uid, mgr);
|
|
switch (msg) {
|
|
case EScriptObjectMessage::Registered:
|
|
x450_bodyController->Activate(mgr);
|
|
mgr.GetActiveParasites().push_back(GetUniqueId());
|
|
CActor::CreateShadow(false);
|
|
x604_activeSpeed = x3b4_speed;
|
|
CPhysicsActor::SetBoundingBox(zeus::CAABox(zeus::CVector3f(-x590_colSphere.GetSphere().radius),
|
|
zeus::CVector3f(x590_colSphere.GetSphere().radius)));
|
|
lastParasite = GetUniqueId();
|
|
AddDoorRepulsors(mgr);
|
|
if (x5d0_walkerType == EWalkerType::IceZoomer) {
|
|
SetupIceZoomerCollision(mgr);
|
|
SetupIceZoomerVulnerability(mgr, x64c_oculusHaltDVuln,
|
|
CHealthInfo(x714_iceZoomerJointHP, HealthInfo(mgr)->GetKnockbackResistance()));
|
|
}
|
|
break;
|
|
case EScriptObjectMessage::Deleted:
|
|
mgr.GetActiveParasites().remove(GetUniqueId());
|
|
if (x5d0_walkerType == EWalkerType::IceZoomer)
|
|
DestroyActorManager(mgr);
|
|
break;
|
|
case EScriptObjectMessage::Jumped:
|
|
if (x742_25_jumpVelDirty) {
|
|
UpdateJumpVelocity();
|
|
x742_25_jumpVelDirty = false;
|
|
}
|
|
break;
|
|
case EScriptObjectMessage::Activate:
|
|
x5d6_27_disableMove = false;
|
|
if (x5d0_walkerType == EWalkerType::Parasite)
|
|
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Lurk);
|
|
break;
|
|
case EScriptObjectMessage::InvulnDamage:
|
|
if (x5d0_walkerType == EWalkerType::Oculus) {
|
|
if (TCastToConstPtr<CActor> act = mgr.GetObjectById(uid)) {
|
|
float distSq = (act->GetTranslation() - GetTranslation()).magSquared();
|
|
auto tb = GetTouchBounds();
|
|
float maxComp =
|
|
std::max(std::max(tb->max.y() - tb->min.y(), tb->max.z() - tb->min.z()), tb->max.x() - tb->min.x());
|
|
float maxCompSq = maxComp * maxComp + 1.f;
|
|
if (distSq < maxCompSq * maxCompSq)
|
|
x743_26_oculusShotAt = true;
|
|
}
|
|
}
|
|
break;
|
|
case EScriptObjectMessage::SuspendedMove:
|
|
if (x620_collisionActorManager)
|
|
x620_collisionActorManager->SetMovable(mgr, false);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CParasite::PreThink(float dt, CStateManager& mgr) {
|
|
CWallWalker::PreThink(dt, mgr);
|
|
x743_26_oculusShotAt = false;
|
|
}
|
|
|
|
void CParasite::UpdateCollisionActors(float dt, CStateManager& mgr) {
|
|
x620_collisionActorManager->Update(dt, mgr, CCollisionActorManager::EUpdateOptions::ObjectSpace);
|
|
if (!x743_25_vulnerable) {
|
|
float totalHP = 0.f;
|
|
for (u32 i = 0; i < x620_collisionActorManager->GetNumCollisionActors(); ++i) {
|
|
const CJointCollisionDescription& cDesc = x620_collisionActorManager->GetCollisionDescFromIndex(i);
|
|
if (TCastToPtr<CCollisionActor> cact = mgr.ObjectById(cDesc.GetCollisionActorId()))
|
|
totalHP += cact->HealthInfo(mgr)->GetHP();
|
|
}
|
|
if (totalHP <= 0.f) {
|
|
x743_25_vulnerable = true;
|
|
AddMaterial(EMaterialTypes::Solid, mgr);
|
|
RemoveMaterial(EMaterialTypes::ProjectilePassthrough, mgr);
|
|
DestroyActorManager(mgr);
|
|
x64_modelData->GetAnimationData()->SubstituteModelData(x624_extraModel);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CParasite::Think(float dt, CStateManager& mgr) {
|
|
if (!GetActive())
|
|
return;
|
|
|
|
++x5d4_thinkCounter;
|
|
if (x5d0_walkerType == EWalkerType::IceZoomer)
|
|
UpdateCollisionActors(dt, mgr);
|
|
|
|
x5d6_26_playerObstructed = false;
|
|
CGameArea* area = mgr.GetWorld()->GetArea(GetAreaIdAlways());
|
|
|
|
CGameArea::EOcclusionState r6 = CGameArea::EOcclusionState::Occluded;
|
|
if (area->IsPostConstructed())
|
|
r6 = area->GetPostConstructed()->x10dc_occlusionState;
|
|
if (r6 != CGameArea::EOcclusionState::Visible)
|
|
x5d6_26_playerObstructed = true;
|
|
|
|
if (!x5d6_26_playerObstructed) {
|
|
zeus::CVector3f plVec = mgr.GetPlayer().GetTranslation();
|
|
float distance = (GetTranslation() - plVec).magnitude();
|
|
|
|
if (distance > x5c4_playerObstructionMinDist) {
|
|
CRayCastResult res = mgr.RayStaticIntersection(plVec, (GetTranslation() - plVec).normalized(), distance,
|
|
CMaterialFilter::skPassEverything);
|
|
if (res.IsValid())
|
|
x5d6_26_playerObstructed = true;
|
|
}
|
|
}
|
|
|
|
if (x5d6_26_playerObstructed) {
|
|
xf8_24_movable = false;
|
|
return;
|
|
}
|
|
|
|
xf8_24_movable = !x5d6_24_alignToFloor;
|
|
|
|
if (!x5d6_27_disableMove) {
|
|
if (x450_bodyController->IsFrozen()) {
|
|
if ((GetTranslation() - x614_lastStuckPos).magSquared() < 0.3f /* <- Used to be a static variable */ * dt)
|
|
x60c_stuckTime += dt;
|
|
else
|
|
x60c_stuckTime = 0.f;
|
|
|
|
x614_lastStuckPos = GetTranslation();
|
|
if (x608_telegraphRemTime > 0.f)
|
|
x608_telegraphRemTime -= dt;
|
|
else
|
|
x608_telegraphRemTime = 0.f;
|
|
}
|
|
}
|
|
|
|
if (x400_25_alive) {
|
|
CPlayer* pl = mgr.Player();
|
|
float radius;
|
|
if (pl->GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Morphed && !x742_30_attackOver)
|
|
radius = x590_colSphere.GetSphere().radius;
|
|
else
|
|
radius = x708_unmorphedRadius;
|
|
|
|
zeus::CAABox aabox{GetTranslation() - radius, GetTranslation() + radius};
|
|
auto plBox = pl->GetTouchBounds();
|
|
|
|
if (plBox && plBox->intersects(aabox)) {
|
|
if (!x742_30_attackOver) {
|
|
x742_30_attackOver = true;
|
|
x742_27_landed = false;
|
|
}
|
|
|
|
if (x420_curDamageRemTime <= 0.f) {
|
|
mgr.ApplyDamage(GetUniqueId(), pl->GetUniqueId(), GetUniqueId(), GetContactDamage(),
|
|
CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {}), {});
|
|
x420_curDamageRemTime = x424_damageWaitTime;
|
|
}
|
|
}
|
|
}
|
|
|
|
CWallWalker::Think(dt, mgr);
|
|
|
|
if (x5d6_27_disableMove)
|
|
return;
|
|
|
|
if (x450_bodyController->IsFrozen())
|
|
return;
|
|
|
|
x3b4_speed = x604_activeSpeed;
|
|
if (x5d6_24_alignToFloor)
|
|
AlignToFloor(mgr, x590_colSphere.GetSphere().radius, GetTranslation() + 2.f * dt * x138_velocity, dt);
|
|
|
|
x742_27_landed = false;
|
|
}
|
|
|
|
void CParasite::Render(CStateManager& mgr) { CWallWalker::Render(mgr); }
|
|
|
|
const CDamageVulnerability* CParasite::GetDamageVulnerability() const {
|
|
switch (x5d0_walkerType) {
|
|
case EWalkerType::Oculus:
|
|
if (x743_24_halted)
|
|
return &x64c_oculusHaltDVuln;
|
|
break;
|
|
case EWalkerType::IceZoomer:
|
|
if (!x743_25_vulnerable)
|
|
return &CDamageVulnerability::ImmuneVulnerabilty();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return CAi::GetDamageVulnerability();
|
|
}
|
|
|
|
CDamageInfo CParasite::GetContactDamage() const {
|
|
if (x5d0_walkerType == EWalkerType::Oculus && x743_24_halted)
|
|
return x6b4_oculusHaltDInfo;
|
|
return CPatterned::GetContactDamage();
|
|
}
|
|
|
|
void CParasite::Touch(CActor& actor, CStateManager& mgr) { CPatterned::Touch(actor, mgr); }
|
|
|
|
zeus::CVector3f CParasite::GetAimPosition(const CStateManager&, float) const { return GetTranslation(); }
|
|
|
|
void CParasite::CollidedWith(TUniqueId uid, const CCollisionInfoList& list, CStateManager&) {
|
|
static constexpr CMaterialList testList(EMaterialTypes::Character, EMaterialTypes::Player);
|
|
if (x743_27_inJump) {
|
|
for (const auto& info : list) {
|
|
if (!x5d6_24_alignToFloor && info.GetMaterialLeft().Intersection(testList) == 0) {
|
|
OrientToSurfaceNormal(info.GetNormalLeft(), 360.f);
|
|
CPhysicsActor::Stop();
|
|
SetVelocityWR(zeus::skZero3f);
|
|
x742_27_landed = true;
|
|
x742_28_onGround = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CParasite::Death(CStateManager& mgr, const zeus::CVector3f& direction, EScriptObjectState state) {
|
|
CPhysicsActor::Stop();
|
|
TelegraphAttack(mgr, EStateMsg::Activate, 0.f);
|
|
SetMomentumWR({0.f, 0.f, -GetWeight()});
|
|
CPatterned::Death(mgr, direction, state);
|
|
}
|
|
|
|
void CParasite::Patrol(CStateManager& mgr, EStateMsg msg, float dt) {
|
|
switch (msg) {
|
|
case EStateMsg::Activate:
|
|
x742_26_ = true;
|
|
x5d6_24_alignToFloor = true;
|
|
if (!x5d6_27_disableMove && x5d0_walkerType == EWalkerType::Parasite)
|
|
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Lurk);
|
|
SetMomentumWR(zeus::skZero3f);
|
|
x5d6_25_hasAlignSurface = false;
|
|
xf8_24_movable = false;
|
|
break;
|
|
case EStateMsg::Update:
|
|
if (x5bc_patrolPauseRemTime > 0.f) {
|
|
x5bc_patrolPauseRemTime -= dt;
|
|
if (x5bc_patrolPauseRemTime <= 0.f) {
|
|
if (x5d0_walkerType == EWalkerType::Parasite)
|
|
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Lurk);
|
|
x5bc_patrolPauseRemTime = 0.f;
|
|
}
|
|
}
|
|
GotoNextWaypoint(mgr);
|
|
if (x5bc_patrolPauseRemTime <= 0.f && !x5d6_27_disableMove)
|
|
DoFlockingBehavior(mgr);
|
|
break;
|
|
case EStateMsg::Deactivate:
|
|
x5d6_24_alignToFloor = false;
|
|
xf8_24_movable = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CParasite::UpdatePFDestination(CStateManager& mgr) {
|
|
// Empty
|
|
}
|
|
|
|
void CParasite::DoFlockingBehavior(CStateManager& mgr) {
|
|
zeus::CVector3f upVec = x34_transform.basis[2];
|
|
EntityList parasiteList;
|
|
zeus::CAABox aabb(GetTranslation() - x6e4_parasiteSearchRadius, GetTranslation() + x6e4_parasiteSearchRadius);
|
|
if ((x5d4_thinkCounter % 6) == 0) {
|
|
EntityList nearList;
|
|
static constexpr CMaterialFilter filter = CMaterialFilter::MakeInclude(EMaterialTypes::Character);
|
|
CParasite* closestParasite = nullptr;
|
|
float minDistSq = 2.f + x6e8_parasiteSeparationDist * x6e8_parasiteSeparationDist;
|
|
mgr.BuildNearList(nearList, aabb, filter, nullptr);
|
|
for (TUniqueId id : nearList) {
|
|
if (CParasite* parasite = CPatterned::CastTo<CParasite>(mgr.ObjectById(id))) {
|
|
if (parasite != this && parasite->IsAlive()) {
|
|
parasiteList.push_back(parasite->GetUniqueId());
|
|
float distSq = (parasite->GetTranslation() - GetTranslation()).magSquared();
|
|
if (distSq < minDistSq) {
|
|
minDistSq = distSq;
|
|
closestParasite = parasite;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (closestParasite && x6ec_parasiteSeparationWeight > 0.f && x6e8_parasiteSeparationDist > 0.f)
|
|
x628_parasiteSeparationMove =
|
|
x45c_steeringBehaviors.Separation(*this, closestParasite->GetTranslation(), x6e8_parasiteSeparationDist) *
|
|
x604_activeSpeed;
|
|
else
|
|
x628_parasiteSeparationMove = zeus::skZero3f;
|
|
x634_parasiteCohesionMove = x45c_steeringBehaviors.Cohesion(*this, parasiteList, 0.6f, mgr) * x604_activeSpeed;
|
|
x640_parasiteAlignmentMove = x45c_steeringBehaviors.Alignment(*this, parasiteList, mgr) * x604_activeSpeed;
|
|
}
|
|
|
|
if ((mgr.GetPlayer().GetTranslation() - GetTranslation()).magSquared() <
|
|
x700_playerSeparationDist * x700_playerSeparationDist) {
|
|
x450_bodyController->GetCommandMgr().DeliverCmd(
|
|
CBCLocomotionCmd(ProjectVectorToPlane(x45c_steeringBehaviors.Separation(*this, mgr.GetPlayer().GetTranslation(),
|
|
x700_playerSeparationDist),
|
|
upVec) *
|
|
x604_activeSpeed,
|
|
zeus::skZero3f, x704_playerSeparationWeight));
|
|
}
|
|
|
|
if (x628_parasiteSeparationMove != zeus::skZero3f) {
|
|
x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(
|
|
ProjectVectorToPlane(x628_parasiteSeparationMove, upVec), zeus::skZero3f, x6ec_parasiteSeparationWeight));
|
|
}
|
|
|
|
for (const auto& r : x5d8_doorRepulsors) {
|
|
if ((r.GetVector() - GetTranslation()).magSquared() < r.GetFloat() * r.GetFloat()) {
|
|
x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(
|
|
ProjectVectorToPlane(x45c_steeringBehaviors.Separation(*this, r.GetVector(), r.GetFloat()) * x604_activeSpeed,
|
|
upVec),
|
|
zeus::skZero3f, 1.f));
|
|
}
|
|
}
|
|
|
|
if (x608_telegraphRemTime <= 0.f) {
|
|
x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(
|
|
ProjectVectorToPlane(x634_parasiteCohesionMove, upVec), zeus::skZero3f, x6f4_parasiteCohesionWeight));
|
|
x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(
|
|
ProjectVectorToPlane(x640_parasiteAlignmentMove, upVec), zeus::skZero3f, x6f0_parasiteAlignmentWeight));
|
|
x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(
|
|
ProjectVectorToPlane(
|
|
ProjectVectorToPlane(x45c_steeringBehaviors.Seek(*this, x2e0_destPos), upVec) * x604_activeSpeed, upVec),
|
|
zeus::skZero3f, x6f8_destinationSeekWeight));
|
|
x450_bodyController->GetCommandMgr().DeliverCmd(
|
|
CBCLocomotionCmd(x34_transform.basis[1] * x604_activeSpeed, zeus::skZero3f, x6fc_forwardMoveWeight));
|
|
}
|
|
}
|
|
|
|
void CParasite::PathFind(CStateManager& mgr, EStateMsg msg, float dt) {
|
|
switch (msg) {
|
|
case EStateMsg::Activate:
|
|
x742_26_ = true;
|
|
x5d6_24_alignToFloor = true;
|
|
if (x5d0_walkerType == EWalkerType::Parasite)
|
|
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Lurk);
|
|
SetMomentumWR(zeus::skZero3f);
|
|
xf8_24_movable = false;
|
|
break;
|
|
case EStateMsg::Update:
|
|
UpdatePFDestination(mgr);
|
|
DoFlockingBehavior(mgr);
|
|
break;
|
|
case EStateMsg::Deactivate:
|
|
xf8_24_movable = true;
|
|
x5d6_24_alignToFloor = false;
|
|
x742_26_ = false;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CParasite::TargetPlayer(CStateManager& mgr, EStateMsg msg, float dt) {
|
|
switch (msg) {
|
|
case EStateMsg::Activate:
|
|
x5f8_targetPos = mgr.GetPlayer().GetTranslation() + zeus::CVector3f(0.f, 0.f, 1.5f);
|
|
break;
|
|
case EStateMsg::Update:
|
|
x450_bodyController->FaceDirection3D(
|
|
ProjectVectorToPlane(x5f8_targetPos - GetTranslation(), x34_transform.basis[2]), x34_transform.basis[1], 2.f);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
TUniqueId CParasite::RecursiveFindClosestWayPoint(CStateManager& mgr, TUniqueId id, float& dist) {
|
|
TUniqueId ret = id;
|
|
TCastToPtr<CScriptWaypoint> wp = mgr.ObjectById(id);
|
|
if (!wp)
|
|
return ret;
|
|
wp->SetActive(false);
|
|
dist = (wp->GetTranslation() - GetTranslation()).magSquared();
|
|
for (const auto& conn : wp->GetConnectionList()) {
|
|
if (conn.x0_state == EScriptObjectState::Arrived && conn.x4_msg == EScriptObjectMessage::Next) {
|
|
TUniqueId nextId = mgr.GetIdForScript(conn.x8_objId);
|
|
if (nextId != kInvalidUniqueId) {
|
|
if (TCastToConstPtr<CScriptWaypoint> wp2 = mgr.GetObjectById(nextId)) {
|
|
if (wp2->GetActive()) {
|
|
float nextDist;
|
|
TUniqueId closestWp = RecursiveFindClosestWayPoint(mgr, nextId, nextDist);
|
|
if (nextDist < dist) {
|
|
dist = nextDist;
|
|
ret = closestWp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
wp->SetActive(true);
|
|
return ret;
|
|
}
|
|
|
|
TUniqueId CParasite::GetClosestWaypointForState(EScriptObjectState state, CStateManager& mgr) {
|
|
float minDist = FLT_MAX;
|
|
TUniqueId ret = kInvalidUniqueId;
|
|
for (const auto& conn : GetConnectionList()) {
|
|
if (conn.x0_state == state && conn.x4_msg == EScriptObjectMessage::Follow) {
|
|
TUniqueId id = mgr.GetIdForScript(conn.x8_objId);
|
|
float dist;
|
|
TUniqueId closestWp = RecursiveFindClosestWayPoint(mgr, id, dist);
|
|
if (dist < minDist) {
|
|
minDist = dist;
|
|
ret = closestWp;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void CParasite::TargetPatrol(CStateManager& mgr, EStateMsg msg, float dt) {
|
|
if (msg == EStateMsg::Activate) {
|
|
SetMomentumWR(zeus::skZero3f);
|
|
TUniqueId wpId = GetClosestWaypointForState(EScriptObjectState::Patrol, mgr);
|
|
if (wpId != kInvalidUniqueId)
|
|
x2dc_destObj = wpId;
|
|
}
|
|
}
|
|
|
|
void CParasite::Halt(CStateManager& mgr, EStateMsg msg, float) {
|
|
switch (msg) {
|
|
case EStateMsg::Activate:
|
|
x330_stateMachineState.SetDelay(x710_haltDelay);
|
|
x32c_animState = EAnimState::Ready;
|
|
x743_24_halted = true;
|
|
x5d6_24_alignToFloor = true;
|
|
if (x5d0_walkerType == EWalkerType::Geemer)
|
|
CSfxManager::AddEmitter(x73c_haltSfx, GetTranslation(), zeus::skZero3f, true, false, 0x7f, kInvalidAreaId);
|
|
break;
|
|
case EStateMsg::Update:
|
|
TryCommand(mgr, pas::EAnimationState::LoopReaction, &CPatterned::TryLoopReaction, 1);
|
|
x400_24_hitByPlayerProjectile = false;
|
|
break;
|
|
case EStateMsg::Deactivate:
|
|
x450_bodyController->GetCommandMgr().DeliverCmd(CBodyStateCmd(EBodyStateCmd::ExitState));
|
|
x32c_animState = EAnimState::NotReady;
|
|
x743_24_halted = false;
|
|
x5d6_24_alignToFloor = false;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CParasite::Run(CStateManager&, EStateMsg, float) {
|
|
// Empty
|
|
}
|
|
|
|
void CParasite::Generate(CStateManager&, EStateMsg msg, float) {
|
|
switch (msg) {
|
|
case EStateMsg::Activate:
|
|
x5e8_stateProgress = 0;
|
|
break;
|
|
case EStateMsg::Update:
|
|
switch (x5e8_stateProgress) {
|
|
case 0:
|
|
if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Generate)
|
|
x5e8_stateProgress = 1;
|
|
else
|
|
x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::Zero));
|
|
break;
|
|
case 1:
|
|
if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Generate)
|
|
x5e8_stateProgress = 2;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CParasite::Deactivate(CStateManager& mgr, EStateMsg msg, float) {
|
|
switch (msg) {
|
|
case EStateMsg::Activate:
|
|
x5e8_stateProgress = 0;
|
|
SendScriptMsgs(EScriptObjectState::DeactivateState, mgr, EScriptObjectMessage::None);
|
|
mgr.FreeScriptObject(GetUniqueId());
|
|
break;
|
|
case EStateMsg::Update:
|
|
if (x5e8_stateProgress == 0) {
|
|
if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Generate)
|
|
x5e8_stateProgress = 1;
|
|
else
|
|
x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::One));
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CParasite::Attack(CStateManager& mgr, EStateMsg msg, float) {
|
|
switch (msg) {
|
|
case EStateMsg::Activate:
|
|
x608_telegraphRemTime = 0.f;
|
|
if (mgr.GetPlayer().GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Morphed) {
|
|
float rz = mgr.GetActiveRandom()->Float();
|
|
float ry = mgr.GetActiveRandom()->Float();
|
|
float rx = mgr.GetActiveRandom()->Float();
|
|
x5f8_targetPos = (zeus::CVector3f(rx, ry, rz) - 0.5f) * 0.5f + mgr.GetPlayer().GetTranslation();
|
|
} else {
|
|
float rz = mgr.GetActiveRandom()->Float();
|
|
float ry = mgr.GetActiveRandom()->Float();
|
|
float rx = mgr.GetActiveRandom()->Float();
|
|
x5f8_targetPos =
|
|
(zeus::CVector3f(rx, ry, rz) + mgr.GetPlayer().GetTranslation() - GetTranslation()).normalized() * 15.f +
|
|
GetTranslation();
|
|
}
|
|
FaceTarget(x5f8_targetPos);
|
|
x5e8_stateProgress = 0;
|
|
x742_30_attackOver = false;
|
|
x742_24_receivedTelegraph = false;
|
|
x742_28_onGround = false;
|
|
break;
|
|
case EStateMsg::Update:
|
|
switch (x5e8_stateProgress) {
|
|
case 0:
|
|
if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Jump) {
|
|
x5e8_stateProgress = 1;
|
|
} else {
|
|
x742_25_jumpVelDirty = true;
|
|
FaceTarget(x5f8_targetPos);
|
|
x450_bodyController->GetCommandMgr().DeliverCmd(CBCJumpCmd(x5f8_targetPos, pas::EJumpType::Normal));
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case EStateMsg::Deactivate:
|
|
x742_28_onGround = true;
|
|
x742_30_attackOver = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CParasite::Crouch(CStateManager&, EStateMsg msg, float) {
|
|
if (msg == EStateMsg::Activate) {
|
|
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Crouch);
|
|
if (x5d0_walkerType == EWalkerType::Geemer)
|
|
CSfxManager::AddEmitter(x740_crouchSfx, GetTranslation(), zeus::skZero3f, true, false, 0x7f, kInvalidAreaId);
|
|
}
|
|
}
|
|
|
|
void CParasite::GetUp(CStateManager&, EStateMsg msg, float) {
|
|
if (msg == EStateMsg::Activate) {
|
|
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed);
|
|
if (x5d0_walkerType == EWalkerType::Geemer)
|
|
CSfxManager::AddEmitter(x73e_getUpSfx, GetTranslation(), zeus::skZero3f, true, false, 0x7f, kInvalidAreaId);
|
|
}
|
|
}
|
|
|
|
void CParasite::TelegraphAttack(CStateManager& mgr, EStateMsg msg, float) {
|
|
switch (msg) {
|
|
case EStateMsg::Activate:
|
|
for (auto it = mgr.GetActiveParasites().begin(); it != mgr.GetActiveParasites().end();) {
|
|
CParasite* other = CPatterned::CastTo<CParasite>(mgr.ObjectById(*it));
|
|
if (!other) {
|
|
it = mgr.GetActiveParasites().erase(it);
|
|
continue;
|
|
}
|
|
if (other != this && other->IsAlive() &&
|
|
(other->GetTranslation() - GetTranslation()).magSquared() <
|
|
x6d0_maxTelegraphReactDist * x6d0_maxTelegraphReactDist) {
|
|
other->x742_24_receivedTelegraph = true;
|
|
other->x608_telegraphRemTime = mgr.GetActiveRandom()->Float() * 0.5f + 0.5f;
|
|
other->x5f8_targetPos = GetTranslation();
|
|
}
|
|
++it;
|
|
}
|
|
x400_24_hitByPlayerProjectile = false;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CParasite::Jump(CStateManager& mgr, EStateMsg msg, float) {
|
|
switch (msg) {
|
|
case EStateMsg::Activate:
|
|
AddMaterial(EMaterialTypes::GroundCollider, mgr);
|
|
SetMomentumWR({0.f, 0.f, -GetWeight()});
|
|
x742_28_onGround = false;
|
|
x5d6_24_alignToFloor = false;
|
|
x742_27_landed = false;
|
|
x743_27_inJump = true;
|
|
break;
|
|
case EStateMsg::Update:
|
|
SetMomentumWR({0.f, 0.f, -GetWeight()});
|
|
break;
|
|
case EStateMsg::Deactivate:
|
|
RemoveMaterial(EMaterialTypes::GroundCollider, mgr);
|
|
SetMomentumWR(zeus::skZero3f);
|
|
x742_28_onGround = true;
|
|
x742_27_landed = false;
|
|
x743_27_inJump = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CParasite::FaceTarget(const zeus::CVector3f& target) {
|
|
zeus::CQuaternion q =
|
|
zeus::CQuaternion::lookAt(zeus::CTransform().basis[1], target - GetTranslation(), zeus::degToRad(360.f));
|
|
SetTransform(q.toTransform(GetTranslation()));
|
|
}
|
|
|
|
void CParasite::Retreat(CStateManager& mgr, EStateMsg msg, float) {
|
|
switch (msg) {
|
|
case EStateMsg::Activate: {
|
|
zeus::CVector3f dir = mgr.GetPlayer().GetTranslation() - GetTranslation();
|
|
dir.z() = 0.f;
|
|
if (dir.canBeNormalized())
|
|
dir.normalize();
|
|
else
|
|
dir = mgr.GetPlayer().GetTransform().basis[1];
|
|
x5f8_targetPos = GetTranslation() - dir * 3.f;
|
|
FaceTarget(x5f8_targetPos);
|
|
x5e8_stateProgress = 0;
|
|
x742_27_landed = false;
|
|
x742_28_onGround = false;
|
|
x742_25_jumpVelDirty = true;
|
|
x450_bodyController->GetCommandMgr().DeliverCmd(CBCJumpCmd(x5f8_targetPos, pas::EJumpType::One));
|
|
break;
|
|
}
|
|
case EStateMsg::Update:
|
|
x3b4_speed = 1.f;
|
|
break;
|
|
case EStateMsg::Deactivate:
|
|
x742_28_onGround = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool CParasite::AnimOver(CStateManager&, float) { return x5e8_stateProgress == 2; }
|
|
|
|
bool CParasite::ShouldAttack(CStateManager& mgr, float arg) {
|
|
bool shouldAttack = false;
|
|
if (x742_24_receivedTelegraph && x608_telegraphRemTime > 0.1f)
|
|
shouldAttack = true;
|
|
if (!TooClose(mgr, arg) && InMaxRange(mgr, arg))
|
|
return shouldAttack || InDetectionRange(mgr, 0.f);
|
|
return false;
|
|
}
|
|
|
|
bool CParasite::CloseToWall(const CStateManager& mgr) const {
|
|
static constexpr CMaterialFilter filter = CMaterialFilter::MakeInclude(EMaterialTypes::Solid);
|
|
zeus::CAABox aabb = CPhysicsActor::GetBoundingBox();
|
|
const float margin = x590_colSphere.GetSphere().radius + x5b0_collisionCloseMargin;
|
|
aabb.min -= zeus::CVector3f(margin);
|
|
aabb.max += zeus::CVector3f(margin);
|
|
const CCollidableAABox cAABB(aabb, x68_material);
|
|
return CGameCollision::DetectStaticCollisionBoolean(mgr, cAABB, {}, filter);
|
|
}
|
|
|
|
bool CParasite::HitSomething(CStateManager& mgr, float) {
|
|
if (x5d4_thinkCounter & 0x1)
|
|
return true;
|
|
return x5b8_tumbleAngle < 270.f && CloseToWall(mgr);
|
|
}
|
|
|
|
bool CParasite::Stuck(CStateManager&, float) { return x60c_stuckTime > x6e0_stuckTimeThreshold; }
|
|
|
|
bool CParasite::Landed(CStateManager&, float) { return x742_27_landed; }
|
|
|
|
bool CParasite::AttackOver(CStateManager&, float) { return x742_30_attackOver; }
|
|
|
|
bool CParasite::ShotAt(CStateManager&, float) {
|
|
if (x5d0_walkerType != EWalkerType::Oculus)
|
|
return x400_24_hitByPlayerProjectile;
|
|
return x743_26_oculusShotAt;
|
|
}
|
|
|
|
void CParasite::MassiveDeath(CStateManager& mgr) { CPatterned::MassiveDeath(mgr); }
|
|
|
|
void CParasite::MassiveFrozenDeath(CStateManager& mgr) { CPatterned::MassiveFrozenDeath(mgr); }
|
|
|
|
void CParasite::ThinkAboutMove(float dt) {
|
|
if (!x68_material.HasMaterial(EMaterialTypes::GroundCollider))
|
|
CPatterned::ThinkAboutMove(dt);
|
|
}
|
|
|
|
bool CParasite::IsOnGround() const { return x742_28_onGround; }
|
|
|
|
void CParasite::UpdateWalkerAnimation(CStateManager& mgr, float dt) { CActor::UpdateAnimation(dt, mgr, true); }
|
|
|
|
void CParasite::DestroyActorManager(CStateManager& mgr) { x620_collisionActorManager->Destroy(mgr); }
|
|
|
|
void CParasite::UpdateJumpVelocity() {
|
|
SetMomentumWR({0.f, 0.f, -GetWeight()});
|
|
zeus::CVector3f vec;
|
|
|
|
if (!x742_30_attackOver) {
|
|
vec = skAttackVelocity * GetTransform().frontVector();
|
|
vec.z() = 0.5f * skRetreatVelocity;
|
|
} else {
|
|
vec = skRetreatVelocity * GetTransform().frontVector();
|
|
vec.z() = 0.5f * skAttackVelocity;
|
|
}
|
|
|
|
float f30 = x150_momentum.z() / xe8_mass;
|
|
float f31 = x5f8_targetPos.z() - GetTranslation().z();
|
|
zeus::CVector3f vec2 = x5f8_targetPos - GetTranslation();
|
|
vec2.z() = 0.f;
|
|
float f29 = vec2.magnitude();
|
|
|
|
if (f29 > FLT_EPSILON) {
|
|
vec2 *= zeus::CVector3f{1.f / f29};
|
|
float f28 = vec2.dot(vec);
|
|
if (f28 > FLT_EPSILON) {
|
|
float f27 = 0.f;
|
|
bool isNeg = f31 < 0.f;
|
|
float xPos, xNeg;
|
|
if (CSteeringBehaviors::SolveQuadratic(f30, vec.z(), -f31, xPos, xNeg))
|
|
f27 = isNeg ? xPos : xNeg;
|
|
|
|
if (!isNeg)
|
|
f27 = f27 * f29 / f28;
|
|
|
|
if (f27 < 10.f) {
|
|
vec = f29 / f27 * vec2;
|
|
vec.z() = (0.5f * f30 * f27 + f31 / f27);
|
|
}
|
|
}
|
|
}
|
|
SetVelocityWR(vec);
|
|
}
|
|
|
|
} // namespace metaforce::MP1
|