Files
GTASource/game/PedGroup/PedGroup.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

1939 lines
49 KiB
C++

// Framework headers
#include "fwscript/scriptguid.h"
// Game headers
#include "PedGroup.h"
#include "camera/CamInterface.h"
#include "control\gamelogic.h"
#include "event\EventHandler.h"
#include "event\EventGroup.h"
#include "event\Events.h"
#include "event\EventDamage.h"
#include "frontend\MiniMap.h"
#include "game\ModelIndices.h"
#include "modelinfo\VehicleModelInfo.h"
#include "network\arrays\NetworkArrayHandlerTypes.h"
#include "network\arrays\NetworkArrayMgr.h"
#include "network/players/NetGamePlayer.h"
#include "network\NetworkInterface.h"
#include "objects\Object.h"
#include "pedgroup\PedGroup.h"
#include "peds\Ped.h"
#include "peds\PedGeometryAnalyser.h"
#include "peds\PedIntelligence.h"
#include "peds\PedPlacement.h"
#include "peds\pedpopulation.h"
#include "peds/Ped.h"
#include "peds\gangs.h"
#include "physics\GtaArchetype.h"
#include "physics/WorldProbe/worldprobe.h"
#include "scene\Entity.h"
#include "script/Handlers/GameScriptEntity.h"
#include "script\Handlers\GameScriptHandler.h"
#include "script\Handlers\GameScriptHandlerNetwork.h"
#include "script\Handlers\GameScriptResources.h"
#include "script\Script.h"
#include "Task/Scenario/TaskScenario.h"
#include "Task/Default/TaskWander.h"
#include "Task\General\TaskBasic.h"
#include "Task/Vehicle/TaskCar.h"
#include "Task/Vehicle/TaskCarAccessories.h"
#include "Task/Vehicle/TaskCarUtils.h"
#include "Task\Response\TaskFlee.h"
#include "Task\Response\TaskGangs.h"
#include "Task/Movement/TaskGoto.h"
#include "Task/Scenario/Info/ScenarioInfo.h"
#include "Task/Scenario/ScenarioManager.h"
#include "Task\System\TaskManager.h"
#include "Task/Vehicle/TaskEnterVehicle.h"
#include "Task\General\TaskSecondary.h"
#include "Task/Movement/TaskSeekEntity.h"
#include "task\Combat/TaskThreatResponse.h"
#include "Task\System\TaskTypes.h"
#include "Vfx\Misc\Fire.h"
AI_OPTIMISATIONS()
////////////////
//GROUP MEMBER
////////////////
void CPedGroupMembership::CPedGroupMember::ClearPedGroupMember()
{
if (m_pPedGroupMember)
{
m_pPedGroupMember->SetPedGroupIndex(PEDGROUP_INDEX_NONE);
}
m_pPedGroupMember = NULL;
m_pedNetworkId = NETWORK_INVALID_OBJECT_ID;
};
////////////////
//MEMBERSHIP
////////////////
#define __OPTIMIZED_GET_PEDS_GROUP 1
const float CPedGroupMembership::ms_fMaxSeparation=30.0f;
const float CPedGroupMembership::ms_fPlayerGroupMaxSeparation=30.0f;
CPedGroupMembership::CPedGroupMembership()
: m_initialLeaderNetId(NETWORK_INVALID_OBJECT_ID)
{
m_fMaxSeparation = ms_fMaxSeparation;
s32 i;
for(i=0;i<MAX_NUM_MEMBERS;i++)
{
m_members[i].ClearPedGroupMember();
}
}
CPedGroupMembership::CPedGroupMembership(const CPedGroupMembership& src)
: m_initialLeaderNetId(NETWORK_INVALID_OBJECT_ID)
{
From(src);
}
CPedGroupMembership::~CPedGroupMembership()
{
Flush(false);
}
CPedGroupMembership& CPedGroupMembership::operator=(const CPedGroupMembership& src)
{
From(src);
return *this;
}
void CPedGroupMembership::Flush(bool bDirty)
{
Assertf(!bDirty || m_pPedGroup->IsLocallyControlled(), "Only the machine which controls this ped group can flush it");
s32 i;
for(i=0;i<MAX_NUM_MEMBERS;i++)
{
if(m_members[i].IsAssigned())
{
RemoveMember(i, bDirty);
}
}
m_initialLeaderNetId = NETWORK_INVALID_OBJECT_ID;
}
void CPedGroupMembership::RemoveAllFollowers(bool bLeaveMissionPeds)
{
Assertf(m_pPedGroup->IsLocallyControlled(), "Only the machine which controls this ped group can remove all followers");
s32 i;
for(i=0;i<MAX_NUM_MEMBERS;i++)
{
if(m_members[i].IsAssigned() && (i != LEADER))
{
// mission peds will always exist on all machines in a network game, so this function will still work properly
if ((!bLeaveMissionPeds) || !m_members[i].GetPedGroupMember() || !m_members[i].GetPedGroupMember()->PopTypeIsMission())
{
RemoveMember(i);
}
}
}
}
void CPedGroupMembership::RemoveNFollowers(s32 N)
{
Assertf(m_pPedGroup->IsLocallyControlled(), "Only the machine which controls this ped group can remove N followers");
s32 i;
for(i=0;i<LEADER;i++)
{
if(m_members[i].IsAssigned() && N > 0)
{
// mission peds will always exist on all machines in a network game, so this function will still work properly
if(!m_members[i].GetPedGroupMember() || !m_members[i].GetPedGroupMember()->PopTypeIsMission())
{
RemoveMember(i);
N--;
}
}
}
Assert(N == 0);
}
void CPedGroupMembership::From(const CPedGroupMembership& src)
{
if (m_members[LEADER].IsAssigned())
{
Assertf(m_pPedGroup->IsLocallyControlled(), "Only the machine which controls this ped group can alter the group");
}
s32 i;
for(i=0;i<MAX_NUM_MEMBERS;i++)
{
if (src.m_members[i].GetPedGroupMember())
AddMember(src.m_members[i].GetPedGroupMember(),i);
else
m_members[i].m_pedNetworkId = src.m_members[i].m_pedNetworkId;
}
m_pPedGroup=src.m_pPedGroup;
m_fMaxSeparation = src.m_fMaxSeparation;
}
void CPedGroupMembership::AssignNetworkPedIds()
{
// see if we can assign the ped pointers in a network game
for(s32 i=0;i<MAX_NUM_MEMBERS;i++)
{
if (m_members[i].IsAssigned() && !m_members[i].GetPedGroupMember())
{
netObject* pObject = NetworkInterface::GetNetworkObject(m_members[i].m_pedNetworkId);
if (pObject)
{
CPed *pPed = NetworkUtils::GetPedFromNetworkObject(pObject);
if (!pPed)
{
Assertf(0, "%s is a member of a ped group!", pObject->GetLogName());
RemoveMember(i, false);
}
// is ped a member of another group? If so remove him.
CPedGroup* pExistingGroup = pPed? pPed->GetPedsGroup() : NULL;
if (pExistingGroup)
{
pPed->GetPedsGroup()->GetGroupMembership()->RemoveMember(pPed, false);
}
if (pPed)
{
m_members[i].ClearPedGroupMember();
bool bAddDefaultTask = false;
if (!pPed->IsNetworkClone())
{
bAddDefaultTask = !pPed->IsSwatPed() || !pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_CreatedByDispatch) || pPed->PopTypeIsMission();
}
AddMember(pPed, i, false, bAddDefaultTask);
}
}
}
}
}
/*
s32 CPedGroupMembership::GetObjectForPedToHold()
{
s32 randNum = fwRandom::GetRandomNumberInRange(0, 100);
if (randNum<33)
{
return MI_GANG_SMOKE;
}
else if (randNum<66)
{
return MI_GANG_DRINK;
}
else
{
return -1;
}
}
*/
void CPedGroupMembership::SetMaxSeparation(float fNewSeparationRange, bool bDirty)
{
m_fMaxSeparation = fNewSeparationRange;
if (bDirty)
{
m_pPedGroup->SetDirty();
}
}
void CPedGroupMembership::SetLeader(CPed* pLeader, bool ASSERT_ONLY(bNetCall))
{
#if __ASSERT
Assert(pLeader);
// check if the leader is a player ped here, because otherwise it will assert when we setup a player's group for a joining player
Assertf(bNetCall || m_pPedGroup->IsLocallyControlled() || pLeader->IsAPlayerPed(), "Only the machine which controls this ped group can set a leader");
#endif
//Check if the new leader is already in the group.
if(IsFollower(pLeader))
{
//Remove the follower from the group so that he
//can be added later as the leader.
RemoveMember(pLeader);
}
//Check if the group already has a leader.
if(m_members[LEADER].IsAssigned())
{
if (pLeader->GetPedType() == PEDTYPE_SWAT)
{
CPed* pOldLeader = GetLeader();
//Remove the old leader from the group.
RemoveMember(LEADER);
AddFollower(pOldLeader);
}
else
{
//Remove the old leader from the group.
RemoveMember(LEADER);
}
}
Assertf(pLeader->GetPedGroupIndex() == PEDGROUP_INDEX_NONE,
"About to set a ped as leader of group %d, but the ped appears to already be a member of a group (%d).",
m_pPedGroup->GetGroupIndex(), pLeader->GetPedGroupIndex());
//Add the new leader to the group.
AddMember(pLeader,LEADER);
// if (m_pPedGroup->PopTypeIsRandom())
// {
// CWeapon* currWeapon = pLeader->GetWeaponMgr()->GetWeaponUsable();
// if (currWeapon->GetWeaponType() == WEAPONTYPE_UNARMED)
// {
// s32 modelIndex = GetObjectForPedToHold();
// // Assert(modelIndex!=-1);
// if (modelIndex!=-1)
// {
// pLeader->GiveObjectToPedToHold(modelIndex);
// }
// }
// }
}
void CPedGroupMembership::AddFollower(CPed* pFollower)
{
Assertf(!IsMember(pFollower), "Added ped to group but he is already in the group");
Assertf(!pFollower || pFollower->GetPedGroupIndex() == PEDGROUP_INDEX_NONE,
"About to add a ped to a group (%d), but the ped appears to already be a member of a group (%d).",
m_pPedGroup->GetGroupIndex(), pFollower->GetPedGroupIndex());
Assertf(m_pPedGroup->IsLocallyControlled(), "Only the machine which controls this ped group can add a follower");
//Check that the new follower isn't already a member of the group.
if(!IsMember(pFollower))
{
//The new follower isn't already in the group.
//Find an empty slot for the new follower.
s32 iSlot=-1;
s32 i;
for(i=0;i<(MAX_NUM_MEMBERS-1);i++)
{
if(!m_members[i].IsAssigned())
{
//Found an empty slot.
iSlot=i;
break;
}
}
Assertf(iSlot!=-1, "Ped was added to group that was already full");
//Check if an empty slot has been found and add the new follower.
if(iSlot!=-1)
{
AddMember(pFollower,iSlot);
// if (m_pPedGroup->PopTypeIsRandom())
// {
// CWeapon* currWeapon = pFollower->GetWeaponMgr()->GetWeaponUsable();
// if (currWeapon->GetWeaponType() == WEAPONTYPE_UNARMED)
// {
// s32 modelIndex = GetObjectForPedToHold();
// // Assert(modelIndex!=-1);
// if (modelIndex!=-1)
// {
// pFollower->GiveObjectToPedToHold(modelIndex);
// }
// }
// }
}
}
}
void CPedGroupMembership::RemoveMember(CPed* pMember, bool bDirty)
{
Assertf(!bDirty || m_pPedGroup->IsLocallyControlled(), "Only the machine which controls this ped group can remove a member");
Assert(pMember);
s32 iSlot=-1;
s32 i;
for(i=0;i<MAX_NUM_MEMBERS;i++)
{
if (m_members[i].IsAssigned())
{
if(pMember==m_members[i].GetPedGroupMember())
{
iSlot=i;
break;
}
}
}
if(iSlot!=-1)
{
RemoveMember(iSlot, bDirty);
}
}
bool CPedGroupMembership::HasLeader() const
{
return m_members[LEADER].IsAssigned();
}
CPed* CPedGroupMembership::GetLeader() const
{
return m_members[LEADER].GetPedGroupMember();
}
ObjectId CPedGroupMembership::GetLeaderNetId() const
{
return m_members[LEADER].m_pedNetworkId;
}
bool CPedGroupMembership::LeaderHasChanged() const
{
aiAssertf(NetworkInterface::IsGameInProgress(), "CPedGroupMembership::LeaderHasChanged() can only be called in MP");
return m_initialLeaderNetId != NETWORK_INVALID_OBJECT_ID && m_members[LEADER].m_pedNetworkId != m_initialLeaderNetId;
}
bool CPedGroupMembership::HasMember(const s32 i) const
{
Assert(i>=0);
Assert(i<MAX_NUM_MEMBERS);
return m_members[i].IsAssigned();
}
CPed* CPedGroupMembership::GetMember(const s32 i) const
{
Assert(i>=0);
Assert(i<MAX_NUM_MEMBERS);
return m_members[i].GetPedGroupMember();
}
s32 CPedGroupMembership::GetMemberId(const CPed* pMember) const
{
s32 i;
for(i=0;i<LEADER;i++)
{
if(m_members[i].GetPedGroupMember()==pMember)
{
return i;
}
}
return -1;
}
CPed * CPedGroupMembership::GetNthMember(const s32 iIndex) const
{
const s32 iNumMembersExcludingLeader = CountMembersExcludingLeader();
Assert(iIndex >= 0 && iIndex < iNumMembersExcludingLeader);
if(iIndex < 0 || iIndex >= iNumMembersExcludingLeader)
return NULL;
s32 iCount=0;
for(s32 i=0;i<LEADER;i++)
{
if(m_members[i].GetPedGroupMember())
{
if(iIndex==iCount)
return m_members[i].GetPedGroupMember();
iCount++;
}
}
Assertf(false, "CPedGroupMembership::GetNthMember iterated members but didn't return one.");
return NULL;
}
s32 CPedGroupMembership::GetNonLeaderMemberPriority(const CPed* pMember) const
{
s32 i;
s32 iPriority = 0;
for(i=0;i<LEADER;i++)
{
if(m_members[i].GetPedGroupMember()==pMember)
{
return iPriority;
}
else if( m_members[i].IsAssigned() )
{
++iPriority;
}
}
Assertf(0, "Ped not member!" );
return -1;
}
bool CPedGroupMembership::IsLeader(const CPed* pLeader) const
{
return (pLeader && m_members[LEADER].GetPedGroupMember()==pLeader);
}
bool CPedGroupMembership::IsFollower(const CPed* pFollower) const
{
if(0==pFollower)
{
return false;
}
bool b=false;
s32 i;
for(i=0;i<LEADER;i++)
{
if(m_members[i].GetPedGroupMember()==pFollower)
{
b=true;
break;
}
}
return b;
}
bool CPedGroupMembership::IsMember(const CPed* pMember) const
{
return (IsFollower(pMember) || IsLeader(pMember));
}
s32 CPedGroupMembership::CountMembers() const
{
s32 iCountResult=0;
for(s32 i=0;i<MAX_NUM_MEMBERS;i++)
{
if(m_members[i].GetPedGroupMember())
{
iCountResult++;
}
}
return iCountResult;
}
s32 CPedGroupMembership::CountMembersExcludingLeader() const
{
s32 iCountResult=0;
for(s32 i=0;i<MAX_NUM_MEMBERS-1;i++)
{
if(m_members[i].GetPedGroupMember())
{
iCountResult++;
}
}
return iCountResult;
}
CPed* CPedGroupMembership::GetFurthestMemberFromPosition(Vector3& vPos, bool bIncludeLeader) const
{
float fFurthestPedSq = 0.0f;
CPed* pFurthestPed = NULL;
s32 iEnd = bIncludeLeader ? MAX_NUM_MEMBERS : MAX_NUM_MEMBERS-1;
for(s32 i=0;i<iEnd-1;i++)
{
if(m_members[i].GetPedGroupMember())
{
float fDistSq = DistSquared(m_members[i].GetPedGroupMember()->GetTransform().GetPosition(), RCC_VEC3V(vPos)).Getf();
if( pFurthestPed == NULL || fDistSq > fFurthestPedSq )
{
pFurthestPed = m_members[i].GetPedGroupMember();
fFurthestPedSq = fDistSq;
}
}
}
return pFurthestPed;
}
// PURPOSE: Sort the membership by distance to the leader
void CPedGroupMembership::SortByDistanceFromLeader()
{
// If we don't have a leader then we need to return
if(!GetLeader())
{
return;
}
// We don't want to include the leader when going through the members
static const s32 iEnd = MAX_NUM_MEMBERS - 1;
float fDistancesSq[iEnd];
// Get the leader position and set the distances for each index
Vec3V vPos = GetLeader()->GetTransform().GetPosition();
for(s32 i = 0; i < iEnd; i++)
{
fDistancesSq[i] = m_members[i].GetPedGroupMember() ? DistSquared(m_members[i].GetPedGroupMember()->GetTransform().GetPosition(), vPos).Getf() : -1.0f;
}
// Loop through valid peds in the membership
for(s32 i = 0; i < iEnd - 1; i++)
{
if(m_members[i].GetPedGroupMember())
{
float fLowestDistanceSq = fDistancesSq[i];
int iLowestIndex = i;
// Loop through the valid peds coming after the current index and if the distance is lower than our index store it
for(s32 j = (i + 1); j < iEnd; j++)
{
if(m_members[j].GetPedGroupMember() && fDistancesSq[j] < fLowestDistanceSq)
{
fLowestDistanceSq = fDistancesSq[j];
iLowestIndex = j;
}
}
// if we found a ped with a shorter distance to the leader then swap these indices
if(iLowestIndex != i)
{
float fTemp = fDistancesSq[i];
fDistancesSq[i] = fDistancesSq[iLowestIndex];
fDistancesSq[iLowestIndex] = fTemp;
CPedGroupMember tempMember = m_members[i];
m_members[i] = m_members[iLowestIndex];
m_members[iLowestIndex] = tempMember;
}
}
}
}
void CPedGroupMembership::Process()
{
s32 i;
if (m_pPedGroup->IsLocallyControlled())
{
// If a member (follower or leader) is dead then remove him from the group
for(i=0;i<MAX_NUM_MEMBERS;i++)
{
if (m_members[i].GetPedGroupMember())
{
if ( m_members[i].GetPedGroupMember()->IsInjured() && m_members[i].GetPedGroupMember()->GetIsDeadOrDying() )
{
if ( (LEADER==i) && m_members[i].GetPedGroupMember()->IsPlayer() )
{ // can't remove the player if he is the leader
}
else
{
RemoveMember(i);
}
}
}
else if (m_members[i].IsAssigned())
{
RemoveMember(i);
}
}
//If there is no leader (maybe dead and just removed) then appoint a new one.
if(!HasLeader())
{
AppointNewLeader();
}
//Remove any of the followers that are too far away
//from the leader to remain in the group.
if(GetLeader())
{
const Vector3 vLeaderPosition = VEC3V_TO_VECTOR3(GetLeader()->GetTransform().GetPosition());
for(i=0;i<LEADER;i++)
{
CPed* pFollower=m_members[i].GetPedGroupMember();
// Remove any peds automatically on death of a player in multiplayer
if( pFollower && GetLeader()->IsAPlayerPed() && GetLeader()->IsInjured() && NetworkInterface::IsGameInProgress() )
{
RemoveMember(i);
continue;
}
if(pFollower && !pFollower->GetPedConfigFlag( CPED_CONFIG_FLAG_NeverLeavesGroup ))
{
Vector3 vDiff = VEC3V_TO_VECTOR3(pFollower->GetTransform().GetPosition()) - vLeaderPosition;
const float f2=vDiff.Mag2();
if((f2> (m_fMaxSeparation*m_fMaxSeparation))/* && pFollower->GetAreaCode()==GetLeader()->GetAreaCode()*/)
{
RemoveMember(i);
}
}
}
}
}
if (NetworkInterface::IsGameInProgress())
{
AssignNetworkPedIds();
}
}
void CPedGroupMembership::AppointNewLeader()
{
Assertf(m_pPedGroup->IsLocallyControlled(), "Only the machine which controls this ped group can appoint a new leader");
if(!m_members[LEADER].IsAssigned())
{
//Find a member that can be appointed the new leader.
s32 i;
s32 iSlot=-1;
for(i=0;i<LEADER;i++)
{
if(m_members[i].IsAssigned())
{
// can only appoint this ped as leader if we are in control of him
if (!m_members[i].GetPedGroupMember() || m_members[i].GetPedGroupMember()->IsNetworkClone())
{
continue;
}
iSlot=i;
break;
}
}
//Test if a member has been found.
if(iSlot!=-1)
{
//Remove the member and set the new leader.
CPed* pNewLeader=m_members[iSlot].GetPedGroupMember();
RemoveMember(iSlot);
bool bAddDefaultTask = !pNewLeader->IsSwatPed() || !pNewLeader->GetPedConfigFlag(CPED_CONFIG_FLAG_CreatedByDispatch) || pNewLeader->PopTypeIsMission();
AddMember(pNewLeader, LEADER, true, bAddDefaultTask);
}
}
}
void CPedGroupMembership::RemoveMember(const s32 iSlot, bool bSetDirty)
{
Assert(iSlot>=0);
Assert(iSlot<MAX_NUM_MEMBERS);
if (m_members[iSlot].GetPedGroupMember())
{
m_members[iSlot].GetPedGroupMember()->SetPedGroupIndex(PEDGROUP_INDEX_NONE);
AssertEntityPointerValid_NotInWorld( m_members[iSlot].GetPedGroupMember() );
}
if (m_members[iSlot].GetPedGroupMember() && !m_members[iSlot].GetPedGroupMember()->IsNetworkClone())
if(m_members[iSlot].GetPedGroupMember() && !m_members[iSlot].GetPedGroupMember()->IsPlayer())
{
// Remove the radar blip this ped may have been given for being in the player group
if (m_members[iSlot].GetPedGroupMember()->GetPedConfigFlag( CPED_CONFIG_FLAG_ClearRadarBlipOnDeath ))
{
Assert(!NetworkInterface::IsGameInProgress());
m_members[iSlot].GetPedGroupMember()->RemoveBlip(BLIP_TYPE_CHAR);
m_members[iSlot].GetPedGroupMember()->SetPedConfigFlag( CPED_CONFIG_FLAG_ClearRadarBlipOnDeath, false );
}
m_members[iSlot].GetPedGroupMember()->GetPedIntelligence()->GetPrimaryDefensiveArea()->Reset();
// #if __BANK
// if (CGroupDebug::ms_bUseSquadAIStuff)
// {
// m_members[iSlot].Clear();
// }
// else
// #endif // __BANK
{
CPed* pMember = m_members[iSlot].GetPedGroupMember();
m_members[iSlot].ClearPedGroupMember();
// recalculate the members default task
if( !pMember->IsInjured())
{
CTask* pActiveTask = pMember->GetPedIntelligence()->GetTaskActive();
bool bIsWritheTask = pActiveTask && pActiveTask->GetTaskType() == CTaskTypes::TASK_WRITHE;
bool bIsFireTask = pActiveTask && pActiveTask->GetTaskType() == CTaskTypes::TASK_COMPLEX_ON_FIRE;
if (!pMember->GetIsInVehicle() && !bIsWritheTask && !bIsFireTask)
{
pMember->GetPedIntelligence()->ClearTaskEventResponse();
}
if (pMember->GetPedType() != PEDTYPE_SWAT) // Swat hack - don't want to redo the default task when removed from the group
{
pMember->GetPedIntelligence()->AddTaskDefault( pMember->ComputeDefaultTask( *pMember ) );
}
}
}
}
if (bSetDirty)
m_pPedGroup->SetDirty();
m_members[iSlot].ClearPedGroupMember();
}
void CPedGroupMembership::AddMember(CPed* pPed, const s32 iSlot, bool bSetDirty, bool bAddDefaultTask)
{
Assert(iSlot>=0);
Assert(iSlot<MAX_NUM_MEMBERS);
Assert(pPed);
Assert(!m_members[iSlot].IsAssigned());
m_members[iSlot].SetPedGroupMember(pPed);
m_members[iSlot].GetPedGroupMember()->SetPedGroupIndex(m_pPedGroup->GetGroupIndex());
if (pPed->GetNetworkObject())
{
m_members[iSlot].m_pedNetworkId = pPed->GetNetworkObject()->GetObjectID();
if (iSlot == LEADER && m_initialLeaderNetId == NETWORK_INVALID_OBJECT_ID)
{
m_initialLeaderNetId = m_members[iSlot].m_pedNetworkId;
}
}
if(m_members[iSlot].GetPedGroupMember())
{
AssertEntityPointerValid_NotInWorld( m_members[iSlot].GetPedGroupMember() );
};
#if __DEV
// Debug thing:
// Test whether this ped gets added to a group other than the players
// when he is actually already in the player group.
CPed * pPlayerPed = FindPlayerPed();
if (pPlayerPed)
{
s32 localPlayerGroup = CPedGroups::GetPlayersGroup(FindPlayerPed());
if (AssertVerify(localPlayerGroup >=0) && CPedGroups::ms_groups[localPlayerGroup].GetGroupMembership() != this)
{
if (CPedGroups::ms_groups[localPlayerGroup].GetGroupMembership()->IsMember(pPed))
{
Assertf(0, "This ped joined a group while he was in the player group.");
}
}
}
#endif //__DEV
if (bSetDirty)
{
if (iSlot == LEADER)
{
if (m_pPedGroup->PopTypeGet() == POPTYPE_PERMANENT) // we know that the leaders of all the permanent groups are the players, so we don't need to send this info
{
bSetDirty = false;
}
else if (pPed->GetNetworkObject() && pPed->GetNetworkObject()->GetObjectID() != m_initialLeaderNetId)
{
// don't dirty the group if the leader is changing, the group will be moved to another slot by the ped groups manager
bSetDirty = false;
}
}
if (bSetDirty)
{
m_pPedGroup->SetDirty();
}
}
if(!m_members[iSlot].GetPedGroupMember()->IsPlayer())
{
// we can't alter peds we don't control
if (m_members[iSlot].GetPedGroupMember()->IsNetworkClone())
return;
if(bAddDefaultTask)
{
m_members[iSlot].GetPedGroupMember()->GetPedIntelligence()->AddTaskDefault( m_members[iSlot].GetPedGroupMember()->ComputeDefaultTask( *m_members[iSlot].GetPedGroupMember() ) );
}
}
}
void CPedGroupMembership::RemoveMembers(const CPedGroupMembership& members)
{
s32 i;
for(i=0;i<CPedGroupMembership::MAX_NUM_MEMBERS;i++)
{
if(members.HasMember(i))
{
RemoveMember(i);
}
}
}
// these functions are called by the network array sync stuff when a ped group is being synced across the network
void CPedGroupMembership::CopyNetworkInfo(const CPedGroupMembership& membership, bool bApplyToPeds)
{
for (u32 i=0; i<CPedGroupMembership::MAX_NUM_MEMBERS; i++)
{
// ignore leader for player groups (the players are always the leaders)
if (i == LEADER && m_pPedGroup->IsPlayerGroup())
{
continue;
}
ObjectId pedGlobalId = membership.m_members[i].m_pedNetworkId;
if (bApplyToPeds)
{
if (m_pPedGroup->IsActive())
{
// copy membership to this group, assigning ped pointers and tidying up
if (membership.HasMember(i))
{
if (pedGlobalId != m_members[i].m_pedNetworkId)
{
RemoveMember(i, false);
}
}
else if (HasMember(i))
{
RemoveMember(i, false);
}
}
}
m_members[i].m_pedNetworkId = pedGlobalId;
}
if (bApplyToPeds)
{
AssignNetworkPedIds();
}
}
void CPedGroupMembership::Serialise(CSyncDataBase& serialiser)
{
SERIALISE_PACKED_FLOAT(serialiser, m_fMaxSeparation, 200.0f, SIZEOF_MAX_SEPARATION, "Max separation");
for (s32 i=0; i<MAX_NUM_MEMBERS; i++)
{
bool bMember = m_members[i].IsAssigned() || serialiser.GetIsMaximumSizeSerialiser();
// don't need to sync the leader for player groups
if (i==LEADER && m_pPedGroup->IsPlayerGroup())
bMember = false;
SERIALISE_BOOL(serialiser, bMember);
if (bMember)
{
if (i==LEADER)
{
SERIALISE_OBJECTID(serialiser, m_members[i].m_pedNetworkId, "Leader");
}
else
{
SERIALISE_OBJECTID(serialiser, m_members[i].m_pedNetworkId, "Member");
}
}
else
{
m_members[i].ClearPedGroupMember();
}
}
}
//////////////
//PED GROUP
//////////////
CPedGroup::CPedGroup() :
m_pFormation(NULL),
m_pedGroupPopType(POPTYPE_UNKNOWN),
m_bActive(false),
m_pScriptHandler(NULL),
m_bCanDirtyDuringFlush(true)
{
m_membership.SetPedGroup(this);
m_lastPedRespondedTo = NULL;
m_pFormation = CreateDefaultFormation();
if (m_pFormation)
m_pFormation->Init(this);
m_bNeedsGroupEventScan = true;
}
CPedGroup::~CPedGroup()
{
if(m_pFormation)
{
delete m_pFormation;
m_pFormation = NULL;
}
}
void CPedGroup::SetActive()
{
m_bActive = true;
}
void CPedGroup::SetInactive()
{
m_bActive = false;
}
CPedFormation * CPedGroup::CreateDefaultFormation()
{
// JB: Please don't change this from FORMATION_LOOSE, as it messes up the default AI buddy following behaviour.
// The function CPedGroup::SetFormation(iType) should be used to set a custom formation on a pedgroup if required.
return CPedFormation::NewFormation(CPedFormationTypes::FORMATION_LOOSE);
}
// Includes subgroup members
s32 CPedGroup::FindTotalNumberOfMembersInGroup(CPedGroup* pGroup)
{
s32 iTotal = pGroup->GetGroupMembership()->CountMembers();
return iTotal;
}
void
CPedGroup::SetFormation(s32 iType)
{
if(m_pFormation) delete m_pFormation;
CPedFormation * pFormation = CPedFormation::NewFormation(iType);
Assert(pFormation);
m_pFormation = pFormation;
m_pFormation->Init(this);
m_pFormation->Process();
SetDirty();
}
bool CPedGroup::IsLocallyControlled() const
{
bool bLocallyControlled = false;
if (NetworkInterface::IsGameInProgress())
{
if (IsPlayerGroup())
{
CNetGamePlayer* pLocalPlayer = NetworkInterface::GetLocalPlayer();
if (pLocalPlayer && m_iGroupIndex == pLocalPlayer->GetPhysicalPlayerIndex())
{
bLocallyControlled = true;
}
else
{
// player bots control their own groups
CPed* leader = GetGroupMembership() ? GetGroupMembership()->GetLeader() : NULL;
if (leader && leader->IsNetworkBot() && !leader->IsNetworkClone())
{
bLocallyControlled = true;
}
}
}
else if (NetworkInterface::GetArrayManager().GetPedGroupsArrayHandler())
{
bLocallyControlled = !NetworkInterface::GetArrayManager().GetPedGroupsArrayHandler()->IsElementRemotelyArbitrated(GetNetArrayHandlerIndex());
}
}
else
{
bLocallyControlled = true;
}
return bLocallyControlled;
}
bool CPedGroup::IsPlayerGroup() const
{
return CPedGroups::IsPlayerGroup(m_iGroupIndex);
}
void
CPedGroup::SetFormation(CPedFormation * pFormation)
{
Assert(pFormation);
if(m_pFormation) delete m_pFormation;
m_pFormation = pFormation;
m_pFormation->Init(this);
m_pFormation->Process();
SetDirty();
}
void CPedGroup::Process()
{
// Process group membership (appointing leader, removing distant peds, etc)
m_membership.Process();
// Process the group's formation. Calculate new target positions.
Assert(m_pFormation);
if (m_membership.GetLeader())
m_pFormation->Process();
// Remove empty random groups
if(PopTypeIsRandomNonPermanent() &&
IsLocallyControlled() &&
m_membership.CountMembers() == 0 )
{
Flush();
}
}
void CPedGroup::Flush(bool bDirty, bool ASSERT_ONLY(bNetAssert))
{
m_bCanDirtyDuringFlush = bDirty;
Assertf((IsLocallyControlled() || !bNetAssert), "Trying to flush group %d which is controlled by another machine", m_iGroupIndex);
m_membership.Flush(bDirty);
SetInactive();
if(GetFormation())
{
GetFormation()->SetDefaultSpacing();
GetFormation()->ResetScriptModified();
}
if (bDirty)
SetDirty();
m_pScriptHandler = NULL;
// SetPedGroupPopType(POPTYPE_RANDOM_AMBIENT); can't do this as CPlayerPed::SetupPlayerGroup() needs to flush the players group but keep it permanent
m_bCanDirtyDuringFlush = true;
}
void CPedGroup::RemoveAllFollowers()
{
m_membership.RemoveAllFollowers();
}
void CPedGroup::Teleport(const Vector3& vNewPos, bool bKeepTasks, bool bKeepIK, bool bResetPlants)
{
Assertf(IsLocallyControlled(), "Only the machine which controls this ped group can teleport the group");
//Teleport the leader first.
CPed* pLeader=m_membership.GetLeader();
if(pLeader)
{
if(!pLeader->IsDead())
{
pLeader->Teleport(vNewPos, pLeader->GetCurrentHeading(), bKeepTasks, true, bKeepIK, bResetPlants);
}
}
//Work out if the leader is in a vehicle.
//If the leader isn't in a vehicle and and any member is in a vehicle then
//set all that member out the car.
if(pLeader && !pLeader->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ))
{
s32 i;
for(i=0;i<CPedGroupMembership::LEADER;i++)
{
CPed* pFollower=m_membership.GetMember(i);
if(pFollower && pFollower->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ) && !pFollower->PopTypeIsMission())
{
if(!pFollower->IsDead())
{
pFollower->SetPedOutOfVehicle(CPed::PVF_Warp);
}
}
}
}
//Teleport all the members.
//Resolve all collisions in the new coord.
//Abort all event response tasks because the peds have been
//moved to a new coord where the event might no longer be valid.
Assert(m_pFormation);
// Update the formation to be relative to the leader's new coords
m_pFormation->Process();
s32 i;
for(i=0;i<CPedGroupMembership::LEADER;i++)
{
CPed* pFollower=m_membership.GetMember(i);
if(pFollower)
{
if(!pFollower->IsInjured())
{
Assertf(!pFollower->IsNetworkClone(), "Cannot teleport a group member as it is not controlled by this machine");
// Vector2 vOffset2D=CTaskComplexFollowLeaderInFormation::ms_offsets.GetOffSet(i);
// Vector3 vNewFollowerPos=vNewPos;
// vNewFollowerPos+=Vector3(vOffset2D.x,vOffset2D.y,0);
const CPedPositioning & pedPositioning = m_pFormation->GetPedPositioning(i);
Vector3 vNewFollowerPos = pedPositioning.GetPosition();
pFollower->Teleport(vNewFollowerPos, pFollower->GetCurrentHeading(), bKeepTasks, true, bKeepIK, bResetPlants);
pFollower->PositionAnyPedOutOfCollision();
}
}
}
}
void CPedGroup::SetPedGroupPopType(const ePopType iGroupCreatedBy, bool bDirty)
{
m_pedGroupPopType = static_cast<ePopType>(iGroupCreatedBy);
if (bDirty)
{
SetDirty();
}
}
CPed* CPedGroup::GetClosestGroupPed(CPed* pPed, float* retDistSqr)
{
CPed* pClosestPed = NULL;
float closestDistSqr = 99999999.0f;
Vector3 vPedPosition(Vector3::ZeroType);
if (pPed)
{
vPedPosition = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
}
for (s32 i=0; i<CPedGroupMembership::MAX_NUM_MEMBERS; i++)
{
CPed* pPedThis = GetGroupMembership()->GetMember(i);
if (pPedThis && pPedThis!=pPed)
{
Vector3 vec = vPedPosition - VEC3V_TO_VECTOR3(pPedThis->GetTransform().GetPosition());
float distSqr = vec.Mag2();
if (distSqr < closestDistSqr)
{
pClosestPed = pPedThis;
closestDistSqr = distSqr;
}
}
}
if (retDistSqr)
{
*retDistSqr = closestDistSqr;
}
return pClosestPed;
}
void CPedGroup::SetDirty()
{
if(!m_bCanDirtyDuringFlush)
{
return;
}
if (NetworkInterface::IsGameInProgress() && Verifyf(IsLocallyControlled(), "Only the machine which controls this group can alter it"))
{
if (IsPlayerGroup())
{
CPed* leader = GetGroupMembership() ? GetGroupMembership()->GetLeader() : NULL;
bool isNetworkBot = leader && leader->IsNetworkBot();
CPed * pLocalPlayer = isNetworkBot ? GetGroupMembership()->GetLeader() : CGameWorld::FindLocalPlayer();
netObject* pPlayerNetObj = AssertVerify(pLocalPlayer) ? pLocalPlayer->GetNetworkObject() : NULL;
if (pPlayerNetObj && pPlayerNetObj->GetSyncData())
{
// manually dirty the ped group sync data
CPlayerSyncTree* pSyncTree = static_cast<CPlayerSyncTree*>(pPlayerNetObj->GetSyncTree());
pSyncTree->DirtyNode(pPlayerNetObj, *pSyncTree->GetPedGroupNode());
}
}
else if (NetworkInterface::GetArrayManager().GetPedGroupsArrayHandler())
{
if (GetGroupMembership()->GetLeader() || NetworkInterface::GetArrayManager().GetPedGroupsArrayHandler()->GetElementArbitration(GetNetArrayHandlerIndex()) != NULL)
{
// flag ped groups array handler as dirty so that this ped group gets sent to the other machines
NetworkInterface::GetArrayManager().GetPedGroupsArrayHandler()->SetElementDirty(GetNetArrayHandlerIndex());
}
}
}
}
bool CPedGroup::IsAnyoneUsingCar(const CVehicle& vehicle) const
{
s32 i;
for(i=0;i<CPedGroupMembership::MAX_NUM_MEMBERS;i++)
{
CPed* pMember=m_membership.GetMember(i);
if(pMember)
{
//If pMember is in car or is exiting car then the pMember will have a record of the car.
if(pMember->GetMyVehicle() && pMember->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ) && pMember->GetMyVehicle()==&vehicle)
{
return true;
}
CTaskEnterVehicle* pEnterTask=(CTaskEnterVehicle*)pMember->GetPedIntelligence()->FindTaskByType(CTaskTypes::TASK_ENTER_VEHICLE);
if( pEnterTask && pEnterTask->GetVehicle() == &vehicle)
{
return true;
}
}
}
return false;
}
////////////////////////////////////////////////////////
// FUNCTION: FindDistanceToFurthestMember
// Calculates the distance from the leader to the nearest member
float CPedGroup::FindDistanceToFurthestMember()
{
float GreatestDistance = 0;
CPed *pLeader = GetGroupMembership()->GetLeader();
if (pLeader)
{
for (s32 M = 0; M < CPedGroupMembership::LEADER; M++)
{
CPed *pPed = GetGroupMembership()->GetMember(M);
if (pPed)
{
float fDistanceToLeader = Dist(pPed->GetTransform().GetPosition(), pLeader->GetTransform().GetPosition()).Getf();
GreatestDistance = rage::Max(GreatestDistance, fDistanceToLeader);
}
}
}
return GreatestDistance;
}
////////////////////////////////////////////////////////
// FUNCTION: FindDistanceToNearestMember
// Calculates the distance from the leader to the nearest member
float CPedGroup::FindDistanceToNearestMember(CPed **ppNearestPed) const
{
float SmallestDistance = 9999999999.9f;
CPed *pLeader = GetGroupMembership()->GetLeader();
if (pLeader)
{
for (s32 M = 0; M < CPedGroupMembership::LEADER; M++)
{
CPed *pPed = GetGroupMembership()->GetMember(M);
if (pPed)
{
float fDistanceToLeader = Dist(pPed->GetTransform().GetPosition(), pLeader->GetTransform().GetPosition()).Getf();
SmallestDistance = rage::Min(SmallestDistance, fDistanceToLeader);
if (ppNearestPed) *ppNearestPed = pPed;
}
}
}
return SmallestDistance;
}
void
CPedGroup::GiveEventToAllMembers(CEvent & event, CPed * pExcludeThisMember)
{
CPedGroupMembership * pMembers = GetGroupMembership();
for(s32 m=0; m<CPedGroupMembership::MAX_NUM_MEMBERS; m++)
{
CPed * pMember = pMembers->GetMember(m);
if(!pMember || pMember == pExcludeThisMember)
continue;
if(pMember->IsPlayer()) // Don't give events to the player
continue;
if(pMember->IsNetworkClone())
continue;
pMember->GetPedIntelligence()->AddEvent(event);
}
}
bool CPedGroup::IsSyncedWithPlayer(const CNetGamePlayer& player) const
{
bool bSynced = true;
if (NetworkInterface::IsGameInProgress())
{
if (IsPlayerGroup())
{
CNetGamePlayer* pLocalPlayer = NetworkInterface::GetLocalPlayer();
if (pLocalPlayer && m_iGroupIndex == pLocalPlayer->GetPhysicalPlayerIndex() && pLocalPlayer->GetPlayerPed())
{
netObject* pPlayerNetObj = pLocalPlayer->GetPlayerPed()->GetNetworkObject();
if (AssertVerify(pPlayerNetObj) && pPlayerNetObj->GetSyncData())
{
CPlayerSyncTree* pSyncTree = static_cast<CPlayerSyncTree*>(pPlayerNetObj->GetSyncTree());
bSynced = pSyncTree->IsNodeSyncedWithPlayer(pPlayerNetObj, *pSyncTree->GetPedGroupNode(), player.GetPhysicalPlayerIndex());
}
}
}
else
{
CPedGroupsArrayHandler* pPedGroupsHandler = NetworkInterface::GetArrayManager().GetPedGroupsArrayHandler();
if (pPedGroupsHandler && pPedGroupsHandler->IsElementLocallyArbitrated(GetNetArrayHandlerIndex()))
{
bSynced = pPedGroupsHandler->IsElementSyncedWithPlayer(GetNetArrayHandlerIndex(), player.GetPhysicalPlayerIndex());
}
}
}
return bSynced;
}
bool CPedGroup::IsSyncedWithAllPlayers() const
{
bool bSynced = true;
if (NetworkInterface::IsGameInProgress())
{
if (IsPlayerGroup())
{
CNetGamePlayer* pLocalPlayer = NetworkInterface::GetLocalPlayer();
if (pLocalPlayer && m_iGroupIndex == pLocalPlayer->GetPhysicalPlayerIndex() && pLocalPlayer->GetPlayerPed())
{
netObject* pPlayerNetObj = pLocalPlayer->GetPlayerPed()->GetNetworkObject();
if (AssertVerify(pPlayerNetObj) && pPlayerNetObj->GetSyncData())
{
CPlayerSyncTree* pSyncTree = static_cast<CPlayerSyncTree*>(pPlayerNetObj->GetSyncTree());
bSynced = pSyncTree->IsNodeSyncedWithAllPlayers(pPlayerNetObj, *pSyncTree->GetPedGroupNode());
}
}
}
else
{
CPedGroupsArrayHandler* pPedGroupsHandler = NetworkInterface::GetArrayManager().GetPedGroupsArrayHandler();
if (pPedGroupsHandler && pPedGroupsHandler->IsElementLocallyArbitrated(GetNetArrayHandlerIndex()))
{
bSynced = pPedGroupsHandler->IsElementSyncedWithAllPlayers(GetNetArrayHandlerIndex());
}
}
}
return bSynced;
}
// the array handler ignores the player ped groups (they are synced via the player ped updates
u32 CPedGroup::GetNetArrayHandlerIndex() const
{
Assert(m_iGroupIndex >= MAX_NUM_PHYSICAL_PLAYERS);
return m_iGroupIndex - MAX_NUM_PHYSICAL_PLAYERS;
}
void CPedGroup::Serialise(CSyncDataBase& serialiser)
{
bool bPlayerGroup = IsPlayerGroup();
SERIALISE_BOOL(serialiser, bPlayerGroup, "Player group");
m_membership.Serialise(serialiser);
if (!bPlayerGroup || serialiser.GetIsMaximumSizeSerialiser())
{
SERIALISE_UNSIGNED(serialiser, reinterpret_cast<u32&>(m_pedGroupPopType), SIZEOF_POPTYPE, "Pop type");
}
if (AssertVerify(m_pFormation))
{
m_pFormation->Serialise(serialiser);
}
SERIALISE_BOOL(serialiser, m_bNeedsGroupEventScan, "Needs group event scan");
}
netObject* CPedGroup::GetNetworkObject() const
{
CPed* pLeader = GetGroupMembership()->GetLeader();
netObject* pNetObj = NULL;
if (pLeader)
{
pNetObj = pLeader->GetNetworkObject();
}
else if (GetGroupMembership()->GetLeaderNetId() != NETWORK_INVALID_OBJECT_ID)
{
pNetObj = NetworkInterface::GetNetworkObject(GetGroupMembership()->GetLeaderNetId());
}
return pNetObj;
}
///////////////
//PED GROUPS
///////////////
CPedGroup CPedGroups::ms_groups[CPedGroups::MAX_NUM_GROUPS];
bool CPedGroups::ms_bIsPlayerOnAMission=false;
s32 CPedGroups::ms_iNoOfPlayerKills=0;
s32 CPedGroups::AddGroup(const ePopType pedGroupPopType, CGameScriptHandler* pScriptHandler)
{
s32 iGroupID=-1;
s32 i;
s32 iStart = 0;
s32 iEnd = MAX_NUM_GROUPS;
Assertf(!(pedGroupPopType==POPTYPE_MISSION && !pScriptHandler), "Mission ped groups must have an associated script handler");
Assertf(!(pedGroupPopType!=POPTYPE_MISSION && pScriptHandler), "Non-mission ped groups should not have an associated script handler");
// The first MAX_NUM_PHYSICAL_PLAYERS groups are permanently allocated to each player
// The next 16 are for script created groups
// The rest are for random groups
switch (pedGroupPopType)
{
case POPTYPE_UNKNOWN:
Assertf(false, "A valid pop type should be passed in.");
return 0;
case POPTYPE_RANDOM_PERMANENT : // Purposeful fall through.
case POPTYPE_RANDOM_PARKED : // Purposeful fall through.
case POPTYPE_RANDOM_PATROL : // Purposeful fall through.
case POPTYPE_RANDOM_SCENARIO : // Purposeful fall through.
case POPTYPE_RANDOM_AMBIENT :
iStart = MAX_NUM_PHYSICAL_PLAYERS + MAX_NUM_SCRIPT_GROUPS;
iEnd = MAX_NUM_GROUPS;
break;
case POPTYPE_PERMANENT :
iStart = 0;
iEnd = MAX_NUM_PHYSICAL_PLAYERS;
break;
case POPTYPE_MISSION :// Purposeful fall through.
case POPTYPE_REPLAY :// Purposeful fall through.
case POPTYPE_TOOL :
case POPTYPE_CACHE :
iStart = MAX_NUM_PHYSICAL_PLAYERS;
iEnd = MAX_NUM_PHYSICAL_PLAYERS + MAX_NUM_SCRIPT_GROUPS;
break;
case NUM_POPTYPES :
Assert(0);
}
CPedGroupsArrayHandler* pPedGroupsHandler = NetworkInterface::IsGameInProgress() ? NetworkInterface::GetArrayManager().GetPedGroupsArrayHandler() : NULL;
for(i=iStart;i<iEnd;i++)
{
if(!ms_groups[i].IsActive())
{
// we can't use an empty slot that is still in the process of cleaning up (ie informing other players it is empty), because the net id
// of the slot will now differ from the net id of the ped group leader.
if (i>=MAX_NUM_PHYSICAL_PLAYERS && pPedGroupsHandler && pPedGroupsHandler->GetElementArbitration(i-MAX_NUM_PHYSICAL_PLAYERS))
{
continue;
}
iGroupID=i;
ms_groups[i].Flush(false);
ms_groups[i].SetActive();
ms_groups[i].m_pScriptHandler = pScriptHandler;
ms_groups[i].SetPedGroupPopType(pedGroupPopType);
ms_groups[i].SetNeedsGroupEventScan(true);
break;
}
}
return iGroupID;
}
void CPedGroups::AddGroupAtIndex(const ePopType pedGroupPopType, s32 index)
{
ms_groups[index].Flush(false, false);
ms_groups[index].SetActive();
ms_groups[index].SetPedGroupPopType(pedGroupPopType, false);
ms_groups[index].SetNeedsGroupEventScan(true);
}
void CPedGroups::RemoveGroup(const s32 iGroupID, bool bDirty)
{
Assert(ms_groups[iGroupID].PopTypeGet() != POPTYPE_PERMANENT && ms_groups[iGroupID].PopTypeGet() != POPTYPE_MISSION);
RemoveGroupInternal(iGroupID, bDirty);
}
void CPedGroups::RemoveGroupFromScript(const s32 iGroupID, bool bDirty)
{
Assert(ms_groups[iGroupID].PopTypeGet() != POPTYPE_PERMANENT && ms_groups[iGroupID].PopTypeGet() == POPTYPE_MISSION);
RemoveGroupInternal(iGroupID, bDirty);
}
void CPedGroups::RemoveGroupInternal(const s32 iGroupID, bool bDirty)
{
Assert(iGroupID<MAX_NUM_GROUPS);
if(!ms_groups[iGroupID].IsActive())
{
return;
}
ms_groups[iGroupID].Flush(bDirty, bDirty);
}
bool CPedGroups::MoveGroupToFreeSlot(const s32 iGroupID, s32* freeSlotID )
{
CPedGroup* pOriginalGroup = &ms_groups[iGroupID];
Assert(NetworkInterface::IsGameInProgress()); // this should only be used in the network game otherwise it wont work properly
Assert(pOriginalGroup->IsActive());
Assert(pOriginalGroup->IsLocallyControlled());
s32 newGroupID = AddGroup(pOriginalGroup->PopTypeGet());
if (newGroupID != -1)
{
CPedGroup* pNewGroup = &ms_groups[newGroupID];
pNewGroup->CopyNetworkInfo(*pOriginalGroup, true);
// membership of original group should now be 0 (CopyNetworkInfo should have reassigned all the peds via AssignNetworkPedIds)
Assert(pOriginalGroup->GetGroupMembership()->CountMembers() == 0);
if (pOriginalGroup->m_pScriptHandler)
{
pNewGroup->m_pScriptHandler = pOriginalGroup->m_pScriptHandler;
// fixup resource reference to moved group
scriptResource* pResource = pOriginalGroup->m_pScriptHandler->GetScriptResource(CGameScriptResource::SCRIPT_RESOURCE_PEDGROUP, iGroupID);
if (AssertVerify(pResource))
{
pResource->SetReference(newGroupID);
}
}
pOriginalGroup->Flush(false, false);
if (freeSlotID)
{
*freeSlotID = newGroupID;
}
pNewGroup->SetDirty();
return true;
}
return false;
}
// CPedGroups::RemoveAllFollowersFromGroup - Removes all followers but leaves the leader
void CPedGroups::RemoveAllFollowersFromGroup(const s32 iGroupID)
{
Assert(iGroupID<MAX_NUM_GROUPS);
if(!ms_groups[iGroupID].IsActive())
{
return;
}
ms_groups[iGroupID].RemoveAllFollowers();
}
void CPedGroups::RemoveAllRandomGroups()
{
for(u32 index = 0; index < MAX_NUM_GROUPS; index++)
{
CPedGroup *pedGroup = &ms_groups[index];
if(pedGroup->IsActive() && pedGroup->PopTypeIsRandomNonPermanent())
{
RemoveGroup(index);
}
}
}
void CPedGroups::RemoveAllNonPermanentGroups()
{
for(u32 index = 0; index < MAX_NUM_GROUPS; index++)
{
CPedGroup *pedGroup = &ms_groups[index];
if(pedGroup->IsActive() && pedGroup->PopTypeGet() != POPTYPE_PERMANENT)
{
RemoveGroup(index);
}
}
}
//-------------------------------------------------------------------------
// Creates any needed default groups
//-------------------------------------------------------------------------
void CPedGroups::CreateDefaultGroups()
{
// create a group for each player in a network game
for (u32 i=0; i<MAX_NUM_PHYSICAL_PLAYERS; i++)
{
AddGroup(POPTYPE_PERMANENT);
// set up any existing players with their groups
if (NetworkInterface::IsNetworkOpen())
{
CNetGamePlayer* pPlayer = static_cast<CNetGamePlayer*>(NetworkInterface::GetPlayerMgr().GetPhysicalPlayerFromIndex((PhysicalPlayerIndex) i));
if (pPlayer && pPlayer->GetPlayerPed())
{
pPlayer->GetPlayerPed()->GetPlayerInfo()->SetupPlayerGroup();
}
}
}
if (!NetworkInterface::IsNetworkOpen() && FindPlayerPed())
FindPlayerPed()->GetPlayerInfo()->SetupPlayerGroup();
}
void CPedGroups::Init()
{
s32 i;
for(i=0;i<MAX_NUM_GROUPS;i++)
{
ms_groups[i].SetGroupIndex((u8)i);
RemoveGroupInternal(i, false);
ms_groups[i].m_scriptRefIndex = 1;
}
// create any default groups
CreateDefaultGroups();
}
#define THRESHOLD_NUM_PLAYER_KILLS (8)
void CPedGroups::Process()
{
u32 i;
//********************************************************************************************************
// DEBUG : Make sure that the "m_iPedGroupIndex" member in CPed is up to date for all peds in all groups.
//********************************************************************************************************
#if __ASSERT
for(i=0;i<MAX_NUM_GROUPS;i++)
{
if(ms_groups[i].IsActive())
{
CPedGroup & group=ms_groups[i];
CPedGroupMembership * pMembership = group.GetGroupMembership();
for(s32 m=0; m<CPedGroupMembership::MAX_NUM_MEMBERS; m++)
{
CPed * pMember = pMembership->GetMember(m);
if(pMember)
{
Assertf(pMember->GetPedGroupIndex() == i, "Ped thinks he is part of group %i, but his m_iPedGroupIndex is %i", i, pMember->GetPedGroupIndex());
}
}
}
}
#endif // __ASSERT
//Count the number of members of each active group
//Appoint new leader for groups with no leader.
//Remove groups with no members left.
for(i=0;i<MAX_NUM_GROUPS;i++)
{
if(ms_groups[i].IsActive())
{
CPedGroup& r=ms_groups[i];
r.Process();
//Count the number of members.
//If none are left then remove the group.
//If the group remains then test if the leader
//or membership has changed.
// Never destroy permanent groups
if( ( r.PopTypeGet() != POPTYPE_PERMANENT ) && r.IsLocallyControlled())
{
if (r.GetGroupMembership()->CountMembers() == 0)
{
RemoveGroupInternal(i, true);
}
// groups that change leadership need to be moved to a new slot in MP, this is because the group is identified by the leader's network id across the
// network, so we need to create a new group for the new leader. The previous group is flushed.
if (NetworkInterface::IsGameInProgress() &&
r.IsActive() &&
r.GetGroupMembership()->HasLeader() &&
r.GetGroupMembership()->LeaderHasChanged())
{
if (!MoveGroupToFreeSlot(i))
{
RemoveGroupInternal(i, true);
}
}
}
}
}
ms_bIsPlayerOnAMission=CTheScripts::GetPlayerIsOnAMission();
if(!ms_bIsPlayerOnAMission)
{
ms_iNoOfPlayerKills=0;
}
}
void CPedGroups::ResetAllFormationsModifiedByThisScript(const scrThreadId iThreadId)
{
Assert(iThreadId);
for(s32 i=0;i<MAX_NUM_GROUPS;i++)
{
CPedFormation * pFormation = ms_groups[i].GetFormation();
Assert(pFormation);
if(pFormation->GetScriptModified() == iThreadId)
{
pFormation->SetDefaultSpacing();
pFormation->ResetScriptModified();
ms_groups[i].SetDirty();
}
}
}
void CPedGroups::RegisterKillByPlayer()
{
if(ms_bIsPlayerOnAMission)
{
ms_iNoOfPlayerKills++;
}
}
// need to flush out the tasks which are held as static data before shutdown
void CPedGroups::Shutdown()
{
u32 i;
for(i=0; i<MAX_NUM_GROUPS; i++)
{
ms_groups[i].Flush(false, false);
}
}
bool CPedGroups::IsPlayerGroup(s32 iGroupID)
{
return iGroupID < static_cast<s32>(MAX_NUM_PHYSICAL_PLAYERS);
}
bool CPedGroups::IsGroupLeader(CPed* pPed)
{
s32 i;
for(i=0;i<MAX_NUM_GROUPS;i++)
{
if(ms_groups[i].IsActive())
{
CPedGroup& r=ms_groups[i];
if(r.GetGroupMembership()->IsLeader(pPed))
{
return true;
}
}
}
return false;
}
CPedGroup* CPedGroups::GetPedsGroup(const CPed* pPed)
{
Assert(pPed);
u32 group_index = pPed->GetPedGroupIndex();
if(group_index==PEDGROUP_INDEX_NONE)
return NULL;
Assert(group_index < MAX_NUM_GROUPS);
CPedGroup * pPedGroup = &ms_groups[group_index];
#if __ASSERT
const CPedGroupMembership* const pGroupMembership = pPedGroup->GetGroupMembership();
bool bIsMember = pGroupMembership->IsMember(pPed);
s32 NumberOfMembers = pGroupMembership->CountMembers();
if(pPed)
{
Assertf(bIsMember, "The ped isn't a member of the group he thinks he is part of. Group Index: %i, GroupSize: %i, Ped Model Name - %s", group_index, NumberOfMembers, pPed->GetDebugName());
}
else
{
Assertf(bIsMember, "The ped isn't a member of the group he thinks he is part of. Group Index: %i, GroupSize: %i", group_index, NumberOfMembers);
}
#endif
return pPedGroup;
}
s32 CPedGroups::GetGroupId(CPedGroup* pPedGroup)
{
Assert(pPedGroup);
for (s32 i=0; i<MAX_NUM_GROUPS; i++)
{
if (&ms_groups[i] == pPedGroup)
{
return i;
}
}
return -1;
}
bool CPedGroups::IsInPlayersGroup(const CPed* pPed)
{
// don't say the player is in the player's group!
if(pPed->GetPlayerInfo())
return false;
const CPedGroupMembership* const pGroupMembership = ms_groups[FindPlayerPed()->GetPlayerInfo()->GetPlayerDataPlayerGroup()].GetGroupMembership();
if(pGroupMembership->IsMember(pPed))
return true;
return false;
}
s32 CPedGroups::GetPlayersGroup(const CPed* pPlayer)
{
s32 playerGroup = 0;
// the first bunch of groups correspond to each physical player in MP
if (pPlayer->GetNetworkObject() && AssertVerify(pPlayer->GetNetworkObject()->GetPlayerOwner()))
{
if (AssertVerify(pPlayer->GetNetworkObject()->GetPlayerOwner()->GetPhysicalPlayerIndex() != INVALID_PLAYER_INDEX))
{
playerGroup = pPlayer->GetNetworkObject()->GetPlayerOwner()->GetPhysicalPlayerIndex();
}
}
return playerGroup;
}
bool CPedGroups::AreInSameGroup(const CPed* pPed1, const CPed* pPed2)
{
if( pPed1->GetPedsGroup() && ( pPed1->GetPedsGroup() == pPed2->GetPedsGroup()) )
{
return true;
}
return false;
// if((0==pPed1)||(0==pPed2))
// {
// return false;
// }
//
// s32 i;
// for(i=0;i<MAX_NUM_GROUPS;i++)
// {
// if(ms_groups[i].IsActive())
// {
// CPedGroup& r=ms_groups[i];
// if(r.GetGroupMembership()->IsMember(pPed1) && r.GetGroupMembership()->IsMember(pPed2))
// {
// return true;
// }
// }
// }
// return false;
}