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.
614 lines
15 KiB
614 lines
15 KiB
// |
|
// 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 "IGameManager.h" |
|
#include "IGameObject.h" |
|
#include "GlobalParameters.h" |
|
#include "IObjectUtils.h" |
|
#include "ISplashScreen.h" |
|
#include "IBlackboardManager.h" |
|
#include "IBlackboard.h" |
|
#include "BB_Global.h" |
|
|
|
#include "../../Common/AUVec3f.inl" |
|
#include "../../RuntimeObjectSystem/ObjectInterfacePerModule.h" |
|
#include "../../RuntimeCompiler/IFileChangeNotifier.h" |
|
#include "../../Systems/SystemTable.h" |
|
#include "../../RuntimeObjectSystem/IObjectFactorySystem.h" |
|
#include "../../Systems/IEntitySystem.h" |
|
#include "../../Systems/IAssetSystem.h" |
|
#include "../../Systems/ILogSystem.h" |
|
#include "../../RuntimeObjectSystem/ISimpleSerializer.h" |
|
#include "../../Systems/IGame.h" |
|
|
|
#include <assert.h> |
|
#include <vector> |
|
#include <set> |
|
#include <algorithm> |
|
#include <stdio.h> |
|
|
|
class GameManager: public IGameManager |
|
{ |
|
// 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::set<IGameObject*> TGameObjects; |
|
typedef std::vector<ObjectId> TGameObjectIds; |
|
|
|
struct GameObjectSpawnParams |
|
{ |
|
EGameObject type; |
|
AUVec3f spawnPosition; // If infinite, will be positioned randomly |
|
|
|
GameObjectSpawnParams( EGameObject type ) : type(type) |
|
{ |
|
spawnPosition.SetInfinite(); |
|
} |
|
|
|
GameObjectSpawnParams( EGameObject type, const AUVec3f& spawnPosition ) : type(type), spawnPosition(spawnPosition) {} |
|
}; |
|
|
|
public: |
|
GameManager() |
|
: m_CurrentState(EGS_STARTUP) |
|
, m_pSplashScreen(0) |
|
, m_pBBGlobal(0) |
|
{ |
|
m_GameObjects.resize(EGO_COUNT); |
|
m_NextSpawnTimes.resize(EGO_COUNT); |
|
m_NextGameObjectNumber.resize(EGO_COUNT); |
|
} |
|
|
|
virtual ~GameManager() |
|
{ |
|
if( m_pEntity ) |
|
{ |
|
m_pEntity->SetUpdateable(NULL); |
|
} |
|
|
|
if (!IsRuntimeDelete()) |
|
{ |
|
DestroyGameObjects(); |
|
DestroySplashScreen(); |
|
} |
|
} |
|
|
|
|
|
// IEntityObject |
|
|
|
virtual void Serialize(ISimpleSerializer *pSerializer) |
|
{ |
|
IEntityObject::Serialize(pSerializer); |
|
SERIALIZE(m_GlobalParameters); |
|
SERIALIZE(m_NextSpawnTimes); |
|
SERIALIZE(m_NextGameObjectNumber); |
|
SERIALIZE(m_CurrentState); |
|
SERIALIZE(m_GameObjectsToDestroy); |
|
SERIALIZE(m_GameObjectsToSpawn); |
|
SERIALIZEIOBJPTR(m_pSplashScreen); |
|
SERIALIZEIOBJPTR(m_pBBGlobal); |
|
|
|
SerializeObjectsList( pSerializer ); |
|
} |
|
|
|
virtual void Init( bool isFirstInit ) |
|
{ |
|
if (isFirstInit) |
|
{ |
|
PreloadModels(); |
|
|
|
CreateSplashScreen( "//GUI/title.tga", 1.0f, 0.0f, 0.5f, false ); |
|
} |
|
|
|
m_pEntity->SetUpdateable( this ); |
|
} |
|
|
|
// ~IEntityObject |
|
|
|
// IAUUpdateable |
|
|
|
virtual void Update( float deltaTime ) |
|
{ |
|
UpdateSplashScreen(); |
|
|
|
UpdateGameState(); |
|
|
|
if (EGS_PLAYING == m_CurrentState) |
|
{ |
|
UpdateSpawning( deltaTime ); |
|
|
|
UpdateGlobalBlackboard( deltaTime ); |
|
} |
|
|
|
DestroyPendingObjects(); |
|
SpawnPendingObjects(); |
|
} |
|
|
|
// ~IAUUpdateable |
|
|
|
// IGameManager |
|
|
|
virtual void ResetGame() |
|
{ |
|
DestroyGameObjects(); |
|
|
|
m_GameObjectsToDestroy.clear(); |
|
m_GameObjectsToSpawn.clear(); |
|
|
|
m_GlobalParameters = GlobalParameters(); |
|
|
|
IBlackboardManager* pBBManager = (IBlackboardManager*)IObjectUtils::GetUniqueInterface( "BlackboardManager", IID_IBLACKBOARDMANAGER ); |
|
pBBManager->ResetBlackboards(); |
|
m_pBBGlobal = (BB_Global*)pBBManager->GetBlackboardGlobal(); |
|
|
|
if (m_CurrentState != EGS_STARTUP && m_pSplashScreen) |
|
{ |
|
DestroySplashScreen(); |
|
} |
|
|
|
// Set Initial spawn times |
|
for (int i=0; i<EGO_COUNT; ++i) |
|
{ |
|
m_NextSpawnTimes[i] = m_GlobalParameters.go[i].spawn_rate; |
|
} |
|
|
|
// Notify any listeners that game was reset |
|
for (size_t i=0; i<m_Listeners.size(); ++i) |
|
{ |
|
m_Listeners[i]->OnGameReset(); |
|
} |
|
|
|
SetGameState(EGS_NEWGAME); |
|
} |
|
|
|
virtual void SpawnGameObject( EGameObject type ) |
|
{ |
|
m_GameObjectsToSpawn.push_back( GameObjectSpawnParams(type) ); |
|
} |
|
|
|
virtual void SpawnGameObject( EGameObject type, const AUVec3f& spawnPosition ) |
|
{ |
|
m_GameObjectsToSpawn.push_back( GameObjectSpawnParams(type, spawnPosition) ); |
|
} |
|
|
|
virtual void DestroyGameObject( ObjectId id ) |
|
{ |
|
m_GameObjectsToDestroy.push_back( id ); |
|
} |
|
|
|
virtual void DestroyGameObject( const char* name ) |
|
{ |
|
IEntitySystem* pEntitySystem = PerModuleInterface::g_pSystemTable->pEntitySystem; |
|
IAUEntity* pEnt = pEntitySystem->Get(name); |
|
if (pEnt) |
|
{ |
|
DestroyGameObject( pEnt->GetObject()->GetObjectId() ); |
|
} |
|
} |
|
|
|
virtual GlobalParameters* GetGlobalParameters() |
|
{ |
|
return &m_GlobalParameters; |
|
} |
|
|
|
virtual EGameState GetGameState() const |
|
{ |
|
return m_CurrentState; |
|
} |
|
|
|
virtual void GetAll( EGameObject type, IAUDynArray<ObjectId> &objects ) const |
|
{ |
|
AU_ASSERT(type < EGO_COUNT); |
|
if (type < EGO_COUNT) |
|
{ |
|
size_t count = m_GameObjects[type].size(); |
|
|
|
objects.Resize(count); |
|
TGameObjects::const_iterator it = m_GameObjects[type].begin(); |
|
TGameObjects::const_iterator itEnd = m_GameObjects[type].end(); |
|
for(int i = 0; it != itEnd; ++it, ++i) |
|
{ |
|
objects[i] = (*it)->GetObjectId(); |
|
} |
|
} |
|
} |
|
|
|
void AddListener(IGameEventListener* pListener) |
|
{ |
|
if ( std::find(m_Listeners.begin(), m_Listeners.end(), pListener) == m_Listeners.end() ) |
|
{ |
|
m_Listeners.push_back(pListener); |
|
} |
|
} |
|
|
|
void RemoveListener(IGameEventListener* pListener) |
|
{ |
|
TGameEventListeners::iterator it = std::find(m_Listeners.begin(), m_Listeners.end(), pListener); |
|
if (it != m_Listeners.end()) |
|
{ |
|
m_Listeners.erase(it); |
|
} |
|
} |
|
|
|
// ~IGameManager |
|
|
|
|
|
private: |
|
|
|
void CreateInitialGameObjects() |
|
{ |
|
for (int i=0; i<(int)EGO_COUNT; ++i) |
|
{ |
|
int count = m_GlobalParameters.go[(EGameObject)i].initial_count; |
|
for (int j=0; j<count; ++j) |
|
{ |
|
DoSpawnGameObject( GameObjectSpawnParams( (EGameObject)i ) ); |
|
} |
|
} |
|
} |
|
|
|
AUVec3f GetSpawnPosition( EGameObject type ) |
|
{ |
|
float width, height; |
|
PerModuleInterface::g_pSystemTable->pGame->GetWindowSize( width, height ); |
|
float edgeMargin = 0.05f; |
|
|
|
// Spawn WBC in bottom quarter of screen, RBC in middle quarter, and Virus and Infected Cell |
|
// in the top quarter |
|
float min = height * edgeMargin; // from bottom of screen |
|
float max = height * (1 - edgeMargin); // from bottom of screen |
|
if (type == EGO_WBC) |
|
{ |
|
max = height * 0.25f; |
|
} |
|
else if (type == EGO_RBC) |
|
{ |
|
min = height * 0.375f; |
|
max = height * 0.625f; |
|
} |
|
else |
|
{ |
|
min = height * 0.75f; |
|
} |
|
|
|
AUVec3f position; |
|
position.SetX( rand() * 1.0f / RAND_MAX * width * (1.0f - edgeMargin * 2) - width * (0.5f - edgeMargin) ); |
|
position.SetY( 0.0f ); |
|
position.SetZ( rand() * 1.0f / RAND_MAX * (max - min) + min - height * 0.5f); |
|
|
|
return position; |
|
} |
|
|
|
void UpdateSpawning( float deltaTime ) |
|
{ |
|
for (int i=0; i<EGO_COUNT; ++i) |
|
{ |
|
m_NextSpawnTimes[i] -= deltaTime; |
|
if (m_GlobalParameters.go[i].spawn_rate > 0.0f && m_NextSpawnTimes[i] < 0.0f) |
|
{ |
|
m_NextSpawnTimes[i] += m_GlobalParameters.go[i].spawn_rate; |
|
if (m_GameObjects[i].size() < (size_t)m_GlobalParameters.go[i].max_count) |
|
{ |
|
DoSpawnGameObject( GameObjectSpawnParams( (EGameObject)i ) ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
void SpawnPendingObjects() |
|
{ |
|
TSpawnParams::iterator it = m_GameObjectsToSpawn.begin(); |
|
TSpawnParams::iterator itEnd = m_GameObjectsToSpawn.end(); |
|
while (it != itEnd) |
|
{ |
|
DoSpawnGameObject( *it ); |
|
++it; |
|
} |
|
m_GameObjectsToSpawn.clear(); |
|
} |
|
|
|
void DestroyPendingObjects() |
|
{ |
|
TGameObjectIds::iterator it = m_GameObjectsToDestroy.begin(); |
|
TGameObjectIds::iterator itEnd = m_GameObjectsToDestroy.end(); |
|
while (it != itEnd) |
|
{ |
|
DoDestroyGameObject( *it ); |
|
++it; |
|
} |
|
m_GameObjectsToDestroy.clear(); |
|
} |
|
|
|
IGameObject* DoSpawnGameObject( const GameObjectSpawnParams& params ) |
|
{ |
|
IGameObject* pGameObject = 0; |
|
EGameObject type = params.type; |
|
|
|
if (type < EGO_COUNT) |
|
{ |
|
std::string name = m_GlobalParameters.go[type].base_name; |
|
char buff[16]; |
|
_snprintf_s(buff, sizeof(buff), _TRUNCATE, "%d",++(m_NextGameObjectNumber[type])); |
|
name += buff; |
|
|
|
IObject* pObj = IObjectUtils::CreateObjectAndEntity( "GameObject", name.c_str() ); |
|
IObjectUtils::GetObject( &pGameObject, pObj->GetObjectId() ); |
|
|
|
AUVec3f pos = (params.spawnPosition.IsInfinite()) ? GetSpawnPosition(type) : params.spawnPosition; |
|
pGameObject->Init(type, pos); |
|
m_GameObjects[type].insert(pGameObject); |
|
} |
|
|
|
// Notify any listeners that object was created |
|
for (size_t i=0; i<m_Listeners.size(); ++i) |
|
{ |
|
m_Listeners[i]->OnGameObjectCreated(pGameObject); |
|
} |
|
|
|
return pGameObject; |
|
} |
|
|
|
void DoDestroyGameObject( ObjectId id ) |
|
{ |
|
IObjectFactorySystem* pFactory = PerModuleInterface::g_pSystemTable->pObjectFactorySystem; |
|
IObject* pObj = pFactory->GetObject(id); |
|
if (pObj) |
|
{ |
|
IGameObject* pGameObject = 0; |
|
IObjectUtils::GetObject( &pGameObject, pObj->GetObjectId() ); |
|
|
|
// Notify any listeners that object is about to be destroyed |
|
for (size_t i=0; i<m_Listeners.size(); ++i) |
|
{ |
|
m_Listeners[i]->OnGameObjectAboutToDestroy(pGameObject); |
|
} |
|
|
|
m_GameObjects[pGameObject->GetGameObjectType()].erase(pGameObject); |
|
IObjectUtils::DestroyObjectAndEntity( pGameObject->GetEntityId() ); |
|
} |
|
} |
|
|
|
void UpdateGameState() |
|
{ |
|
switch (m_CurrentState) |
|
{ |
|
case EGS_STARTUP: |
|
// Created a few dummy RBCs on initial startup to set the scene until a new game is started |
|
if (!m_pSplashScreen && m_GameObjects[EGO_RBC].size() == 0) |
|
{ |
|
for (int j=0; j<8; ++j) |
|
{ |
|
DoSpawnGameObject( GameObjectSpawnParams( EGO_RBC ) ); |
|
} |
|
} |
|
|
|
break; |
|
case EGS_NEWGAME: |
|
if (!m_pSplashScreen) |
|
{ |
|
CreateInitialGameObjects(); |
|
|
|
SetGameState(EGS_PLAYING); |
|
} |
|
|
|
break; |
|
case EGS_PLAYING: |
|
if (m_GameObjects[EGO_WBC].size() == 0) |
|
{ |
|
SetGameState(EGS_INFECTIONWON); |
|
CreateSplashScreen( "//GUI/infectionwin.tga", 1.0f, 0.5f, 0.0f, false ); |
|
} |
|
else if (m_GameObjects[EGO_VIRUS].size() == 0 && m_GameObjects[EGO_INFECTED].size() == 0) |
|
{ |
|
SetGameState(EGS_IMMUNEWON); |
|
CreateSplashScreen( "//GUI/immunewin.tga", 1.0f, 0.5f, 0.0f, false ); |
|
} |
|
|
|
break; |
|
case EGS_IMMUNEWON: |
|
case EGS_INFECTIONWON: |
|
if (!m_pSplashScreen) |
|
{ |
|
ResetGame(); |
|
} |
|
|
|
break; |
|
} |
|
} |
|
|
|
void CreateSplashScreen( const char* file, float fMinTime, float fFadeInTime, float fFadeOutTime, bool bAutoClose ) |
|
{ |
|
DestroySplashScreen(); |
|
|
|
IObject* pObj = IObjectUtils::CreateObjectAndEntity( "SplashScreen", "SplashScreen" ); |
|
IObjectUtils::GetObject( &m_pSplashScreen, pObj->GetObjectId() ); |
|
m_pSplashScreen->SetImage(file); |
|
m_pSplashScreen->SetMinViewTime(fMinTime); |
|
m_pSplashScreen->SetFadeInTime(fFadeInTime); |
|
m_pSplashScreen->SetFadeOutTime(fFadeOutTime); |
|
m_pSplashScreen->SetAutoClose(bAutoClose); |
|
} |
|
|
|
void UpdateSplashScreen() |
|
{ |
|
if (m_pSplashScreen && m_pSplashScreen->ReadyToClose()) |
|
{ |
|
DestroySplashScreen(); |
|
} |
|
} |
|
|
|
void DestroyGameObjects() |
|
{ |
|
for (size_t i=0; i<m_GameObjects.size(); ++i) |
|
{ |
|
TGameObjects& objects = m_GameObjects[i]; |
|
TGameObjects::iterator it = objects.begin(); |
|
TGameObjects::iterator itEnd = objects.end(); |
|
while (it != itEnd) |
|
{ |
|
IGameObject* pGameObject = *it; |
|
if (pGameObject) |
|
{ |
|
IObjectUtils::DestroyObjectAndEntity( pGameObject->GetEntityId() ); |
|
} |
|
|
|
++it; |
|
} |
|
objects.clear(); |
|
} |
|
} |
|
|
|
void DestroySplashScreen() |
|
{ |
|
if (m_pSplashScreen) |
|
{ |
|
IObjectUtils::DestroyObjectAndEntity( m_pSplashScreen->GetEntityId() ); |
|
m_pSplashScreen = 0; |
|
} |
|
} |
|
|
|
void PreloadModels() |
|
{ |
|
IAssetSystem* pAssetSystem = PerModuleInterface::g_pSystemTable->pAssetSystem; |
|
|
|
for (int i=0; i<EGO_COUNT; ++i) |
|
{ |
|
std::string path = "/Models/"; //directories relative to asset dir |
|
path += m_GlobalParameters.go[i].model; |
|
IAURenderableMesh* pMesh = pAssetSystem->CreateRenderableMeshFromFile( path.c_str() ); |
|
if (pMesh) |
|
{ |
|
pAssetSystem->DestroyRenderableMesh(pMesh); |
|
} |
|
} |
|
} |
|
|
|
void SetGameState( EGameState state ) |
|
{ |
|
if (state != m_CurrentState) |
|
{ |
|
m_CurrentState = state; |
|
|
|
// Notify any listeners that state has changed |
|
for (size_t i=0; i<m_Listeners.size(); ++i) |
|
{ |
|
m_Listeners[i]->OnStateChange(state); |
|
} |
|
} |
|
} |
|
|
|
void UpdateGlobalBlackboard( float deltaTime ) |
|
{ |
|
m_pBBGlobal->gameTimeElapsed += deltaTime; |
|
|
|
// Calculate Immune team strength (we don't count RBCs here, since they don't actively fight) |
|
m_pBBGlobal->immune_team_strength = GetStrengthSum( m_GameObjects[EGO_WBC] ); |
|
|
|
// Calculate Infection team strength |
|
m_pBBGlobal->infection_team_strength = GetStrengthSum( m_GameObjects[EGO_VIRUS] ); |
|
m_pBBGlobal->infection_team_strength += GetStrengthSum( m_GameObjects[EGO_INFECTED] ); |
|
} |
|
|
|
float GetStrengthSum( const TGameObjects& objects ) const |
|
{ |
|
float sum = 0; |
|
TGameObjects::const_iterator it = objects.begin(); |
|
TGameObjects::const_iterator itEnd = objects.end(); |
|
while (it != itEnd) |
|
{ |
|
IGameObject* pGameObject = *it; |
|
sum += pGameObject->GetThreatRating(); |
|
|
|
++it; |
|
} |
|
|
|
return sum; |
|
} |
|
|
|
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_GameObjects[i].size(); |
|
m_ObjectIds[i].reserve( count ); |
|
m_ObjectIds[i].clear(); |
|
|
|
TGameObjects::iterator it = m_GameObjects[i].begin(); |
|
TGameObjects::iterator itEnd = m_GameObjects[i].end(); |
|
while (it != itEnd) |
|
{ |
|
m_ObjectIds[i].push_back( (*it)->GetObjectId() ); |
|
++it; |
|
} |
|
} |
|
} |
|
|
|
SERIALIZE(m_ObjectIds); |
|
|
|
if ( pSerializer->IsLoading() ) |
|
{ |
|
// Rebuild m_objects pointer collection |
|
|
|
for (int i=0; i<EGO_COUNT; ++i) |
|
{ |
|
m_GameObjects[i].clear(); |
|
size_t count = m_ObjectIds[i].size(); |
|
|
|
for (size_t j=0; j<count; ++j) |
|
{ |
|
IGameObject* pGameObject = 0; |
|
IObjectUtils::GetObject( &pGameObject, m_ObjectIds[i][j] ); |
|
|
|
m_GameObjects[i].insert( pGameObject ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
// Private Members |
|
|
|
typedef std::vector<unsigned int> TGameObjectNumbers; |
|
typedef std::vector<IGameEventListener*> TGameEventListeners; |
|
typedef std::vector<GameObjectSpawnParams> TSpawnParams; |
|
|
|
TGameEventListeners m_Listeners; |
|
GlobalParameters m_GlobalParameters; |
|
TGameObjectNumbers m_NextGameObjectNumber; |
|
EGameState m_CurrentState; |
|
|
|
std::vector<TGameObjects> m_GameObjects; |
|
std::vector<float> m_NextSpawnTimes; |
|
|
|
TGameObjectIds m_GameObjectsToDestroy; |
|
TSpawnParams m_GameObjectsToSpawn; |
|
|
|
ISplashScreen* m_pSplashScreen; |
|
BB_Global* m_pBBGlobal; |
|
}; |
|
|
|
REGISTERCLASS(GameManager); |
|
|
|
|
|
|
|
|
|
|