#include "CBeetle.hpp" #include "CStateManager.hpp" #include "World/CDamageInfo.hpp" #include "Character/CCharLayoutInfo.hpp" #include "TCastTo.hpp" #include "World/CPatternedInfo.hpp" #include "Character/CPASAnimParmData.hpp" #include "World/CTeamAiMgr.hpp" #include "World/CWorld.hpp" #include "World/CPlayer.hpp" #include "World/CScriptWaypoint.hpp" namespace urde::MP1 { CBeetle::CBeetle(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo, CPatterned::EFlavorType flavor, CBeetle::EEntranceType entranceType, const CDamageInfo& touchDamage, const CDamageVulnerability& platingVuln, const zeus::CVector3f& tailAimReference, float initialAttackDelay, float retreatTime, float f3, const CDamageVulnerability& tailVuln, const CActorParameters& aParams, const rstl::optional& tailModel) : CPatterned(ECharacter::Beetle, uid, name, flavor, info, xf, std::move(mData), pInfo, EMovementType::Ground, EColliderType::One, EBodyType::BiPedal, aParams, EKnockBackVariant(flavor)) , x56c_entranceType(entranceType) , x574_tailAimReference(tailAimReference) , x580_f3(f3) , x584_touchDamage(touchDamage) , x5ac_tailModel(tailModel ? rstl::optional(CModelData(*tailModel)) : rstl::optional()) , x5fc_pathFindSearch(nullptr, 1, pInfo.GetPathfindingIndex(), 1.f, 1.f) , x744_platingVuln(platingVuln) , x7ac_tailVuln(tailVuln) , x814_attackDelayTimer(initialAttackDelay) , x834_retreatTime(retreatTime) { x5a0_headbuttDist = GetAnimationDistance(CPASAnimParmData(7, CPASAnimParm::FromEnum(0), CPASAnimParm::FromEnum(1))); x5a4_jumpBackwardDist = x64_modelData->GetScale().y() * GetAnimationDistance(CPASAnimParmData(3, CPASAnimParm::FromEnum(1), CPASAnimParm::FromEnum(0))); MakeThermalColdAndHot(); if (x3fc_flavor == EFlavorType::One) x460_knockBackController.SetLocomotionDuringElectrocution(true); } void CBeetle::Accept(IVisitor& visitor) { visitor.Visit(this); } void CBeetle::SquadAdd(CStateManager& mgr) { if (x570_aiMgr != kInvalidUniqueId) if (TCastToPtr aimgr = mgr.ObjectById(x570_aiMgr)) aimgr->AssignTeamAiRole(*this, CTeamAiRole::ETeamAiRole::Melee, CTeamAiRole::ETeamAiRole::Unknown, CTeamAiRole::ETeamAiRole::Invalid); } void CBeetle::SquadRemove(CStateManager& mgr) { if (x570_aiMgr != kInvalidUniqueId) if (TCastToPtr aimgr = mgr.ObjectById(x570_aiMgr)) if (aimgr->IsPartOfTeam(GetUniqueId())) aimgr->RemoveTeamAiRole(GetUniqueId()); } void CBeetle::Think(float dt, CStateManager& mgr) { if (!GetActive()) return; if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x570_aiMgr, GetUniqueId())) { x450_bodyController->SetLocomotionType(role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Melee ? pas::ELocomotionType::Lurk : pas::ELocomotionType::Relaxed); } else { SquadAdd(mgr); x450_bodyController->SetLocomotionType(pas::ELocomotionType::Lurk); } x460_knockBackController.SetAutoResetImpulse(IsOnGround()); if (x814_attackDelayTimer > 0.f) x814_attackDelayTimer -= dt; if ((x824_predictPos - GetTranslation()).toVec2f().magSquared() > 0.1f * dt) x820_posDeviationCounter += 1; else x820_posDeviationCounter = 0; CPatterned::Think(dt, mgr); x824_predictPos = x138_velocity * dt + GetTranslation(); } void CBeetle::SetupRetreatPoints(CStateManager& mgr) { for (const auto& conn : GetConnectionList()) { if (conn.x0_state == EScriptObjectState::Retreat && conn.x4_msg == EScriptObjectMessage::Follow) { if (TCastToConstPtr wp = mgr.GetObjectById(mgr.GetIdForScript(conn.x8_objId))) { x6e0_retreatPoints.push_back(wp->GetTranslation()); if (x6e0_retreatPoints.size() == 8) break; } } } } void CBeetle::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CStateManager& mgr) { bool forward = true; switch (msg) { case EScriptObjectMessage::Activate: SquadAdd(mgr); break; case EScriptObjectMessage::Deactivate: case EScriptObjectMessage::Deleted: SquadRemove(mgr); break; case EScriptObjectMessage::OnFloor: forward = false; SetMomentumWR(zeus::skZero3f); x328_27_onGround = true; break; case EScriptObjectMessage::Falling: if (!x450_bodyController->IsFrozen()) { SetMomentumWR({0.f, 0.f, -(GetGravityConstant() * xe8_mass)}); x328_27_onGround = false; } forward = false; break; case EScriptObjectMessage::InitializedInArea: if (x570_aiMgr == kInvalidUniqueId) { x570_aiMgr = CTeamAiMgr::GetTeamAiMgr(*this, mgr); if (GetActive()) SquadAdd(mgr); } SetupRetreatPoints(mgr); x5fc_pathFindSearch.SetArea(mgr.GetWorld()->GetAreaAlways(GetAreaIdAlways())->GetPostConstructed()->x10bc_pathArea); break; default: break; } if (forward) CPatterned::AcceptScriptMsg(msg, sender, mgr); } void CBeetle::PreRender(CStateManager& mgr, const zeus::CFrustum& frustum) { if (x400_25_alive) { switch (mgr.GetPlayerState()->GetActiveVisor(mgr)) { case CPlayerState::EPlayerVisor::XRay: x42c_color.a() = 76.5f / 255.f; break; case CPlayerState::EPlayerVisor::Thermal: if (x838_25_burrowing) x42c_color.a() = x830_intoGroundFactor; else x42c_color.a() = 1.f; break; default: x42c_color.a() = 1.f; break; } } CPatterned::PreRender(mgr, frustum); } void CBeetle::Render(const CStateManager& mgr) const { if (x3fc_flavor == EFlavorType::One && x400_25_alive) { zeus::CTransform tailXf = GetLctrTransform("Target_Tail"sv); if (x428_damageCooldownTimer >= 0.f && x42c_color.a() == 1.f) { if (x5ac_tailModel) { CModelFlags flags(2, 0, 3, zeus::skWhite); flags.addColor = x42c_color; x5ac_tailModel->Render(mgr, tailXf, x90_actorLights.get(), flags); } } else if (x5ac_tailModel) { CModelFlags flags(0, 0, 3, zeus::skWhite); x5ac_tailModel->Render(mgr, tailXf, x90_actorLights.get(), flags); } } CPatterned::Render(mgr); } const CDamageVulnerability* CBeetle::GetDamageVulnerability() const { if (x838_25_burrowing) return &CDamageVulnerability::PassThroughVulnerabilty(); if (x3fc_flavor == EFlavorType::One) return x450_bodyController->IsOnFire() ? &x7ac_tailVuln : &x744_platingVuln; return CAi::GetDamageVulnerability(); } const CDamageVulnerability* CBeetle::GetDamageVulnerability(const zeus::CVector3f& pos, const zeus::CVector3f& dir, const CDamageInfo& dInfo) const { if (x838_25_burrowing) return &CDamageVulnerability::PassThroughVulnerabilty(); if (x3fc_flavor == EFlavorType::One) { if (dInfo.GetWeaponMode().IsComboed() && dInfo.GetWeaponMode().GetType() == EWeaponType::Wave) return &x7ac_tailVuln; if (GetTransform().basis[1].dot(dir) > 0.f && GetTransform().basis[1].dot(zeus::CUnitVector3f(pos - GetBoundingBox().center())) < -0.5f) return &x7ac_tailVuln; else return &x744_platingVuln; } return GetDamageVulnerability(); } zeus::CVector3f CBeetle::GetOrbitPosition(const CStateManager& mgr) const { zeus::CVector3f ret = CPatterned::GetOrbitPosition(mgr); ret.z() = float(GetBoundingBox().center().z()); return ret; } zeus::CVector3f CBeetle::GetAimPosition(const CStateManager& mgr, float dt) const { if (x3fc_flavor == EFlavorType::One || x3fc_flavor == EFlavorType::Two) { zeus::CTransform tailXf = GetLctrTransform("Target_Tail"sv); zeus::CVector3f scaleRange = tailXf * x574_tailAimReference - GetTranslation(); zeus::CAABox aabb = GetBoundingBox(); float minFactor = 10.f; for (int i = 0; i < 3; ++i) { if (scaleRange[i] < 0.f) { float factor = (aabb.min[i] - GetTranslation()[i]) / scaleRange[i]; if (factor < minFactor) minFactor = factor; } else if (scaleRange[i] > 0.f) { float factor = (aabb.max[i] - GetTranslation()[i]) / scaleRange[i]; if (factor < minFactor) minFactor = factor; } } return scaleRange * minFactor + GetTranslation(); } else { return CPhysicsActor::GetAimPosition(mgr, dt); } } EWeaponCollisionResponseTypes CBeetle::GetCollisionResponseType(const zeus::CVector3f& pos, const zeus::CVector3f& dir, const CWeaponMode& wMode, EProjectileAttrib attribs) const { if (x450_bodyController->IsFrozen() && wMode.GetType() == EWeaponType::Ice) return EWeaponCollisionResponseTypes::None; if (x838_25_burrowing) return EWeaponCollisionResponseTypes::Unknown69; if (x3fc_flavor == EFlavorType::One) { if (GetTransform().basis[1].dot(dir) > 0.f && GetTransform().basis[1].dot(zeus::CUnitVector3f(pos - GetBoundingBox().center())) < -0.5f) return EWeaponCollisionResponseTypes::Unknown44; if (!x744_platingVuln.WeaponHurts(wMode, false)) return EWeaponCollisionResponseTypes::Unknown69; } return EWeaponCollisionResponseTypes::Unknown19; } void CBeetle::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt) { bool handled = false; switch (type) { case EUserEventType::ChangeMaterial: AddMaterial(EMaterialTypes::Character, EMaterialTypes::Solid, mgr); x328_25_verticalMovement = true; RemoveMaterial(EMaterialTypes::GroundCollider, mgr); handled = true; break; case EUserEventType::GenerateEnd: x328_25_verticalMovement = false; AddMaterial(EMaterialTypes::GroundCollider, mgr); handled = true; break; case EUserEventType::DamageOn: { zeus::CVector3f center = GetTransform() * (x64_modelData->GetScale() * GetLocatorTransform("LCTR_GARMOUTH"sv).origin); zeus::CVector3f extent = x64_modelData->GetScale() * zeus::CVector3f(2.f, 2.f, 0.5f); if (zeus::CAABox(center - extent, center + extent).intersects(mgr.GetPlayer().GetBoundingBox())) { mgr.ApplyDamage(GetUniqueId(), mgr.GetPlayer().GetUniqueId(), GetUniqueId(), x584_touchDamage, CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {}), zeus::skZero3f); } handled = true; break; } case EUserEventType::Delete: handled = true; break; default: break; } if (!handled) CPatterned::DoUserAnimEvent(mgr, node, type, dt); } void CBeetle::CollidedWith(TUniqueId uid, const CCollisionInfoList& list, CStateManager& mgr) { static CMaterialList envList(EMaterialTypes::Ceiling, EMaterialTypes::Wall); for (const auto& c : list) { if (c.GetMaterialLeft().Intersection(envList) != 0 && x450_bodyController->GetCurrentStateId() == pas::EAnimationState::MeleeAttack) { x450_bodyController->GetCommandMgr().DeliverCmd(CBodyStateCmd(EBodyStateCmd::NextState)); SetVelocityWR(zeus::skZero3f); } } CPatterned::CollidedWith(uid, list, mgr); } void CBeetle::Death(CStateManager& mgr, const zeus::CVector3f& direction, EScriptObjectState state) { if (x400_25_alive) { if (x3fc_flavor == EFlavorType::One) { zeus::CTransform backupXf = GetTransform(); SetTransform(GetLctrTransform("Target_Tail"sv)); SendScriptMsgs(EScriptObjectState::DeathRattle, mgr, EScriptObjectMessage::None); SetTransform(backupXf); } CPatterned::Death(mgr, direction, state); } } void CBeetle::TakeDamage(const zeus::CVector3f& direction, float magnitude) { x428_damageCooldownTimer = 0.33f; } bool CBeetle::IsListening() const { return true; } zeus::CVector3f CBeetle::GetOrigin(const CStateManager& mgr, const CTeamAiRole& role, const zeus::CVector3f& aimPos) const { float midRange = (x2fc_minAttackRange + x300_maxAttackRange) * 0.5f; zeus::CVector3f playerToThis = GetTranslation() - aimPos; if (playerToThis.canBeNormalized()) return aimPos + playerToThis.normalized() * midRange; else return aimPos + GetTransform().basis[1] * midRange; } void CBeetle::FollowPattern(CStateManager& mgr, EStateMsg msg, float dt) { switch (msg) { case EStateMsg::Activate: x568_stateProg = 1; break; case EStateMsg::Update: switch (x568_stateProg) { case 1: if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Step) { x568_stateProg = 3; } else if (IsOnGround()) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCStepCmd(pas::EStepDirection::Left, pas::EStepType::Normal)); } break; case 3: if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Step && IsOnGround()) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCStepCmd(pas::EStepDirection::Right, pas::EStepType::Normal)); x568_stateProg = 2; } break; case 2: if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Step) x568_stateProg = x814_attackDelayTimer <= 0.f ? 4 : 1; break; default: break; } x450_bodyController->GetCommandMgr().DeliverTargetVector(mgr.GetPlayer().GetTranslation() - GetTranslation()); break; default: break; } } void CBeetle::RefinePathFindDest(CStateManager& mgr, zeus::CVector3f& dest) { dest = mgr.GetPlayer().GetTranslation(); if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x570_aiMgr, GetUniqueId())) { dest = role->GetTeamPosition(); } else { zeus::CVector3f thisToDest = dest - GetTranslation(); dest += (thisToDest.canBeNormalized() ? thisToDest.normalized() : GetTransform().basis[1]) * (0.5f * (x2fc_minAttackRange + x300_maxAttackRange)); } } void CBeetle::SeparateFromMelees(CStateManager& mgr) { for (CEntity* ent : mgr.GetListeningAiObjectList()) { if (TCastToPtr ai = ent) { if (ai.GetPtr() != this && ai->GetAreaIdAlways() == GetAreaIdAlways()) { float dist = 4.f; if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x570_aiMgr, ai->GetUniqueId())) if (role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Melee) dist *= 2.f; zeus::CVector3f move = x45c_steeringBehaviors.Separation(*this, ai->GetTranslation(), dist); if (!move.isZero()) x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(move, zeus::skZero3f, 1.f)); } } } } void CBeetle::PathFind(CStateManager& mgr, EStateMsg msg, float dt) { switch (msg) { case EStateMsg::Activate: if (GetSearchPath()) { RefinePathFindDest(mgr, x2e0_destPos); CPatterned::PathFind(mgr, msg, dt); } break; case EStateMsg::Update: { zeus::CVector3f dest = mgr.GetPlayer().GetTranslation(); RefinePathFindDest(mgr, dest); zeus::CVector3f delta = dest - GetTranslation(); zeus::CVector3f move; if (!PathShagged(mgr, 0.f) && !x5fc_pathFindSearch.IsOver()) { CPatterned::PathFind(mgr, msg, dt); move = x450_bodyController->GetCommandMgr().GetMoveVector(); } else { move = x45c_steeringBehaviors.Arrival(*this, dest, 5.f); } if (GetTransform().basis[1].dot(move) >= 0.f || GetTransform().basis[1].dot(delta) <= 0.f) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(move, zeus::skZero3f, 1.f)); } else { zeus::CVector3f face = delta.canBeNormalized() ? delta.normalized() : GetTransform().basis[1]; x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(move, face, 1.f)); } SeparateFromMelees(mgr); break; } default: break; } } void CBeetle::TargetPlayer(CStateManager& mgr, EStateMsg msg, float dt) { if (msg == EStateMsg::Activate) { if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x570_aiMgr, GetUniqueId())) x2e0_destPos = role->GetTeamPosition(); else x2e0_destPos = mgr.GetPlayer().GetTranslation(); x2dc_destObj = mgr.GetPlayer().GetUniqueId(); x2ec_reflectedDestPos = GetTranslation(); x328_24_inPosition = false; } } void CBeetle::Generate(CStateManager& mgr, EStateMsg msg, float dt) { switch (msg) { case EStateMsg::Activate: if (x56c_entranceType == EEntranceType::FacePlayer || x450_bodyController->GetActive()) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::Zero)); SetTransform(zeus::lookAt(GetTranslation(), mgr.GetPlayer().GetTranslation())); } else { x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::Zero, x388_anim)); } if (!x450_bodyController->GetActive()) x450_bodyController->Activate(mgr); RemoveMaterial(EMaterialTypes::Character, EMaterialTypes::Solid, mgr); x568_stateProg = 0; break; case EStateMsg::Update: switch (x568_stateProg) { case 0: if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Generate) { x568_stateProg = 2; x5a8_animTimeRem = x450_bodyController->GetAnimTimeRemaining(); } else { x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::Zero)); } break; case 2: if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Generate) { x568_stateProg = 4; } else if (x68_material.HasMaterial(EMaterialTypes::Solid) && x5a8_animTimeRem > 0.f) { rstl::reserved_vector nearList; mgr.BuildNearList(nearList, zeus::CAABox(GetTranslation() - 5.f, GetTranslation() + 5.f), CMaterialFilter::MakeInclude({EMaterialTypes::Solid}), this); if (!nearList.empty()) { zeus::CVector3f totalSep; float sepFactor = 5.f * dt / x5a8_animTimeRem; for (TUniqueId id : nearList) { if (CActor* act = static_cast(mgr.ObjectById(id))) { zeus::CVector3f sep = x45c_steeringBehaviors.Separation(*this, act->GetTranslation(), 5.f); sep.z() = std::max(0.f, float(sep.z())); totalSep += sep * sepFactor; } } SetTranslation(GetTranslation() + totalSep); } } break; default: break; } break; case EStateMsg::Deactivate: AddMaterial(EMaterialTypes::Character, EMaterialTypes::Solid, EMaterialTypes::GroundCollider, mgr); x328_25_verticalMovement = false; x838_25_burrowing = false; if (x328_26_solidCollision) DeathDelete(mgr); break; default: break; } } void CBeetle::Deactivate(CStateManager& mgr, EStateMsg msg, float dt) { switch (msg) { case EStateMsg::Activate: x568_stateProg = 0; SquadRemove(mgr); x818_stateFinishTimer = 0.f; break; case EStateMsg::Update: switch (x568_stateProg) { case 0: if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Generate) { RemoveMaterial(EMaterialTypes::Character, EMaterialTypes::Solid, EMaterialTypes::Target, EMaterialTypes::Orbit, mgr); mgr.GetPlayer().SetOrbitRequestForTarget(GetUniqueId(), CPlayer::EPlayerOrbitRequest::ActivateOrbitSource, mgr); x838_25_burrowing = true; x5a8_animTimeRem = x450_bodyController->GetAnimTimeRemaining(); x568_stateProg = 2; } else if (IsOnGround()) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::One)); } else { x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd( x45c_steeringBehaviors.Seek(*this, mgr.GetPlayer().GetTranslation()), zeus::skZero3f, 1.f)); } break; case 2: if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Generate) { x568_stateProg = 3; x830_intoGroundFactor = 0.f; auto aabb = GetBoundingBox(); SetTranslation((aabb.max.z() - aabb.min.z()) * 3.f * zeus::skDown + GetTranslation()); } else { float remTime = x450_bodyController->GetAnimTimeRemaining(); x830_intoGroundFactor = x5a8_animTimeRem > 0.f ? remTime / x5a8_animTimeRem : 0.f; } break; case 3: x818_stateFinishTimer += dt; if (x818_stateFinishTimer >= 0.75f) { SendScriptMsgs(EScriptObjectState::DeactivateState, mgr, EScriptObjectMessage::None); mgr.FreeScriptObject(GetUniqueId()); x568_stateProg = 4; x830_intoGroundFactor = 0.f; } else { auto aabb = GetBoundingBox(); SetTranslation((aabb.max.z() - aabb.min.z()) * 4.f * zeus::skDown * dt + GetTranslation()); } break; default: break; } break; default: break; } } void CBeetle::Attack(CStateManager& mgr, EStateMsg msg, float dt) { switch (msg) { case EStateMsg::Activate: x838_26_canSkid = false; x838_24_hitSomething = false; x2e0_destPos = mgr.GetPlayer().GetTranslation(); x2e0_destPos.z() = GetTranslation().z(); x568_stateProg = 0; break; case EStateMsg::Update: switch (x568_stateProg) { case 0: if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::MeleeAttack) { x568_stateProg = 1; x838_26_canSkid = true; } else if (IsOnGround()) { zeus::CVector3f aimPos = mgr.GetPlayer().GetAimPosition(mgr, 0.f); aimPos.z() = GetTranslation().z(); zeus::CVector3f aimDelta = aimPos - GetTranslation(); aimDelta.z() = 0.f; if (aimDelta.canBeNormalized()) aimPos += aimDelta.normalized() * 5.f; x450_bodyController->GetCommandMgr().DeliverCmd(CBCMeleeAttackCmd(pas::ESeverity::One, aimPos)); } break; case 1: if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::MeleeAttack) { x568_stateProg = 4; } else if (IsOnGround()) { x450_bodyController->GetCommandMgr().DeliverTargetVector(x2e0_destPos - GetTranslation()); } else { x568_stateProg = 2; } break; case 2: if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::MeleeAttack) x568_stateProg = 4; break; default: break; } if (x328_26_solidCollision) x838_24_hitSomething = true; break; case EStateMsg::Deactivate: CTeamAiMgr::ResetTeamAiRole(CTeamAiMgr::EAttackType::Melee, mgr, x570_aiMgr, GetUniqueId(), true); x814_attackDelayTimer = mgr.GetActiveRandom()->Float() * x308_attackTimeVariation + x304_averageAttackTime; break; } } void CBeetle::JumpBack(CStateManager&, EStateMsg msg, float dt) { switch (msg) { case EStateMsg::Activate: x568_stateProg = 0; break; case EStateMsg::Update: switch (x568_stateProg) { case 0: if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Step) { x568_stateProg = 2; } else if (IsOnGround()) { x450_bodyController->GetCommandMgr().DeliverCmd( CBCStepCmd(pas::EStepDirection::Backward, pas::EStepType::Normal)); } break; case 2: if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Step) x568_stateProg = 4; break; default: break; } break; default: break; } } void CBeetle::DoubleSnap(CStateManager&, EStateMsg msg, float dt) { switch (msg) { case EStateMsg::Activate: x568_stateProg = 0; 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::Zero)); } break; case 2: if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::MeleeAttack) x568_stateProg = 4; else x450_bodyController->GetCommandMgr().DeliverTargetVector(x2e0_destPos - GetTranslation()); break; default: break; } break; default: break; } } void CBeetle::Shuffle(CStateManager& mgr, EStateMsg msg, float dt) { switch (msg) { case EStateMsg::Activate: x328_24_inPosition = false; break; case EStateMsg::Update: { zeus::CVector3f playerPos = mgr.GetPlayer().GetTranslation(); zeus::CVector3f playerToThis = GetTranslation() - playerPos; float midRange = 0.5f * (x2fc_minAttackRange + x300_maxAttackRange); zeus::CVector3f playerLimit; if (playerToThis.canBeNormalized()) playerLimit = playerToThis.normalized() * midRange + playerPos; else playerLimit = GetTransform().basis[1] * midRange + playerPos; if ((GetTranslation() - playerLimit).magSquared() > 4.f) { pas::EStepDirection dir = GetStepDirection(-playerToThis); switch (dir) { case pas::EStepDirection::Forward: case pas::EStepDirection::Backward: { zeus::CVector3f move = x45c_steeringBehaviors.Arrival(*this, x2e0_destPos, x300_maxAttackRange); if (GetTransform().basis[1].dot(move) >= 0.f || GetTransform().basis[1].dot(playerToThis) >= 0.f) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(move, zeus::skZero3f, 1.f)); } else { x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd( move, playerToThis.canBeNormalized() ? -playerToThis.normalized() : GetTransform().basis[1], 1.f)); } break; } case pas::EStepDirection::Left: case pas::EStepDirection::Right: { x450_bodyController->GetCommandMgr().DeliverCmd(CBCStepCmd(dir, pas::EStepType::Normal)); break; } default: break; } x450_bodyController->GetCommandMgr().DeliverTargetVector(-playerToThis); } else { x328_24_inPosition = true; } break; } default: break; } } void CBeetle::TurnAround(CStateManager& mgr, EStateMsg msg, float dt) { switch (msg) { case EStateMsg::Activate: x568_stateProg = 0; break; case EStateMsg::Update: switch (x568_stateProg) { case 0: if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Turn) { x568_stateProg = 2; } else { zeus::CVector3f thisToPlayer = mgr.GetPlayer().GetTranslation() - GetTranslation(); x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd( zeus::skZero3f, (thisToPlayer.magnitude() > FLT_EPSILON) ? thisToPlayer.normalized() : zeus::skZero3f, 1.f)); } break; case 2: if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Turn) x568_stateProg = 4; break; default: break; } break; default: break; } } void CBeetle::Skid(CStateManager&, EStateMsg msg, float dt) { switch (msg) { case EStateMsg::Activate: if (IsOnGround() && x838_26_canSkid) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCSlideCmd(pas::ESlideType::Zero, GetTransform().basis[1])); x568_stateProg = 2; } break; case EStateMsg::Update: if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Slide) x568_stateProg = 4; break; case EStateMsg::Deactivate: x838_26_canSkid = false; break; default: break; } } void CBeetle::Taunt(CStateManager& mgr, EStateMsg msg, float dt) { switch (msg) { case EStateMsg::Activate: x450_bodyController->GetCommandMgr().DeliverCmd( CBCTauntCmd(mgr.GetActiveRandom()->Float() < 0.75f ? pas::ETauntType::One : pas::ETauntType::Zero)); x568_stateProg = 2; break; case EStateMsg::Update: if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Taunt) x568_stateProg = 4; else x450_bodyController->GetCommandMgr().DeliverTargetVector(mgr.GetPlayer().GetTranslation() - GetTranslation()); break; default: break; } } s32 CBeetle::FindFurthestRetreatPoint(CStateManager& mgr) const { s32 ret = -1; if (x6e0_retreatPoints.empty()) return ret; zeus::CVector2f playerPos = mgr.GetPlayer().GetTranslation().toVec2f(); ret = mgr.GetActiveRandom()->Range(0, s32(x6e0_retreatPoints.size() - 1)); float maxDist = (playerPos - x6e0_retreatPoints[ret].toVec2f()).magSquared(); if (maxDist < 100.f) { s32 i = 0; for (const auto& v : x6e0_retreatPoints) { float dist = (playerPos - v.toVec2f()).magSquared(); if (dist > maxDist) { maxDist = dist; ret = i; } ++i; } } return ret; } void CBeetle::Retreat(CStateManager& mgr, EStateMsg msg, float dt) { switch (msg) { case EStateMsg::Activate: x568_stateProg = 0; SquadRemove(mgr); x818_stateFinishTimer = 0.f; x3b4_speed = 2.f * x81c_; break; case EStateMsg::Update: switch (x568_stateProg) { case 0: if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Generate) { RemoveMaterial(EMaterialTypes::Character, EMaterialTypes::Solid, EMaterialTypes::Target, EMaterialTypes::Orbit, mgr); mgr.GetPlayer().SetOrbitRequestForTarget(GetUniqueId(), CPlayer::EPlayerOrbitRequest::ActivateOrbitSource, mgr); x838_25_burrowing = true; x5a8_animTimeRem = x450_bodyController->GetAnimTimeRemaining(); x568_stateProg = 2; } else if (IsOnGround()) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::One)); } else { x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd( x45c_steeringBehaviors.Seek(*this, mgr.GetPlayer().GetTranslation()), zeus::skZero3f, 1.f)); } break; case 2: if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Generate) { x568_stateProg = 3; x830_intoGroundFactor = 0.f; auto aabb = GetBoundingBox(); SetTranslation((aabb.max.z() - aabb.min.z()) * 3.f * zeus::skDown + GetTranslation()); } else { float remTime = x450_bodyController->GetAnimTimeRemaining(); x830_intoGroundFactor = (x5a8_animTimeRem > 0.f) ? remTime / x5a8_animTimeRem : 0.f; } break; case 3: x818_stateFinishTimer += dt; if (x818_stateFinishTimer >= x834_retreatTime) { x568_stateProg = 4; x830_intoGroundFactor = 0.f; } else { auto aabb = GetBoundingBox(); zeus::CVector3f downVec = (aabb.max.z() - aabb.min.z()) * 3.f * zeus::skDown; SetTranslation(((x834_retreatTime > 0.f) ? (1.f / x834_retreatTime) * downVec : downVec) * dt + GetTranslation()); } break; default: break; } break; case EStateMsg::Deactivate: { s32 idx = FindFurthestRetreatPoint(mgr); if (idx != -1) { SetTranslation(x6e0_retreatPoints[idx]); AddMaterial(EMaterialTypes::Character, EMaterialTypes::Solid, EMaterialTypes::Target, EMaterialTypes::Orbit, mgr); } else { SendScriptMsgs(EScriptObjectState::DeactivateState, mgr, EScriptObjectMessage::None); mgr.FreeScriptObject(GetUniqueId()); } x400_24_hitByPlayerProjectile = false; x3b4_speed = x81c_; x838_25_burrowing = false; break; } } } bool CBeetle::InAttackPosition(CStateManager& mgr, float arg) { float distSq = (mgr.GetPlayer().GetTranslation() - GetTranslation()).magSquared(); if (distSq > x2fc_minAttackRange * x2fc_minAttackRange && distSq < x300_maxAttackRange * x300_maxAttackRange) if (SpotPlayer(mgr, x3c8_leashRadius)) return true; return false; } bool CBeetle::PathShagged(CStateManager&, float arg) { return false; } bool CBeetle::InRange(CStateManager& mgr, float arg) { zeus::CVector3f targetPos = mgr.GetPlayer().GetTranslation(); if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x570_aiMgr, GetUniqueId())) { if (role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Melee) targetPos = role->GetTeamPosition(); } return (targetPos - GetTranslation()).magSquared() < 100.f; } bool CBeetle::PatternOver(CStateManager& mgr, float arg) { return AnimOver(mgr, arg); } bool CBeetle::HasAttackPattern(CStateManager&, float arg) { return true; } bool CBeetle::AnimOver(CStateManager&, float arg) { return x568_stateProg == 4; } bool CBeetle::ShouldAttack(CStateManager& mgr, float arg) { if (x814_attackDelayTimer <= 0.f) { if (TCastToPtr tmgr = mgr.ObjectById(x570_aiMgr)) { if (tmgr->HasTeamAiRole(GetUniqueId())) return tmgr->AddMeleeAttacker(GetUniqueId()); } return true; } return false; } bool CBeetle::ShouldDoubleSnap(CStateManager& mgr, float arg) { if (!GetSearchPath() && IsOnGround()) { zeus::CVector3f targetPos = mgr.GetPlayer().GetTranslation(); float dist = x5a0_headbuttDist + x300_maxAttackRange; if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x570_aiMgr, GetUniqueId())) targetPos = role->GetTeamPosition(); zeus::CVector3f delta = targetPos - GetTranslation(); if (delta.magSquared() > dist * dist && GetTransform().basis[1].dot(delta.normalized()) > 0.98f) { rstl::reserved_vector nearList; mgr.BuildNearList(nearList, GetTranslation(), GetTransform().basis[1], x5a0_headbuttDist, CMaterialFilter::MakeInclude({EMaterialTypes::Character}), this); TUniqueId bestId = kInvalidUniqueId; CRayCastResult res = mgr.RayWorldIntersection(bestId, GetTranslation(), GetTransform().basis[1], x5a0_headbuttDist, CMaterialFilter::MakeInclude({EMaterialTypes::Solid}), nearList); if (res.IsInvalid()) return true; } } return false; } bool CBeetle::ShouldTurn(CStateManager& mgr, float arg) { return zeus::CVector2f::getAngleDiff(GetTransform().basis[1].toVec2f(), (mgr.GetPlayer().GetTranslation() - GetTranslation()).toVec2f()) > zeus::degToRad(30.f); } bool CBeetle::HitSomething(CStateManager&, float arg) { return x838_24_hitSomething; } bool CBeetle::ShouldJumpBack(CStateManager& mgr, float arg) { zeus::CVector3f backDir = -GetTransform().basis[1]; const auto& aabb = GetBaseBoundingBox(); zeus::CVector3f pos = GetTranslation() + zeus::CVector3f(0.f, 0.f, (aabb.max.z() - aabb.min.z()) * 0.5f); rstl::reserved_vector nearList; mgr.BuildNearList(nearList, pos, backDir, x5a4_jumpBackwardDist, CMaterialFilter::MakeInclude({EMaterialTypes::Character}), this); TUniqueId bestId = kInvalidUniqueId; CRayCastResult res = mgr.RayWorldIntersection(bestId, pos, backDir, x5a4_jumpBackwardDist, CMaterialFilter::MakeInclude({EMaterialTypes::Solid}), nearList); return res.IsInvalid(); } bool CBeetle::Stuck(CStateManager&, float arg) { return x820_posDeviationCounter > 30; } bool CBeetle::NoPathNodes(CStateManager&, float arg) { return false; } bool CBeetle::ShouldTaunt(CStateManager& mgr, float arg) { if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x570_aiMgr, GetUniqueId())) { if (role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Unknown || role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Unassigned) { return (role->GetTeamPosition() - GetTranslation()).magSquared() < 100.f; } } return false; } bool CBeetle::ShotAt(CStateManager&, float arg) { if (x3fc_flavor == EFlavorType::Two && x6e0_retreatPoints.size() > 0) return x400_24_hitByPlayerProjectile; return false; } void CBeetle::Burn(float duration, float damage) { CDamageVulnerability dVuln = *GetDamageVulnerability(); if (!x838_25_burrowing && x3fc_flavor == EFlavorType::One) dVuln = x7ac_tailVuln; switch (dVuln.GetVulnerability(CWeaponMode(EWeaponType::Wave), false)) { case EVulnerability::Weak: x450_bodyController->SetOnFire(1.5f * duration); x3ec_pendingFireDamage = 1.5f * damage; break; case EVulnerability::Normal: x450_bodyController->SetOnFire(duration); x3ec_pendingFireDamage = damage; break; default: break; } } void CBeetle::Shock(CStateManager& mgr, float duration, float damage) { CDamageVulnerability dVuln = *GetDamageVulnerability(); if (!x838_25_burrowing && x3fc_flavor == EFlavorType::One) dVuln = x7ac_tailVuln; switch (dVuln.GetVulnerability(CWeaponMode(EWeaponType::Wave), false)) { case EVulnerability::Weak: x450_bodyController->SetElectrocuting(1.5f * duration); x3f0_pendingShockDamage = 1.5f * damage; break; case EVulnerability::Normal: x450_bodyController->SetElectrocuting(duration); x3f0_pendingShockDamage = damage; break; default: break; } } CPathFindSearch* CBeetle::GetSearchPath() { return &x5fc_pathFindSearch; } float CBeetle::GetGravityConstant() const { return 4.f * 24.525f; } } // namespace urde::MP1