You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

339 lines
8.5 KiB

2 months ago
//
// Copyright (c) 2010-2011 Matthew Jack and Doug Binks
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "IPerceptionManager.h"
#include "IObjectUtils.h"
#include "IGameObject.h"
#include "GlobalParameters.h"
#include "../../RuntimeObjectSystem/ObjectInterfacePerModule.h"
#include "../../Systems/SystemTable.h"
#include "../../Systems/IEntitySystem.h"
#include "../../Systems/ILogSystem.h"
#include "../../RuntimeObjectSystem/ISimpleSerializer.h"
#include <assert.h>
#include <set>
#include <vector>
#include <algorithm>
const AUVec3f ZERO(0,0,0);
class PerceptionManager: public IPerceptionManager
{
// We have two sets of typedefs here, one for fast access during runtime, and another
// that is used for safe storage during serialization
typedef std::vector<IGameObject*> TGameObjects;
typedef std::vector<ObjectId> TGameObjectIds;
public:
PerceptionManager()
: m_bDoUpdate(false)
, m_pGlobalParameters(0)
{
m_Objects.resize(EGO_COUNT);
}
virtual ~PerceptionManager()
{
((IGameManager*)IObjectUtils::GetUniqueInterface( "GameManager", IID_IGAMEMANAGER ))->RemoveListener(this);
}
// IObject
virtual void Serialize( ISimpleSerializer *pSerializer )
{
AU_ASSERT(pSerializer);
SERIALIZE(m_bDoUpdate);
SerializeObjectsList( pSerializer );
}
virtual void Init( bool isFirstInit )
{
IGameManager* pGameManager = (IGameManager*)IObjectUtils::GetUniqueInterface( "GameManager", IID_IGAMEMANAGER );
pGameManager->AddListener(this);
m_pGlobalParameters = pGameManager->GetGlobalParameters();
}
// ~IObject
// IGameEventListener
virtual void OnGameReset()
{
for (int i=0; i<EGO_COUNT; ++i)
{
m_Objects[i].clear();
}
}
virtual void OnStateChange( EGameState newState )
{
m_bDoUpdate = (newState == EGS_PLAYING);
}
virtual void OnGameObjectCreated( IGameObject* pGameObject )
{
m_Objects[pGameObject->GetGameObjectType()].push_back( pGameObject );
}
virtual void OnGameObjectAboutToDestroy( IGameObject* pGameObject )
{
TGameObjects& data = m_Objects[pGameObject->GetGameObjectType()];
TGameObjects::iterator it = std::find(data.begin(), data.end(), pGameObject);
if (it != data.end())
{
data.erase(it);
}
}
// ~IGameEventListener
// IPerceptionManager
virtual int GetNumberPerceived( const IGameObject* pPerceiver, EGameObject perceivedType ) const
{
AUVec3f center = pPerceiver->GetEntity()->GetPosition();
float radius = m_pGlobalParameters->go[pPerceiver->GetGameObjectType()].perceptionDist[perceivedType];
m_workingData.clear();
// Fill working array with results
DoGetPerceived( m_Objects[perceivedType], pPerceiver, center, radius );
return (int)m_workingData.size();
}
virtual int GetNumberPerceived( const IGameObject* pPerceiver ) const
{
int num = 0;
for (int i=0; i<EGO_COUNT; ++i)
{
num += GetNumberPerceived( pPerceiver, (EGameObject)i );
}
return num;
}
virtual void GetPerceived( const IGameObject* pPerceiver, EGameObject perceivedType, IAUDynArray<ObjectId>& objects ) const
{
objects.Clear();
DoAddPerceived( pPerceiver, perceivedType, objects );
}
virtual void AddPerceived( const IGameObject* pPerceiver, EGameObject perceivedType, IAUDynArray<ObjectId>& objects ) const
{
DoAddPerceived( pPerceiver, perceivedType, objects );
}
virtual void GetPerceived( const IGameObject* pPerceiver, IAUDynArray<ObjectId>& objects ) const
{
objects.Clear();
AUVec3f center = pPerceiver->GetEntity()->GetPosition();
m_workingData.clear();
// Fill working array with results
for (int i=0; i<EGO_COUNT; ++i)
{
float radius = m_pGlobalParameters->go[pPerceiver->GetGameObjectType()].perceptionDist[i];
DoGetPerceived( m_Objects[i], pPerceiver, center, radius );
}
DoAddToArray(objects);
}
virtual AUVec3f GetGlobalAveragePos( EGameObject perceivedType ) const
{
AUVec3f avg = ZERO;
const TGameObjects& data = m_Objects[perceivedType];
size_t count = data.size();
if (count > 0)
{
for (size_t i=0; i<count; ++i)
{
avg += data[i]->GetEntity()->GetPosition();
}
avg /= (float)count;
}
return avg;
}
virtual AUVec3f GetGlobalAveragePos() const
{
AUVec3f avg = ZERO;
for (int i=0; i<EGO_COUNT; ++i)
{
avg += GetGlobalAveragePos( (EGameObject)i );
}
avg /= EGO_COUNT;
return avg;
}
virtual AUVec3f GetPerceivedAveragePos( const IGameObject* pPerceiver, EGameObject perceivedType ) const
{
AUVec3f center = pPerceiver->GetEntity()->GetPosition();
float radius = m_pGlobalParameters->go[pPerceiver->GetGameObjectType()].perceptionDist[perceivedType];
m_workingData.clear();
// Fill working array with results
DoGetPerceived( m_Objects[perceivedType], pPerceiver, center, radius );
// Calculate average position
AUVec3f avg = ZERO;
size_t count = m_workingData.size();
if (count > 0)
{
for (size_t i=0; i<count; ++i)
{
avg += m_workingData[i]->GetEntity()->GetPosition();
}
avg /= (float)count;
}
return avg;
}
virtual AUVec3f GetPerceivedAveragePos( const IGameObject* pPerceiver ) const
{
AUVec3f avg = ZERO;
for (int i=0; i<EGO_COUNT; ++i)
{
avg += GetPerceivedAveragePos( pPerceiver, (EGameObject)i );
}
avg /= EGO_COUNT;
return avg;
}
// ~IPerceptionManager
private:
void DoAddPerceived( const IGameObject* pPerceiver, EGameObject perceivedType, IAUDynArray<ObjectId>& objects ) const
{
AUVec3f center = pPerceiver->GetEntity()->GetPosition();
float radius = m_pGlobalParameters->go[pPerceiver->GetGameObjectType()].perceptionDist[perceivedType];
m_workingData.clear();
// Fill working array with results
DoGetPerceived( m_Objects[perceivedType], pPerceiver, center, radius );
DoAddToArray(objects);
}
// Pushes results into m_workingData
void DoGetPerceived( const TGameObjects& data, const IGameObject* pPerceiver, const AUVec3f& center, float radius ) const
{
size_t count = data.size();
for (size_t j=0; j<count; ++j)
{
IGameObject* pObj = data[j];
float dist = (pObj->GetEntity()->GetPosition() - center).Magnitude();
if ( pObj->GetEntityId() != pPerceiver->GetEntityId() && dist < radius )
{
m_workingData.push_back( pObj );
}
}
}
// Fills with current contents of m_workingData
void DoAddToArray( IAUDynArray<ObjectId>& objects ) const
{
size_t count = m_workingData.size();
size_t origCount = objects.Size();
objects.Resize(origCount + count);
for (size_t i=0; i < count; ++i)
{
objects[i+origCount] = m_workingData[i]->GetObjectId();
}
}
void SerializeObjectsList( ISimpleSerializer *pSerializer )
{
std::vector<TGameObjectIds> m_ObjectIds;
if ( !pSerializer->IsLoading() )
{
// Create a collection of ObjectIds that matches m_Objects pointer collection
m_ObjectIds.resize(EGO_COUNT);
for (int i=0; i<EGO_COUNT; ++i)
{
size_t count = m_Objects[i].size();
m_ObjectIds[i].resize( count );
for (size_t j=0; j<count; ++j)
{
m_ObjectIds[i][j] = m_Objects[i][j]->GetObjectId();
}
}
}
SERIALIZE(m_ObjectIds);
if ( pSerializer->IsLoading() )
{
// Rebuild m_objects pointer collection
for (int i=0; i<EGO_COUNT; ++i)
{
size_t count = m_ObjectIds[i].size();
m_Objects[i].clear();
m_Objects[i].resize( count );
for (size_t j=0; j<count; ++j)
{
IGameObject* pGameObject = 0;
IObjectUtils::GetObject( &pGameObject, m_ObjectIds[i][j] );
m_Objects[i][j] = pGameObject;
}
}
}
}
// Private Members
std::vector<TGameObjects> m_Objects; // Keeps its own set of valid GameObjects for more efficient calculations
bool m_bDoUpdate;
GlobalParameters* m_pGlobalParameters;
mutable TGameObjects m_workingData; // Vector to be used when making calculations to avoid constant allocations
};
REGISTERCLASS(PerceptionManager);