diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index e3b0700a2..d7e7012ec 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -12,25 +12,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/DataSpec/DNAMP1/PATH.cpp b/DataSpec/DNAMP1/PATH.cpp
index 126c97b0c..577660e2f 100644
--- a/DataSpec/DNAMP1/PATH.cpp
+++ b/DataSpec/DNAMP1/PATH.cpp
@@ -105,6 +105,16 @@ void PATH::sendToBlender(hecl::blender::Connection& conn, std::string_view entry
#endif
}
+#if 0
+ for (const Node& n : nodes) {
+ zeus::simd_floats f(n.position.simd);
+ zeus::simd_floats no(n.position.simd + n.normal.simd);
+ os.format("v = bm.verts.new((%f,%f,%f))\n"
+ "v2 = bm.verts.new((%f,%f,%f))\n"
+ "bm.edges.new((v, v2))\n", f[0], f[1], f[2], no[0], no[1], no[2]);
+ }
+#endif
+
os << "bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.001)\n"
"path_mesh = bpy.data.meshes.new('PATH')\n"
"bm.to_mesh(path_mesh)\n"
diff --git a/DataSpec/DNAMP1/ScriptObjects/Eyeball.hpp b/DataSpec/DNAMP1/ScriptObjects/Eyeball.hpp
index cd6cadb47..ecb74956a 100644
--- a/DataSpec/DNAMP1/ScriptObjects/Eyeball.hpp
+++ b/DataSpec/DNAMP1/ScriptObjects/Eyeball.hpp
@@ -9,26 +9,26 @@ struct Eyeball : IScriptObject {
AT_DECL_DNA_YAML
AT_DECL_DNAV
String<-1> name;
- Value unknown1;
+ Value flavor;
Value location;
Value orientation;
Value scale;
PatternedInfo patternedInfo;
ActorParameters actorParameters;
- Value unknown2;
- Value unknown3;
+ Value attackDelay;
+ Value attackStartTime;
UniqueID32 wpsc;
DamageInfo damageInfo;
- UniqueID32 particle1;
- UniqueID32 particle2;
- UniqueID32 texture1;
- UniqueID32 texture2;
- Value unknown4;
- Value unknown5; // always ff
- Value unknown6; // always ff
- Value unknown7; // always ff
- Value unknown8;
- Value unknown9;
+ UniqueID32 beamContactFxId;
+ UniqueID32 beamPulseFxId;
+ UniqueID32 beamTextureId;
+ UniqueID32 beamGlowTextureId;
+ Value anim0;
+ Value anim1; // always ff
+ Value anim2; // always ff
+ Value anim3; // always ff
+ Value beamSfx;
+ Value attackDisabled;
void addCMDLRigPairs(PAKRouter& pakRouter, CharacterAssociations& charAssoc) const {
actorParameters.addCMDLRigPairs(pakRouter, charAssoc, patternedInfo.animationParameters);
@@ -39,20 +39,20 @@ struct Eyeball : IScriptObject {
PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(wpsc);
ent->name = name + "_wpsc";
}
- if (particle1) {
- PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(particle1);
+ if (beamContactFxId) {
+ PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(beamContactFxId);
ent->name = name + "_part1";
}
- if (particle2) {
- PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(particle2);
+ if (beamPulseFxId) {
+ PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(beamPulseFxId);
ent->name = name + "_part2";
}
- if (texture1) {
- PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(texture1);
+ if (beamTextureId) {
+ PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(beamTextureId);
ent->name = name + "_tex1";
}
- if (texture2) {
- PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(texture2);
+ if (beamGlowTextureId) {
+ PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(beamGlowTextureId);
ent->name = name + "_tex2";
}
patternedInfo.nameIDs(pakRouter, name + "_patterned");
@@ -61,10 +61,10 @@ struct Eyeball : IScriptObject {
void gatherDependencies(std::vector& pathsOut, std::vector& lazyOut) const {
g_curSpec->flattenDependencies(wpsc, pathsOut);
- g_curSpec->flattenDependencies(particle1, pathsOut);
- g_curSpec->flattenDependencies(particle2, pathsOut);
- g_curSpec->flattenDependencies(texture1, pathsOut);
- g_curSpec->flattenDependencies(texture2, pathsOut);
+ g_curSpec->flattenDependencies(beamContactFxId, pathsOut);
+ g_curSpec->flattenDependencies(beamPulseFxId, pathsOut);
+ g_curSpec->flattenDependencies(beamTextureId, pathsOut);
+ g_curSpec->flattenDependencies(beamGlowTextureId, pathsOut);
patternedInfo.depIDs(pathsOut);
actorParameters.depIDs(pathsOut, lazyOut);
}
diff --git a/DataSpec/DNAMP1/ScriptObjects/ScriptBeam.hpp b/DataSpec/DNAMP1/ScriptObjects/ScriptBeam.hpp
index 85b4dee6b..67aca7354 100644
--- a/DataSpec/DNAMP1/ScriptObjects/ScriptBeam.hpp
+++ b/DataSpec/DNAMP1/ScriptObjects/ScriptBeam.hpp
@@ -11,7 +11,7 @@ struct ScriptBeam : IScriptObject {
String<-1> name;
Value location;
Value orientation;
- Value unknown1;
+ Value active;
UniqueID32 wpsc;
BeamInfo beamInfo;
DamageInfo damageInfo;
diff --git a/DataSpec/DNAMP1/ScriptObjects/Warwasp.hpp b/DataSpec/DNAMP1/ScriptObjects/Warwasp.hpp
index ff55c55be..f368ae6ec 100644
--- a/DataSpec/DNAMP1/ScriptObjects/Warwasp.hpp
+++ b/DataSpec/DNAMP1/ScriptObjects/Warwasp.hpp
@@ -9,30 +9,30 @@ struct Warwasp : IScriptObject {
AT_DECL_DNA_YAML
AT_DECL_DNAV
String<-1> name;
- Value unknown1;
+ Value flavor;
Value location;
Value orientation;
Value scale;
PatternedInfo patternedInfo;
ActorParameters actorParameters;
- Value unknown2;
+ Value colliderType;
DamageInfo damageInfo1;
- UniqueID32 wpsc1;
- DamageInfo damageInfo2;
- UniqueID32 particle;
- Value unknown3;
+ UniqueID32 projectileWeapon;
+ DamageInfo projectileDamage;
+ UniqueID32 projectileVisorParticle;
+ Value projectileVisorSfx;
void addCMDLRigPairs(PAKRouter& pakRouter, CharacterAssociations& charAssoc) const {
actorParameters.addCMDLRigPairs(pakRouter, charAssoc, patternedInfo.animationParameters);
}
void nameIDs(PAKRouter& pakRouter) const {
- if (wpsc1) {
- PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(wpsc1);
+ if (projectileWeapon) {
+ PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(projectileWeapon);
ent->name = name + "_wpsc";
}
- if (particle) {
- PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(particle);
+ if (projectileVisorParticle) {
+ PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(projectileVisorParticle);
ent->name = name + "_part";
}
patternedInfo.nameIDs(pakRouter, name + "_patterned");
@@ -40,8 +40,8 @@ struct Warwasp : IScriptObject {
}
void gatherDependencies(std::vector& pathsOut, std::vector& lazyOut) const {
- g_curSpec->flattenDependencies(wpsc1, pathsOut);
- g_curSpec->flattenDependencies(particle, pathsOut);
+ g_curSpec->flattenDependencies(projectileWeapon, pathsOut);
+ g_curSpec->flattenDependencies(projectileVisorParticle, pathsOut);
patternedInfo.depIDs(pathsOut);
actorParameters.depIDs(pathsOut, lazyOut);
}
diff --git a/Runtime/Character/CAnimTreeTimeScale.cpp b/Runtime/Character/CAnimTreeTimeScale.cpp
index 6238eb60c..2e82a32e8 100644
--- a/Runtime/Character/CAnimTreeTimeScale.cpp
+++ b/Runtime/Character/CAnimTreeTimeScale.cpp
@@ -176,6 +176,7 @@ SAdvancementResults CAnimTreeTimeScale::VAdvanceView(const CCharAnimTime& dt) {
SAdvancementResults res2;
if (newDt.GreaterThanZero())
res2 = x14_child->VAdvanceView(newDt);
+ x20_curAccelTime = x28_targetAccelTime;
res2.x0_remTime = res2.x0_remTime + (newTime - x28_targetAccelTime);
return res2;
}
diff --git a/Runtime/Character/CGroundMovement.cpp b/Runtime/Character/CGroundMovement.cpp
index f0afff683..056d22b80 100644
--- a/Runtime/Character/CGroundMovement.cpp
+++ b/Runtime/Character/CGroundMovement.cpp
@@ -331,7 +331,7 @@ void CGroundMovement::MoveGroundCollider_New(CStateManager& mgr, CPhysicsActor&
player.x9c5_28_slidingOnWall = false;
bool applyJump = player.x258_movementState == CPlayer::EPlayerMovementState::ApplyJump;
bool dampUnderwater = false;
- if (player.x9c4_31_dampUnderwaterMotion)
+ if (player.x9c4_31_inWaterMovement)
if (!mgr.GetPlayerState()->HasPowerUp(CPlayerState::EItemType::GravitySuit))
dampUnderwater = true;
diff --git a/Runtime/Character/CharacterCommon.hpp b/Runtime/Character/CharacterCommon.hpp
index ee94632a5..47378741a 100644
--- a/Runtime/Character/CharacterCommon.hpp
+++ b/Runtime/Character/CharacterCommon.hpp
@@ -78,7 +78,7 @@ enum class EStepDirection { Invalid = -1, Forward = 0, Backward = 1, Left = 2, R
enum class EStepType { Normal = 0, Dodge = 1, BreakDodge = 2, RollDodge = 3 };
-enum class ESeverity { Invalid = -1, Zero = 0, One = 1, Two = 2 };
+enum class ESeverity { Invalid = -1, Zero = 0, One = 1, Two = 2, Eight = 8 };
enum class EGetupType { Invalid = -1, Zero = 0 };
diff --git a/Runtime/MP1/World/CEyeball.cpp b/Runtime/MP1/World/CEyeball.cpp
index 4ed926b1d..05d89c46d 100644
--- a/Runtime/MP1/World/CEyeball.cpp
+++ b/Runtime/MP1/World/CEyeball.cpp
@@ -9,29 +9,30 @@
#include "TCastTo.hpp"
namespace urde::MP1 {
CEyeball::CEyeball(TUniqueId uid, std::string_view name, CPatterned::EFlavorType flavor, const CEntityInfo& info,
- const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo, float f1, float f2,
- CAssetId aId1, const CDamageInfo& dInfo, CAssetId aId2, CAssetId aId3, CAssetId aId4, CAssetId aId5,
- u32 w1, u32 w2, u32 w3, u32 w4, u32 w5, bool b1, const CActorParameters& actParms)
+ const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo, float attackDelay,
+ float attackStartTime, CAssetId wpscId, const CDamageInfo& dInfo, CAssetId beamContactFxId,
+ CAssetId beamPulseFxId, CAssetId beamTextureId, CAssetId beamGlowTextureId, u32 anim0, u32 anim1,
+ u32 anim2, u32 anim3, u32 beamSfx, bool attackDisabled, const CActorParameters& actParms)
: CPatterned(ECharacter::EyeBall, uid, name, flavor, info, xf, std::move(mData), pInfo, EMovementType::Flyer,
EColliderType::Zero, EBodyType::Restricted, actParms, EKnockBackVariant::Medium)
-, x568_attackDelay(f1)
-, x56c_maxAttackDelay(f2)
+, x568_attackDelay(attackDelay)
+, x56c_attackStartTime(attackStartTime)
, x570_boneTracking(*GetModelData()->GetAnimationData(), "Eye"sv, zeus::degToRad(45.f), zeus::degToRad(180.f), true)
-, x5b4_projectileInfo(aId1, dInfo)
-, x5dc_(aId2)
-, x5e0_(aId3)
-, x5e4_(aId4)
-, x5e8_(aId5)
-, x604_beamSfxId(CSfxManager::TranslateSFXID(w5))
+, x5b4_projectileInfo(wpscId, dInfo)
+, x5dc_beamContactFxId(beamContactFxId)
+, x5e0_beamPulseFxId(beamPulseFxId)
+, x5e4_beamTextureId(beamTextureId)
+, x5e8_beamGlowTextureId(beamGlowTextureId)
+, x604_beamSfxId(CSfxManager::TranslateSFXID(beamSfx))
, x60c_24_canAttack(false)
, x60c_25_playerInRange(false)
, x60c_26_alert(false)
-, x60c_27_attackDisabled(b1)
+, x60c_27_attackDisabled(attackDisabled)
, x60c_28_firingBeam(false) {
- x5f4_animIdxs[0] = w1;
- x5f4_animIdxs[1] = w2;
- x5f4_animIdxs[2] = w3;
- x5f4_animIdxs[3] = w4;
+ x5f4_animIdxs[0] = anim0;
+ x5f4_animIdxs[1] = anim1;
+ x5f4_animIdxs[2] = anim2;
+ x5f4_animIdxs[3] = anim3;
x460_knockBackController.SetAutoResetImpulse(false);
}
@@ -119,8 +120,9 @@ void CEyeball::CreateBeam(CStateManager& mgr) {
if (x5ec_projectileId != kInvalidUniqueId)
return;
- CBeamInfo beamInfo(3, x5dc_, x5e0_, x5e4_, x5e8_, 50, .5f, 1.f, 2.f, 20.f, 1.f, 1.f, 2.f,
- zeus::CColor(1.f, 1.f, 1.f, 0.f), zeus::CColor(0.f, 1.f, 0.5f, 0.f), 150.f);
+ CBeamInfo beamInfo(3, x5dc_beamContactFxId, x5e0_beamPulseFxId, x5e4_beamTextureId, x5e8_beamGlowTextureId,
+ 50, 0.05f, 1.f, 2.f, 20.f, 1.f, 1.f, 2.f, zeus::CColor(1.f, 1.f, 1.f, 0.f),
+ zeus::CColor(0.f, 1.f, 0.5f, 0.f), 150.f);
x5ec_projectileId = mgr.AllocateUniqueId();
mgr.AddObject(new CPlasmaProjectile(x5b4_projectileInfo.Token(), "EyeBall_Beam"sv, EWeaponType::AI, beamInfo,
zeus::CTransform::Identity(), EMaterialTypes::Immovable,
@@ -161,7 +163,7 @@ void CEyeball::Active(CStateManager& mgr, EStateMsg msg, float) {
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Combat);
x60c_24_canAttack = false;
} else if (msg == EStateMsg::Update) {
- if (x330_stateMachineState.GetDelay() > x56c_maxAttackDelay)
+ if (x330_stateMachineState.GetTime() > x56c_attackStartTime)
x60c_24_canAttack = true;
UpdateAnimation();
diff --git a/Runtime/MP1/World/CEyeball.hpp b/Runtime/MP1/World/CEyeball.hpp
index 9b4338017..b45de0218 100644
--- a/Runtime/MP1/World/CEyeball.hpp
+++ b/Runtime/MP1/World/CEyeball.hpp
@@ -8,14 +8,14 @@ namespace urde::MP1 {
class CEyeball : public CPatterned {
static constexpr std::string_view skEyeLocator = "Laser_LCTR"sv;
float x568_attackDelay;
- float x56c_maxAttackDelay;
+ float x56c_attackStartTime;
CBoneTracking x570_boneTracking;
zeus::CVector3f x5a8_targetPosition;
CProjectileInfo x5b4_projectileInfo;
- CAssetId x5dc_;
- CAssetId x5e0_;
- CAssetId x5e4_;
- CAssetId x5e8_;
+ CAssetId x5dc_beamContactFxId;
+ CAssetId x5e0_beamPulseFxId;
+ CAssetId x5e4_beamTextureId;
+ CAssetId x5e8_beamGlowTextureId;
TUniqueId x5ec_projectileId = kInvalidUniqueId;
u32 x5f0_currentAnim = 0;
s32 x5f4_animIdxs[4];
@@ -36,9 +36,11 @@ class CEyeball : public CPatterned {
public:
DEFINE_PATTERNED(EyeBall)
- CEyeball(TUniqueId, std::string_view, EFlavorType, const CEntityInfo&, const zeus::CTransform&, CModelData&&,
- const CPatternedInfo&, float, float, CAssetId, const CDamageInfo&, CAssetId, CAssetId, CAssetId, CAssetId,
- u32, u32, u32, u32, u32, bool, const CActorParameters&);
+ CEyeball(TUniqueId uid, std::string_view name, CPatterned::EFlavorType flavor, const CEntityInfo& info,
+ const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo, float attackDelay,
+ float attackStartTime, CAssetId wpscId, const CDamageInfo& dInfo, CAssetId beamContactFxId,
+ CAssetId beamPulseFxId, CAssetId beamTextureId, CAssetId beamGlowTextureId, u32 anim0, u32 anim1,
+ u32 anim2, u32 anim3, u32 beamSfx, bool attackDisabled, const CActorParameters& actParms);
void Accept(IVisitor& visitor);
void PreRender(CStateManager&, const zeus::CFrustum&);
diff --git a/Runtime/MP1/World/CWarWasp.cpp b/Runtime/MP1/World/CWarWasp.cpp
index b3500f231..ac58d2c49 100644
--- a/Runtime/MP1/World/CWarWasp.cpp
+++ b/Runtime/MP1/World/CWarWasp.cpp
@@ -1,14 +1,1231 @@
#include "CWarWasp.hpp"
#include "Character/CCharLayoutInfo.hpp"
#include "TCastTo.hpp"
+#include "World/CPatternedInfo.hpp"
+#include "GameGlobalObjects.hpp"
+#include "CSimplePool.hpp"
+#include "World/CPlayer.hpp"
+#include "World/CTeamAiMgr.hpp"
+#include "World/CWorld.hpp"
+#include "World/CScriptWaypoint.hpp"
+#include "Weapon/CGameProjectile.hpp"
namespace urde::MP1 {
CWarWasp::CWarWasp(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf,
CModelData&& mData, const CPatternedInfo& pInfo, CPatterned::EFlavorType flavor,
CPatterned::EColliderType collider, const CDamageInfo& dInfo1, const CActorParameters& actorParms,
- CAssetId weapon, const CDamageInfo& dInfo2, CAssetId particle, u32 w3)
+ CAssetId projectileWeapon, const CDamageInfo& projectileDamage, CAssetId projectileVisorParticle,
+ u32 projecileVisorSfx)
: CPatterned(ECharacter::WarWasp, uid, name, flavor, info, xf, std::move(mData), pInfo, EMovementType::Flyer, collider,
- EBodyType::Flyer, actorParms, EKnockBackVariant::Small) {}
+ EBodyType::Flyer, actorParms, EKnockBackVariant::Small)
+, x570_cSphere(zeus::CSphere({0.f, 0.f, 1.8f}, 1.f), x68_material)
+, x590_pfSearch(nullptr, 0x3, pInfo.GetPathfindingIndex(), 1.f, 1.f)
+, x684_(dInfo1)
+, x6d4_projectileInfo(projectileWeapon, projectileDamage)
+, x72c_projectileVisorSfx(CSfxManager::TranslateSFXID(projecileVisorSfx)) {
+ x72e_24_jumpBackRepeat = true;
+ x72e_26_initiallyInactive = !pInfo.GetActive();
+ x6d4_projectileInfo.Token().Lock();
+ UpdateTouchBounds();
+ SetCoefficientOfRestitutionModifier(0.1f);
+ if (projectileVisorParticle.IsValid())
+ x71c_projectileVisorParticle = g_SimplePool->GetObj(SObjectTag{FOURCC('PART'), projectileVisorParticle});
+ x328_29_noPatternShagging = true;
+ x460_knockBackController.SetAnimationStateRange(EKnockBackAnimationState::KnockBack,
+ EKnockBackAnimationState::KnockBack);
+}
void CWarWasp::Accept(IVisitor& visitor) { visitor.Visit(this); }
+
+void CWarWasp::SwarmAdd(CStateManager& mgr) {
+ if (x674_aiMgr != kInvalidUniqueId) {
+ if (TCastToPtr aimgr = mgr.ObjectById(x674_aiMgr)) {
+ CTeamAiRole::ETeamAiRole role = x3fc_flavor == EFlavorType::Two ?
+ CTeamAiRole::ETeamAiRole::Projectile : CTeamAiRole::ETeamAiRole::Melee;
+ if (!aimgr->IsPartOfTeam(GetUniqueId())) {
+ aimgr->AssignTeamAiRole(*this, role, CTeamAiRole::ETeamAiRole::Invalid, CTeamAiRole::ETeamAiRole::Invalid);
+ }
+ }
+ }
+}
+
+void CWarWasp::SwarmRemove(CStateManager& mgr) {
+ if (x674_aiMgr != kInvalidUniqueId) {
+ if (TCastToPtr aimgr = mgr.ObjectById(x674_aiMgr)) {
+ if (aimgr->IsPartOfTeam(GetUniqueId())) {
+ aimgr->RemoveTeamAiRole(GetUniqueId());
+ }
+ }
+ }
+}
+
+void CWarWasp::ApplyDamage(CStateManager& mgr) {
+ if (x72e_25_canApplyDamage && x450_bodyController->GetCurrentStateId() == pas::EAnimationState::MeleeAttack) {
+ if (mgr.GetPlayer().GetBoundingBox().pointInside(
+ GetTransform() * (GetLocatorTransform("LCTR_WARTAIL"sv).origin * x64_modelData->GetScale()))) {
+ mgr.ApplyDamage(GetUniqueId(), mgr.GetPlayer().GetUniqueId(), GetUniqueId(), GetContactDamage(),
+ CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {}), zeus::CVector3f::skZero);
+ x72e_25_canApplyDamage = false;
+ }
+ }
+}
+
+void CWarWasp::Think(float dt, CStateManager& mgr) {
+ if (!GetActive())
+ return;
+
+ if (x700_attackRemTime > 0.f) {
+ float rate = 1.f;
+ if (mgr.GetPlayer().GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Unmorphed)
+ rate = 1.f - zeus::CVector2f::getAngleDiff(mgr.GetPlayer().GetTransform().basis[1].toVec2f(),
+ GetTranslation().toVec2f() - mgr.GetPlayer().GetTranslation().toVec2f()) / M_PIF * 0.666f;
+ x700_attackRemTime -= rate * dt;
+ }
+
+ ApplyDamage(mgr);
+ CPatterned::Think(dt, mgr);
+}
+
+void CWarWasp::SetUpCircleBurstWaypoint(CStateManager& mgr) {
+ for (const auto& conn : GetConnectionList()) {
+ if (conn.x0_state == EScriptObjectState::CloseIn && conn.x4_msg == EScriptObjectMessage::Follow) {
+ if (TCastToConstPtr wp = mgr.GetObjectById(mgr.GetIdForScript(conn.x8_objId))) {
+ x6b0_circleBurstPos = wp->GetTranslation();
+ x6bc_circleBurstDir = wp->GetTransform().basis[1];
+ x6c8_circleBurstRight = wp->GetTransform().basis[0];
+ break;
+ }
+ }
+ }
+}
+
+void CWarWasp::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CStateManager& mgr) {
+ CPatterned::AcceptScriptMsg(msg, sender, mgr);
+ switch (msg) {
+ case EScriptObjectMessage::Deleted:
+ case EScriptObjectMessage::Deactivate:
+ SwarmRemove(mgr);
+ break;
+ case EScriptObjectMessage::InitializedInArea:
+ if (x674_aiMgr == kInvalidUniqueId)
+ x674_aiMgr = CTeamAiMgr::GetTeamAiMgr(*this, mgr);
+ x590_pfSearch.SetArea(mgr.GetWorld()->GetAreaAlways(GetAreaIdAlways())->GetPostConstructed()->x10bc_pathArea);
+ if (x6b0_circleBurstPos.isZero())
+ SetUpCircleBurstWaypoint(mgr);
+ break;
+ default:
+ break;
+ }
+}
+
+std::experimental::optional CWarWasp::GetTouchBounds() const {
+ return {x570_cSphere.CalculateAABox(GetTransform())};
+}
+
+zeus::CVector3f CWarWasp::GetProjectileAimPos(CStateManager& mgr, float zBias) {
+ zeus::CVector3f ret = mgr.GetPlayer().GetAimPosition(mgr, 0.f);
+ if (mgr.GetPlayer().GetMorphballTransitionState() != CPlayer::EPlayerMorphBallState::Morphed)
+ ret += zeus::CVector3f(0.f, 0.f, zBias);
+ return ret;
+}
+
+void CWarWasp::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt) {
+ bool handled = false;
+ switch (type) {
+ case EUserEventType::Projectile: {
+ zeus::CTransform xf = GetLctrTransform(node.GetLocatorName());
+ zeus::CVector3f aimPos = GetProjectileAimPos(mgr, -0.07f);
+ if (mgr.GetPlayer().GetMorphballTransitionState() != CPlayer::EPlayerMorphBallState::Morphed) {
+ zeus::CVector3f delta = aimPos - xf.origin;
+ if (delta.canBeNormalized()) {
+ rstl::reserved_vector nearList;
+ TUniqueId bestId = kInvalidUniqueId;
+ CRayCastResult res = mgr.RayWorldIntersection(bestId, xf.origin, delta.normalized(), delta.magnitude(),
+ CMaterialFilter::MakeInclude({EMaterialTypes::Solid}), nearList);
+ if (res.IsValid())
+ aimPos = res.GetPoint();
+ }
+ }
+ LaunchProjectile(
+ zeus::lookAt(xf.origin, GetProjectileInfo()->PredictInterceptPos(xf.origin, aimPos, mgr.GetPlayer(), true, dt)),
+ mgr, 4, EProjectileAttrib::None, false, {x71c_projectileVisorParticle}, x72c_projectileVisorSfx, true,
+ zeus::CVector3f::skOne);
+ handled = true;
+ break;
+ }
+ case EUserEventType::DeGenerate:
+ SendScriptMsgs(EScriptObjectState::DeactivateState, mgr, EScriptObjectMessage::None);
+ mgr.FreeScriptObject(GetUniqueId());
+ handled = true;
+ break;
+ case EUserEventType::GenerateEnd:
+ AddMaterial(EMaterialTypes::Character, EMaterialTypes::Solid, mgr);
+ handled = true;
+ break;
+ case EUserEventType::DamageOn:
+ x72e_25_canApplyDamage = true;
+ break;
+ case EUserEventType::DamageOff:
+ x72e_25_canApplyDamage = false;
+ break;
+ default:
+ break;
+ }
+ if (!handled)
+ CPatterned::DoUserAnimEvent(mgr, node, type, dt);
+}
+
+const CCollisionPrimitive* CWarWasp::GetCollisionPrimitive() const {
+ return &x570_cSphere;
+}
+
+void CWarWasp::Death(CStateManager& mgr, const zeus::CVector3f& direction, EScriptObjectState state) {
+ CPatterned::Death(mgr, direction, state);
+ x328_25_verticalMovement = false;
+ AddMaterial(EMaterialTypes::GroundCollider, mgr);
+ SwarmRemove(mgr);
+}
+
+bool CWarWasp::IsListening() const {
+ return true;
+}
+
+bool CWarWasp::Listen(const zeus::CVector3f& pos, EListenNoiseType type) {
+ switch (type) {
+ case EListenNoiseType::PlayerFire:
+ case EListenNoiseType::BombExplode:
+ case EListenNoiseType::ProjectileExplode:
+ if ((GetTranslation() - pos).magSquared() < x3bc_detectionRange * x3bc_detectionRange) {
+ x72e_31_heardNoise = true;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+float CWarWasp::GetCloseInZBasis(const CStateManager& mgr) const {
+ return mgr.GetPlayer().GetTranslation().z() + mgr.GetPlayer().GetEyeHeight() - 0.5f;
+}
+
+zeus::CVector3f CWarWasp::GetCloseInPos(const CStateManager& mgr, const zeus::CVector3f& aimPos) const {
+ float midRange = (x2fc_minAttackRange + x300_maxAttackRange) * 0.5f;
+ zeus::CVector3f ret;
+ if (mgr.GetPlayer().GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Unmorphed) {
+ ret = mgr.GetPlayer().GetTransform().basis[1] * midRange + aimPos;
+ } else {
+ zeus::CVector3f delta = GetTranslation() - aimPos;
+ if (delta.canBeNormalized())
+ ret = delta.normalized() * midRange + aimPos;
+ else
+ ret = GetTransform().basis[1] * midRange + aimPos;
+ }
+ ret.z() = 0.5f + GetCloseInZBasis(mgr);
+ return ret;
+}
+
+zeus::CVector3f CWarWasp::GetOrigin(const CStateManager& mgr, const CTeamAiRole& role,
+ const zeus::CVector3f& aimPos) const {
+ if (x6b0_circleBurstPos.isZero())
+ return GetCloseInPos(mgr, aimPos);
+ else
+ return GetTranslation();
+}
+
+void CWarWasp::UpdateTouchBounds() {
+ zeus::CAABox aabb = x64_modelData->GetAnimationData()->GetBoundingBox();
+ x570_cSphere.SetSphereCenter(aabb.center());
+ SetBoundingBox(aabb.getTransformedAABox(zeus::CTransform(GetTransform().basis)));
+}
+
+void CWarWasp::Patrol(CStateManager& mgr, EStateMsg msg, float dt) {
+ switch (msg) {
+ case EStateMsg::Activate: {
+ float maxSpeed = x450_bodyController->GetBodyStateInfo().GetMaxSpeed();
+ if (maxSpeed > 0.f) {
+ x450_bodyController->GetCommandMgr().SetSteeringBlendMode(ESteeringBlendMode::FullSpeed);
+ float speedFactor =
+ x450_bodyController->GetBodyStateInfo().GetLocomotionSpeed(pas::ELocomotionAnim::Walk) * 0.9f / maxSpeed;
+ x450_bodyController->GetCommandMgr().SetSteeringSpeedRange(speedFactor, speedFactor);
+ }
+ x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed);
+ x72e_31_heardNoise = false;
+ break;
+ }
+ case EStateMsg::Deactivate:
+ x450_bodyController->GetCommandMgr().SetSteeringBlendMode(ESteeringBlendMode::Normal);
+ break;
+ default:
+ break;
+ }
+ CPatterned::Patrol(mgr, msg, dt);
+}
+
+void CWarWasp::SetUpPathFindBehavior(CStateManager& mgr) {
+ x72e_29_pathObstructed = false;
+ if (GetSearchPath()) {
+ SwarmAdd(mgr);
+ zeus::CVector3f pos;
+ if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x674_aiMgr, GetUniqueId()))
+ pos = role->GetTeamPosition();
+ else
+ pos = GetCloseInPos(mgr, GetProjectileAimPos(mgr, -1.25f));
+ SetDestPos(pos);
+ if ((x2e0_destPos - GetTranslation()).magSquared() > 64.f ||
+ IsPatternObstructed(mgr, GetTranslation(), x2e0_destPos)) {
+ zeus::CVector3f aimPos = GetProjectileAimPos(mgr, -1.25f);
+ zeus::CVector3f delta = x2e0_destPos - aimPos;
+ if (delta.canBeNormalized()) {
+ zeus::CVector3f dir = delta.normalized();
+ CRayCastResult res = mgr.RayStaticIntersection(aimPos, dir, delta.magnitude(),
+ CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {EMaterialTypes::Player}));
+ if (res.IsValid()) {
+ SetDestPos(dir * res.GetT() * 0.5f + aimPos);
+ x72e_29_pathObstructed = true;
+ }
+ }
+ CPatterned::PathFind(mgr, EStateMsg::Activate, 0.f);
+ }
+ }
+}
+
+void CWarWasp::ApplyNormalSteering(CStateManager& mgr) {
+ zeus::CVector3f delta = mgr.GetPlayer().GetTranslation() - GetTranslation();
+ zeus::CVector3f teamPos;
+ if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x674_aiMgr, GetUniqueId()))
+ teamPos = role->GetTeamPosition();
+ else
+ teamPos = GetProjectileAimPos(mgr, -1.25f);
+ zeus::CVector2f toTeamPos2d = (teamPos - GetTranslation()).toVec2f();
+ float toTeamPosH = teamPos.z() - GetTranslation().z();
+ if (toTeamPos2d.magSquared() > 1.f || std::fabs(toTeamPosH) > 2.5f) {
+ pas::EStepDirection stepDir = GetStepDirection(toTeamPos2d);
+ if (stepDir != pas::EStepDirection::Forward) {
+ x450_bodyController->GetCommandMgr().DeliverCmd(CBCStepCmd(stepDir, pas::EStepType::Normal));
+ } else {
+ x450_bodyController->GetCommandMgr().DeliverCmd(
+ CBCLocomotionCmd(x45c_steeringBehaviors.Arrival(*this, teamPos, 3.f), zeus::CVector3f::skZero, 1.f));
+ zeus::CVector3f target = GetTranslation();
+ target.z() = float(teamPos.z());
+ zeus::CVector3f moveVec = x45c_steeringBehaviors.Arrival(*this, target, 3.f);
+ if (moveVec.magSquared() > 0.01f) {
+ x450_bodyController->GetCommandMgr().DeliverCmd(
+ CBCLocomotionCmd(moveVec, zeus::CVector3f::skZero, 3.f));
+ }
+ }
+ } else {
+ switch (mgr.GetActiveRandom()->Range(0, 2)) {
+ case 0:
+ x450_bodyController->GetCommandMgr().DeliverCmd(CBCStepCmd(pas::EStepDirection::Left, pas::EStepType::Normal));
+ break;
+ case 1:
+ x450_bodyController->GetCommandMgr().DeliverCmd(CBCStepCmd(pas::EStepDirection::Right, pas::EStepType::Normal));
+ break;
+ case 2:
+ if (ShouldTurn(mgr, 30.f) && delta.canBeNormalized()) {
+ x450_bodyController->GetCommandMgr().DeliverCmd(
+ CBCLocomotionCmd(zeus::CVector3f::skZero, delta.normalized(), 1.f));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ x450_bodyController->GetCommandMgr().DeliverTargetVector(delta);
+}
+
+void CWarWasp::ApplySeparationBehavior(CStateManager& mgr, float sep) {
+ for (CEntity* ent : mgr.GetListeningAiObjectList()) {
+ if (TCastToPtr ai = ent) {
+ if (ai.GetPtr() != this && ai->GetAreaIdAlways() == GetAreaIdAlways()) {
+ float useSep = sep;
+ if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x674_aiMgr, ai->GetUniqueId())) {
+ if (role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Melee ||
+ role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Projectile)
+ useSep *= 2.f;
+ }
+ zeus::CVector3f separation = x45c_steeringBehaviors.Separation(*this, ai->GetTranslation(), useSep);
+ if (!separation.isZero()) {
+ x450_bodyController->GetCommandMgr().DeliverCmd(
+ CBCLocomotionCmd(separation, zeus::CVector3f::skZero, 1.f));
+ }
+ }
+ }
+ }
+}
+
+bool CWarWasp::PathToHiveIsClear(CStateManager& mgr) {
+ zeus::CVector3f delta = x3a0_latestLeashPosition - GetTranslation();
+ if (GetTransform().basis[1].dot(delta) > 0.f) {
+ zeus::CAABox aabb(GetTranslation() - 10.f, GetTranslation() + 10.f);
+ rstl::reserved_vector nearList;
+ mgr.BuildNearList(nearList, aabb, CMaterialFilter::MakeInclude({EMaterialTypes::Character}), nullptr);
+ float deltaMagSq = delta.magSquared();
+ for (TUniqueId id : nearList) {
+ if (const CWarWasp* other = CPatterned::CastTo(mgr.GetObjectById(id))) {
+ if (other->GetUniqueId() != GetUniqueId() && other->x72e_30_isRetreating &&
+ zeus::close_enough(other->x3a0_latestLeashPosition, x3a0_latestLeashPosition, 3.f)) {
+ zeus::CVector3f waspDelta = other->GetTranslation() - GetTranslation();
+ if (GetTransform().basis[1].dot(waspDelta) > 0.f && waspDelta.magSquared() < 3.f &&
+ (other->GetTranslation() - x3a0_latestLeashPosition).magSquared() < deltaMagSq) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool CWarWasp::SteerToDeactivatePos(CStateManager& mgr, EStateMsg msg, float dt) {
+ float distSq = (x3a0_latestLeashPosition - GetTranslation()).magSquared();
+ if (distSq > 1.f + x570_cSphere.GetSphere().radius) {
+ if (PathToHiveIsClear(mgr)) {
+ zeus::CVector3f arrival1 = x45c_steeringBehaviors.Arrival(*this, x3a0_latestLeashPosition, 15.f);
+ float maxSpeed = x450_bodyController->GetBodyStateInfo().GetMaxSpeed();
+ float minMoveFactor;
+ if (maxSpeed > 0.f)
+ minMoveFactor = x450_bodyController->GetBodyStateInfo().GetLocomotionSpeed(
+ pas::ELocomotionAnim::Walk) * 0.5f / maxSpeed;
+ else
+ minMoveFactor = 1.f;
+ float moveFactor = zeus::clamp(minMoveFactor, arrival1.magnitude(), 1.f);
+ zeus::CVector3f moveVec;
+ if (arrival1.canBeNormalized())
+ moveVec = arrival1.normalized() * moveFactor;
+ else
+ moveVec = GetTransform().basis[1] * moveFactor;
+ x450_bodyController->GetCommandMgr().SetSteeringBlendMode(ESteeringBlendMode::FullSpeed);
+ x450_bodyController->GetCommandMgr().SetSteeringSpeedRange(moveFactor, moveFactor);
+ if (distSq > 64.f + x570_cSphere.GetSphere().radius) {
+ if (GetSearchPath() && !PathShagged(mgr, 0.f) && !GetSearchPath()->IsOver()) {
+ CPatterned::PathFind(mgr, msg, dt);
+ } else {
+ x450_bodyController->GetCommandMgr().DeliverCmd(
+ CBCLocomotionCmd(moveVec, zeus::CVector3f::skZero, 1.f));
+ }
+ } else {
+ RemoveMaterial(EMaterialTypes::Solid, mgr);
+ zeus::CVector3f target = GetTranslation();
+ target.z() = float(x3a0_latestLeashPosition.z());
+ zeus::CVector3f arrival2 = x45c_steeringBehaviors.Arrival(*this, target, 2.5f);
+ if (arrival2.magSquared() > 0.01f) {
+ x450_bodyController->GetCommandMgr().DeliverCmd(
+ CBCLocomotionCmd(arrival2, zeus::CVector3f::skZero, 3.f));
+ }
+ x450_bodyController->GetCommandMgr().DeliverCmd(
+ CBCLocomotionCmd(moveVec, zeus::CVector3f::skZero, 1.f));
+ }
+ }
+ return false;
+ } else if (distSq > 0.01f) {
+ RemoveMaterial(EMaterialTypes::Solid, mgr);
+ zeus::CQuaternion q;
+ q.rotateZ(zeus::degToRad(180.f));
+ SetTranslation(GetTranslation() * 0.9f + x3a0_latestLeashPosition * 0.1f);
+ SetTransform(zeus::CQuaternion::slerpShort(zeus::CQuaternion(GetTransform().basis), x6a0_initialRot * q, 0.1f).
+ normalized().toTransform(GetTranslation()));
+ return false;
+ }
+ return true;
+}
+
+void CWarWasp::PathFind(CStateManager& mgr, EStateMsg msg, float dt) {
+ switch (msg) {
+ case EStateMsg::Activate:
+ SetUpPathFindBehavior(mgr);
+ break;
+ case EStateMsg::Update: {
+ if (mgr.GetPlayer().GetOrbitState() != CPlayer::EPlayerOrbitState::NoOrbit &&
+ mgr.GetPlayer().GetOrbitTargetId() == GetUniqueId())
+ x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed);
+ else
+ x450_bodyController->SetLocomotionType(pas::ELocomotionType::Lurk);
+ if (GetSearchPath() && !PathShagged(mgr, 0.f) && !GetSearchPath()->IsOver()) {
+ CPatterned::PathFind(mgr, msg, dt);
+ } else {
+ ApplyNormalSteering(mgr);
+ }
+ ApplySeparationBehavior(mgr, 9.f);
+ float distTest = 2.f * x300_maxAttackRange;
+ if ((mgr.GetPlayer().GetTranslation() - GetTranslation()).magSquared() > distTest * distTest) {
+ x450_bodyController->GetCommandMgr().SetSteeringBlendMode(ESteeringBlendMode::FullSpeed);
+ x450_bodyController->GetCommandMgr().SetSteeringSpeedRange(1.f, 1.f);
+ } else {
+ x450_bodyController->GetCommandMgr().SetSteeringBlendMode(ESteeringBlendMode::Normal);
+ }
+ break;
+ }
+ case EStateMsg::Deactivate:
+ x450_bodyController->GetCommandMgr().SetSteeringBlendMode(ESteeringBlendMode::Normal);
+ break;
+ }
+}
+
+void CWarWasp::TargetPatrol(CStateManager& mgr, EStateMsg msg, float dt) {
+ switch (msg) {
+ case EStateMsg::Activate:
+ x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed);
+ SwarmRemove(mgr);
+ CPatterned::Patrol(mgr, msg, dt);
+ CPatterned::UpdateDest(mgr);
+ x678_targetPos = x2e0_destPos;
+ if (GetSearchPath())
+ CPatterned::PathFind(mgr, msg, dt);
+ break;
+ case EStateMsg::Update:
+ if (GetSearchPath() && !PathShagged(mgr, 0.f))
+ CPatterned::PathFind(mgr, msg, dt);
+ else
+ CPatterned::Patrol(mgr, msg, dt);
+ ApplySeparationBehavior(mgr, 9.f);
+ break;
+ default:
+ break;
+ }
+}
+
+void CWarWasp::Generate(CStateManager& mgr, EStateMsg msg, float dt) {
+ switch (msg) {
+ case EStateMsg::Activate:
+ x450_bodyController->Activate(mgr);
+ if (x72e_26_initiallyInactive) {
+ RemoveMaterial(EMaterialTypes::Character, EMaterialTypes::Solid, mgr);
+ x6a0_initialRot = zeus::CQuaternion(GetTransform().basis);
+ zeus::CQuaternion q;
+ q.rotateZ(mgr.GetActiveRandom()->Float() * zeus::degToRad(45.f) - zeus::degToRad(22.5f));
+ SetTransform((x6a0_initialRot * q).normalized().toTransform(GetTranslation()));
+ x568_stateProg = 0;
+ } else {
+ x568_stateProg = 3;
+ }
+ break;
+ case EStateMsg::Update:
+ switch (x568_stateProg) {
+ case 0:
+ if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Generate) {
+ x568_stateProg = 2;
+ } else {
+ x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::Zero, x388_anim));
+ }
+ break;
+ case 2:
+ if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Generate) {
+ x568_stateProg = 3;
+ } else {
+ zeus::CVector3f moveVec;
+ for (CEntity* ent : mgr.GetListeningAiObjectList())
+ if (TCastToPtr act = ent)
+ if (act.GetPtr() != this && act->GetAreaIdAlways() == GetAreaIdAlways())
+ moveVec += x45c_steeringBehaviors.Separation(*this, act->GetTranslation(), 5.f) * (5.f * dt);
+ if (!moveVec.isZero())
+ ApplyImpulseWR(GetMoveToORImpulseWR(moveVec, dt), {});
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case EStateMsg::Deactivate:
+ if (x72e_26_initiallyInactive) {
+ AddMaterial(EMaterialTypes::Character, EMaterialTypes::Solid, mgr);
+ if (x328_26_solidCollision)
+ x401_30_pendingDeath = true;
+ }
+ break;
+ }
+}
+
+void CWarWasp::Deactivate(CStateManager& mgr, EStateMsg msg, float dt) {
+ switch (msg) {
+ case EStateMsg::Activate:
+ x568_stateProg = 1;
+ x72e_30_isRetreating = true;
+ x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed);
+ SwarmRemove(mgr);
+ x674_aiMgr = kInvalidUniqueId;
+ x678_targetPos = x3a0_latestLeashPosition;
+ SetDestPos(x678_targetPos);
+ if (GetSearchPath())
+ CPatterned::PathFind(mgr, msg, dt);
+ break;
+ case EStateMsg::Update:
+ switch (x568_stateProg) {
+ case 1:
+ if (SteerToDeactivatePos(mgr, msg, dt)) {
+ RemoveMaterial(EMaterialTypes::Solid, mgr);
+ x568_stateProg = 0;
+ }
+ break;
+ case 0:
+ if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Generate) {
+ RemoveMaterial(EMaterialTypes::Character, EMaterialTypes::Target, EMaterialTypes::Orbit, mgr);
+ mgr.GetPlayer().SetOrbitRequestForTarget(GetUniqueId(), CPlayer::EPlayerOrbitRequest::ActivateOrbitSource, mgr);
+ x568_stateProg = 2;
+ } else {
+ x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::One));
+ }
+ break;
+ case 2:
+ if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Generate)
+ x568_stateProg = 3;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void CWarWasp::Attack(CStateManager& mgr, EStateMsg msg, float dt) {
+ switch (msg) {
+ case EStateMsg::Activate:
+ x72e_27_teamMatesMelee = true;
+ x72e_25_canApplyDamage = false;
+ if (x674_aiMgr != kInvalidUniqueId)
+ x568_stateProg = CTeamAiMgr::AddAttacker(CTeamAiMgr::EAttackType::Melee, mgr, x674_aiMgr, GetUniqueId()) ? 0 : 3;
+ else
+ x568_stateProg = 0;
+ break;
+ case EStateMsg::Update:
+ switch (x568_stateProg) {
+ case 0:
+ if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::MeleeAttack) {
+ x568_stateProg = 2;
+ } else {
+ zeus::CVector3f aimPos = GetProjectileAimPos(mgr, -1.25f);
+ zeus::CVector3f aimDelta = aimPos - GetTranslation();
+ if (aimDelta.canBeNormalized())
+ aimPos += aimDelta.normalized() * 7.5f;
+ x450_bodyController->GetCommandMgr().DeliverCmd(CBCMeleeAttackCmd(pas::ESeverity::One, aimPos));
+ }
+ break;
+ case 2:
+ if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::MeleeAttack) {
+ x568_stateProg = 3;
+ } else {
+ x450_bodyController->GetCommandMgr().DeliverTargetVector(mgr.GetPlayer().GetTranslation() - GetTranslation());
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case EStateMsg::Deactivate:
+ CTeamAiMgr::ResetTeamAiRole(CTeamAiMgr::EAttackType::Melee, mgr, x674_aiMgr, GetUniqueId(), false);
+ x700_attackRemTime = CalcTimeToNextAttack(mgr);
+ x72e_27_teamMatesMelee = false;
+ break;
+ }
+}
+
+void CWarWasp::JumpBack(CStateManager& mgr, EStateMsg msg, float dt) {
+ switch (msg) {
+ case EStateMsg::Activate:
+ x568_stateProg = 0;
+ x72e_24_jumpBackRepeat = true;
+ SwarmRemove(mgr);
+ break;
+ case EStateMsg::Update:
+ switch (x568_stateProg) {
+ case 0:
+ if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Step) {
+ x568_stateProg = 2;
+ } else {
+ x450_bodyController->GetCommandMgr().DeliverCmd(
+ CBCStepCmd(pas::EStepDirection::Backward, pas::EStepType::Normal));
+ }
+ break;
+ case 2:
+ if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Step) {
+ x568_stateProg = x72e_24_jumpBackRepeat ? 0 : 3;
+ x72e_24_jumpBackRepeat = false;
+ } else {
+ x450_bodyController->GetCommandMgr().DeliverTargetVector(mgr.GetPlayer().GetTranslation() - GetTranslation());
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+zeus::CVector3f CWarWasp::CalcShuffleDest(CStateManager& mgr) {
+ zeus::CVector2f aimPos2d = GetProjectileAimPos(mgr, -1.25f).toVec2f();
+ zeus::CVector3f playerDir2d = mgr.GetPlayer().GetTransform().basis[1];
+ playerDir2d.z() = 0.f;
+ zeus::CVector3f useDir;
+ if (playerDir2d.canBeNormalized())
+ useDir = playerDir2d.normalized();
+ else
+ useDir = zeus::CVector3f::skForward;
+ aimPos2d += useDir.toVec2f() * (7.5f + x300_maxAttackRange);
+ zeus::CVector3f ret(aimPos2d);
+ ret.z() = GetCloseInZBasis(mgr);
+ return ret;
+}
+
+void CWarWasp::Shuffle(CStateManager& mgr, EStateMsg msg, float dt) {
+ switch (msg) {
+ case EStateMsg::Activate:
+ x450_bodyController->SetLocomotionType(pas::ELocomotionType::Lurk);
+ x568_stateProg = 2;
+ break;
+ case EStateMsg::Update: {
+ s32 numRoles = 0;
+ if (TCastToConstPtr aiMgr = mgr.GetObjectById(x674_aiMgr))
+ numRoles = aiMgr->GetNumAssignedAiRoles();
+ if (numRoles && x700_attackRemTime > 0.f) {
+ zeus::CVector3f shuffleDest = CalcShuffleDest(mgr);
+ float zDelta = shuffleDest.z() - GetTranslation().z();
+ if (zDelta * zDelta > 1.f) {
+ zeus::CVector3f dest = GetTranslation();
+ dest.z() = float(shuffleDest.z());
+ zeus::CVector3f moveVec = x45c_steeringBehaviors.Arrival(*this, dest, 1.f);
+ if (moveVec.magSquared() > 0.01f)
+ x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(moveVec, zeus::CVector3f::skZero, 1.f));
+ } else {
+ zeus::CVector3f moveVec = shuffleDest - GetTranslation();
+ moveVec.z() = zDelta;
+ if (moveVec.magSquared() > 64.f) {
+ pas::EStepDirection stepDir = CPatterned::GetStepDirection(moveVec);
+ if (stepDir != pas::EStepDirection::Forward) {
+ x450_bodyController->GetCommandMgr().DeliverCmd(CBCStepCmd(stepDir, pas::EStepType::Normal));
+ } else {
+ x450_bodyController->GetCommandMgr().DeliverCmd(
+ CBCLocomotionCmd(x45c_steeringBehaviors.Seek(*this, shuffleDest), zeus::CVector3f::skZero, 1.f));
+ ApplySeparationBehavior(mgr, 15.f);
+ }
+ } else {
+ x568_stateProg = 3;
+ }
+ }
+ x450_bodyController->GetCommandMgr().DeliverTargetVector(mgr.GetPlayer().GetTranslation() - GetTranslation());
+ } else {
+ x568_stateProg = 3;
+ }
+ break;
+ }
+ case EStateMsg::Deactivate:
+ x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed);
+ break;
+ }
+}
+
+void CWarWasp::ProjectileAttack(CStateManager& mgr, EStateMsg msg, float dt) {
+ switch (msg) {
+ case EStateMsg::Activate:
+ x72e_28_inProjectileAttack = true;
+ if (x674_aiMgr != kInvalidUniqueId) {
+ x568_stateProg = CTeamAiMgr::AddAttacker(CTeamAiMgr::EAttackType::Projectile,
+ mgr, x674_aiMgr, GetUniqueId()) ? 0 : 3;
+ } else {
+ x568_stateProg = 0;
+ }
+ break;
+ case EStateMsg::Update:
+ switch (x568_stateProg) {
+ case 0:
+ if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::ProjectileAttack) {
+ x568_stateProg = 2;
+ } else {
+ SetDestPos(GetProjectileAimPos(mgr, -0.07f));
+ x450_bodyController->GetCommandMgr().DeliverCmd(
+ CBCProjectileAttackCmd(pas::ESeverity::One, x2e0_destPos, false));
+ }
+ break;
+ case 2:
+ if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::ProjectileAttack) {
+ x450_bodyController->GetCommandMgr().DeliverTargetVector(x2e0_destPos - GetTranslation());
+ x568_stateProg = 3;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case EStateMsg::Deactivate:
+ CTeamAiMgr::ResetTeamAiRole(CTeamAiMgr::EAttackType::Projectile, mgr, x674_aiMgr, GetUniqueId(), false);
+ x700_attackRemTime = CalcTimeToNextAttack(mgr);
+ x72e_28_inProjectileAttack = false;
+ break;
+ }
+}
+
+s32 CWarWasp::GetAttackTeamSize(CStateManager& mgr, s32 team) {
+ s32 count = 0;
+ if (TCastToConstPtr aimgr = mgr.GetObjectById(x674_aiMgr)) {
+ if (aimgr->IsPartOfTeam(GetUniqueId())) {
+ for (const CTeamAiRole& role : aimgr->GetRoles()) {
+ if (const CWarWasp* other = CPatterned::CastTo(mgr.GetObjectById(role.GetOwnerId()))) {
+ if (team == other->x708_circleAttackTeam)
+ ++count;
+ }
+ }
+ }
+ }
+ return count;
+}
+
+float CWarWasp::CalcTimeToNextAttack(CStateManager& mgr) {
+ float mul = 1.f;
+ if (TCastToConstPtr aimgr = mgr.GetObjectById(x674_aiMgr)) {
+ s32 maxCount = (x3fc_flavor == EFlavorType::Two) ?
+ aimgr->GetMaxProjectileAttackerCount() : aimgr->GetMaxMeleeAttackerCount();
+ s32 count = (x3fc_flavor == EFlavorType::Two) ?
+ aimgr->GetNumAssignedOfRole(CTeamAiRole::ETeamAiRole::Projectile) :
+ aimgr->GetNumAssignedOfRole(CTeamAiRole::ETeamAiRole::Melee);
+ if (count <= maxCount)
+ mul *= 0.5f;
+ }
+ return (mgr.GetActiveRandom()->Float() * x308_attackTimeVariation + x304_averageAttackTime) * mul;
+}
+
+float CWarWasp::CalcOffTotemAngle(CStateManager& mgr) {
+ return mgr.GetActiveRandom()->Float() * zeus::degToRad(80.f) + zeus::degToRad(10.f);
+}
+
+void CWarWasp::JoinCircleAttackTeam(s32 unit, CStateManager& mgr) {
+ if (!x6b0_circleBurstPos.isZero()) {
+ if (x70c_initialCircleAttackTeam == -1) {
+ x710_initialCircleAttackTeamUnit = GetAttackTeamSize(mgr, unit);
+ x70c_initialCircleAttackTeam = unit;
+ }
+ x708_circleAttackTeam = unit;
+ x700_attackRemTime = CalcTimeToNextAttack(mgr);
+ x718_circleBurstOffTotemAngle = CalcOffTotemAngle(mgr);
+ }
+}
+
+void CWarWasp::SetUpCircleTelegraphTeam(CStateManager& mgr) {
+ if (x708_circleAttackTeam == -1) {
+ if (TCastToConstPtr aimgr = mgr.GetObjectById(x674_aiMgr)) {
+ if (aimgr->IsPartOfTeam(GetUniqueId()) && aimgr->GetMaxMeleeAttackerCount() > 0) {
+ s32 teamUnit = 0;
+ s32 targetUnitSize = 0;
+ bool rejoinInitial = false;
+ for (const CTeamAiRole& role : aimgr->GetRoles()) {
+ if (const CWarWasp* other = CPatterned::CastTo(mgr.GetObjectById(role.GetOwnerId()))) {
+ if (x70c_initialCircleAttackTeam != -1 &&
+ other->x70c_initialCircleAttackTeam == x70c_initialCircleAttackTeam &&
+ other->x708_circleAttackTeam >= 0) {
+ teamUnit = other->x708_circleAttackTeam;
+ rejoinInitial = true;
+ break;
+ }
+ if (other->x708_circleAttackTeam > teamUnit) {
+ teamUnit = other->x708_circleAttackTeam;
+ targetUnitSize = 1;
+ } else if (other->x708_circleAttackTeam == teamUnit) {
+ ++targetUnitSize;
+ }
+ }
+ }
+ if (!rejoinInitial && (x70c_initialCircleAttackTeam != -1 ||
+ targetUnitSize >= aimgr->GetMaxMeleeAttackerCount()))
+ ++teamUnit;
+ JoinCircleAttackTeam(teamUnit, mgr);
+ x714_circleTelegraphSeekHeight = mgr.GetActiveRandom()->Float() * -0.5f;
+ }
+ }
+ }
+}
+
+TUniqueId CWarWasp::GetAttackTeamLeader(CStateManager& mgr, s32 team) {
+ if (TCastToConstPtr aimgr = mgr.GetObjectById(x674_aiMgr)) {
+ if (aimgr->IsPartOfTeam(GetUniqueId())) {
+ for (const CTeamAiRole& role : aimgr->GetRoles()) {
+ if (const CWarWasp* other = CPatterned::CastTo(mgr.GetObjectById(role.GetOwnerId()))) {
+ if (team == other->x708_circleAttackTeam)
+ return role.GetOwnerId();
+ }
+ }
+ }
+ }
+ return kInvalidUniqueId;
+}
+
+void CWarWasp::TryCircleTeamMerge(CStateManager& mgr) {
+ if (x708_circleAttackTeam > 0) {
+ if (TCastToConstPtr aimgr = mgr.GetObjectById(x674_aiMgr)) {
+ if (aimgr->IsPartOfTeam(GetUniqueId())) {
+ if (GetAttackTeamLeader(mgr, x708_circleAttackTeam) == GetUniqueId()) {
+ if (GetAttackTeamSize(mgr, x708_circleAttackTeam - 1) == 0) {
+ for (const CTeamAiRole& role : aimgr->GetRoles()) {
+ if (const CWarWasp* other = CPatterned::CastTo(mgr.GetObjectById(role.GetOwnerId()))) {
+ if (x708_circleAttackTeam == other->x708_circleAttackTeam)
+ JoinCircleAttackTeam(x708_circleAttackTeam - 1, mgr);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+float CWarWasp::GetTeamZStratum(s32 team) {
+ if (team > 0) {
+ if ((team & 1) == 1)
+ return -3.f - float(team / 2) * 3.f;
+ else
+ return float(team / 2) * 3.f;
+ }
+ return 0.f;
+}
+
+static const float Table[] = {
+ 0.4f, 0.6f, 1.f
+};
+
+float CWarWasp::CalcSeekMagnitude(CStateManager& mgr) {
+ float ret = ((x708_circleAttackTeam >= 0 && x708_circleAttackTeam < 3) ? Table[x708_circleAttackTeam] : 1.f) * 0.9f;
+ if (TCastToConstPtr aimgr = mgr.GetObjectById(x674_aiMgr)) {
+ if (aimgr->IsPartOfTeam(GetUniqueId())) {
+ if (aimgr->GetMaxMeleeAttackerCount() > 1) {
+ if (GetAttackTeamLeader(mgr, x708_circleAttackTeam) != GetUniqueId()) {
+ zeus::CVector3f fromPlatformCenter = GetTranslation() - x6b0_circleBurstPos;
+ float minAngle = zeus::degToRad(360.f);
+ for (const CTeamAiRole& role : aimgr->GetRoles()) {
+ if (const CWarWasp* other = CPatterned::CastTo(mgr.GetObjectById(role.GetOwnerId()))) {
+ if (x708_circleAttackTeam == other->x708_circleAttackTeam &&
+ GetTransform().basis[1].dot(other->GetTranslation() - GetTranslation()) > 0.f) {
+ float angle = zeus::CVector3f::getAngleDiff(
+ fromPlatformCenter, other->GetTranslation() - x6b0_circleBurstPos);
+ if (angle < minAngle)
+ minAngle = angle;
+ }
+ }
+ }
+ if (minAngle < zeus::degToRad(30.f))
+ return 0.8f;
+ if (minAngle > zeus::degToRad(50.f))
+ return 1.f;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+void CWarWasp::UpdateTelegraphMoveSpeed(CStateManager& mgr) {
+ TUniqueId leaderId = GetAttackTeamLeader(mgr, x708_circleAttackTeam);
+ if (const CWarWasp* other = CPatterned::CastTo(mgr.GetObjectById(leaderId))) {
+ if (leaderId == GetUniqueId()) {
+ float cycleTime = x330_stateMachineState.GetTime() - std::trunc(x330_stateMachineState.GetTime() / 2.8f) * 2.8f;
+ if (cycleTime < 2.f) {
+ x3b4_speed = x6fc_initialSpeed;
+ } else {
+ float t = (cycleTime - 2.f) / 0.8f;
+ x3b4_speed = ((1.f - t) * 0.7f + 2.f * t) * x6fc_initialSpeed;
+ }
+ } else {
+ x3b4_speed = other->x3b4_speed;
+ }
+ } else {
+ x3b4_speed = x6fc_initialSpeed;
+ }
+}
+
+void CWarWasp::TelegraphAttack(CStateManager& mgr, EStateMsg msg, float dt) {
+ switch (msg) {
+ case EStateMsg::Activate:
+ x450_bodyController->SetLocomotionType(pas::ELocomotionType::Combat);
+ RemoveMaterial(EMaterialTypes::Orbit, mgr);
+ mgr.GetPlayer().SetOrbitRequestForTarget(GetUniqueId(), CPlayer::EPlayerOrbitRequest::ActivateOrbitSource, mgr);
+ SwarmAdd(mgr);
+ SetUpCircleTelegraphTeam(mgr);
+ break;
+ case EStateMsg::Update:
+ if (!x6b0_circleBurstPos.isZero()) {
+ TryCircleTeamMerge(mgr);
+ zeus::CVector3f closeInDelta = GetTranslation() - x6b0_circleBurstPos;
+ closeInDelta.z() = 0.f;
+ zeus::CVector3f moveVec = GetTransform().basis[1];
+ if (closeInDelta.canBeNormalized()) {
+ zeus::CVector3f closeInDeltaNorm = closeInDelta.normalized();
+ moveVec = closeInDeltaNorm.cross(zeus::CVector3f::skUp);
+ zeus::CVector3f seekOrigin = x6b0_circleBurstPos + closeInDeltaNorm * x2fc_minAttackRange;
+ if (x708_circleAttackTeam > 0)
+ moveVec = moveVec * -1.f;
+ float seekHeight = x714_circleTelegraphSeekHeight + GetTeamZStratum(x708_circleAttackTeam);
+ float seekMag = CalcSeekMagnitude(mgr);
+ moveVec = x45c_steeringBehaviors.Seek(*this,
+ seekOrigin + moveVec * 5.f + zeus::CVector3f(0.f, 0.f, seekHeight)) * seekMag;
+ }
+ x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(moveVec, zeus::CVector3f::skZero, 1.f));
+ UpdateTelegraphMoveSpeed(mgr);
+ }
+ break;
+ case EStateMsg::Deactivate:
+ AddMaterial(EMaterialTypes::Orbit, mgr);
+ break;
+ }
+}
+
+void CWarWasp::Dodge(CStateManager& mgr, EStateMsg msg, float dt) {
+ switch (msg) {
+ case EStateMsg::Activate:
+ if (x704_dodgeDir != pas::EStepDirection::Invalid) {
+ x450_bodyController->GetCommandMgr().DeliverCmd(CBCStepCmd(x704_dodgeDir, pas::EStepType::Dodge));
+ x568_stateProg = 2;
+ }
+ break;
+ case EStateMsg::Update:
+ if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Step) {
+ x568_stateProg = 3;
+ } else {
+ x450_bodyController->GetCommandMgr().DeliverTargetVector(mgr.GetPlayer().GetTranslation() - GetTranslation());
+ }
+ break;
+ case EStateMsg::Deactivate:
+ x704_dodgeDir = pas::EStepDirection::Invalid;
+ break;
+ }
+}
+
+void CWarWasp::Retreat(CStateManager& mgr, EStateMsg msg, float dt) {
+ switch (msg) {
+ case EStateMsg::Activate:
+ SwarmRemove(mgr);
+ x450_bodyController->SetLocomotionType(pas::ELocomotionType::Internal5);
+ break;
+ case EStateMsg::Update:
+ x450_bodyController->GetCommandMgr().DeliverCmd(
+ CBCLocomotionCmd(x45c_steeringBehaviors.Flee2D(
+ *this, mgr.GetPlayer().GetTranslation().toVec2f()), zeus::CVector3f::skZero, 1.f));
+ break;
+ case EStateMsg::Deactivate:
+ x400_24_hitByPlayerProjectile = false;
+ x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed);
+ break;
+ }
+}
+
+void CWarWasp::SpecialAttack(CStateManager& mgr, EStateMsg msg, float dt) {
+ switch (msg) {
+ case EStateMsg::Activate:
+ x72e_27_teamMatesMelee = true;
+ x72e_25_canApplyDamage = true;
+ x568_stateProg = 0;
+ x3b4_speed = x6fc_initialSpeed;
+ break;
+ case EStateMsg::Update:
+ switch (x568_stateProg) {
+ case 0:
+ if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::MeleeAttack) {
+ x568_stateProg = 2;
+ } else {
+ x450_bodyController->GetCommandMgr().DeliverCmd(CBCMeleeAttackCmd(pas::ESeverity::Eight));
+ }
+ break;
+ case 2:
+ if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::MeleeAttack) {
+ x568_stateProg = 3;
+ } else if (GetTransform().basis[1].dot(x6b0_circleBurstPos - GetTranslation()) > 0.f) {
+ x450_bodyController->GetCommandMgr().DeliverTargetVector(x6b0_circleBurstPos - GetTranslation());
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case EStateMsg::Deactivate:
+ CTeamAiMgr::ResetTeamAiRole(CTeamAiMgr::EAttackType::Melee, mgr, x674_aiMgr, GetUniqueId(), false);
+ x72e_27_teamMatesMelee = false;
+ x708_circleAttackTeam = -1;
+ x718_circleBurstOffTotemAngle = CalcOffTotemAngle(mgr);
+ break;
+ }
+}
+
+bool CWarWasp::InAttackPosition(CStateManager& mgr, float arg) {
+ zeus::CVector3f delta = GetProjectileAimPos(mgr, -1.25f) - GetTranslation();
+ float negTest = x2fc_minAttackRange - 5.f;
+ float posTest = 5.f + x300_maxAttackRange;
+ float magSq = delta.magSquared();
+ bool ret = magSq > negTest * negTest && magSq < posTest * posTest && !ShouldTurn(mgr, 45.f);
+ if (ret && delta.canBeNormalized()) {
+ float deltaMag = delta.magnitude();
+ ret = mgr.RayStaticIntersection(GetTranslation(), delta / deltaMag, deltaMag,
+ CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {EMaterialTypes::Player})).IsInvalid();
+ }
+ return ret;
+}
+
+bool CWarWasp::Leash(CStateManager& mgr, float arg) {
+ if ((x3a0_latestLeashPosition - GetTranslation()).magSquared() > x3c8_leashRadius * x3c8_leashRadius) {
+ if ((mgr.GetPlayer().GetTranslation() - GetTranslation()).magSquared() >
+ x3cc_playerLeashRadius * x3cc_playerLeashRadius && x3d4_curPlayerLeashTime > x3d0_playerLeashTime) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CWarWasp::PathShagged(CStateManager& mgr, float arg) {
+ if (CPathFindSearch* pf = GetSearchPath())
+ return pf->IsShagged();
+ return false;
+}
+
+bool CWarWasp::AnimOver(CStateManager& mgr, float arg) {
+ return x568_stateProg == 3;
+}
+
+bool CWarWasp::ShouldAttack(CStateManager& mgr, float arg) {
+ if (x700_attackRemTime <= 0.f) {
+ if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x674_aiMgr, GetUniqueId())) {
+ if (role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Melee && !mgr.GetPlayer().IsInWaterMovement()) {
+ if (TCastToPtr tmgr = mgr.ObjectById(x674_aiMgr)) {
+ if (!tmgr->HasMeleeAttackers()) {
+ return !IsPatternObstructed(mgr, GetTranslation(), GetProjectileAimPos(mgr, -1.25f));
+ }
+ }
+ }
+ }
+ if (x3fc_flavor != EFlavorType::Two && !mgr.GetPlayer().IsInWaterMovement()) {
+ return !IsPatternObstructed(mgr, GetTranslation(), GetProjectileAimPos(mgr, -1.25f));
+ }
+ }
+ return false;
+}
+
+bool CWarWasp::InPosition(CStateManager& mgr, float arg) {
+ if (CPathFindSearch* pf = GetSearchPath()) {
+ return pf->IsOver();
+ } else {
+ return (x678_targetPos - GetTranslation()).magSquared() < 1.f;
+ }
+}
+
+bool CWarWasp::ShouldTurn(CStateManager& mgr, float arg) {
+ return zeus::CVector2f::getAngleDiff(GetTransform().basis[1].toVec2f(),
+ (mgr.GetPlayer().GetTranslation() - GetTranslation()).toVec2f()) > zeus::degToRad(arg);
+}
+
+bool CWarWasp::HearShot(CStateManager& mgr, float arg) {
+ if (x72e_31_heardNoise || x400_24_hitByPlayerProjectile)
+ return true;
+ if (TCastToConstPtr aimgr = mgr.GetObjectById(x674_aiMgr))
+ return aimgr->GetNumRoles() != 0;
+ return false;
+}
+
+bool CWarWasp::ShouldFire(CStateManager& mgr, float arg) {
+ if (x700_attackRemTime <= 0.f) {
+ if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x674_aiMgr, GetUniqueId())) {
+ if (role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Projectile) {
+ zeus::CVector3f delta = GetProjectileAimPos(mgr, -1.25f) - GetTranslation();
+ if (delta.canBeNormalized() && GetTransform().basis[1].dot(delta.normalized()) >= 0.906f) {
+ if (TCastToPtr aimgr = mgr.ObjectById(x674_aiMgr)) {
+ return !aimgr->HasProjectileAttackers();
+ }
+ }
+ }
+ } else if (x3fc_flavor == EFlavorType::Two) {
+ zeus::CVector3f delta = GetProjectileAimPos(mgr, -1.25f) - GetTranslation();
+ if (delta.canBeNormalized()) {
+ return GetTransform().basis[1].dot(delta.normalized()) >= 0.906f;
+ }
+ }
+ }
+ return false;
+}
+
+bool CWarWasp::ShouldDodge(CStateManager& mgr, float arg) {
+ zeus::CAABox aabb(GetTranslation() - 7.5f, GetTranslation() + 7.5f);
+ rstl::reserved_vector nearList;
+ mgr.BuildNearList(nearList, aabb, CMaterialFilter::MakeInclude({EMaterialTypes::Projectile}), nullptr);
+ for (TUniqueId id : nearList) {
+ if (TCastToConstPtr proj = mgr.GetObjectById(id)) {
+ if (mgr.GetPlayer().GetUniqueId() == proj->GetOwnerId()) {
+ zeus::CVector3f delta = proj->GetTranslation() - GetTranslation();
+ if (zeus::CVector3f::getAngleDiff(GetTransform().basis[1], delta) < zeus::degToRad(45.f)) {
+ x704_dodgeDir = GetTransform().basis[0].dot(delta) > 0.f ?
+ pas::EStepDirection::Right : pas::EStepDirection::Left;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool CWarWasp::CheckCircleAttackSpread(CStateManager& mgr, s32 team) {
+ if (TCastToConstPtr aimgr = mgr.GetObjectById(x674_aiMgr)) {
+ s32 teamSize = GetAttackTeamSize(mgr, team);
+ if (teamSize == 1)
+ return true;
+ TUniqueId leaderId = GetAttackTeamLeader(mgr, team);
+ if (const CWarWasp* leaderWasp = CPatterned::CastTo(mgr.GetObjectById(leaderId))) {
+ zeus::CVector3f platformToLeaderWasp = leaderWasp->GetTranslation() - x6b0_circleBurstPos;
+ float maxAng = 0.f;
+ for (const CTeamAiRole& role : aimgr->GetRoles()) {
+ if (role.GetOwnerId() == leaderId)
+ continue;
+ if (const CWarWasp* wasp2 = CPatterned::CastTo(mgr.GetObjectById(role.GetOwnerId()))) {
+ if (wasp2->x708_circleAttackTeam == team) {
+ if (leaderWasp->GetTransform().basis[1].dot(wasp2->GetTranslation() - leaderWasp->GetTranslation()) > 0.f)
+ return false;
+ float angle = zeus::CVector3f::getAngleDiff(wasp2->GetTranslation() - x6b0_circleBurstPos,
+ platformToLeaderWasp);
+ if (angle > maxAng)
+ maxAng = angle;
+ }
+ }
+ }
+ return maxAng < zeus::degToRad(40.f) * float(teamSize - 1) + zeus::degToRad(20.f);
+ }
+ }
+ return false;
+}
+
+bool CWarWasp::ShouldSpecialAttack(CStateManager& mgr, float arg) {
+ if (x708_circleAttackTeam == 0 && !mgr.GetPlayer().IsInWaterMovement()) {
+ if (TCastToConstPtr aimgr = mgr.GetObjectById(x674_aiMgr)) {
+ if (CheckCircleAttackSpread(mgr, x708_circleAttackTeam)) {
+ TUniqueId leaderId = GetAttackTeamLeader(mgr, x708_circleAttackTeam);
+ if (leaderId == GetUniqueId()) {
+ if (x700_attackRemTime <= 0.f &&
+ (mgr.GetPlayer().GetTranslation().toVec2f() - x6b0_circleBurstPos.toVec2f()).magSquared() < 90.25f) {
+ zeus::CVector3f fromPlatformCenter = GetTranslation() - x6b0_circleBurstPos;
+ zeus::CVector3f thresholdVec = x6bc_circleBurstDir;
+ if (x718_circleBurstOffTotemAngle <= zeus::degToRad(90.f)) {
+ thresholdVec = zeus::CVector3f::slerp(x6c8_circleBurstRight, x6bc_circleBurstDir,
+ x718_circleBurstOffTotemAngle);
+ } else {
+ thresholdVec = zeus::CVector3f::slerp(x6bc_circleBurstDir, -x6c8_circleBurstRight,
+ x718_circleBurstOffTotemAngle - zeus::degToRad(90.f));
+ }
+ if (zeus::CVector3f::getAngleDiff(thresholdVec, fromPlatformCenter) < zeus::degToRad(10.f))
+ return CTeamAiMgr::AddAttacker(CTeamAiMgr::EAttackType::Melee, mgr, x674_aiMgr, GetUniqueId());
+ }
+ } else {
+ if (const CWarWasp* leaderWasp = CPatterned::CastTo(mgr.GetObjectById(leaderId))) {
+ if (leaderWasp->x72e_27_teamMatesMelee) {
+ return CTeamAiMgr::AddAttacker(CTeamAiMgr::EAttackType::Melee, mgr, x674_aiMgr, GetUniqueId());
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+CPathFindSearch* CWarWasp::GetSearchPath() {
+ return &x590_pfSearch;
+}
+
+CProjectileInfo* CWarWasp::GetProjectileInfo() {
+ return &x6d4_projectileInfo;
+}
} // namespace urde::MP1
diff --git a/Runtime/MP1/World/CWarWasp.hpp b/Runtime/MP1/World/CWarWasp.hpp
index a58b3b9a3..baf131d52 100644
--- a/Runtime/MP1/World/CWarWasp.hpp
+++ b/Runtime/MP1/World/CWarWasp.hpp
@@ -1,19 +1,122 @@
#pragma once
#include "World/CPatterned.hpp"
+#include "World/CPathFindSearch.hpp"
+#include "Weapon/CProjectileInfo.hpp"
+#include "Collision/CCollidableSphere.hpp"
namespace urde {
class CDamageInfo;
namespace MP1 {
class CWarWasp : public CPatterned {
+ s32 x568_stateProg = -1;
+ CCollidableSphere x570_cSphere;
+ CPathFindSearch x590_pfSearch;
+ TUniqueId x674_aiMgr = kInvalidUniqueId;
+ zeus::CVector3f x678_targetPos;
+ CDamageInfo x684_;
+ zeus::CQuaternion x6a0_initialRot;
+ zeus::CVector3f x6b0_circleBurstPos;
+ zeus::CVector3f x6bc_circleBurstDir;
+ zeus::CVector3f x6c8_circleBurstRight;
+ CProjectileInfo x6d4_projectileInfo;
+ float x6fc_initialSpeed = x3b4_speed;
+ float x700_attackRemTime = 0.f;
+ pas::EStepDirection x704_dodgeDir = pas::EStepDirection::Invalid;
+ s32 x708_circleAttackTeam = -1;
+ s32 x70c_initialCircleAttackTeam = -1;
+ s32 x710_initialCircleAttackTeamUnit = -1;
+ float x714_circleTelegraphSeekHeight = 0.f;
+ float x718_circleBurstOffTotemAngle = zeus::degToRad(90.f);
+ TLockedToken x71c_projectileVisorParticle; // Used to be optional
+ u16 x72c_projectileVisorSfx;
+ union {
+ struct {
+ bool x72e_24_jumpBackRepeat : 1;
+ bool x72e_25_canApplyDamage : 1;
+ bool x72e_26_initiallyInactive : 1;
+ bool x72e_27_teamMatesMelee : 1;
+ bool x72e_28_inProjectileAttack : 1;
+ bool x72e_29_pathObstructed : 1;
+ bool x72e_30_isRetreating : 1;
+ bool x72e_31_heardNoise : 1;
+ };
+ u32 _dummy = 0;
+ };
+ void SwarmAdd(CStateManager& mgr);
+ void SwarmRemove(CStateManager& mgr);
+ void ApplyDamage(CStateManager& mgr);
+ void SetUpCircleBurstWaypoint(CStateManager& mgr);
+ zeus::CVector3f GetProjectileAimPos(CStateManager& mgr, float zBias);
+ zeus::CVector3f GetCloseInPos(const CStateManager& mgr, const zeus::CVector3f& aimPos) const;
+ float GetCloseInZBasis(const CStateManager& mgr) const;
+ void SetUpPathFindBehavior(CStateManager& mgr);
+ s32 GetAttackTeamSize(CStateManager& mgr, s32 team);
+ float CalcTimeToNextAttack(CStateManager& mgr);
+ float CalcOffTotemAngle(CStateManager& mgr);
+ void JoinCircleAttackTeam(s32 unit, CStateManager& mgr);
+ void SetUpCircleTelegraphTeam(CStateManager& mgr);
+ TUniqueId GetAttackTeamLeader(CStateManager& mgr, s32 team);
+ void TryCircleTeamMerge(CStateManager& mgr);
+ float GetTeamZStratum(s32 team);
+ float CalcSeekMagnitude(CStateManager& mgr);
+ void UpdateTelegraphMoveSpeed(CStateManager& mgr);
+ bool CheckCircleAttackSpread(CStateManager& mgr, s32 team);
+ void ApplyNormalSteering(CStateManager& mgr);
+ void ApplySeparationBehavior(CStateManager& mgr, float sep);
+ bool PathToHiveIsClear(CStateManager& mgr);
+ bool SteerToDeactivatePos(CStateManager& mgr, EStateMsg msg, float dt);
+ zeus::CVector3f CalcShuffleDest(CStateManager& mgr);
public:
DEFINE_PATTERNED(WarWasp)
CWarWasp(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf,
CModelData&& mData, const CPatternedInfo& pInfo, CPatterned::EFlavorType flavor, CPatterned::EColliderType,
- const CDamageInfo& dInfo1, const CActorParameters&, CAssetId weapon, const CDamageInfo& dInfo2,
- CAssetId particle, u32 w3);
+ const CDamageInfo& dInfo1, const CActorParameters&, CAssetId projectileWeapon,
+ const CDamageInfo& projectileDamage, CAssetId projectileVisorParticle, u32 projecileVisorSfx);
void Accept(IVisitor& visitor);
+ void Think(float dt, CStateManager& mgr);
+ void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CStateManager& mgr);
+
+ std::experimental::optional GetTouchBounds() const;
+ void DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt);
+ const CCollisionPrimitive* GetCollisionPrimitive() const;
+ void Death(CStateManager& mgr, const zeus::CVector3f& direction, EScriptObjectState state);
+ bool IsListening() const;
+ bool Listen(const zeus::CVector3f&, EListenNoiseType);
+ zeus::CVector3f GetOrigin(const CStateManager& mgr, const CTeamAiRole& role,
+ const zeus::CVector3f& aimPos) const;
+ void UpdateTouchBounds();
+ bool IsRetreating() const { return x72e_30_isRetreating; }
+
+ void Patrol(CStateManager&, EStateMsg, float);
+ void PathFind(CStateManager& mgr, EStateMsg msg, float dt);
+ void TargetPatrol(CStateManager&, EStateMsg, float);
+ void Generate(CStateManager&, EStateMsg, float);
+ void Deactivate(CStateManager&, EStateMsg, float);
+ void Attack(CStateManager&, EStateMsg, float);
+ void JumpBack(CStateManager&, EStateMsg, float);
+ void Shuffle(CStateManager&, EStateMsg, float);
+ void ProjectileAttack(CStateManager&, EStateMsg, float);
+ void TelegraphAttack(CStateManager&, EStateMsg, float);
+ void Dodge(CStateManager&, EStateMsg, float);
+ void Retreat(CStateManager&, EStateMsg, float);
+ void SpecialAttack(CStateManager&, EStateMsg, float);
+
+ bool InAttackPosition(CStateManager&, float);
+ bool Leash(CStateManager&, float);
+ bool PathShagged(CStateManager&, float);
+ bool AnimOver(CStateManager&, float);
+ bool ShouldAttack(CStateManager&, float);
+ bool InPosition(CStateManager&, float);
+ bool ShouldTurn(CStateManager&, float);
+ bool HearShot(CStateManager&, float);
+ bool ShouldFire(CStateManager&, float);
+ bool ShouldDodge(CStateManager&, float);
+ bool ShouldSpecialAttack(CStateManager&, float);
+
+ CPathFindSearch* GetSearchPath();
+ CProjectileInfo* GetProjectileInfo();
};
} // namespace MP1
} // namespace urde
diff --git a/Runtime/MkCastTo.py b/Runtime/MkCastTo.py
index f08014a98..918219893 100644
--- a/Runtime/MkCastTo.py
+++ b/Runtime/MkCastTo.py
@@ -27,6 +27,7 @@ CENTITY_TYPES = (
('CInterpolationCamera', 'Camera/CInterpolationCamera.hpp'),
Namespace('MP1'),
('CMetroidPrimeRelay', 'MP1/World/CMetroidPrimeRelay.hpp', 'MP1'),
+ ('CWarWasp', 'MP1/World/CWarWasp.hpp', 'MP1'),
EndNamespace(),
('CPathCamera', 'Camera/CPathCamera.hpp'),
('CAi', 'World/CAi.hpp'),
diff --git a/Runtime/Weapon/CBeamInfo.hpp b/Runtime/Weapon/CBeamInfo.hpp
index f0a6359a2..f374aefef 100644
--- a/Runtime/Weapon/CBeamInfo.hpp
+++ b/Runtime/Weapon/CBeamInfo.hpp
@@ -51,8 +51,9 @@ public:
, x40_outerColor(zeus::CColor::ReadRGBABig(in)) {}
CBeamInfo(s32 beamAttributes, CAssetId contactFxId, CAssetId pulseFxId, CAssetId textureId, CAssetId glowTextureId,
- s32 length, float radius, float f2, float f3, float f4, float f5, float contactFxScale, float pulseFxScale,
- const zeus::CColor& innerColor, const zeus::CColor& outerColor, float travelSpeed)
+ s32 length, float radius, float expansionSpeed, float lifeTime, float pulseSpeed, float shutdownTime,
+ float contactFxScale, float pulseFxScale, const zeus::CColor& innerColor, const zeus::CColor& outerColor,
+ float travelSpeed)
: x4_beamAttributes(beamAttributes)
, x8_contactFxId(contactFxId)
, xc_pulseFxId(pulseFxId)
@@ -60,10 +61,10 @@ public:
, x14_glowTextureId(glowTextureId)
, x18_length(length)
, x1c_radius(radius)
- , x20_expansionSpeed(f2)
- , x24_lifeTime(f3)
- , x28_pulseSpeed(f4)
- , x2c_shutdownTime(f5)
+ , x20_expansionSpeed(expansionSpeed)
+ , x24_lifeTime(lifeTime)
+ , x28_pulseSpeed(pulseSpeed)
+ , x2c_shutdownTime(shutdownTime)
, x30_contactFxScale(contactFxScale)
, x34_pulseFxScale(pulseFxScale)
, x38_travelSpeed(travelSpeed)
diff --git a/Runtime/Weapon/CProjectileInfo.hpp b/Runtime/Weapon/CProjectileInfo.hpp
index b29c8aee1..e67e90cd5 100644
--- a/Runtime/Weapon/CProjectileInfo.hpp
+++ b/Runtime/Weapon/CProjectileInfo.hpp
@@ -22,6 +22,6 @@ public:
const CPlayer& player, bool gravity, float dt);
CDamageInfo GetDamage() const { return xc_damageInfo; }
- const TToken& Token() { return x0_weaponDescription; }
+ TToken& Token() { return x0_weaponDescription; }
};
} // namespace urde
diff --git a/Runtime/World/CPathFindRegion.cpp b/Runtime/World/CPathFindRegion.cpp
index 480d26f84..55e062170 100644
--- a/Runtime/World/CPathFindRegion.cpp
+++ b/Runtime/World/CPathFindRegion.cpp
@@ -54,11 +54,11 @@ bool CPFRegion::IsPointInside(const zeus::CVector3f& point) const {
if ((point - node.GetPos()).dot(node.GetNormal()) < 0.f)
break;
}
+ if (i != x0_numNodes)
+ return false;
zeus::CVector3f nodeToPoint = point - x4_startNode->GetPos();
- if (i == x0_numNodes && nodeToPoint.dot(x18_normal) >= 0.f)
- if ((nodeToPoint - x14_height * zeus::CVector3f::skUp).dot(x18_normal) <= 0.f)
- return true;
- return false;
+ return nodeToPoint.dot(x18_normal) >= 0.f &&
+ (nodeToPoint - x14_height * zeus::CVector3f::skUp).dot(x18_normal) <= 0.f;
}
float CPFRegion::PointHeight(const zeus::CVector3f& point) const {
@@ -212,20 +212,18 @@ zeus::CVector3f CPFRegion::FitThroughLink3d(const zeus::CVector3f& p1, const CPF
float f24 = (node.GetPos() - p2).dot(node.GetNormal());
float f23 = f25 + f24;
#if 0
- if (chRadius < 0.5f * link.Get2dWidth())
- {
- zeus::CVector2f delta2d(nodeDelta.x, nodeDelta.y);
- delta2d *= link.GetOO2dWidth();
- zeus::CVector3f nodeToP1 = p1 - node.GetPos();
- float f29 = delta2d.dot(zeus::CVector2f(nodeToP1.y, nodeToP1.y));
- zeus::CVector3f nodeToP2 = p2 - node.GetPos();
- float f1b = delta2d.dot(zeus::CVector2f(nodeToP2.y, nodeToP2.y));
- if (f23 > FLT_EPSILON)
- {
- zeus::clamp(chRadius, 1.f / f23 * f24 * f29 + f25 * f1b, link.Get2dWidth() - chRadius) *
- link.GetOO2dWidth();
- }
+ if (chRadius < 0.5f * link.Get2dWidth()) {
+ zeus::CVector2f delta2d(nodeDelta.x, nodeDelta.y);
+ delta2d *= link.GetOO2dWidth();
+ zeus::CVector3f nodeToP1 = p1 - node.GetPos();
+ float f29 = delta2d.dot(zeus::CVector2f(nodeToP1.y, nodeToP1.y));
+ zeus::CVector3f nodeToP2 = p2 - node.GetPos();
+ float f1b = delta2d.dot(zeus::CVector2f(nodeToP2.y, nodeToP2.y));
+ if (f23 > FLT_EPSILON) {
+ zeus::clamp(chRadius, 1.f / f23 * f24 * f29 + f25 * f1b, link.Get2dWidth() - chRadius) *
+ link.GetOO2dWidth();
}
+ }
#endif
zeus::CVector3f midPoint = nodeDelta * 0.5f + node.GetPos();
float z;
@@ -233,7 +231,7 @@ zeus::CVector3f CPFRegion::FitThroughLink3d(const zeus::CVector3f& p1, const CPF
float minZ = chHalfHeight + midPoint.z();
z = 0.5f * (p1.z() + p2.z());
if (f23 > FLT_EPSILON)
- z = 1.f / f23 * (f24 * p1.z() + f25 * p2.z());
+ z = (f24 * p1.z() + f25 * p2.z()) / f23;
z = zeus::clamp(minZ, z, regionHeight + midPoint.z() - chHalfHeight);
} else {
z = (p1.z() + p2.z()) * 0.5f;
diff --git a/Runtime/World/CPathFindSearch.cpp b/Runtime/World/CPathFindSearch.cpp
index 31278ffbd..e2d9afe85 100644
--- a/Runtime/World/CPathFindSearch.cpp
+++ b/Runtime/World/CPathFindSearch.cpp
@@ -306,7 +306,7 @@ bool CPathFindSearch::Search(rstl::reserved_vector& regs1, const
/* Next G */
float g = (linkReg->GetCentroid() - reg->GetCentroid()).magnitude() + reg->Data()->GetG();
if ((!x0_area->ClosedSet().Test(linkReg->GetIndex()) && !x0_area->OpenList().Test(linkReg)) ||
- linkReg->Data()->GetG() <= g) {
+ linkReg->Data()->GetG() > g) {
if (x0_area->OpenList().Test(linkReg)) {
/* In rare cases, revisiting a region will yield a lower G (actual cost) */
x0_area->OpenList().Pop(linkReg);
diff --git a/Runtime/World/CPathFindSearch.hpp b/Runtime/World/CPathFindSearch.hpp
index d55c176c2..596573a3a 100644
--- a/Runtime/World/CPathFindSearch.hpp
+++ b/Runtime/World/CPathFindSearch.hpp
@@ -43,7 +43,7 @@ public:
u32 GetCurrentWaypoint() const { return xc8_curWaypoint; }
void SetCurrentWaypoint(u32 wp) { xc8_curWaypoint = wp; }
const rstl::reserved_vector& GetWaypoints() const { return x4_waypoints; }
- bool IsOver() const { return GetCurrentWaypoint() >= x4_waypoints.size(); }
+ bool IsOver() const { return GetCurrentWaypoint() >= x4_waypoints.size() - 1; }
bool IsShagged() const { return GetResult() != EResult::Success; }
bool SegmentOver(const zeus::CVector3f& p1) const;
void GetSplinePoint(zeus::CVector3f& pOut, const zeus::CVector3f& p1) const;
diff --git a/Runtime/World/CPatternedInfo.hpp b/Runtime/World/CPatternedInfo.hpp
index bbaccbfea..f0bbbf8f7 100644
--- a/Runtime/World/CPatternedInfo.hpp
+++ b/Runtime/World/CPatternedInfo.hpp
@@ -44,9 +44,9 @@ class CPatternedInfo {
float x108_;
u32 x10c_pathfindingIndex;
+
zeus::CVector3f x110_particle1Scale;
CAssetId x11c_particle1;
-
CAssetId x120_electric;
zeus::CVector3f x124_particle2Scale;
CAssetId x130_particle2;
@@ -66,5 +66,6 @@ public:
CAnimationParameters& GetAnimationParameters() { return xec_animParams; }
const CAnimationParameters& GetAnimationParameters() const { return xec_animParams; }
u32 GetPathfindingIndex() const { return x10c_pathfindingIndex; }
+ bool GetActive() const { return xf8_active; }
};
} // namespace urde
diff --git a/Runtime/World/CPhysicsActor.cpp b/Runtime/World/CPhysicsActor.cpp
index 797cb88dc..2437b8cf4 100644
--- a/Runtime/World/CPhysicsActor.cpp
+++ b/Runtime/World/CPhysicsActor.cpp
@@ -50,8 +50,6 @@ float CPhysicsActor::GetWeight() const { return 24.525002f * xe8_mass; }
void CPhysicsActor::SetPrimitiveOffset(const zeus::CVector2f& offset) { x1e8_primitiveOffset = offset; }
-zeus::CVector3f CPhysicsActor::GetPrimitiveOffset() const { return x1e8_primitiveOffset; }
-
void CPhysicsActor::MoveCollisionPrimitive(const zeus::CVector3f& offset) { x1e8_primitiveOffset = offset; }
void CPhysicsActor::SetBoundingBox(const zeus::CAABox& box) {
diff --git a/Runtime/World/CPhysicsActor.hpp b/Runtime/World/CPhysicsActor.hpp
index 85bf74a2d..724784889 100644
--- a/Runtime/World/CPhysicsActor.hpp
+++ b/Runtime/World/CPhysicsActor.hpp
@@ -124,7 +124,7 @@ public:
float GetMass() const { return xe8_mass; }
void SetPrimitiveOffset(const zeus::CVector2f& offset);
- zeus::CVector3f GetPrimitiveOffset() const;
+ const zeus::CVector3f& GetPrimitiveOffset() const { return x1e8_primitiveOffset; }
void MoveCollisionPrimitive(const zeus::CVector3f& offset);
void SetBoundingBox(const zeus::CAABox& box);
zeus::CAABox GetMotionVolume(float dt) const;
@@ -145,6 +145,8 @@ public:
const zeus::CAxisAngle& GetAngularVelocityWR() const { return x144_angularVelocity; }
void SetAngularVelocityWR(const zeus::CAxisAngle& angVel);
const zeus::CVector3f& GetForceOR() const { return x15c_force; }
+ const zeus::CVector3f& GetImpulseOR() const { return x168_impulse; }
+ const zeus::CVector3f& GetMoveImpulseOR() const { return x18c_moveImpulse; }
void SetVelocityWR(const zeus::CVector3f& vel);
void SetVelocityOR(const zeus::CVector3f& vel);
void SetMomentumWR(const zeus::CVector3f& m) { x150_momentum = m; }
diff --git a/Runtime/World/CPlayer.cpp b/Runtime/World/CPlayer.cpp
index 5a4d0e8db..0e44e94c8 100644
--- a/Runtime/World/CPlayer.cpp
+++ b/Runtime/World/CPlayer.cpp
@@ -2235,7 +2235,7 @@ void CPlayer::Think(float dt, CStateManager& mgr) {
x2b0_outOfWaterTicks += 1;
x9c5_24_ = x9c4_24_visorChangeRequested;
- x9c4_31_dampUnderwaterMotion = x9c5_25_splashUpdated;
+ x9c4_31_inWaterMovement = x9c5_25_splashUpdated;
x9c5_25_splashUpdated = false;
UpdateBombJumpStuff();
@@ -5242,7 +5242,7 @@ const CScriptWater* CPlayer::GetVisorRunoffEffect(const CStateManager& mgr) cons
void CPlayer::SetMorphBallState(EPlayerMorphBallState state, CStateManager& mgr) {
if (x2f8_morphBallState == EPlayerMorphBallState::Morphed && state != EPlayerMorphBallState::Morphed)
- x9c5_26_ = x9c4_31_dampUnderwaterMotion;
+ x9c5_26_ = x9c4_31_inWaterMovement;
x2f8_morphBallState = state;
xf9_standardCollider = state == EPlayerMorphBallState::Morphed;
diff --git a/Runtime/World/CPlayer.hpp b/Runtime/World/CPlayer.hpp
index 53e6458e3..2d3b8bb32 100644
--- a/Runtime/World/CPlayer.hpp
+++ b/Runtime/World/CPlayer.hpp
@@ -288,7 +288,7 @@ private:
bool x9c4_28_canLeaveMorphBall : 1;
bool x9c4_29_spiderBallControlXY : 1;
bool x9c4_30_controlDirOverride : 1;
- bool x9c4_31_dampUnderwaterMotion : 1;
+ bool x9c4_31_inWaterMovement : 1;
bool x9c5_24_ : 1;
bool x9c5_25_splashUpdated : 1;
bool x9c5_26_ : 1;
@@ -314,7 +314,7 @@ private:
float x9c8_eyeZBias = 0.f;
float x9cc_stepCameraZBias = 0.f;
u32 x9d0_bombJumpCount = 0;
- u32 x9d4_bombJumpCheckDelayFrames = 0;
+ s32 x9d4_bombJumpCheckDelayFrames = 0;
zeus::CVector3f x9d8_controlDirOverrideDir = zeus::CVector3f::skForward;
rstl::reserved_vector x9e4_orbitDisableList;
@@ -614,5 +614,6 @@ public:
void Set_X590(bool b) { x590_ = b; }
const zeus::CVector3f& GetOrbitPoint() const { return x314_orbitPoint; }
float GetAverageSpeed() const;
+ bool IsInWaterMovement() const { return x9c4_31_inWaterMovement; }
};
} // namespace urde
diff --git a/Runtime/World/CScriptCounter.cpp b/Runtime/World/CScriptCounter.cpp
index 2fc4fd54d..0b65c5c18 100644
--- a/Runtime/World/CScriptCounter.cpp
+++ b/Runtime/World/CScriptCounter.cpp
@@ -48,7 +48,7 @@ void CScriptCounter::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId objId,
if (GetActive() && x38_current < x3c_max) {
++x38_current;
if (x38_current >= x3c_max) {
- SendScriptMsgs(EScriptObjectState::Zero, stateMgr, EScriptObjectMessage::None);
+ SendScriptMsgs(EScriptObjectState::MaxReached, stateMgr, EScriptObjectMessage::None);
if (x40_autoReset)
x38_current = x34_initial;
}
diff --git a/Runtime/World/CTeamAiMgr.hpp b/Runtime/World/CTeamAiMgr.hpp
index 9e66414b7..0b822ed1e 100644
--- a/Runtime/World/CTeamAiMgr.hpp
+++ b/Runtime/World/CTeamAiMgr.hpp
@@ -100,6 +100,13 @@ public:
bool AddProjectileAttacker(TUniqueId aiId);
void RemoveProjectileAttacker(TUniqueId aiId);
+ bool HasMeleeAttackers() const { return !x68_meleeAttackers.empty(); }
+ bool HasProjectileAttackers() const { return !x78_projectileAttackers.empty(); }
+ s32 GetNumRoles() const { return x58_roles.size(); }
+ const std::vector& GetRoles() const { return x58_roles; }
+ s32 GetMaxMeleeAttackerCount() const { return x34_data.x10_maxMeleeAttackerCount; }
+ s32 GetMaxProjectileAttackerCount() const { return x34_data.x14_maxProjectileAttackerCount; }
+
static CTeamAiRole* GetTeamAiRole(CStateManager& mgr, TUniqueId mgrId, TUniqueId aiId);
static void ResetTeamAiRole(EAttackType type, CStateManager& mgr, TUniqueId mgrId, TUniqueId aiId, bool clearRole);
static bool CanAcceptAttacker(EAttackType type, CStateManager& mgr, TUniqueId mgrId, TUniqueId aiId);
diff --git a/Runtime/World/ScriptLoader.cpp b/Runtime/World/ScriptLoader.cpp
index 54e0328ae..b79edbf43 100644
--- a/Runtime/World/ScriptLoader.cpp
+++ b/Runtime/World/ScriptLoader.cpp
@@ -1307,10 +1307,10 @@ CEntity* ScriptLoader::LoadWarWasp(CStateManager& mgr, CInputStream& in, int pro
CActorParameters actorParms = LoadActorParameters(in);
CPatterned::EColliderType collider = CPatterned::EColliderType(in.readBool());
CDamageInfo damageInfo1(in);
- CAssetId weaponDesc = in.readUint32Big();
- CDamageInfo damageInfo2(in);
- CAssetId particle = in.readUint32Big();
- u32 w1 = in.readUint32Big();
+ CAssetId projectileWeapon = in.readUint32Big();
+ CDamageInfo projectileDamage(in);
+ CAssetId projectileVisorParticle = in.readUint32Big();
+ u32 projectileVisorSfx = in.readUint32Big();
const CAnimationParameters& aParms = pInfo.GetAnimationParameters();
FourCC animType = g_ResFactory->GetResourceTypeById(aParms.GetACSFile());
@@ -1320,7 +1320,8 @@ CEntity* ScriptLoader::LoadWarWasp(CStateManager& mgr, CInputStream& in, int pro
CAnimRes res(aParms.GetACSFile(), aParms.GetCharacter(), scale, aParms.GetInitialAnimation(), true);
CModelData mData(res);
return new MP1::CWarWasp(mgr.AllocateUniqueId(), name, info, xf, std::move(mData), pInfo, flavor, collider,
- damageInfo1, actorParms, weaponDesc, damageInfo2, particle, w1);
+ damageInfo1, actorParms, projectileWeapon, projectileDamage, projectileVisorParticle,
+ projectileVisorSfx);
}
CEntity* ScriptLoader::LoadSpacePirate(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) {
@@ -2657,30 +2658,31 @@ CEntity* ScriptLoader::LoadEyeball(CStateManager& mgr, CInputStream& in, int pro
CPatternedInfo pInfo(in, pair.second);
CActorParameters actParms = LoadActorParameters(in);
- float f1 = in.readFloatBig();
- float f2 = in.readFloatBig();
+ float attackDelay = in.readFloatBig();
+ float attackStartTime = in.readFloatBig();
CAssetId wpsc(in);
CDamageInfo dInfo(in);
- CAssetId partId1(in);
- CAssetId partId2(in);
- CAssetId texture1(in);
- CAssetId texture2(in);
- u32 w1 = in.readUint32Big();
- u32 w2 = in.readUint32Big();
- u32 w3 = in.readUint32Big();
- u32 w4 = in.readUint32Big();
- u32 w5 = in.readUint32Big();
+ CAssetId beamContactFxId(in);
+ CAssetId beamPulseFxId(in);
+ CAssetId beamTextureId(in);
+ CAssetId beamGlowTextureId(in);
+ u32 anim0 = in.readUint32Big();
+ u32 anim1 = in.readUint32Big();
+ u32 anim2 = in.readUint32Big();
+ u32 anim3 = in.readUint32Big();
+ u32 beamSfx = in.readUint32Big();
if (g_ResFactory->GetResourceTypeById(pInfo.GetAnimationParameters().GetACSFile()) != SBIG('ANCS'))
return nullptr;
- bool b1 = in.readBool();
+ bool attackDisabled = in.readBool();
CModelData mData(CAnimRes(pInfo.GetAnimationParameters().GetACSFile(), pInfo.GetAnimationParameters().GetCharacter(),
scale, pInfo.GetAnimationParameters().GetInitialAnimation(), true));
- return new MP1::CEyeball(mgr.AllocateUniqueId(), name, flavor, info, xf, std::move(mData), pInfo, f1, f2, wpsc, dInfo,
- partId1, partId2, texture1, texture2, w1, w2, w3, w4, w5, b1, actParms);
+ return new MP1::CEyeball(mgr.AllocateUniqueId(), name, flavor, info, xf, std::move(mData), pInfo, attackDelay,
+ attackStartTime, wpsc, dInfo, beamContactFxId, beamPulseFxId, beamTextureId,
+ beamGlowTextureId, anim0, anim1, anim2, anim3, beamSfx, attackDisabled, actParms);
}
CEntity* ScriptLoader::LoadRadialDamage(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) {
diff --git a/hecl b/hecl
index d4a0f1733..296d8733c 160000
--- a/hecl
+++ b/hecl
@@ -1 +1 @@
-Subproject commit d4a0f17336f19906a6c4f2ed83fd34945f017593
+Subproject commit 296d8733c21e7e9276af44cdafdd63f44856975b