mirror of
				https://github.com/AxioDL/metaforce.git
				synced 2025-10-27 15:30:23 +00:00 
			
		
		
		
	Like the prior changes normalizing the inclusions within headers, this tackles the cpp files of the RuntimeCommonB target, making these source files consistent with their headers.
		
			
				
	
	
		
			379 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			379 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "Runtime/World/CTeamAiMgr.hpp"
 | |
| 
 | |
| #include "Runtime/CStateManager.hpp"
 | |
| #include "Runtime/World/CPlayer.hpp"
 | |
| 
 | |
| #include "TCastTo.hpp" // Generated file, do not modify include path
 | |
| 
 | |
| namespace urde {
 | |
| 
 | |
| struct TeamAiRoleSorter {
 | |
|   zeus::CVector3f x0_pos;
 | |
|   s32 xc_type;
 | |
|   bool operator()(const CTeamAiRole& a, const CTeamAiRole& b) const {
 | |
|     float aDist = (x0_pos - a.GetTeamPosition()).magSquared();
 | |
|     float bDist = (x0_pos - b.GetTeamPosition()).magSquared();
 | |
|     switch (xc_type) {
 | |
|     case 0:
 | |
|       return a.GetOwnerId() < b.GetOwnerId();
 | |
|     case 1:
 | |
|       return aDist < bDist;
 | |
|     default:
 | |
|       if (a.GetTeamAiRole() == b.GetTeamAiRole())
 | |
|         return aDist < bDist;
 | |
|       else
 | |
|         return a.GetTeamAiRole() < b.GetTeamAiRole();
 | |
|     }
 | |
|   }
 | |
|   TeamAiRoleSorter(const zeus::CVector3f& pos, s32 type) : x0_pos(pos), xc_type(type) {}
 | |
| };
 | |
| 
 | |
| CTeamAiData::CTeamAiData(CInputStream& in, s32 propCount)
 | |
| : x0_aiCount(in.readUint32Big())
 | |
| , x4_meleeCount(in.readUint32Big())
 | |
| , x8_rangedCount(in.readUint32Big())
 | |
| , xc_unknownCount(in.readUint32Big())
 | |
| , x10_maxMeleeAttackerCount(in.readUint32Big())
 | |
| , x14_maxRangedAttackerCount(in.readUint32Big())
 | |
| , x18_positionMode(in.readUint32Big())
 | |
| , x1c_meleeTimeInterval(propCount > 8 ? in.readFloatBig() : 0.f)
 | |
| , x20_rangedTimeInterval(propCount > 8 ? in.readFloatBig() : 0.f) {}
 | |
| 
 | |
| CTeamAiMgr::CTeamAiMgr(TUniqueId uid, std::string_view name, const CEntityInfo& info, const CTeamAiData& data)
 | |
| : CEntity(uid, info, true, name), x34_data(data) {
 | |
|   if (x34_data.x0_aiCount)
 | |
|     x58_roles.reserve(x34_data.x0_aiCount);
 | |
|   if (x34_data.x4_meleeCount)
 | |
|     x68_meleeAttackers.reserve(x34_data.x4_meleeCount);
 | |
|   if (x34_data.x8_rangedCount)
 | |
|     x78_rangedAttackers.reserve(x34_data.x8_rangedCount);
 | |
| }
 | |
| 
 | |
| void CTeamAiMgr::Accept(IVisitor& visitor) { visitor.Visit(this); }
 | |
| 
 | |
| void CTeamAiMgr::UpdateTeamCaptain() {
 | |
|   int maxPriority = INT_MIN;
 | |
|   x8c_teamCaptainId = kInvalidUniqueId;
 | |
|   for (const auto& role : x58_roles) {
 | |
|     if (role.x18_captainPriority > maxPriority) {
 | |
|       maxPriority = role.x18_captainPriority;
 | |
|       x8c_teamCaptainId = role.GetOwnerId();
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool CTeamAiMgr::ShouldUpdateRoles(float dt) {
 | |
|   if (x58_roles.empty())
 | |
|     return false;
 | |
| 
 | |
|   x88_timeDirty += dt;
 | |
|   if (x88_timeDirty >= 1.5f)
 | |
|     return true;
 | |
| 
 | |
|   for (const auto& role : x58_roles) {
 | |
|     if (role.GetTeamAiRole() <= CTeamAiRole::ETeamAiRole::Initial ||
 | |
|         role.GetTeamAiRole() > CTeamAiRole::ETeamAiRole::Unassigned)
 | |
|       return true;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void CTeamAiMgr::ResetRoles(CStateManager& mgr) {
 | |
|   for (auto& role : x58_roles) {
 | |
|     role.x10_curRole = CTeamAiRole::ETeamAiRole::Initial;
 | |
|     role.x14_roleIndex = 0;
 | |
|     if (const CAi* ai = static_cast<const CAi*>(mgr.GetObjectById(role.GetOwnerId())))
 | |
|       role.x1c_position = ai->GetTranslation();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CTeamAiMgr::SpacingSort(CStateManager& mgr, const zeus::CVector3f& pos) {
 | |
|   TeamAiRoleSorter sorter(pos, 2);
 | |
|   std::sort(x58_roles.begin(), x58_roles.end(), sorter);
 | |
|   float tierStagger = 4.5f;
 | |
|   for (const auto& role : x58_roles) {
 | |
|     if (TCastToPtr<CAi> ai = mgr.ObjectById(role.GetOwnerId())) {
 | |
|       float length = (ai->GetBaseBoundingBox().max.y() - ai->GetBaseBoundingBox().min.y()) * 1.5f;
 | |
|       if (length > tierStagger)
 | |
|         tierStagger = length;
 | |
|     }
 | |
|   }
 | |
|   float curTierDist = tierStagger;
 | |
|   int tierTeamSize = 0;
 | |
|   int maxTierTeamSize = 3;
 | |
|   for (auto& role : x58_roles) {
 | |
|     if (TCastToPtr<CAi> ai = mgr.ObjectById(role.GetOwnerId())) {
 | |
|       zeus::CVector3f delta = ai->GetTranslation() - pos;
 | |
|       zeus::CVector3f newPos;
 | |
|       if (delta.canBeNormalized())
 | |
|         newPos = pos + delta.normalized() * curTierDist;
 | |
|       else
 | |
|         newPos = pos + ai->GetTransform().basis[1] * curTierDist;
 | |
|       role.x1c_position = newPos;
 | |
|       role.x1c_position.z() = ai->GetTranslation().z();
 | |
|       tierTeamSize += 1;
 | |
|       if (tierTeamSize > maxTierTeamSize) {
 | |
|         curTierDist += tierStagger;
 | |
|         tierTeamSize = 0;
 | |
|         maxTierTeamSize += 1;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   TeamAiRoleSorter sorter2(pos, 0);
 | |
|   std::sort(x58_roles.begin(), x58_roles.end(), sorter2);
 | |
| }
 | |
| 
 | |
| void CTeamAiMgr::PositionTeam(CStateManager& mgr) {
 | |
|   zeus::CVector3f aimPos = mgr.GetPlayer().GetAimPosition(mgr, 0.f);
 | |
|   switch (x34_data.x18_positionMode) {
 | |
|   case 1:
 | |
|     SpacingSort(mgr, aimPos);
 | |
|     break;
 | |
|   default:
 | |
|     for (auto& role : x58_roles)
 | |
|       if (TCastToPtr<CAi> ai = mgr.ObjectById(role.GetOwnerId()))
 | |
|         role.x1c_position = ai->GetOrigin(mgr, role, aimPos);
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CTeamAiMgr::AssignRoles(CTeamAiRole::ETeamAiRole assRole, s32 count) {
 | |
|   if (count == 0)
 | |
|     return;
 | |
|   s32 lastIdx = 0;
 | |
|   for (auto& role : x58_roles) {
 | |
|     if (role.GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Initial) {
 | |
|       if (role.x4_roleA == assRole || role.x8_roleB == assRole || role.xc_roleC == assRole) {
 | |
|         role.x10_curRole = assRole;
 | |
|         role.x14_roleIndex = lastIdx++;
 | |
|         if (lastIdx == count)
 | |
|           return;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CTeamAiMgr::UpdateRoles(CStateManager& mgr) {
 | |
|   ResetRoles(mgr);
 | |
|   zeus::CVector3f aimPos = mgr.GetPlayer().GetAimPosition(mgr, 0.f);
 | |
|   TeamAiRoleSorter sorter(aimPos, 1);
 | |
|   std::sort(x58_roles.begin(), x58_roles.end(), sorter);
 | |
|   AssignRoles(CTeamAiRole::ETeamAiRole::Melee, x34_data.x4_meleeCount);
 | |
|   AssignRoles(CTeamAiRole::ETeamAiRole::Ranged, x34_data.x8_rangedCount);
 | |
|   AssignRoles(CTeamAiRole::ETeamAiRole::Unknown, x34_data.xc_unknownCount);
 | |
|   for (auto& role : x58_roles) {
 | |
|     if (role.GetTeamAiRole() <= CTeamAiRole::ETeamAiRole::Initial ||
 | |
|         role.GetTeamAiRole() > CTeamAiRole::ETeamAiRole::Unassigned)
 | |
|       role.SetTeamAiRole(CTeamAiRole::ETeamAiRole::Unassigned);
 | |
|   }
 | |
|   TeamAiRoleSorter sorter2(aimPos, 0);
 | |
|   std::sort(x58_roles.begin(), x58_roles.end(), sorter2);
 | |
|   x88_timeDirty = 0.f;
 | |
| }
 | |
| 
 | |
| void CTeamAiMgr::Think(float dt, CStateManager& mgr) {
 | |
|   CEntity::Think(dt, mgr);
 | |
|   if (ShouldUpdateRoles(dt))
 | |
|     UpdateRoles(mgr);
 | |
|   PositionTeam(mgr);
 | |
|   x90_timeSinceMelee += dt;
 | |
|   x94_timeSinceRanged += dt;
 | |
| }
 | |
| 
 | |
| void CTeamAiMgr::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId objId, CStateManager& mgr) {
 | |
|   CEntity::AcceptScriptMsg(msg, objId, mgr);
 | |
| }
 | |
| 
 | |
| CTeamAiRole* CTeamAiMgr::GetTeamAiRole(TUniqueId aiId) {
 | |
|   auto search =
 | |
|       rstl::binary_find(x58_roles.begin(), x58_roles.end(), aiId, [](const auto& obj) { return obj.GetOwnerId(); });
 | |
|   return search != x58_roles.end() ? &*search : nullptr;
 | |
| }
 | |
| 
 | |
| bool CTeamAiMgr::IsPartOfTeam(TUniqueId aiId) const {
 | |
|   auto search =
 | |
|       rstl::binary_find(x58_roles.begin(), x58_roles.end(), aiId, [](const auto& obj) { return obj.GetOwnerId(); });
 | |
|   return search != x58_roles.end();
 | |
| }
 | |
| 
 | |
| bool CTeamAiMgr::HasTeamAiRole(TUniqueId aiId) const {
 | |
|   auto search =
 | |
|       rstl::binary_find(x58_roles.begin(), x58_roles.end(), aiId, [](const auto& obj) { return obj.GetOwnerId(); });
 | |
|   return (search != x58_roles.end() && search->GetTeamAiRole() > CTeamAiRole::ETeamAiRole::Initial &&
 | |
|           search->GetTeamAiRole() <= CTeamAiRole::ETeamAiRole::Unassigned);
 | |
| }
 | |
| 
 | |
| bool CTeamAiMgr::IsMeleeAttacker(TUniqueId aiId) const {
 | |
|   auto search = rstl::binary_find(x68_meleeAttackers.begin(), x68_meleeAttackers.end(), aiId);
 | |
|   return search != x68_meleeAttackers.end();
 | |
| }
 | |
| 
 | |
| bool CTeamAiMgr::CanAcceptMeleeAttacker(TUniqueId aiId) const {
 | |
|   if (x90_timeSinceMelee >= x34_data.x1c_meleeTimeInterval &&
 | |
|       x68_meleeAttackers.size() < x34_data.x10_maxMeleeAttackerCount)
 | |
|     return true;
 | |
|   return IsMeleeAttacker(aiId);
 | |
| }
 | |
| 
 | |
| bool CTeamAiMgr::AddMeleeAttacker(TUniqueId aiId) {
 | |
|   if (x90_timeSinceMelee >= x34_data.x1c_meleeTimeInterval &&
 | |
|       x68_meleeAttackers.size() < x34_data.x10_maxMeleeAttackerCount && HasTeamAiRole(aiId)) {
 | |
|     auto search = rstl::binary_find(x68_meleeAttackers.begin(), x68_meleeAttackers.end(), aiId);
 | |
|     if (search == x68_meleeAttackers.end()) {
 | |
|       x68_meleeAttackers.insert(std::lower_bound(x68_meleeAttackers.begin(), x68_meleeAttackers.end(), aiId), aiId);
 | |
|       x90_timeSinceMelee = 0.f;
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void CTeamAiMgr::RemoveMeleeAttacker(TUniqueId aiId) {
 | |
|   auto search = rstl::binary_find(x68_meleeAttackers.begin(), x68_meleeAttackers.end(), aiId);
 | |
|   if (search != x68_meleeAttackers.end())
 | |
|     x68_meleeAttackers.erase(search);
 | |
| }
 | |
| 
 | |
| bool CTeamAiMgr::IsRangedAttacker(TUniqueId aiId) const {
 | |
|   auto search = rstl::binary_find(x78_rangedAttackers.begin(), x78_rangedAttackers.end(), aiId);
 | |
|   return search != x78_rangedAttackers.end();
 | |
| }
 | |
| 
 | |
| bool CTeamAiMgr::CanAcceptRangedAttacker(TUniqueId aiId) const {
 | |
|   if (x94_timeSinceRanged >= x34_data.x20_rangedTimeInterval &&
 | |
|       x78_rangedAttackers.size() < x34_data.x14_maxRangedAttackerCount)
 | |
|     return true;
 | |
|   return IsRangedAttacker(aiId);
 | |
| }
 | |
| 
 | |
| bool CTeamAiMgr::AddRangedAttacker(TUniqueId aiId) {
 | |
|   if (x94_timeSinceRanged >= x34_data.x20_rangedTimeInterval &&
 | |
|       x78_rangedAttackers.size() < x34_data.x14_maxRangedAttackerCount && HasTeamAiRole(aiId)) {
 | |
|     auto search = rstl::binary_find(x78_rangedAttackers.begin(), x78_rangedAttackers.end(), aiId);
 | |
|     if (search == x78_rangedAttackers.end()) {
 | |
|       x78_rangedAttackers.insert(
 | |
|           std::lower_bound(x78_rangedAttackers.begin(), x78_rangedAttackers.end(), aiId), aiId);
 | |
|       x94_timeSinceRanged = 0.f;
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void CTeamAiMgr::RemoveRangedAttacker(TUniqueId aiId) {
 | |
|   auto search = rstl::binary_find(x78_rangedAttackers.begin(), x78_rangedAttackers.end(), aiId);
 | |
|   if (search != x78_rangedAttackers.end())
 | |
|     x78_rangedAttackers.erase(search);
 | |
| }
 | |
| 
 | |
| bool CTeamAiMgr::AssignTeamAiRole(const CAi& ai, CTeamAiRole::ETeamAiRole roleA, CTeamAiRole::ETeamAiRole roleB,
 | |
|                                   CTeamAiRole::ETeamAiRole roleC) {
 | |
|   CTeamAiRole newRole(ai.GetUniqueId(), roleA, roleB, roleC);
 | |
|   auto search = rstl::binary_find(x58_roles.begin(), x58_roles.end(), newRole);
 | |
|   if (search == x58_roles.end()) {
 | |
|     if (x58_roles.size() >= x58_roles.capacity())
 | |
|       return false;
 | |
|     x58_roles.insert(std::lower_bound(x58_roles.begin(), x58_roles.end(), newRole), newRole);
 | |
|   } else {
 | |
|     *search = newRole;
 | |
|   }
 | |
|   UpdateTeamCaptain();
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void CTeamAiMgr::RemoveTeamAiRole(TUniqueId aiId) {
 | |
|   if (IsMeleeAttacker(aiId))
 | |
|     RemoveMeleeAttacker(aiId);
 | |
|   if (IsRangedAttacker(aiId))
 | |
|     RemoveRangedAttacker(aiId);
 | |
|   auto search =
 | |
|       rstl::binary_find(x58_roles.begin(), x58_roles.end(), aiId, [](const auto& obj) { return obj.GetOwnerId(); });
 | |
|   x58_roles.erase(search);
 | |
|   UpdateTeamCaptain();
 | |
| }
 | |
| 
 | |
| void CTeamAiMgr::ClearTeamAiRole(TUniqueId aiId) {
 | |
|   auto search =
 | |
|       rstl::binary_find(x58_roles.begin(), x58_roles.end(), aiId, [](const auto& obj) { return obj.GetOwnerId(); });
 | |
|   if (search != x58_roles.end())
 | |
|     search->x10_curRole = CTeamAiRole::ETeamAiRole::Initial;
 | |
| }
 | |
| 
 | |
| s32 CTeamAiMgr::GetNumAssignedOfRole(CTeamAiRole::ETeamAiRole testRole) const {
 | |
|   s32 ret = 0;
 | |
|   for (const auto& role : x58_roles)
 | |
|     if (role.GetTeamAiRole() == testRole)
 | |
|       ++ret;
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| s32 CTeamAiMgr::GetNumAssignedAiRoles() const {
 | |
|   s32 ret = 0;
 | |
|   for (const auto& role : x58_roles)
 | |
|     if (role.GetTeamAiRole() > CTeamAiRole::ETeamAiRole::Initial &&
 | |
|         role.GetTeamAiRole() <= CTeamAiRole::ETeamAiRole::Unassigned)
 | |
|       ++ret;
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| CTeamAiRole* CTeamAiMgr::GetTeamAiRole(CStateManager& mgr, TUniqueId mgrId, TUniqueId aiId) {
 | |
|   if (TCastToPtr<CTeamAiMgr> aimgr = mgr.ObjectById(mgrId))
 | |
|     return aimgr->GetTeamAiRole(aiId);
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| void CTeamAiMgr::ResetTeamAiRole(EAttackType type, CStateManager& mgr, TUniqueId mgrId, TUniqueId aiId,
 | |
|                                  bool clearRole) {
 | |
|   if (TCastToPtr<CTeamAiMgr> tmgr = mgr.ObjectById(mgrId)) {
 | |
|     if (tmgr->HasTeamAiRole(aiId)) {
 | |
|       if (type == EAttackType::Melee) {
 | |
|         if (tmgr->IsMeleeAttacker(aiId))
 | |
|           tmgr->RemoveMeleeAttacker(aiId);
 | |
|       } else if (type == EAttackType::Ranged) {
 | |
|         if (tmgr->IsRangedAttacker(aiId))
 | |
|           tmgr->RemoveRangedAttacker(aiId);
 | |
|       }
 | |
|       if (clearRole)
 | |
|         tmgr->ClearTeamAiRole(aiId);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool CTeamAiMgr::CanAcceptAttacker(EAttackType type, CStateManager& mgr, TUniqueId mgrId, TUniqueId aiId) {
 | |
|   if (TCastToPtr<CTeamAiMgr> tmgr = mgr.ObjectById(mgrId)) {
 | |
|     if (tmgr->HasTeamAiRole(aiId)) {
 | |
|       if (type == EAttackType::Melee)
 | |
|         return tmgr->CanAcceptMeleeAttacker(aiId);
 | |
|       else if (type == EAttackType::Ranged)
 | |
|         return tmgr->CanAcceptRangedAttacker(aiId);
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool CTeamAiMgr::AddAttacker(EAttackType type, CStateManager& mgr, TUniqueId mgrId, TUniqueId aiId) {
 | |
|   if (TCastToPtr<CTeamAiMgr> tmgr = mgr.ObjectById(mgrId)) {
 | |
|     if (tmgr->HasTeamAiRole(aiId)) {
 | |
|       if (type == EAttackType::Melee)
 | |
|         return tmgr->AddMeleeAttacker(aiId);
 | |
|       else if (type == EAttackType::Ranged)
 | |
|         return tmgr->AddRangedAttacker(aiId);
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| TUniqueId CTeamAiMgr::GetTeamAiMgr(CAi& ai, CStateManager& mgr) {
 | |
|   for (const auto& conn : ai.GetConnectionList()) {
 | |
|     if (conn.x0_state == EScriptObjectState::Active && conn.x4_msg == EScriptObjectMessage::Play) {
 | |
|       TUniqueId id = mgr.GetIdForScript(conn.x8_objId);
 | |
|       if (TCastToConstPtr<CTeamAiMgr> aimgr = mgr.GetObjectById(id))
 | |
|         return aimgr->GetUniqueId();
 | |
|     }
 | |
|   }
 | |
|   return kInvalidUniqueId;
 | |
| }
 | |
| 
 | |
| } // namespace urde
 |