Show EntityManager.cpp syntax highlighted
/**
**************************************************************************************
*Palisma - Secrets of the Illuminati is an open-source 2D RPG *
*Copyright (C) 2006, Tony Sparks *
* *
*This library is free software; you can redistribute it and/or *
*modify it under the terms of the GNU Lesser General Public *
*License as published by the Free Software Foundation; either *
*version 2.1 of the License, or (at your option) any later version. *
* *
*This library is distributed in the hope that it will be useful, *
*but WITHOUT ANY WARRANTY; without even the implied warranty of *
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
*Lesser General Public License for more details. *
* *
*You should have received a copy of the GNU Lesser General Public *
*License along with this library; if not, write to the Free Software *
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
**************************************************************************************
*/
#include "StdAfx.h"
#include "EntityManager.h"
#include "../shared/stringutil.h"
#include "../shared/vector.h"
#include "../shared/QuadTree.h"
#include "../render/viewmanager.h"
#include "../kernel.h"
extern Kernel* g_kernel;
// id counter
unsigned int EntityManager::m_idcounter = 1;
// our singleton
EntityManager* EntityManager::instance = new EntityManager;
/**
====================================
EntityManager
====================================
*/
EntityManager::EntityManager(void)
{
m_player = NULL;
m_playerCreated = false;
// temp
m_scene = NULL;
quadTree = NULL;
}
/**
=====================
Create an entity
=====================
*/
IEntity* EntityManager::CreateEntity( const std::string &entName )
{
// allocate mem for the new Entity
IEntity* ent = new IEntity;
// Set the new ents name
ent->SetName ( entName );
// Register it with the manager
RegisterEntity( ent );
return ent;
}
/**
=====================
Create the player
Only creates one per game instance
=====================
*/
Player* EntityManager::CreatePlayer(const std::string &pName)
{
if ( !m_playerCreated )
{
m_player = new Player;
m_player->SetName( pName );
// the player has the special id of 0
m_player->SetID( 0 );
RegisterEntity( m_player );
// player has been created
m_playerCreated = true;
}
return m_player;
}
/** Create a weapon */
IWeapon* EntityManager::CreateWeapon(const std::string &wName )
{
IWeapon* weapon = new IWeapon;
weapon->SetName( wName );
// Register it with the manager
RegisterEntity( weapon );
return weapon;
}
/**
============================
Get the player
============================
*/
Player* EntityManager::GetPlayer()
{
// return the player
return m_playerCreated ? m_player : NULL;
}
/**
==================================
Update each entity
==================================
*/
void EntityManager::Update(long dt)
{
ICamera* cam = g_kernel->GetRenderer()->GetCamera(0);
CleanDeadEntities();
// current time
float time = g_kernel->GetTime();
type_ents::iterator it = entities.begin();
static bool first = true;
for(; it != entities.end(); it++)
{
IEntity* ent = (*it);
ent->m_flags &= ~EF_NOTONSCREEN; // clear flags for this update
ent->m_flags &= ~EF_UPDATE;
// make sure the entity is on screen
if ( !cam->Contains( ent->m_world.x, ent->m_world.y+32 ) && !first ) { //-8,+32
ent->m_flags |= EF_NOTONSCREEN;
continue;
}
if ( time > ent->m_nextUpdate && m_scene ) // temp scene check!
{
ent->m_flags |= EF_UPDATE;
bool popped = quadTree->Pop( ent );
ent->Update(dt);
Move( ent, dt );
Vector2f v( float(ent->m_world.x - m_scene->GetX()), float(ent->m_world.y - m_scene->GetY()) );
// update the screen coords and collision box
ent->UpdateScreenCoord( v );
// update the QuadTree
if ( popped || first )
{
quadTree->Put( ent );
}
}
}
first = false;
}
/**
===============================
Move each entity -
TODO apply collision detection
===============================
*/
bool EntityManager::Move(IEntity* ent, long deltaTime)
{
// entity is not moving
if ( !ent->m_velocity.Length() )
return false;
float dt = deltaTime/1000.0f;
// apply gravity -- NEEDS SOME WORK
if ( !ent->ArrivedToLocation() && ent->m_heightAboveGround )
{
ent->m_gravity += (9.8f * dt);
ent->m_heightAboveGround -= ent->m_gravity*dt;
if ( ent->m_heightAboveGround <= 0 )
{
ent->m_heightAboveGround = 0;
ent->m_gravity = 0;
}
}
else
{
ent->m_heightAboveGround = 0;
ent->m_gravity = 0;
if ( ent->m_flags & EF_MOVED )
{
ent->m_velocity.ZeroOut();
ent->m_flags &= ~EF_MOVED; // we are done moving
ent->UpdateCollisionBounds( ent->m_world.x, ent->m_world.y ); // dirty hack for throwing things in the portal
return false;
}
}
// get the amount of pixels to move
double xmove = (double)ent->m_velocity.x * (double)ent->m_baseVelocity.x * dt;
double ymove = (double)ent->m_velocity.y * (double)ent->m_baseVelocity.y * dt;
// apply gravity
ymove += ent->m_gravity;
// get the current map
Scene* scene = m_scene; // use m_scene here since this is temporary!!!
// check collisions
MoveX( ent, xmove, ymove, scene );
MoveY( ent, xmove, ymove, scene );
// test to see if we hit a trigger
scene->InvokeTrigger( ent, ent->m_world.x, ent->m_world.y + ent->GetBounds().height/2 );
// move the camera for the player
if ( ent->IsPlayer() )
{
// get the world camera
ICamera* cam = g_kernel->GetRenderer()->GetCamera(0);
// center the camera around the player
scene->CenterCamera( cam, ent->m_world );
}
return true;
}
/** Move X coord of an ent */
void EntityManager::MoveX(IEntity *ent, float xmove, float ymove, Scene* scene)
{
int xToMove = 0;
while( xmove )
{
// move one pixel
// and test collision
if ( xmove > 1 )
{
xToMove = 1.0f;
xmove--;
}
else if ( xmove < -1 )
{
xToMove = -1.0f;
xmove++;
}
else
{
xToMove = xmove;
xmove = 0.0f;
}
// move ent 1 pixel ahead
ent->m_world.x += xToMove;
// check collision
if ( scene->Collide( ent->m_world.x, ent->m_world.y + ent->GetBounds().height/2, ent->GetBounds() ) ||
CheckCollision( ent, ent->m_world.x, ent->m_world.y ) )
{
ent->m_world.x -= xToMove;
ent->UpdateCollisionBounds( ent->m_world.x, ent->m_world.y );
return;
}
}
}
/** Move Y coord of an ent */
void EntityManager::MoveY(IEntity *ent, float xmove, float ymove, Scene* scene)
{
int yToMove = 0;
while( ymove )
{
// move one pixel
// and test collision
if ( ymove > 1 )
{
yToMove = 1.0f;
ymove--;
}
else if ( ymove < -1.0f )
{
yToMove = -1.0f;
ymove++;
}
else
{
// we hit 1 pixel
yToMove = ymove;
ymove = 0;
}
// move ent 1 pixel ahead
ent->m_world.y += yToMove;
// check collision
if ( scene->Collide( ent->m_world.x, ent->m_world.y + ent->GetBounds().height/2, ent->GetBounds() ) ||
CheckCollision( ent, ent->m_world.x , ent->m_world.y ) )
{
ent->m_world.y -= yToMove;
ent->UpdateCollisionBounds( ent->m_world.x, ent->m_world.y );
return;
}
}
}
/** Find Entity by Name */
IEntity* EntityManager::Find( const std::string &name )
{
type_ents::iterator it = entities.begin();
for(; it != entities.end(); it++)
{
// we found the entity
if ( (*it)->GetName() == name )
return (*it);
}
return NULL;
}
/** Find Entity by id */
IEntity* EntityManager::Find( int id )
{
type_ents::iterator it = entities.begin();
for(; it != entities.end(); it++)
{
// we found the entity
if ( (*it)->GetID() == id)
return (*it);
}
return NULL;
}
/** Remove the entity without deleting it */
void EntityManager::RemoveWithoutDeleting(IEntity* ent)
{
type_ents::iterator it = entities.begin();
for(; it != entities.end(); it++)
{
// we found the entity
if ( (*it) == ent) {
quadTree->Pop( ent );
entities.erase( it );
return;
}
}
}
/** Check collision between entities
* O(n) operation - very slow
*/
bool EntityManager::CheckCollision(IEntity* ent, float x, float y)
{
ent->UpdateCollisionBounds( x, y );
typedef std::vector< IEntity* > List;
//typedef std::map<int, IEntity* > List;
List list;
quadTree->GetObjects( ent->GetBounds(), list );
List::iterator it = list.begin();
for (; it != list.end(); ++it )
{
IEntity* other = (*it);
// don't collide with yourself
if ( ent == other ) {
continue;
}
// entity is being erased
if ( other->CanRemove() )
continue;
// if this entity is being carried
// do not col test
if ( other->GetState() == "CARRIED" || other->GetState() == "THROWN" || ent->GetState() == "THROWN" )
continue;
if ( ent->GetBounds().Intersects( other->GetBounds() ) )
{
ent->CollideWith( other );
other->CollideWith( ent );
return true;
}
}
return false;
}
/** Prune the entity List */
void EntityManager::CleanDeadEntities()
{
type_ents::iterator it = entities.begin();
while( it != entities.end() )
{
IEntity* ent = (*it);
// release any free entities
if ( ent->CanRemove() )
{
g_kernel->GetConsole()->Print( ("Trying to Delete: " + ent->GetName() ).c_str() );
g_kernel->GetConsole()->Print( "Before: %d", quadTree->Size() );
if ( !quadTree->Pop( ent ) )
{
g_kernel->GetConsole()->Print( "Could not Pop the entity, continuing!" );
continue;
}
g_kernel->GetConsole()->Print( "After: %d", quadTree->Size() );
// remove its models
ViewManager::GetInstance()->RemoveView( ent->GetID() );
// We only delete the first entity that needs to
// be removed -- is there a better way, without
// creating a linklist?
g_kernel->GetConsole()->Print( "--Deleting from EntityManager:: Releasing %d bytes", sizeof( (*ent) ) );
delete ent;
it = entities.erase( it );
}
else
{
++it;
}
}
}
/** Find the first entity in a given region
* O(n) operation - very slow
*/
IEntity* EntityManager::FindInRegion( Rect ®ion, IEntity* ignoreMe )
{
//type_ents::iterator it = entities.begin();
//for(; it != entities.end(); it++)
//{
// IEntity* other = (*it);
// // ignore this entity (usually the caller entity)
// if ( other == ignoreMe )
// continue;
// // test bounds
// if ( other->GetBounds().Intersects( region ) )
// {
// return other;
// }
//}
//return NULL;
if ( quadTree )
{
typedef std::vector< IEntity* > List;
//typedef std::map<int, IEntity* > List;
List list;
quadTree->GetObjects( region, list );
List::iterator it = list.begin();
for (; it != list.end(); ++it )
{
g_kernel->GetConsole()->Print( ((*it)->GetName()).c_str() );
if ( (*it) == ignoreMe )
continue;
if ( region.Intersects( (*it)->GetBounds() ) )
return (*it);
}
}
return NULL;
}
/** HACK--How to register the Map with entities?
Reference: Currently I register the scene
when we load a new Scene, look at InGameStateView::LoadScene()
with the EntityManager, this is horrible and NEEDS fixing.
*/
void EntityManager::SetScene(Scene* scene)
{
m_scene = scene;
if ( quadTree ) delete quadTree;
int w = 0, h = 0;
m_scene->TileToWorld( m_scene->GetMaxX(), m_scene->GetMaxY(), w, h );
quadTree = new QuadTree<IEntity*>( 16,16, w, h );
}
/** Register the entity */
void EntityManager::RegisterEntity( IEntity* ent ) {
ent->SetID ( m_idcounter++ );
AddEntity( ent );
};
EntityManager::~EntityManager(void)
{
// delete this instance
if ( instance ) {
delete instance;
instance = NULL;
}
// delete each entity
type_ents::iterator it = entities.begin();
for(; it != entities.end(); it++)
{
if ( (*it) )
delete (*it);
}
// delete the player
if ( m_playerCreated )
{
if ( m_player )
delete m_player;
}
if ( quadTree ) delete quadTree;
}
See more files for this project here